Skip to content

Commit

Permalink
compiler: improve "... contains reference to comptime var" errors
Browse files Browse the repository at this point in the history
`Sema.explainWhyValueContainsReferenceToComptimeVar` (concise name!)
adds notes to an error explaining how to get from a given `Value` to a
pointer to some `comptime var` (or a comptime field). Previously, this
error could be very opaque in any case where it wasn't obvious where the
comptime var pointer came from; particularly for type captures. Now, the
error notes explain this to the user.
  • Loading branch information
mlugg committed Jan 11, 2025
1 parent 6cfc9c0 commit 04c9f50
Show file tree
Hide file tree
Showing 12 changed files with 567 additions and 228 deletions.
63 changes: 41 additions & 22 deletions lib/std/zig/AstGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5315,8 +5315,9 @@ fn structDeclInner(
const fields_slice = wip_members.fieldsSlice();
const bodies_slice = astgen.scratch.items[bodies_start..];
try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len + 2 +
decls_slice.len + namespace.captures.count() + fields_slice.len + bodies_slice.len);
decls_slice.len + namespace.captures.count() * 2 + fields_slice.len + bodies_slice.len);
astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values()));
if (backing_int_ref != .none) {
astgen.extra.appendAssumeCapacity(@intCast(backing_int_body_len));
if (backing_int_body_len == 0) {
Expand Down Expand Up @@ -5595,8 +5596,9 @@ fn unionDeclInner(
wip_members.finishBits(bits_per_field);
const decls_slice = wip_members.declsSlice();
const fields_slice = wip_members.fieldsSlice();
try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len + body_len + fields_slice.len);
try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len + body_len + fields_slice.len);
astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values()));
astgen.extra.appendSliceAssumeCapacity(decls_slice);
astgen.appendBodyWithFixups(body);
astgen.extra.appendSliceAssumeCapacity(fields_slice);
Expand Down Expand Up @@ -5855,8 +5857,9 @@ fn containerDecl(
wip_members.finishBits(bits_per_field);
const decls_slice = wip_members.declsSlice();
const fields_slice = wip_members.fieldsSlice();
try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len + body_len + fields_slice.len);
try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len + body_len + fields_slice.len);
astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values()));
astgen.extra.appendSliceAssumeCapacity(decls_slice);
astgen.appendBodyWithFixups(body);
astgen.extra.appendSliceAssumeCapacity(fields_slice);
Expand Down Expand Up @@ -5910,8 +5913,9 @@ fn containerDecl(

wip_members.finishBits(0);
const decls_slice = wip_members.declsSlice();
try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len);
try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len);
astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values()));
astgen.extra.appendSliceAssumeCapacity(decls_slice);

block_scope.unstack();
Expand Down Expand Up @@ -8548,6 +8552,7 @@ fn localVarRef(
num_namespaces_out,
.{ .ref = local_val.inst },
.{ .token = local_val.token_src },
name_str_index,
) else local_val.inst;

