mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 13:30:45 +00:00
Merge pull request #14210 from Vexu/compile-errors
improve struct/union field error locations
This commit is contained in:
commit
a01d2ab0e3
@ -2013,7 +2013,7 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
|
||||
},
|
||||
.local_val => scope = scope.cast(Scope.LocalVal).?.parent,
|
||||
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
|
||||
.namespace => break,
|
||||
.namespace, .enum_namespace => break,
|
||||
.defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
|
||||
.top => unreachable,
|
||||
}
|
||||
@ -2088,7 +2088,7 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
|
||||
try parent_gz.addDefer(defer_scope.index, defer_scope.len);
|
||||
},
|
||||
.defer_error => scope = scope.cast(Scope.Defer).?.parent,
|
||||
.namespace => break,
|
||||
.namespace, .enum_namespace => break,
|
||||
.top => unreachable,
|
||||
}
|
||||
}
|
||||
@ -2179,7 +2179,7 @@ fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.Toke
|
||||
.local_val => scope = scope.cast(Scope.LocalVal).?.parent,
|
||||
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
|
||||
.defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
|
||||
.namespace => break,
|
||||
.namespace, .enum_namespace => break,
|
||||
.top => unreachable,
|
||||
}
|
||||
}
|
||||
@ -2729,7 +2729,7 @@ fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
|
||||
const have_err_payload = defer_scope.remapped_err_code != 0;
|
||||
need_err_code = need_err_code or have_err_payload;
|
||||
},
|
||||
.namespace => unreachable,
|
||||
.namespace, .enum_namespace => unreachable,
|
||||
.top => unreachable,
|
||||
}
|
||||
}
|
||||
@ -2799,7 +2799,7 @@ fn genDefers(
|
||||
.normal_only => continue,
|
||||
}
|
||||
},
|
||||
.namespace => unreachable,
|
||||
.namespace, .enum_namespace => unreachable,
|
||||
.top => unreachable,
|
||||
}
|
||||
}
|
||||
@ -2835,7 +2835,7 @@ fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!v
|
||||
scope = s.parent;
|
||||
},
|
||||
.defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
|
||||
.namespace => unreachable,
|
||||
.namespace, .enum_namespace => unreachable,
|
||||
.top => unreachable,
|
||||
}
|
||||
}
|
||||
@ -4278,7 +4278,7 @@ fn testDecl(
|
||||
.local_val, .local_ptr => unreachable, // a test cannot be in a local scope
|
||||
.gen_zir => s = s.cast(GenZir).?.parent,
|
||||
.defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
|
||||
.namespace => {
|
||||
.namespace, .enum_namespace => {
|
||||
const ns = s.cast(Scope.Namespace).?;
|
||||
if (ns.decls.get(name_str_index)) |i| {
|
||||
if (found_already) |f| {
|
||||
@ -4963,6 +4963,7 @@ fn containerDecl(
|
||||
defer block_scope.unstack();
|
||||
|
||||
_ = try astgen.scanDecls(&namespace, container_decl.ast.members);
|
||||
namespace.base.tag = .enum_namespace;
|
||||
|
||||
const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0)
|
||||
try comptimeExpr(&block_scope, &namespace.base, .{ .rl = .{ .ty = .type_type } }, container_decl.ast.arg)
|
||||
@ -4977,6 +4978,7 @@ fn containerDecl(
|
||||
for (container_decl.ast.members) |member_node| {
|
||||
if (member_node == counts.nonexhaustive_node)
|
||||
continue;
|
||||
namespace.base.tag = .namespace;
|
||||
var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
|
||||
.decl => continue,
|
||||
.field => |field| field,
|
||||
@ -5010,6 +5012,7 @@ fn containerDecl(
|
||||
},
|
||||
);
|
||||
}
|
||||
namespace.base.tag = .enum_namespace;
|
||||
const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr);
|
||||
wip_members.appendToField(@enumToInt(tag_value_inst));
|
||||
}
|
||||
@ -7296,7 +7299,7 @@ fn localVarRef(
|
||||
},
|
||||
.gen_zir => s = s.cast(GenZir).?.parent,
|
||||
.defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
|
||||
.namespace => {
|
||||
.namespace, .enum_namespace => {
|
||||
const ns = s.cast(Scope.Namespace).?;
|
||||
if (ns.decls.get(name_str_index)) |i| {
|
||||
if (found_already) |f| {
|
||||
@ -7308,7 +7311,7 @@ fn localVarRef(
|
||||
// We found a match but must continue looking for ambiguous references to decls.
|
||||
found_already = i;
|
||||
}
|
||||
num_namespaces_out += 1;
|
||||
if (s.tag == .namespace) num_namespaces_out += 1;
|
||||
capturing_namespace = ns;
|
||||
s = ns.parent;
|
||||
},
|
||||
@ -7935,7 +7938,7 @@ fn builtinCall(
|
||||
},
|
||||
.gen_zir => s = s.cast(GenZir).?.parent,
|
||||
.defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
|
||||
.namespace => {
|
||||
.namespace, .enum_namespace => {
|
||||
const ns = s.cast(Scope.Namespace).?;
|
||||
if (ns.decls.get(decl_name)) |i| {
|
||||
if (found_already) |f| {
|
||||
@ -10547,6 +10550,12 @@ const Scope = struct {
|
||||
else => return null,
|
||||
}
|
||||
}
|
||||
if (T == Namespace) {
|
||||
switch (base.tag) {
|
||||
.namespace, .enum_namespace => return @fieldParentPtr(T, "base", base),
|
||||
else => return null,
|
||||
}
|
||||
}
|
||||
if (base.tag != T.base_tag)
|
||||
return null;
|
||||
|
||||
@ -10559,7 +10568,7 @@ const Scope = struct {
|
||||
.local_val => base.cast(LocalVal).?.parent,
|
||||
.local_ptr => base.cast(LocalPtr).?.parent,
|
||||
.defer_normal, .defer_error => base.cast(Defer).?.parent,
|
||||
.namespace => base.cast(Namespace).?.parent,
|
||||
.namespace, .enum_namespace => base.cast(Namespace).?.parent,
|
||||
.top => null,
|
||||
};
|
||||
}
|
||||
@ -10571,6 +10580,7 @@ const Scope = struct {
|
||||
defer_normal,
|
||||
defer_error,
|
||||
namespace,
|
||||
enum_namespace,
|
||||
top,
|
||||
};
|
||||
|
||||
@ -12247,7 +12257,7 @@ fn detectLocalShadowing(
|
||||
}
|
||||
s = local_ptr.parent;
|
||||
},
|
||||
.namespace => {
|
||||
.namespace, .enum_namespace => {
|
||||
outer_scope = true;
|
||||
const ns = s.cast(Scope.Namespace).?;
|
||||
const decl_node = ns.decls.get(ident_name) orelse {
|
||||
@ -12415,7 +12425,7 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.
|
||||
}
|
||||
s = local_ptr.parent;
|
||||
},
|
||||
.namespace => s = s.cast(Scope.Namespace).?.parent,
|
||||
.namespace, .enum_namespace => s = s.cast(Scope.Namespace).?.parent,
|
||||
.gen_zir => s = s.cast(GenZir).?.parent,
|
||||
.defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
|
||||
.top => break,
|
||||
|
||||
@ -1194,6 +1194,49 @@ pub const EnumFull = struct {
|
||||
.lazy = LazySrcLoc.nodeOffset(0),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn fieldSrcLoc(e: EnumFull, mod: *Module, query: FieldSrcQuery) SrcLoc {
|
||||
@setCold(true);
|
||||
const owner_decl = mod.declPtr(e.owner_decl);
|
||||
const file = owner_decl.getFileScope();
|
||||
const tree = file.getTree(mod.gpa) catch |err| {
|
||||
// In this case we emit a warning + a less precise source location.
|
||||
log.warn("unable to load {s}: {s}", .{
|
||||
file.sub_file_path, @errorName(err),
|
||||
});
|
||||
return e.srcLoc(mod);
|
||||
};
|
||||
const node = owner_decl.relativeToNodeIndex(0);
|
||||
const node_tags = tree.nodes.items(.tag);
|
||||
switch (node_tags[node]) {
|
||||
.container_decl,
|
||||
.container_decl_trailing,
|
||||
=> return queryFieldSrc(tree.*, query, file, tree.containerDecl(node)),
|
||||
.container_decl_two, .container_decl_two_trailing => {
|
||||
var buffer: [2]Ast.Node.Index = undefined;
|
||||
return queryFieldSrc(tree.*, query, file, tree.containerDeclTwo(&buffer, node));
|
||||
},
|
||||
.container_decl_arg,
|
||||
.container_decl_arg_trailing,
|
||||
=> return queryFieldSrc(tree.*, query, file, tree.containerDeclArg(node)),
|
||||
|
||||
.tagged_union,
|
||||
.tagged_union_trailing,
|
||||
=> return queryFieldSrc(tree.*, query, file, tree.taggedUnion(node)),
|
||||
.tagged_union_two, .tagged_union_two_trailing => {
|
||||
var buffer: [2]Ast.Node.Index = undefined;
|
||||
return queryFieldSrc(tree.*, query, file, tree.taggedUnionTwo(&buffer, node));
|
||||
},
|
||||
.tagged_union_enum_tag,
|
||||
.tagged_union_enum_tag_trailing,
|
||||
=> return queryFieldSrc(tree.*, query, file, tree.taggedUnionEnumTag(node)),
|
||||
|
||||
.root => return queryFieldSrc(tree.*, query, file, tree.containerDeclRoot()),
|
||||
|
||||
// This struct was generated using @Type
|
||||
else => return e.srcLoc(mod),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Union = struct {
|
||||
@ -3519,14 +3562,6 @@ pub fn deinit(mod: *Module) void {
|
||||
pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
|
||||
const gpa = mod.gpa;
|
||||
{
|
||||
if (mod.failed_decls.contains(decl_index)) {
|
||||
blk: {
|
||||
const errs = mod.comp.getAllErrorsAlloc() catch break :blk;
|
||||
for (errs.list) |err| Compilation.AllErrors.Message.renderToStdErr(err, .no_color);
|
||||
}
|
||||
// TODO restore test case triggering this panic
|
||||
@panic("Zig compiler bug: attempted to destroy declaration with an attached error");
|
||||
}
|
||||
const decl = mod.declPtr(decl_index);
|
||||
log.debug("destroy {*} ({s})", .{ decl, decl.name });
|
||||
_ = mod.test_functions.swapRemove(decl_index);
|
||||
@ -6179,7 +6214,7 @@ pub const PeerTypeCandidateSrc = union(enum) {
|
||||
|
||||
const FieldSrcQuery = struct {
|
||||
index: usize,
|
||||
range: enum { name, type, value, alignment },
|
||||
range: enum { name, type, value, alignment } = .name,
|
||||
};
|
||||
|
||||
fn queryFieldSrc(
|
||||
@ -6749,3 +6784,25 @@ pub fn getDeclExports(mod: Module, decl_index: Decl.Index) []const *Export {
|
||||
return &[0]*Export{};
|
||||
}
|
||||
}
|
||||
|
||||
pub const Feature = enum {
|
||||
panic_fn,
|
||||
panic_unwrap_error,
|
||||
safety_check_formatted,
|
||||
error_return_trace,
|
||||
is_named_enum_value,
|
||||
error_set_has_value,
|
||||
};
|
||||
|
||||
pub fn backendSupportsFeature(mod: Module, feature: Feature) bool {
|
||||
return switch (feature) {
|
||||
.panic_fn => mod.comp.bin_file.options.target.ofmt == .c or
|
||||
mod.comp.bin_file.options.use_llvm,
|
||||
.panic_unwrap_error => mod.comp.bin_file.options.target.ofmt == .c or
|
||||
mod.comp.bin_file.options.use_llvm,
|
||||
.safety_check_formatted => mod.comp.bin_file.options.use_llvm,
|
||||
.error_return_trace => mod.comp.bin_file.options.use_llvm,
|
||||
.is_named_enum_value => mod.comp.bin_file.options.use_llvm,
|
||||
.error_set_has_value => mod.comp.bin_file.options.use_llvm,
|
||||
};
|
||||
}
|
||||
|
||||
566
src/Sema.zig
566
src/Sema.zig
@ -1820,12 +1820,7 @@ fn analyzeAsType(
|
||||
}
|
||||
|
||||
pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void {
|
||||
const backend_supports_error_return_tracing =
|
||||
sema.mod.comp.bin_file.options.use_llvm;
|
||||
if (!backend_supports_error_return_tracing) {
|
||||
// TODO implement this feature in all the backends and then delete this branch
|
||||
return;
|
||||
}
|
||||
if (!sema.mod.backendSupportsFeature(.error_return_trace)) return;
|
||||
|
||||
assert(!block.is_comptime);
|
||||
var err_trace_block = block.makeSubBlock();
|
||||
@ -2099,16 +2094,12 @@ fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazyS
|
||||
const msg = try sema.errMsg(block, init_src, "value stored in comptime field does not match the default value of the field", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
const decl_index = container_ty.getOwnerDeclOrNull() orelse break :msg msg;
|
||||
const decl = sema.mod.declPtr(decl_index);
|
||||
const tree = decl.getFileScope().getTree(sema.gpa) catch |err| {
|
||||
log.err("unable to load AST to report compile error: {s}", .{@errorName(err)});
|
||||
return error.AnalysisFail;
|
||||
};
|
||||
const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_index);
|
||||
const default_value_src: LazySrcLoc = .{ .node_offset_field_default = field_src.node_offset.x };
|
||||
|
||||
try sema.mod.errNoteNonLazy(default_value_src.toSrcLoc(decl), msg, "default value set here", .{});
|
||||
const struct_ty = container_ty.castTag(.@"struct") orelse break :msg msg;
|
||||
const default_value_src = struct_ty.data.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_index,
|
||||
.range = .value,
|
||||
});
|
||||
try sema.mod.errNoteNonLazy(default_value_src, msg, "default value set here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
@ -2146,15 +2137,61 @@ fn addFieldErrNote(
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
@setCold(true);
|
||||
const mod = sema.mod;
|
||||
const decl_index = container_ty.getOwnerDecl();
|
||||
const decl = mod.declPtr(decl_index);
|
||||
const tree = decl.getFileScope().getTree(sema.gpa) catch |err| {
|
||||
log.err("unable to load AST to report compile error: {s}", .{@errorName(err)});
|
||||
return error.AnalysisFail;
|
||||
|
||||
const field_src = blk: {
|
||||
const tree = decl.getFileScope().getTree(sema.gpa) catch |err| {
|
||||
log.err("unable to load AST to report compile error: {s}", .{@errorName(err)});
|
||||
break :blk decl.srcLoc();
|
||||
};
|
||||
|
||||
const container_node = decl.relativeToNodeIndex(0);
|
||||
const node_tags = tree.nodes.items(.tag);
|
||||
var buffer: [2]std.zig.Ast.Node.Index = undefined;
|
||||
const container_decl = switch (node_tags[container_node]) {
|
||||
.root => tree.containerDeclRoot(),
|
||||
.container_decl,
|
||||
.container_decl_trailing,
|
||||
=> tree.containerDecl(container_node),
|
||||
.container_decl_two,
|
||||
.container_decl_two_trailing,
|
||||
=> tree.containerDeclTwo(&buffer, container_node),
|
||||
.container_decl_arg,
|
||||
.container_decl_arg_trailing,
|
||||
=> tree.containerDeclArg(container_node),
|
||||
.tagged_union,
|
||||
.tagged_union_trailing,
|
||||
=> tree.taggedUnion(container_node),
|
||||
.tagged_union_two,
|
||||
.tagged_union_two_trailing,
|
||||
=> tree.taggedUnionTwo(&buffer, container_node),
|
||||
.tagged_union_enum_tag,
|
||||
.tagged_union_enum_tag_trailing,
|
||||
=> tree.taggedUnionEnumTag(container_node),
|
||||
else => break :blk decl.srcLoc(),
|
||||
};
|
||||
|
||||
var it_index: usize = 0;
|
||||
for (container_decl.ast.members) |member_node| {
|
||||
switch (node_tags[member_node]) {
|
||||
.container_field_init,
|
||||
.container_field_align,
|
||||
.container_field,
|
||||
=> {
|
||||
if (it_index == field_index) {
|
||||
break :blk decl.nodeOffsetSrcLoc(decl.nodeIndexToRelative(member_node));
|
||||
}
|
||||
it_index += 1;
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
};
|
||||
const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_index);
|
||||
try mod.errNoteNonLazy(field_src.toSrcLoc(decl), parent, format, args);
|
||||
try mod.errNoteNonLazy(field_src, parent, format, args);
|
||||
}
|
||||
|
||||
fn errMsg(
|
||||
@ -2868,7 +2905,7 @@ fn zirEnumDecl(
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
};
|
||||
defer assert(enum_block.instructions.items.len == 0); // should all be comptime instructions
|
||||
defer enum_block.instructions.deinit(sema.gpa);
|
||||
|
||||
if (body.len != 0) {
|
||||
try sema.analyzeBody(&enum_block, body);
|
||||
@ -2934,13 +2971,12 @@ fn zirEnumDecl(
|
||||
|
||||
const gop_field = enum_obj.fields.getOrPutAssumeCapacity(field_name);
|
||||
if (gop_field.found_existing) {
|
||||
const tree = try sema.getAstTree(block);
|
||||
const field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, field_i);
|
||||
const other_tag_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, gop_field.index);
|
||||
const field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy;
|
||||
const other_field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = gop_field.index }).lazy;
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, field_src, "duplicate enum field '{s}'", .{field_name});
|
||||
errdefer msg.destroy(gpa);
|
||||
try sema.errNote(block, other_tag_src, msg, "other field here", .{});
|
||||
try sema.errNote(block, other_field_src, msg, "other field here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
@ -2949,10 +2985,18 @@ fn zirEnumDecl(
|
||||
if (has_tag_value) {
|
||||
const tag_val_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
// TODO: if we need to report an error here, use a source location
|
||||
// that points to this default value expression rather than the struct.
|
||||
// But only resolve the source location if we need to emit a compile error.
|
||||
const tag_val = (try sema.resolveInstConst(block, src, tag_val_ref, "enum tag value must be comptime-known")).val;
|
||||
const tag_inst = try sema.resolveInst(tag_val_ref);
|
||||
const tag_val = sema.resolveConstValue(block, .unneeded, tag_inst, "") catch |err| switch (err) {
|
||||
error.NeededSourceLocation => {
|
||||
const value_src = enum_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .value,
|
||||
}).lazy;
|
||||
_ = try sema.resolveConstValue(block, value_src, tag_inst, "enum tag value must be comptime-known");
|
||||
unreachable;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
last_tag_val = tag_val;
|
||||
const copied_tag_val = try tag_val.copy(decl_arena_allocator);
|
||||
const gop_val = enum_obj.values.getOrPutAssumeCapacityContext(copied_tag_val, .{
|
||||
@ -2960,11 +3004,13 @@ fn zirEnumDecl(
|
||||
.mod = mod,
|
||||
});
|
||||
if (gop_val.found_existing) {
|
||||
const tree = try sema.getAstTree(block);
|
||||
const field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, field_i);
|
||||
const other_field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, gop_val.index);
|
||||
const value_src = enum_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .value,
|
||||
}).lazy;
|
||||
const other_field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = gop_val.index }).lazy;
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{tag_val.fmtValue(enum_obj.tag_ty, sema.mod)});
|
||||
const msg = try sema.errMsg(block, value_src, "enum tag value {} already taken", .{tag_val.fmtValue(enum_obj.tag_ty, sema.mod)});
|
||||
errdefer msg.destroy(gpa);
|
||||
try sema.errNote(block, other_field_src, msg, "other occurrence here", .{});
|
||||
break :msg msg;
|
||||
@ -2983,9 +3029,8 @@ fn zirEnumDecl(
|
||||
.mod = mod,
|
||||
});
|
||||
if (gop_val.found_existing) {
|
||||
const tree = try sema.getAstTree(block);
|
||||
const field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, field_i);
|
||||
const other_field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, gop_val.index);
|
||||
const field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy;
|
||||
const other_field_src = enum_obj.fieldSrcLoc(sema.mod, .{ .index = gop_val.index }).lazy;
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, field_src, "enum tag value {} already taken", .{tag_val.fmtValue(enum_obj.tag_ty, sema.mod)});
|
||||
errdefer msg.destroy(gpa);
|
||||
@ -3003,9 +3048,11 @@ fn zirEnumDecl(
|
||||
}
|
||||
|
||||
if (!(try sema.intFitsInType(last_tag_val.?, enum_obj.tag_ty, null))) {
|
||||
const tree = try sema.getAstTree(block);
|
||||
const field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, field_i);
|
||||
const msg = try sema.errMsg(block, field_src, "enumeration value '{}' too large for type '{}'", .{
|
||||
const value_src = enum_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = if (has_tag_value) .value else .name,
|
||||
}).lazy;
|
||||
const msg = try sema.errMsg(block, value_src, "enumeration value '{}' too large for type '{}'", .{
|
||||
last_tag_val.?.fmtValue(enum_obj.tag_ty, mod), enum_obj.tag_ty.fmt(mod),
|
||||
});
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
@ -3319,8 +3366,6 @@ fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index
|
||||
operand_ty.childType()
|
||||
else
|
||||
operand_ty;
|
||||
// TODO this should be validated in a more generic instruction that is
|
||||
// emitted for all ifs and whiles with an error union condition.
|
||||
if (err_union_ty.zigTypeTag() != .ErrorUnion) return;
|
||||
const payload_ty = err_union_ty.errorUnionPayload().zigTypeTag();
|
||||
if (payload_ty != .Void and payload_ty != .NoReturn) {
|
||||
@ -5908,9 +5953,8 @@ fn funcDeclSrc(sema: *Sema, func_inst: Air.Inst.Ref) !?*Decl {
|
||||
pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref {
|
||||
const src = sema.src;
|
||||
|
||||
const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
|
||||
if (!backend_supports_error_return_tracing or !sema.mod.comp.bin_file.options.error_return_tracing)
|
||||
return .none;
|
||||
if (!sema.mod.backendSupportsFeature(.error_return_trace)) return .none;
|
||||
if (!sema.mod.comp.bin_file.options.error_return_tracing) return .none;
|
||||
|
||||
if (block.is_comptime)
|
||||
return .none;
|
||||
@ -6148,8 +6192,7 @@ fn zirCall(
|
||||
if (sema.owner_func == null or !sema.owner_func.?.calls_or_awaits_errorable_fn)
|
||||
input_is_error = false; // input was an error type, but no errorable fn's were actually called
|
||||
|
||||
const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
|
||||
if (backend_supports_error_return_tracing and sema.mod.comp.bin_file.options.error_return_tracing and
|
||||
if (sema.mod.backendSupportsFeature(.error_return_trace) and sema.mod.comp.bin_file.options.error_return_tracing and
|
||||
!block.is_comptime and !block.is_typeof and (input_is_error or pop_error_return_trace))
|
||||
{
|
||||
const call_inst: Air.Inst.Ref = if (modifier == .always_tail) undefined else b: {
|
||||
@ -7183,6 +7226,7 @@ fn instantiateGenericCall(
|
||||
return err;
|
||||
},
|
||||
else => {
|
||||
assert(mod.monomorphed_funcs.remove(new_module_func));
|
||||
{
|
||||
errdefer new_decl_arena.deinit();
|
||||
try new_decl.finalizeNewArena(&new_decl_arena);
|
||||
@ -7936,11 +7980,8 @@ fn zirIntToEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
|
||||
|
||||
try sema.requireRuntimeBlock(block, src, operand_src);
|
||||
const result = try block.addTyOp(.intcast, dest_ty, operand);
|
||||
if (block.wantSafety() and
|
||||
!dest_ty.isNonexhaustiveEnum() and
|
||||
// TODO instead of "use_llvm", check a different condition so that backends
|
||||
// can advertise themselves as supporting these extra AIR instructions for safety.
|
||||
sema.mod.comp.bin_file.options.use_llvm)
|
||||
if (block.wantSafety() and !dest_ty.isNonexhaustiveEnum() and
|
||||
sema.mod.backendSupportsFeature(.is_named_enum_value))
|
||||
{
|
||||
const ok = try block.addUnOp(.is_named_enum_value, result);
|
||||
try sema.addSafetyCheck(block, ok, .invalid_enum_value);
|
||||
@ -10658,8 +10699,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
|
||||
return sema.resolveBlockBody(block, src, &child_block, special.body, inst, merges);
|
||||
}
|
||||
|
||||
const backend_supports_is_named_enum = sema.mod.comp.bin_file.options.use_llvm;
|
||||
|
||||
if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {
|
||||
if (empty_enum) {
|
||||
return Air.Inst.Ref.void_value;
|
||||
@ -10670,7 +10709,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
|
||||
if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand)) {
|
||||
return Air.Inst.Ref.unreachable_value;
|
||||
}
|
||||
if (backend_supports_is_named_enum and block.wantSafety() and operand_ty.zigTypeTag() == .Enum and
|
||||
if (sema.mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag() == .Enum and
|
||||
(!operand_ty.isNonexhaustiveEnum() or union_originally))
|
||||
{
|
||||
try sema.zirDbgStmt(block, cond_dbg_node_index);
|
||||
@ -11140,7 +11179,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
|
||||
case_block.wip_capture_scope = wip_captures.scope;
|
||||
case_block.inline_case_capture = .none;
|
||||
|
||||
if (backend_supports_is_named_enum and special.body.len != 0 and block.wantSafety() and
|
||||
if (sema.mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and
|
||||
operand_ty.zigTypeTag() == .Enum and (!operand_ty.isNonexhaustiveEnum() or union_originally))
|
||||
{
|
||||
try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
|
||||
@ -11453,10 +11492,7 @@ fn validateSwitchNoRange(
|
||||
}
|
||||
|
||||
fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !bool {
|
||||
const this_feature_is_implemented_in_the_backend =
|
||||
sema.mod.comp.bin_file.options.use_llvm;
|
||||
|
||||
if (!this_feature_is_implemented_in_the_backend) return false;
|
||||
if (!sema.mod.backendSupportsFeature(.panic_unwrap_error)) return false;
|
||||
|
||||
const tags = sema.code.instructions.items(.tag);
|
||||
for (body) |inst| {
|
||||
@ -16970,20 +17006,17 @@ fn retWithErrTracing(
|
||||
}
|
||||
|
||||
fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool {
|
||||
// TODO implement this feature in all the backends and then delete this check.
|
||||
const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
|
||||
if (!sema.mod.backendSupportsFeature(.error_return_trace)) return false;
|
||||
|
||||
return fn_ret_ty.isError() and
|
||||
sema.mod.comp.bin_file.options.error_return_tracing and
|
||||
backend_supports_error_return_tracing;
|
||||
sema.mod.comp.bin_file.options.error_return_tracing;
|
||||
}
|
||||
|
||||
fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].save_err_ret_index;
|
||||
|
||||
const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
|
||||
const ok = backend_supports_error_return_tracing and sema.mod.comp.bin_file.options.error_return_tracing;
|
||||
if (!ok) return;
|
||||
if (!sema.mod.backendSupportsFeature(.error_return_trace)) return;
|
||||
if (!sema.mod.comp.bin_file.options.error_return_tracing) return;
|
||||
|
||||
// This is only relevant at runtime.
|
||||
if (block.is_comptime or block.is_typeof) return;
|
||||
@ -17005,11 +17038,9 @@ fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index)
|
||||
// This is only relevant at runtime.
|
||||
if (start_block.is_comptime or start_block.is_typeof) return;
|
||||
|
||||
const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm;
|
||||
const ok = sema.owner_func.?.calls_or_awaits_errorable_fn and
|
||||
sema.mod.comp.bin_file.options.error_return_tracing and
|
||||
backend_supports_error_return_tracing;
|
||||
if (!ok) return;
|
||||
if (!sema.mod.backendSupportsFeature(.error_return_trace)) return;
|
||||
if (!sema.owner_func.?.calls_or_awaits_errorable_fn) return;
|
||||
if (!sema.mod.comp.bin_file.options.error_return_tracing) return;
|
||||
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
@ -17988,14 +18019,10 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
|
||||
const stack_trace_ty = try sema.resolveTypeFields(unresolved_stack_trace_ty);
|
||||
const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty);
|
||||
|
||||
// TODO implement this feature in all the backends and then delete this check.
|
||||
const backend_supports_error_return_tracing =
|
||||
sema.mod.comp.bin_file.options.use_llvm;
|
||||
|
||||
if (sema.owner_func != null and
|
||||
sema.owner_func.?.calls_or_awaits_errorable_fn and
|
||||
sema.mod.comp.bin_file.options.error_return_tracing and
|
||||
backend_supports_error_return_tracing)
|
||||
sema.mod.backendSupportsFeature(.error_return_trace))
|
||||
{
|
||||
return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
|
||||
}
|
||||
@ -18175,7 +18202,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
|
||||
return sema.addStrLit(block, field_name);
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src, operand_src);
|
||||
if (block.wantSafety() and sema.mod.comp.bin_file.options.use_llvm) {
|
||||
if (block.wantSafety() and sema.mod.backendSupportsFeature(.is_named_enum_value)) {
|
||||
const ok = try block.addUnOp(.is_named_enum_value, casted_operand);
|
||||
try sema.addSafetyCheck(block, ok, .invalid_enum_value);
|
||||
}
|
||||
@ -19469,7 +19496,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src, operand_src);
|
||||
if (block.wantSafety() and !dest_ty.isAnyError() and sema.mod.comp.bin_file.options.use_llvm) {
|
||||
if (block.wantSafety() and !dest_ty.isAnyError() and sema.mod.backendSupportsFeature(.error_set_has_value)) {
|
||||
const err_int_inst = try block.addBitCast(Type.err_int, operand);
|
||||
const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);
|
||||
try sema.addSafetyCheck(block, ok, .invalid_error_code);
|
||||
@ -21596,7 +21623,7 @@ fn zirVarExtended(
|
||||
.owner_decl = sema.owner_decl_index,
|
||||
.init = init_val,
|
||||
.is_extern = small.is_extern,
|
||||
.is_mutable = true, // TODO get rid of this unused field
|
||||
.is_mutable = true,
|
||||
.is_threadlocal = small.is_threadlocal,
|
||||
.is_weak_linkage = false,
|
||||
.lib_name = null,
|
||||
@ -22075,7 +22102,7 @@ fn zirBuiltinExtern(
|
||||
.owner_decl = sema.owner_decl_index,
|
||||
.init = Value.initTag(.unreachable_value),
|
||||
.is_extern = true,
|
||||
.is_mutable = false, // TODO get rid of this unused field
|
||||
.is_mutable = false,
|
||||
.is_threadlocal = options.is_thread_local,
|
||||
.is_weak_linkage = options.linkage == .Weak,
|
||||
.lib_name = null,
|
||||
@ -22658,11 +22685,7 @@ fn panicWithMsg(
|
||||
const mod = sema.mod;
|
||||
const arena = sema.arena;
|
||||
|
||||
const this_feature_is_implemented_in_the_backend =
|
||||
mod.comp.bin_file.options.target.ofmt == .c or
|
||||
mod.comp.bin_file.options.use_llvm;
|
||||
if (!this_feature_is_implemented_in_the_backend) {
|
||||
// TODO implement this feature in all the backends and then delete this branch
|
||||
if (!mod.backendSupportsFeature(.panic_fn)) {
|
||||
_ = try block.addNoOp(.breakpoint);
|
||||
_ = try block.addNoOp(.unreach);
|
||||
return;
|
||||
@ -22711,11 +22734,7 @@ fn panicUnwrapError(
|
||||
defer fail_block.instructions.deinit(gpa);
|
||||
|
||||
{
|
||||
const this_feature_is_implemented_in_the_backend =
|
||||
sema.mod.comp.bin_file.options.use_llvm;
|
||||
|
||||
if (!this_feature_is_implemented_in_the_backend) {
|
||||
// TODO implement this feature in all the backends and then delete this branch
|
||||
if (!sema.mod.backendSupportsFeature(.panic_unwrap_error)) {
|
||||
_ = try fail_block.addNoOp(.breakpoint);
|
||||
_ = try fail_block.addNoOp(.unreach);
|
||||
} else {
|
||||
@ -22841,18 +22860,12 @@ fn safetyCheckFormatted(
|
||||
|
||||
defer fail_block.instructions.deinit(gpa);
|
||||
|
||||
{
|
||||
const this_feature_is_implemented_in_the_backend =
|
||||
sema.mod.comp.bin_file.options.use_llvm;
|
||||
|
||||
if (!this_feature_is_implemented_in_the_backend) {
|
||||
// TODO implement this feature in all the backends and then delete this branch
|
||||
_ = try fail_block.addNoOp(.breakpoint);
|
||||
_ = try fail_block.addNoOp(.unreach);
|
||||
} else {
|
||||
const panic_fn = try sema.getBuiltin(func);
|
||||
_ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, args, null);
|
||||
}
|
||||
if (!sema.mod.backendSupportsFeature(.safety_check_formatted)) {
|
||||
_ = try fail_block.addNoOp(.breakpoint);
|
||||
_ = try fail_block.addNoOp(.unreach);
|
||||
} else {
|
||||
const panic_fn = try sema.getBuiltin(func);
|
||||
_ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, args, null);
|
||||
}
|
||||
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
|
||||
}
|
||||
@ -24517,7 +24530,7 @@ fn coerceExtra(
|
||||
return block.addBitCast(dest_ty, inst);
|
||||
}
|
||||
|
||||
const is_undef = if (maybe_inst_val) |val| val.isUndef() else false;
|
||||
const is_undef = inst_ty.zigTypeTag() == .Undefined;
|
||||
|
||||
switch (dest_ty.zigTypeTag()) {
|
||||
.Optional => optional: {
|
||||
@ -30662,14 +30675,13 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
|
||||
const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
|
||||
if (gop.found_existing) {
|
||||
const msg = msg: {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i);
|
||||
const field_src = struct_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy;
|
||||
const msg = try sema.errMsg(&block_scope, field_src, "duplicate struct field: '{s}'", .{field_name});
|
||||
errdefer msg.destroy(gpa);
|
||||
|
||||
const prev_field_index = struct_obj.fields.getIndex(field_name).?;
|
||||
const prev_field_src = enumFieldSrcLoc(decl, tree.*, 0, prev_field_index);
|
||||
try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{});
|
||||
const prev_field_src = struct_obj.fieldSrcLoc(sema.mod, .{ .index = prev_field_index });
|
||||
try sema.mod.errNoteNonLazy(prev_field_src, msg, "other field here", .{});
|
||||
try sema.errNote(&block_scope, src, msg, "struct declared here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
@ -30699,34 +30711,51 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
|
||||
// so that init values may depend on type layout.
|
||||
const bodies_index = extra_index;
|
||||
|
||||
for (fields) |zir_field, i| {
|
||||
// TODO emit compile errors for invalid field types
|
||||
// such as arrays and pointers inside packed structs.
|
||||
for (fields) |zir_field, field_i| {
|
||||
const field_ty: Type = ty: {
|
||||
if (zir_field.type_ref != .none) {
|
||||
// TODO: if we need to report an error here, use a source location
|
||||
// that points to this type expression rather than the struct.
|
||||
// But only resolve the source location if we need to emit a compile error.
|
||||
break :ty try sema.resolveType(&block_scope, src, zir_field.type_ref);
|
||||
break :ty sema.resolveType(&block_scope, .unneeded, zir_field.type_ref) catch |err| switch (err) {
|
||||
error.NeededSourceLocation => {
|
||||
const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .type,
|
||||
}).lazy;
|
||||
_ = try sema.resolveType(&block_scope, ty_src, zir_field.type_ref);
|
||||
unreachable;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
assert(zir_field.type_body_len != 0);
|
||||
const body = zir.extra[extra_index..][0..zir_field.type_body_len];
|
||||
extra_index += body.len;
|
||||
const ty_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index);
|
||||
break :ty try sema.analyzeAsType(&block_scope, src, ty_ref);
|
||||
break :ty sema.analyzeAsType(&block_scope, .unneeded, ty_ref) catch |err| switch (err) {
|
||||
error.NeededSourceLocation => {
|
||||
const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .type,
|
||||
}).lazy;
|
||||
_ = try sema.analyzeAsType(&block_scope, ty_src, ty_ref);
|
||||
unreachable;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
};
|
||||
if (field_ty.tag() == .generic_poison) {
|
||||
return error.GenericPoison;
|
||||
}
|
||||
|
||||
const field = &struct_obj.fields.values()[i];
|
||||
const field = &struct_obj.fields.values()[field_i];
|
||||
field.ty = try field_ty.copy(decl_arena_allocator);
|
||||
|
||||
if (field_ty.zigTypeTag() == .Opaque) {
|
||||
const msg = msg: {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const field_src = enumFieldSrcLoc(decl, tree.*, 0, i);
|
||||
const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
|
||||
const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .type,
|
||||
}).lazy;
|
||||
const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
try sema.addDeclaredHereNote(msg, field_ty);
|
||||
@ -30736,9 +30765,11 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
|
||||
}
|
||||
if (field_ty.zigTypeTag() == .NoReturn) {
|
||||
const msg = msg: {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const field_src = enumFieldSrcLoc(decl, tree.*, 0, i);
|
||||
const msg = try sema.errMsg(&block_scope, field_src, "struct fields cannot be 'noreturn'", .{});
|
||||
const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .type,
|
||||
}).lazy;
|
||||
const msg = try sema.errMsg(&block_scope, ty_src, "struct fields cannot be 'noreturn'", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
try sema.addDeclaredHereNote(msg, field_ty);
|
||||
@ -30748,12 +30779,14 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
|
||||
}
|
||||
if (struct_obj.layout == .Extern and !try sema.validateExternType(field.ty, .struct_field)) {
|
||||
const msg = msg: {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const fields_src = enumFieldSrcLoc(decl, tree.*, 0, i);
|
||||
const msg = try sema.errMsg(&block_scope, fields_src, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
|
||||
const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .type,
|
||||
});
|
||||
const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
try sema.explainWhyTypeIsNotExtern(msg, fields_src.toSrcLoc(decl), field.ty, .struct_field);
|
||||
try sema.explainWhyTypeIsNotExtern(msg, ty_src, field.ty, .struct_field);
|
||||
|
||||
try sema.addDeclaredHereNote(msg, field.ty);
|
||||
break :msg msg;
|
||||
@ -30761,12 +30794,14 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
} else if (struct_obj.layout == .Packed and !(validatePackedType(field.ty))) {
|
||||
const msg = msg: {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const fields_src = enumFieldSrcLoc(decl, tree.*, 0, i);
|
||||
const msg = try sema.errMsg(&block_scope, fields_src, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
|
||||
const ty_src = struct_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .type,
|
||||
});
|
||||
const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed structs cannot contain fields of type '{}'", .{field.ty.fmt(sema.mod)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
try sema.explainWhyTypeIsNotPacked(msg, fields_src.toSrcLoc(decl), field.ty);
|
||||
try sema.explainWhyTypeIsNotPacked(msg, ty_src, field.ty);
|
||||
|
||||
try sema.addDeclaredHereNote(msg, field.ty);
|
||||
break :msg msg;
|
||||
@ -30778,7 +30813,17 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
|
||||
const body = zir.extra[extra_index..][0..zir_field.align_body_len];
|
||||
extra_index += body.len;
|
||||
const align_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index);
|
||||
field.abi_align = try sema.analyzeAsAlign(&block_scope, src, align_ref);
|
||||
field.abi_align = sema.analyzeAsAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) {
|
||||
error.NeededSourceLocation => {
|
||||
const align_src = struct_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .alignment,
|
||||
}).lazy;
|
||||
_ = try sema.analyzeAsAlign(&block_scope, align_src, align_ref);
|
||||
unreachable;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
extra_index += zir_field.init_body_len;
|
||||
@ -30788,26 +30833,30 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
|
||||
|
||||
if (any_inits) {
|
||||
extra_index = bodies_index;
|
||||
for (fields) |zir_field, i| {
|
||||
for (fields) |zir_field, field_i| {
|
||||
extra_index += zir_field.type_body_len;
|
||||
extra_index += zir_field.align_body_len;
|
||||
if (zir_field.init_body_len > 0) {
|
||||
const body = zir.extra[extra_index..][0..zir_field.init_body_len];
|
||||
extra_index += body.len;
|
||||
const init = try sema.resolveBody(&block_scope, body, struct_obj.zir_index);
|
||||
const field = &struct_obj.fields.values()[i];
|
||||
const field = &struct_obj.fields.values()[field_i];
|
||||
const coerced = sema.coerce(&block_scope, field.ty, init, .unneeded) catch |err| switch (err) {
|
||||
error.NeededSourceLocation => {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const init_src = containerFieldInitSrcLoc(decl, tree.*, 0, i);
|
||||
const init_src = struct_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .value,
|
||||
}).lazy;
|
||||
_ = try sema.coerce(&block_scope, field.ty, init, init_src);
|
||||
unreachable;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
const default_val = (try sema.resolveMaybeUndefVal(coerced)) orelse {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const init_src = containerFieldInitSrcLoc(decl, tree.*, 0, i);
|
||||
const init_src = struct_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .value,
|
||||
}).lazy;
|
||||
return sema.failWithNeededComptime(&block_scope, init_src, "struct field default value must be comptime-known");
|
||||
};
|
||||
field.default_val = try default_val.copy(decl_arena_allocator);
|
||||
@ -31023,9 +31072,17 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
|
||||
|
||||
if (enum_value_map) |map| {
|
||||
const copied_val = if (tag_ref != .none) blk: {
|
||||
const tag_src = src; // TODO better source location
|
||||
const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, tag_src);
|
||||
const val = try sema.resolveConstValue(&block_scope, tag_src, coerced, "enum tag value must be comptime-known");
|
||||
const val = sema.semaUnionFieldVal(&block_scope, .unneeded, int_tag_ty, tag_ref) catch |err| switch (err) {
|
||||
error.NeededSourceLocation => {
|
||||
const val_src = union_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .value,
|
||||
}).lazy;
|
||||
_ = try sema.semaUnionFieldVal(&block_scope, val_src, int_tag_ty, tag_ref);
|
||||
unreachable;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
last_tag_val = val;
|
||||
|
||||
// This puts the memory into the union arena, not the enum arena, but
|
||||
@ -31045,9 +31102,8 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
|
||||
.mod = mod,
|
||||
});
|
||||
if (gop.found_existing) {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const field_src = enumFieldSrcLoc(sema.mod.declPtr(block_scope.src_decl), tree.*, src.node_offset.x, field_i);
|
||||
const other_field_src = enumFieldSrcLoc(sema.mod.declPtr(block_scope.src_decl), tree.*, src.node_offset.x, gop.index);
|
||||
const field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy;
|
||||
const other_field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = gop.index }).lazy;
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(&block_scope, field_src, "enum tag value {} already taken", .{copied_val.fmtValue(int_tag_ty, sema.mod)});
|
||||
errdefer msg.destroy(gpa);
|
||||
@ -31069,10 +31125,17 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
|
||||
else if (field_type_ref == .none)
|
||||
Type.initTag(.noreturn)
|
||||
else
|
||||
// TODO: if we need to report an error here, use a source location
|
||||
// that points to this type expression rather than the union.
|
||||
// But only resolve the source location if we need to emit a compile error.
|
||||
try sema.resolveType(&block_scope, src, field_type_ref);
|
||||
sema.resolveType(&block_scope, .unneeded, field_type_ref) catch |err| switch (err) {
|
||||
error.NeededSourceLocation => {
|
||||
const ty_src = union_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .type,
|
||||
}).lazy;
|
||||
_ = try sema.resolveType(&block_scope, ty_src, field_type_ref);
|
||||
unreachable;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
if (field_ty.tag() == .generic_poison) {
|
||||
return error.GenericPoison;
|
||||
@ -31081,13 +31144,12 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
|
||||
const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
|
||||
if (gop.found_existing) {
|
||||
const msg = msg: {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i);
|
||||
const field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = field_i }).lazy;
|
||||
const msg = try sema.errMsg(&block_scope, field_src, "duplicate union field: '{s}'", .{field_name});
|
||||
errdefer msg.destroy(gpa);
|
||||
|
||||
const prev_field_index = union_obj.fields.getIndex(field_name).?;
|
||||
const prev_field_src = enumFieldSrcLoc(decl, tree.*, 0, prev_field_index);
|
||||
const prev_field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = prev_field_index }).lazy;
|
||||
try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{});
|
||||
try sema.errNote(&block_scope, src, msg, "union declared here", .{});
|
||||
break :msg msg;
|
||||
@ -31099,9 +31161,11 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
|
||||
const enum_has_field = names.orderedRemove(field_name);
|
||||
if (!enum_has_field) {
|
||||
const msg = msg: {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i);
|
||||
const msg = try sema.errMsg(&block_scope, field_src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(sema.mod) });
|
||||
const ty_src = union_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .type,
|
||||
}).lazy;
|
||||
const msg = try sema.errMsg(&block_scope, ty_src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(sema.mod) });
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
|
||||
break :msg msg;
|
||||
@ -31112,9 +31176,11 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
|
||||
|
||||
if (field_ty.zigTypeTag() == .Opaque) {
|
||||
const msg = msg: {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i);
|
||||
const msg = try sema.errMsg(&block_scope, field_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
|
||||
const ty_src = union_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .type,
|
||||
}).lazy;
|
||||
const msg = try sema.errMsg(&block_scope, ty_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
try sema.addDeclaredHereNote(msg, field_ty);
|
||||
@ -31124,12 +31190,14 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
|
||||
}
|
||||
if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) {
|
||||
const msg = msg: {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i);
|
||||
const msg = try sema.errMsg(&block_scope, field_src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
|
||||
const ty_src = union_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .type,
|
||||
});
|
||||
const msg = try sema.errMsg(&block_scope, ty_src.lazy, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
try sema.explainWhyTypeIsNotExtern(msg, field_src.toSrcLoc(decl), field_ty, .union_field);
|
||||
try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .union_field);
|
||||
|
||||
try sema.addDeclaredHereNote(msg, field_ty);
|
||||
break :msg msg;
|
||||
@ -31137,12 +31205,14 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
} else if (union_obj.layout == .Packed and !(validatePackedType(field_ty))) {
|
||||
const msg = msg: {
|
||||
const tree = try sema.getAstTree(&block_scope);
|
||||
const fields_src = enumFieldSrcLoc(decl, tree.*, 0, field_i);
|
||||
const msg = try sema.errMsg(&block_scope, fields_src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
|
||||
const ty_src = union_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .type,
|
||||
});
|
||||
const msg = try sema.errMsg(&block_scope, ty_src.lazy, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(sema.mod)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
try sema.explainWhyTypeIsNotPacked(msg, fields_src.toSrcLoc(decl), field_ty);
|
||||
try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty);
|
||||
|
||||
try sema.addDeclaredHereNote(msg, field_ty);
|
||||
break :msg msg;
|
||||
@ -31156,10 +31226,17 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
|
||||
};
|
||||
|
||||
if (align_ref != .none) {
|
||||
// TODO: if we need to report an error here, use a source location
|
||||
// that points to this alignment expression rather than the struct.
|
||||
// But only resolve the source location if we need to emit a compile error.
|
||||
gop.value_ptr.abi_align = try sema.resolveAlign(&block_scope, src, align_ref);
|
||||
gop.value_ptr.abi_align = sema.resolveAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) {
|
||||
error.NeededSourceLocation => {
|
||||
const align_src = union_obj.fieldSrcLoc(sema.mod, .{
|
||||
.index = field_i,
|
||||
.range = .alignment,
|
||||
}).lazy;
|
||||
_ = try sema.resolveAlign(&block_scope, align_src, align_ref);
|
||||
unreachable;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
} else {
|
||||
gop.value_ptr.abi_align = 0;
|
||||
}
|
||||
@ -31184,6 +31261,11 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
|
||||
}
|
||||
}
|
||||
|
||||
fn semaUnionFieldVal(sema: *Sema, block: *Block, src: LazySrcLoc, int_tag_ty: Type, tag_ref: Air.Inst.Ref) CompileError!Value {
|
||||
const coerced = try sema.coerce(block, int_tag_ty, tag_ref, src);
|
||||
return sema.resolveConstValue(block, src, coerced, "enum tag value must be comptime-known");
|
||||
}
|
||||
|
||||
fn generateUnionTagTypeNumbered(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
@ -31607,102 +31689,6 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
|
||||
}
|
||||
}
|
||||
|
||||
fn getAstTree(sema: *Sema, block: *Block) CompileError!*const std.zig.Ast {
|
||||
return block.namespace.file_scope.getTree(sema.gpa) catch |err| {
|
||||
log.err("unable to load AST to report compile error: {s}", .{@errorName(err)});
|
||||
return error.AnalysisFail;
|
||||
};
|
||||
}
|
||||
|
||||
fn enumFieldSrcLoc(
|
||||
decl: *Decl,
|
||||
tree: std.zig.Ast,
|
||||
node_offset: i32,
|
||||
field_index: usize,
|
||||
) LazySrcLoc {
|
||||
@setCold(true);
|
||||
const field_node = containerFieldNode(decl, tree, node_offset, field_index) orelse
|
||||
return LazySrcLoc.nodeOffset(0);
|
||||
return decl.nodeSrcLoc(field_node);
|
||||
}
|
||||
|
||||
fn containerFieldInitSrcLoc(
|
||||
decl: *Decl,
|
||||
tree: std.zig.Ast,
|
||||
node_offset: i32,
|
||||
field_index: usize,
|
||||
) LazySrcLoc {
|
||||
@setCold(true);
|
||||
const node_tags = tree.nodes.items(.tag);
|
||||
const field_node = containerFieldNode(decl, tree, node_offset, field_index) orelse
|
||||
return LazySrcLoc.nodeOffset(0);
|
||||
const node_data = tree.nodes.items(.data)[field_node];
|
||||
|
||||
const init_node = switch (node_tags[field_node]) {
|
||||
.container_field_init => node_data.rhs,
|
||||
.container_field => blk: {
|
||||
const extra_data = tree.extraData(node_data.rhs, std.zig.Ast.Node.ContainerField);
|
||||
break :blk extra_data.value_expr;
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
return decl.nodeSrcLoc(init_node);
|
||||
}
|
||||
|
||||
fn containerFieldNode(
|
||||
decl: *Decl,
|
||||
tree: std.zig.Ast,
|
||||
node_offset: i32,
|
||||
field_index: usize,
|
||||
) ?std.zig.Ast.Node.Index {
|
||||
@setCold(true);
|
||||
const enum_node = decl.relativeToNodeIndex(node_offset);
|
||||
const node_tags = tree.nodes.items(.tag);
|
||||
var buffer: [2]std.zig.Ast.Node.Index = undefined;
|
||||
const container_decl = switch (node_tags[enum_node]) {
|
||||
.root => tree.containerDeclRoot(),
|
||||
|
||||
.container_decl,
|
||||
.container_decl_trailing,
|
||||
=> tree.containerDecl(enum_node),
|
||||
|
||||
.container_decl_two,
|
||||
.container_decl_two_trailing,
|
||||
=> tree.containerDeclTwo(&buffer, enum_node),
|
||||
|
||||
.container_decl_arg,
|
||||
.container_decl_arg_trailing,
|
||||
=> tree.containerDeclArg(enum_node),
|
||||
|
||||
.tagged_union,
|
||||
.tagged_union_trailing,
|
||||
=> tree.taggedUnion(enum_node),
|
||||
.tagged_union_two,
|
||||
.tagged_union_two_trailing,
|
||||
=> tree.taggedUnionTwo(&buffer, enum_node),
|
||||
.tagged_union_enum_tag,
|
||||
.tagged_union_enum_tag_trailing,
|
||||
=> tree.taggedUnionEnumTag(enum_node),
|
||||
|
||||
else => return null,
|
||||
};
|
||||
var it_index: usize = 0;
|
||||
for (container_decl.ast.members) |member_node| {
|
||||
switch (node_tags[member_node]) {
|
||||
.container_field_init,
|
||||
.container_field_align,
|
||||
.container_field,
|
||||
=> {
|
||||
if (it_index == field_index) return member_node;
|
||||
it_index += 1;
|
||||
},
|
||||
|
||||
else => continue,
|
||||
}
|
||||
} else unreachable;
|
||||
}
|
||||
|
||||
/// Returns the type of the AIR instruction.
|
||||
fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {
|
||||
return sema.getTmpAir().typeOf(inst);
|
||||
@ -31792,14 +31778,6 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {
|
||||
return sema.addConstant(ty, try Value.Tag.int_u64.create(sema.arena, int));
|
||||
}
|
||||
|
||||
fn addBool(sema: *Sema, ty: Type, boolean: bool) CompileError!Air.Inst.Ref {
|
||||
return switch (ty.zigTypeTag()) {
|
||||
.Vector => sema.addConstant(ty, try Value.Tag.repeated.create(sema.arena, Value.makeBool(boolean))),
|
||||
.Bool => try sema.resolveInst(if (boolean) .bool_true else .bool_false),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
|
||||
return sema.addConstant(ty, Value.undef);
|
||||
}
|
||||
@ -32443,27 +32421,6 @@ fn intAddScalar(sema: *Sema, lhs: Value, rhs: Value) !Value {
|
||||
return Value.fromBigInt(sema.arena, result_bigint.toConst());
|
||||
}
|
||||
|
||||
/// Supports both (vectors of) floats and ints; handles undefined scalars.
|
||||
fn numberAddWrap(
|
||||
sema: *Sema,
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
ty: Type,
|
||||
) !Value {
|
||||
if (ty.zigTypeTag() == .Vector) {
|
||||
const result_data = try sema.arena.alloc(Value, ty.vectorLen());
|
||||
for (result_data) |*scalar, i| {
|
||||
var lhs_buf: Value.ElemValueBuffer = undefined;
|
||||
var rhs_buf: Value.ElemValueBuffer = undefined;
|
||||
const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf);
|
||||
const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf);
|
||||
scalar.* = try sema.numberAddWrapScalar(lhs_elem, rhs_elem, ty.scalarType());
|
||||
}
|
||||
return Value.Tag.aggregate.create(sema.arena, result_data);
|
||||
}
|
||||
return sema.numberAddWrapScalar(lhs, rhs, ty);
|
||||
}
|
||||
|
||||
/// Supports both floats and ints; handles undefined.
|
||||
fn numberAddWrapScalar(
|
||||
sema: *Sema,
|
||||
@ -32522,27 +32479,6 @@ fn intSubScalar(sema: *Sema, lhs: Value, rhs: Value) !Value {
|
||||
return Value.fromBigInt(sema.arena, result_bigint.toConst());
|
||||
}
|
||||
|
||||
/// Supports both (vectors of) floats and ints; handles undefined scalars.
|
||||
fn numberSubWrap(
|
||||
sema: *Sema,
|
||||
lhs: Value,
|
||||
rhs: Value,
|
||||
ty: Type,
|
||||
) !Value {
|
||||
if (ty.zigTypeTag() == .Vector) {
|
||||
const result_data = try sema.arena.alloc(Value, ty.vectorLen());
|
||||
for (result_data) |*scalar, i| {
|
||||
var lhs_buf: Value.ElemValueBuffer = undefined;
|
||||
var rhs_buf: Value.ElemValueBuffer = undefined;
|
||||
const lhs_elem = lhs.elemValueBuffer(sema.mod, i, &lhs_buf);
|
||||
const rhs_elem = rhs.elemValueBuffer(sema.mod, i, &rhs_buf);
|
||||
scalar.* = try sema.numberSubWrapScalar(lhs_elem, rhs_elem, ty.scalarType());
|
||||
}
|
||||
return Value.Tag.aggregate.create(sema.arena, result_data);
|
||||
}
|
||||
return sema.numberSubWrapScalar(lhs, rhs, ty);
|
||||
}
|
||||
|
||||
/// Supports both floats and ints; handles undefined.
|
||||
fn numberSubWrapScalar(
|
||||
sema: *Sema,
|
||||
|
||||
@ -100,8 +100,7 @@ pub fn nullTerminatedString(code: Zir, index: usize) [:0]const u8 {
|
||||
|
||||
pub fn refSlice(code: Zir, start: usize, len: usize) []Inst.Ref {
|
||||
const raw_slice = code.extra[start..][0..len];
|
||||
// TODO we should be able to directly `@ptrCast` the slice to the other slice type.
|
||||
return @ptrCast([*]Inst.Ref, raw_slice.ptr)[0..len];
|
||||
return @ptrCast([]Inst.Ref, raw_slice);
|
||||
}
|
||||
|
||||
pub fn hasCompileErrors(code: Zir) bool {
|
||||
|
||||
@ -3454,8 +3454,7 @@ fn runOrTest(
|
||||
} else if (watch) {
|
||||
warn("process exited with code {d}", .{code});
|
||||
} else {
|
||||
// TODO https://github.com/ziglang/zig/issues/6342
|
||||
process.exit(1);
|
||||
process.exit(code);
|
||||
}
|
||||
},
|
||||
else => {
|
||||
|
||||
46
src/type.zig
46
src/type.zig
@ -1236,7 +1236,7 @@ pub const Type = extern union {
|
||||
// we can't hash these based on tags because they wouldn't match the expanded version.
|
||||
.type_info => unreachable, // needed to resolve the type before now
|
||||
|
||||
.bound_fn => unreachable, // TODO delete from the language
|
||||
.bound_fn => unreachable,
|
||||
.var_args_param => unreachable, // can be any type
|
||||
}
|
||||
}
|
||||
@ -3272,7 +3272,7 @@ pub const Type = extern union {
|
||||
.fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer
|
||||
.function => unreachable, // represents machine code; not a pointer
|
||||
.@"opaque" => unreachable, // no size available
|
||||
.bound_fn => unreachable, // TODO remove from the language
|
||||
.bound_fn => unreachable,
|
||||
.noreturn => unreachable,
|
||||
.inferred_alloc_const => unreachable,
|
||||
.inferred_alloc_mut => unreachable,
|
||||
@ -4088,47 +4088,6 @@ pub const Type = extern union {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns if type can be used for a runtime variable
|
||||
pub fn isValidVarType(self: Type, is_extern: bool) bool {
|
||||
var ty = self;
|
||||
while (true) switch (ty.zigTypeTag()) {
|
||||
.Bool,
|
||||
.Int,
|
||||
.Float,
|
||||
.ErrorSet,
|
||||
.Enum,
|
||||
.Frame,
|
||||
.AnyFrame,
|
||||
=> return true,
|
||||
|
||||
.Opaque => return is_extern,
|
||||
.ComptimeFloat,
|
||||
.ComptimeInt,
|
||||
.EnumLiteral,
|
||||
.NoReturn,
|
||||
.Type,
|
||||
.Void,
|
||||
.Undefined,
|
||||
.Null,
|
||||
=> return false,
|
||||
|
||||
.Optional => {
|
||||
var buf: Payload.ElemType = undefined;
|
||||
return ty.optionalChild(&buf).isValidVarType(is_extern);
|
||||
},
|
||||
.Pointer, .Array, .Vector => ty = ty.elemType(),
|
||||
.ErrorUnion => ty = ty.errorUnionPayload(),
|
||||
|
||||
.Fn => @panic("TODO fn isValidVarType"),
|
||||
.Struct => {
|
||||
// TODO this is not always correct; introduce lazy value mechanism
|
||||
// and here we need to force a resolve of "type requires comptime".
|
||||
return true;
|
||||
},
|
||||
.Union => @panic("TODO union isValidVarType"),
|
||||
};
|
||||
}
|
||||
|
||||
/// For *[N]T, returns [N]T.
|
||||
/// For *T, returns T.
|
||||
/// For [*]T, returns T.
|
||||
@ -5434,7 +5393,6 @@ pub const Type = extern union {
|
||||
}
|
||||
|
||||
/// Asserts the type is an enum or a union.
|
||||
/// TODO support unions
|
||||
pub fn intTagType(ty: Type, buffer: *Payload.Bits) Type {
|
||||
switch (ty.tag()) {
|
||||
.enum_full, .enum_nonexhaustive => return ty.cast(Payload.EnumFull).?.data.tag_ty,
|
||||
|
||||
@ -814,7 +814,6 @@ pub const Value = extern union {
|
||||
.float_80 => return out_stream.print("{}", .{val.castTag(.float_80).?.data}),
|
||||
.float_128 => return out_stream.print("{}", .{val.castTag(.float_128).?.data}),
|
||||
.@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}),
|
||||
// TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that
|
||||
.eu_payload => {
|
||||
try out_stream.writeAll("(eu_payload) ");
|
||||
val = val.castTag(.eu_payload).?.data;
|
||||
@ -989,8 +988,7 @@ pub const Value = extern union {
|
||||
switch (val.tag()) {
|
||||
.enum_field_index => {
|
||||
const field_index = val.castTag(.enum_field_index).?.data;
|
||||
// TODO should `@intToEnum` do this `@intCast` for you?
|
||||
return @intToEnum(E, @intCast(@typeInfo(E).Enum.tag_type, field_index));
|
||||
return @intToEnum(E, field_index);
|
||||
},
|
||||
.the_only_possible_value => {
|
||||
const fields = std.meta.fields(E);
|
||||
|
||||
@ -1185,3 +1185,13 @@ test "runtime int to enum with one possible value" {
|
||||
@compileError("test failed");
|
||||
}
|
||||
}
|
||||
|
||||
test "enum tag from a local variable" {
|
||||
const S = struct {
|
||||
fn Int(comptime Inner: type) type {
|
||||
return enum(Inner) { _ };
|
||||
}
|
||||
};
|
||||
const i = @intToEnum(S.Int(u32), 0);
|
||||
try std.testing.expect(@enumToInt(i) == 0);
|
||||
}
|
||||
|
||||
@ -29,9 +29,9 @@ export fn d() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :3:5: error: opaque types have unknown size and therefore cannot be directly embedded in structs
|
||||
// :3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs
|
||||
// :1:11: note: opaque declared here
|
||||
// :7:5: error: opaque types have unknown size and therefore cannot be directly embedded in unions
|
||||
// :7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions
|
||||
// :19:18: error: opaque types have unknown size and therefore cannot be directly embedded in structs
|
||||
// :18:22: note: opaque declared here
|
||||
// :24:23: error: opaque types have unknown size and therefore cannot be directly embedded in structs
|
||||
|
||||
@ -14,5 +14,5 @@ export fn entry() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :6:5: error: enum tag value 60 already taken
|
||||
// :6:9: error: enum tag value 60 already taken
|
||||
// :4:5: note: other occurrence here
|
||||
|
||||
@ -39,7 +39,7 @@ export fn entry() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :31:5: error: extern structs cannot contain fields of type 'tmp.E'
|
||||
// :31:5: note: enum tag type 'u9' is not extern compatible
|
||||
// :31:5: note: only integers with power of two bits are extern compatible
|
||||
// :31:8: error: extern structs cannot contain fields of type 'tmp.E'
|
||||
// :31:8: note: enum tag type 'u9' is not extern compatible
|
||||
// :31:8: note: only integers with power of two bits are extern compatible
|
||||
// :1:15: note: enum declared here
|
||||
|
||||
@ -11,7 +11,7 @@ export fn entry() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :3:5: error: extern structs cannot contain fields of type 'tmp.E'
|
||||
// :3:5: note: enum tag type 'u31' is not extern compatible
|
||||
// :3:5: note: only integers with power of two bits are extern compatible
|
||||
// :3:8: error: extern structs cannot contain fields of type 'tmp.E'
|
||||
// :3:8: note: enum tag type 'u31' is not extern compatible
|
||||
// :3:8: note: only integers with power of two bits are extern compatible
|
||||
// :1:15: note: enum declared here
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
const Parser = @This();
|
||||
fn Chunk() type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
};
|
||||
}
|
||||
parser_chunk: Chunk,
|
||||
|
||||
comptime {
|
||||
_ = @sizeOf(@This()) + 1;
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :7:15: error: expected type 'type', found 'fn() type'
|
||||
@ -0,0 +1,30 @@
|
||||
pub export fn entry1() void {
|
||||
@setEvalBranchQuota(1001);
|
||||
// Return type evaluation should inherit both the
|
||||
// parent's branch quota and count meaning
|
||||
// at least 2002 backwards branches are required.
|
||||
comptime var i = 0;
|
||||
inline while (i < 1000) : (i += 1) {}
|
||||
_ = simple(10);
|
||||
}
|
||||
pub export fn entry2() void {
|
||||
@setEvalBranchQuota(2001);
|
||||
comptime var i = 0;
|
||||
inline while (i < 1000) : (i += 1) {}
|
||||
_ = simple(10);
|
||||
}
|
||||
fn simple(comptime n: usize) Type(n) {
|
||||
return n;
|
||||
}
|
||||
fn Type(comptime n: usize) type {
|
||||
if (n <= 1) return usize;
|
||||
return Type(n - 1);
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :21:16: error: evaluation exceeded 1001 backwards branches
|
||||
// :21:16: note: use @setEvalBranchQuota() to raise the branch limit from 1001
|
||||
// :16:34: note: called from here
|
||||
@ -7,5 +7,5 @@ export fn testf(fluff: *stroo) void { _ = fluff; }
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :2:5: error: extern structs cannot contain fields of type '?[*c]u8'
|
||||
// :2:5: note: only pointer like optionals are extern compatible
|
||||
// :2:10: error: extern structs cannot contain fields of type '?[*c]u8'
|
||||
// :2:10: note: only pointer like optionals are extern compatible
|
||||
|
||||
@ -9,4 +9,4 @@ comptime {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :2:5: error: struct fields cannot be 'noreturn'
|
||||
// :2:8: error: struct fields cannot be 'noreturn'
|
||||
|
||||
@ -12,9 +12,9 @@ comptime {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :2:5: error: extern structs cannot contain fields of type 'fn() callconv(.C) void'
|
||||
// :2:5: note: type has no guaranteed in-memory representation
|
||||
// :2:5: note: use '*const ' to make a function pointer type
|
||||
// :2:8: error: extern structs cannot contain fields of type 'fn() callconv(.C) void'
|
||||
// :2:8: note: type has no guaranteed in-memory representation
|
||||
// :2:8: note: use '*const ' to make a function pointer type
|
||||
// :8:13: error: C pointers cannot point to non-C-ABI-compatible type '[4]fn() callconv(.C) void'
|
||||
// :8:13: note: type has no guaranteed in-memory representation
|
||||
// :8:13: note: use '*const ' to make a function pointer type
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
pub export fn entry() void {
|
||||
const x: []u8 = undefined;
|
||||
const y: f32 = x;
|
||||
_ = y;
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :3:20: error: expected type 'f32', found '[]u8'
|
||||
@ -70,22 +70,22 @@ export fn entry12() void {
|
||||
// backend=llvm
|
||||
// target=native
|
||||
//
|
||||
// :3:9: error: packed structs cannot contain fields of type 'anyerror'
|
||||
// :3:9: note: type has no guaranteed in-memory representation
|
||||
// :8:9: error: packed structs cannot contain fields of type '[2]u24'
|
||||
// :8:9: note: type has no guaranteed in-memory representation
|
||||
// :13:9: error: packed structs cannot contain fields of type 'anyerror!u32'
|
||||
// :13:9: note: type has no guaranteed in-memory representation
|
||||
// :18:9: error: packed structs cannot contain fields of type 'tmp.S'
|
||||
// :18:9: note: only packed structs layout are allowed in packed types
|
||||
// :3:12: error: packed structs cannot contain fields of type 'anyerror'
|
||||
// :3:12: note: type has no guaranteed in-memory representation
|
||||
// :8:12: error: packed structs cannot contain fields of type '[2]u24'
|
||||
// :8:12: note: type has no guaranteed in-memory representation
|
||||
// :13:20: error: packed structs cannot contain fields of type 'anyerror!u32'
|
||||
// :13:20: note: type has no guaranteed in-memory representation
|
||||
// :18:12: error: packed structs cannot contain fields of type 'tmp.S'
|
||||
// :18:12: note: only packed structs layout are allowed in packed types
|
||||
// :56:11: note: struct declared here
|
||||
// :23:9: error: packed structs cannot contain fields of type 'tmp.U'
|
||||
// :23:9: note: only packed unions layout are allowed in packed types
|
||||
// :23:12: error: packed structs cannot contain fields of type 'tmp.U'
|
||||
// :23:12: note: only packed unions layout are allowed in packed types
|
||||
// :59:18: note: union declared here
|
||||
// :28:9: error: packed structs cannot contain fields of type '?anyerror'
|
||||
// :28:9: note: type has no guaranteed in-memory representation
|
||||
// :38:9: error: packed structs cannot contain fields of type 'fn() void'
|
||||
// :38:9: note: type has no guaranteed in-memory representation
|
||||
// :38:9: note: use '*const ' to make a function pointer type
|
||||
// :65:28: error: packed structs cannot contain fields of type '[]u8'
|
||||
// :65:28: note: slices have no guaranteed in-memory representation
|
||||
// :28:12: error: packed structs cannot contain fields of type '?anyerror'
|
||||
// :28:12: note: type has no guaranteed in-memory representation
|
||||
// :38:12: error: packed structs cannot contain fields of type 'fn() void'
|
||||
// :38:12: note: type has no guaranteed in-memory representation
|
||||
// :38:12: note: use '*const ' to make a function pointer type
|
||||
// :65:31: error: packed structs cannot contain fields of type '[]u8'
|
||||
// :65:31: note: slices have no guaranteed in-memory representation
|
||||
|
||||
@ -15,6 +15,6 @@ export fn entry() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :6:5: error: packed unions cannot contain fields of type 'tmp.Foo'
|
||||
// :6:5: note: only packed structs layout are allowed in packed types
|
||||
// :6:8: error: packed unions cannot contain fields of type 'tmp.Foo'
|
||||
// :6:8: note: only packed structs layout are allowed in packed types
|
||||
// :1:13: note: struct declared here
|
||||
|
||||
@ -18,5 +18,5 @@ export fn entry() void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :10:5: error: no field named 'D' in enum 'tmp.Letter'
|
||||
// :10:8: error: no field named 'D' in enum 'tmp.Letter'
|
||||
// :1:16: note: enum declared here
|
||||
|
||||
@ -16,5 +16,5 @@ export fn entry() usize {
|
||||
// error
|
||||
// target=native
|
||||
//
|
||||
// :10:5: error: no field named 'd' in enum 'tmp.E'
|
||||
// :10:8: error: no field named 'd' in enum 'tmp.E'
|
||||
// :1:11: note: enum declared here
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user