return rvalueNoCoercePreRef(gz, ri, value_inst, ident);
Expand Down Expand Up @@ -8580,6 +8585,7 @@ fn localVarRef(
num_namespaces_out,
.{ .ref = local_ptr.ptr },
.{ .token = local_ptr.token_src },
name_str_index,
) else local_ptr.ptr;
local_ptr.used_as_lvalue = true;
return ptr_inst;
Expand All @@ -8591,6 +8597,7 @@ fn localVarRef(
num_namespaces_out,
.{ .ref_load = local_ptr.ptr },
.{ .token = local_ptr.token_src },
name_str_index,
) else try gz.addUnNode(.load, local_ptr.ptr, ident);
return rvalueNoCoercePreRef(gz, ri, val_inst, ident);
},
Expand Down Expand Up @@ -8636,6 +8643,7 @@ fn localVarRef(
found_namespaces_out,
.{ .decl_ref = name_str_index },
.{ .node = found_already.? },
name_str_index,
),
else => {
const result = try tunnelThroughClosure(
Expand All @@ -8644,6 +8652,7 @@ fn localVarRef(
found_namespaces_out,
.{ .decl_val = name_str_index },
.{ .node = found_already.? },
name_str_index,
);
return rvalueNoCoercePreRef(gz, ri, result, ident);
},
Expand Down Expand Up @@ -8680,6 +8689,7 @@ fn tunnelThroughClosure(
token: Ast.TokenIndex,
node: Ast.Node.Index,
},
name_str_index: Zir.NullTerminatedString,
) !Zir.Inst.Ref {
switch (value) {
.ref => |v| if (v.toIndex() == null) return v, // trivial value; do not need tunnel
Expand Down Expand Up @@ -8714,34 +8724,43 @@ fn tunnelThroughClosure(

// Now that we know the scopes we're tunneling through, begin adding
// captures as required, starting with the outermost namespace.
const root_capture = Zir.Inst.Capture.wrap(switch (value) {
const root_capture: Zir.Inst.Capture = .wrap(switch (value) {
.ref => |v| .{ .instruction = v.toIndex().? },
.ref_load => |v| .{ .instruction_load = v.toIndex().? },
.decl_val => |str| .{ .decl_val = str },
.decl_ref => |str| .{ .decl_ref = str },
});
var cur_capture_index = std.math.cast(
u16,
(try root_ns.captures.getOrPut(gpa, root_capture)).index,
) orelse return astgen.failNodeNotes(root_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{
switch (decl_src) {
.token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
.node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
},
try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
});

for (intermediate_tunnels) |tunnel_ns| {
cur_capture_index = std.math.cast(
u16,
(try tunnel_ns.captures.getOrPut(gpa, Zir.Inst.Capture.wrap(.{ .nested = cur_capture_index }))).index,
) orelse return astgen.failNodeNotes(tunnel_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{
const root_gop = try root_ns.captures.getOrPut(gpa, root_capture);
root_gop.value_ptr.* = name_str_index;
var cur_capture_index = std.math.cast(u16, root_gop.index) orelse return astgen.failNodeNotes(
root_ns.node,
"this compiler implementation only supports up to 65536 captures per namespace",
.{},
&.{
switch (decl_src) {
.token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
.node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
},
try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
});
},
);

for (intermediate_tunnels) |tunnel_ns| {
const tunnel_gop = try tunnel_ns.captures.getOrPut(gpa, .wrap(.{ .nested = cur_capture_index }));
tunnel_gop.value_ptr.* = name_str_index;
cur_capture_index = std.math.cast(u16, tunnel_gop.index) orelse return astgen.failNodeNotes(
tunnel_ns.node,
"this compiler implementation only supports up to 65536 captures per namespace",
.{},
&.{
switch (decl_src) {
.token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
.node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
},
try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
},
);
}

// Incorporate the capture index into the source hash, so that changes in
Expand Down Expand Up @@ -11920,7 +11939,7 @@ const Scope = struct {
declaring_gz: ?*GenZir,

/// Set of captures used by this namespace.
captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Capture, void) = .empty,
captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Capture, Zir.NullTerminatedString) = .empty,

fn deinit(self: *Namespace, gpa: Allocator) void {
self.decls.deinit(gpa);
Expand Down
50 changes: 27 additions & 23 deletions lib/std/zig/Zir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3284,24 +3284,25 @@ pub const Inst = struct {
/// 1. fields_len: u32, // if has_fields_len
/// 2. decls_len: u32, // if has_decls_len
/// 3. capture: Capture // for every captures_len
/// 4. backing_int_body_len: u32, // if has_backing_int
/// 5. backing_int_ref: Ref, // if has_backing_int and backing_int_body_len is 0
/// 6. backing_int_body_inst: Inst, // if has_backing_int and backing_int_body_len is > 0
/// 7. decl: Index, // for every decls_len; points to a `declaration` instruction
/// 8. flags: u32 // for every 8 fields
/// 4. capture_name: NullTerminatedString // for every captures_len
/// 5. backing_int_body_len: u32, // if has_backing_int
/// 6. backing_int_ref: Ref, // if has_backing_int and backing_int_body_len is 0
/// 7. backing_int_body_inst: Inst, // if has_backing_int and backing_int_body_len is > 0
/// 8. decl: Index, // for every decls_len; points to a `declaration` instruction
/// 9. flags: u32 // for every 8 fields
/// - sets of 4 bits:
/// 0b000X: whether corresponding field has an align expression
/// 0b00X0: whether corresponding field has a default expression
/// 0b0X00: whether corresponding field is comptime
/// 0bX000: whether corresponding field has a type expression
/// 9. fields: { // for every fields_len
/// 10. fields: { // for every fields_len
/// field_name: u32,
/// field_type: Ref, // if corresponding bit is not set. none means anytype.
/// field_type_body_len: u32, // if corresponding bit is set
/// align_body_len: u32, // if corresponding bit is set
/// init_body_len: u32, // if corresponding bit is set
/// }
/// 10. bodies: { // for every fields_len
/// 11. bodies: { // for every fields_len
/// field_type_body_inst: Inst, // for each field_type_body_len
/// align_body_inst: Inst, // for each align_body_len
/// init_body_inst: Inst, // for each init_body_len
Expand Down Expand Up @@ -3450,11 +3451,12 @@ pub const Inst = struct {
/// 3. fields_len: u32, // if has_fields_len
/// 4. decls_len: u32, // if has_decls_len
/// 5. capture: Capture // for every captures_len
/// 6. decl: Index, // for every decls_len; points to a `declaration` instruction
/// 7. inst: Index // for every body_len
/// 8. has_bits: u32 // for every 32 fields
/// 6. capture_name: NullTerminatedString // for every captures_len
/// 7. decl: Index, // for every decls_len; points to a `declaration` instruction
/// 8. inst: Index // for every body_len
/// 9. has_bits: u32 // for every 32 fields
/// - the bit is whether corresponding field has an value expression
/// 9. fields: { // for every fields_len
/// 10. fields: { // for every fields_len
/// field_name: u32,
/// value: Ref, // if corresponding bit is set
/// }
Expand Down Expand Up @@ -3488,15 +3490,16 @@ pub const Inst = struct {
/// 3. fields_len: u32, // if has_fields_len
/// 4. decls_len: u32, // if has_decls_len
/// 5. capture: Capture // for every captures_len
/// 6. decl: Index, // for every decls_len; points to a `declaration` instruction
/// 7. inst: Index // for every body_len
/// 8. has_bits: u32 // for every 8 fields
/// 6. capture_name: NullTerminatedString // for every captures_len
/// 7. decl: Index, // for every decls_len; points to a `declaration` instruction
/// 8. inst: Index // for every body_len
/// 9. has_bits: u32 // for every 8 fields
/// - sets of 4 bits:
/// 0b000X: whether corresponding field has a type expression
/// 0b00X0: whether corresponding field has a align expression
/// 0b0X00: whether corresponding field has a tag value expression
/// 0bX000: unused
/// 9. fields: { // for every fields_len
/// 10. fields: { // for every fields_len
/// field_name: NullTerminatedString, // null terminated string index
/// field_type: Ref, // if corresponding bit is set
/// align: Ref, // if corresponding bit is set
Expand Down Expand Up @@ -3537,7 +3540,8 @@ pub const Inst = struct {
/// 0. captures_len: u32, // if has_captures_len
/// 1. decls_len: u32, // if has_decls_len
/// 2. capture: Capture, // for every captures_len
/// 3. decl: Index, // for every decls_len; points to a `declaration` instruction
/// 3. capture_name: NullTerminatedString // for every captures_len
/// 4. decl: Index, // for every decls_len; points to a `declaration` instruction
pub const OpaqueDecl = struct {
src_line: u32,
/// This node provides a new absolute baseline node for all instructions within this struct.
Expand Down Expand Up @@ -3852,7 +3856,7 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
break :decls_len decls_len;
} else 0;

extra_index += captures_len;
extra_index += captures_len * 2;

if (small.has_backing_int) {
const backing_int_body_len = zir.extra[extra_index];
Expand Down Expand Up @@ -3887,7 +3891,7 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
break :decls_len decls_len;
} else 0;

extra_index += captures_len;
extra_index += captures_len * 2;

return .{
.extra_index = extra_index,
Expand All @@ -3912,7 +3916,7 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
break :decls_len decls_len;
} else 0;

extra_index += captures_len;
extra_index += captures_len * 2;

return .{
.extra_index = extra_index,
Expand All @@ -3934,7 +3938,7 @@ pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
break :captures_len captures_len;
} else 0;

extra_index += captures_len;
extra_index += captures_len * 2;

return .{
.extra_index = extra_index,
Expand Down Expand Up @@ -4349,7 +4353,7 @@ fn findTrackableInner(
extra_index += 1;
break :blk decls_len;
} else 0;
extra_index += captures_len;
extra_index += captures_len * 2;
if (small.has_backing_int) {
const backing_int_body_len = zir.extra[extra_index];
extra_index += 1;
Expand Down Expand Up @@ -4441,7 +4445,7 @@ fn findTrackableInner(
extra_index += 1;
break :blk decls_len;
} else 0;
extra_index += captures_len;
extra_index += captures_len * 2;
extra_index += decls_len;
const body = zir.bodySlice(extra_index, body_len);
try zir.findTrackableBody(gpa, contents, defers, body);
Expand Down Expand Up @@ -4471,7 +4475,7 @@ fn findTrackableInner(
extra_index += 1;
break :blk decls_len;
} else 0;
extra_index += captures_len;
extra_index += captures_len * 2;
extra_index += decls_len;
const body = zir.bodySlice(extra_index, body_len);
try zir.findTrackableBody(gpa, contents, defers, body);
Expand Down
Loading

0 comments on commit 04c9f50

Please sign in to comment.