mirror of
https://github.com/ziglang/zig.git
synced 2026-02-04 21:53:38 +00:00
stage2: Sema: implement validate_struct_init_ptr
This commit is contained in:
parent
97d7fddfb7
commit
d47f0abd5b
@ -364,7 +364,7 @@ pub const Struct = struct {
|
||||
/// Represents the declarations inside this struct.
|
||||
container: Scope.Container,
|
||||
|
||||
/// Offset from Decl node index, points to the struct AST node.
|
||||
/// Offset from `owner_decl`, points to the struct AST node.
|
||||
node_offset: i32,
|
||||
|
||||
pub const Field = struct {
|
||||
@ -374,9 +374,16 @@ pub const Struct = struct {
|
||||
default_val: Value,
|
||||
};
|
||||
|
||||
pub fn getFullyQualifiedName(struct_obj: *Struct, gpa: *Allocator) ![]u8 {
|
||||
pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![]u8 {
|
||||
// TODO this should return e.g. "std.fs.Dir.OpenOptions"
|
||||
return gpa.dupe(u8, mem.spanZ(struct_obj.owner_decl.name));
|
||||
return gpa.dupe(u8, mem.spanZ(s.owner_decl.name));
|
||||
}
|
||||
|
||||
pub fn srcLoc(s: Struct) SrcLoc {
|
||||
return .{
|
||||
.container = .{ .decl = s.owner_decl },
|
||||
.lazy = .{ .node_offset = s.node_offset },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -1580,6 +1587,7 @@ pub const SrcLoc = struct {
|
||||
.byte_offset,
|
||||
.token_offset,
|
||||
.node_offset,
|
||||
.node_offset_back2tok,
|
||||
.node_offset_var_decl_ty,
|
||||
.node_offset_for_cond,
|
||||
.node_offset_builtin_call_arg0,
|
||||
@ -1641,6 +1649,14 @@ pub const SrcLoc = struct {
|
||||
const token_starts = tree.tokens.items(.start);
|
||||
return token_starts[tok_index];
|
||||
},
|
||||
.node_offset_back2tok => |node_off| {
|
||||
const decl = src_loc.container.decl;
|
||||
const node = decl.relativeToNodeIndex(node_off);
|
||||
const tree = decl.container.file_scope.base.tree();
|
||||
const tok_index = tree.firstToken(node) - 2;
|
||||
const token_starts = tree.tokens.items(.start);
|
||||
return token_starts[tok_index];
|
||||
},
|
||||
.node_offset_var_decl_ty => |node_off| {
|
||||
const decl = src_loc.container.decl;
|
||||
const node = decl.relativeToNodeIndex(node_off);
|
||||
@ -1755,7 +1771,10 @@ pub const SrcLoc = struct {
|
||||
const node_datas = tree.nodes.items(.data);
|
||||
const node_tags = tree.nodes.items(.tag);
|
||||
const node = decl.relativeToNodeIndex(node_off);
|
||||
const tok_index = node_datas[node].rhs;
|
||||
const tok_index = switch (node_tags[node]) {
|
||||
.field_access => node_datas[node].rhs,
|
||||
else => tree.firstToken(node) - 2,
|
||||
};
|
||||
const token_starts = tree.tokens.items(.start);
|
||||
return token_starts[tok_index];
|
||||
},
|
||||
@ -1996,6 +2015,10 @@ pub const LazySrcLoc = union(enum) {
|
||||
/// from its containing Decl node AST index.
|
||||
/// The Decl is determined contextually.
|
||||
node_offset: i32,
|
||||
/// The source location points to two tokens left of the first token of an AST node,
|
||||
/// which is this value offset from its containing Decl node AST index.
|
||||
/// The Decl is determined contextually.
|
||||
node_offset_back2tok: i32,
|
||||
/// The source location points to a variable declaration type expression,
|
||||
/// found by taking this AST node index offset from the containing
|
||||
/// Decl AST node, which points to a variable declaration AST node. Next, navigate
|
||||
@ -2034,10 +2057,10 @@ pub const LazySrcLoc = union(enum) {
|
||||
/// to the callee expression.
|
||||
/// The Decl is determined contextually.
|
||||
node_offset_call_func: i32,
|
||||
/// The source location points to the field name of a field access expression,
|
||||
/// found by taking this AST node index offset from the containing
|
||||
/// Decl AST node, which points to a field access AST node. Next, navigate
|
||||
/// to the field name token.
|
||||
/// The payload is offset from the containing Decl AST node.
|
||||
/// The source location points to the field name of:
|
||||
/// * a field access expression (`a.b`), or
|
||||
/// * the operand ("b" node) of a field initialization expression (`.a = b`)
|
||||
/// The Decl is determined contextually.
|
||||
node_offset_field_name: i32,
|
||||
/// The source location points to the pointer of a pointer deref expression,
|
||||
@ -2122,6 +2145,7 @@ pub const LazySrcLoc = union(enum) {
|
||||
.byte_offset,
|
||||
.token_offset,
|
||||
.node_offset,
|
||||
.node_offset_back2tok,
|
||||
.node_offset_var_decl_ty,
|
||||
.node_offset_for_cond,
|
||||
.node_offset_builtin_call_arg0,
|
||||
@ -2164,6 +2188,7 @@ pub const LazySrcLoc = union(enum) {
|
||||
.byte_offset,
|
||||
.token_offset,
|
||||
.node_offset,
|
||||
.node_offset_back2tok,
|
||||
.node_offset_var_decl_ty,
|
||||
.node_offset_for_cond,
|
||||
.node_offset_builtin_call_arg0,
|
||||
@ -4011,13 +4036,23 @@ pub fn errNote(
|
||||
parent: *ErrorMsg,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) error{OutOfMemory}!void {
|
||||
return mod.errNoteNonLazy(src.toSrcLoc(scope), parent, format, args);
|
||||
}
|
||||
|
||||
pub fn errNoteNonLazy(
|
||||
mod: *Module,
|
||||
src_loc: SrcLoc,
|
||||
parent: *ErrorMsg,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
) error{OutOfMemory}!void {
|
||||
const msg = try std.fmt.allocPrint(mod.gpa, format, args);
|
||||
errdefer mod.gpa.free(msg);
|
||||
|
||||
parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1);
|
||||
parent.notes[parent.notes.len - 1] = .{
|
||||
.src_loc = src.toSrcLoc(scope),
|
||||
.src_loc = src_loc,
|
||||
.msg = msg,
|
||||
};
|
||||
}
|
||||
|
||||
106
src/Sema.zig
106
src/Sema.zig
@ -838,12 +838,100 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Ind
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
|
||||
const instrs = sema.code.extra[extra.end..][0..extra.data.body_len];
|
||||
const gpa = sema.gpa;
|
||||
const mod = sema.mod;
|
||||
const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const struct_init_src = validate_inst.src();
|
||||
const validate_extra = sema.code.extraData(zir.Inst.Block, validate_inst.payload_index);
|
||||
const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
|
||||
|
||||
log.warn("TODO implement zirValidateStructInitPtr (compile errors for missing/dupe fields)", .{});
|
||||
const struct_obj: *Module.Struct = s: {
|
||||
const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
|
||||
const field_ptr_extra = sema.code.extraData(zir.Inst.Field, field_ptr_data.payload_index).data;
|
||||
const object_ptr = try sema.resolveInst(field_ptr_extra.lhs);
|
||||
break :s object_ptr.ty.elemType().castTag(.@"struct").?.data;
|
||||
};
|
||||
|
||||
// Maps field index to field_ptr index of where it was already initialized.
|
||||
const found_fields = try gpa.alloc(zir.Inst.Index, struct_obj.fields.entries.items.len);
|
||||
defer gpa.free(found_fields);
|
||||
|
||||
mem.set(zir.Inst.Index, found_fields, 0);
|
||||
|
||||
for (instrs) |field_ptr| {
|
||||
const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node;
|
||||
const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_ptr_data.src_node };
|
||||
const field_ptr_extra = sema.code.extraData(zir.Inst.Field, field_ptr_data.payload_index).data;
|
||||
const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start);
|
||||
const field_index = struct_obj.fields.getIndex(field_name) orelse
|
||||
return sema.failWithBadFieldAccess(block, struct_obj, field_src, field_name);
|
||||
if (found_fields[field_index] != 0) {
|
||||
const other_field_ptr = found_fields[field_index];
|
||||
const other_field_ptr_data = sema.code.instructions.items(.data)[other_field_ptr].pl_node;
|
||||
const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_ptr_data.src_node };
|
||||
const msg = msg: {
|
||||
const msg = try mod.errMsg(&block.base, field_src, "duplicate field", .{});
|
||||
errdefer msg.destroy(gpa);
|
||||
try mod.errNote(&block.base, other_field_src, msg, "other field here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
}
|
||||
found_fields[field_index] = field_ptr;
|
||||
}
|
||||
|
||||
var root_msg: ?*Module.ErrorMsg = null;
|
||||
|
||||
for (found_fields) |field_ptr, i| {
|
||||
if (field_ptr != 0) continue;
|
||||
|
||||
const field_name = struct_obj.fields.entries.items[i].key;
|
||||
const template = "mising struct field: {s}";
|
||||
const args = .{field_name};
|
||||
if (root_msg) |msg| {
|
||||
try mod.errNote(&block.base, struct_init_src, msg, template, args);
|
||||
} else {
|
||||
root_msg = try mod.errMsg(&block.base, struct_init_src, template, args);
|
||||
}
|
||||
}
|
||||
if (root_msg) |msg| {
|
||||
const fqn = try struct_obj.getFullyQualifiedName(gpa);
|
||||
defer gpa.free(fqn);
|
||||
try mod.errNoteNonLazy(
|
||||
struct_obj.srcLoc(),
|
||||
msg,
|
||||
"'{s}' declared here",
|
||||
.{fqn},
|
||||
);
|
||||
return mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
}
|
||||
}
|
||||
|
||||
fn failWithBadFieldAccess(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
struct_obj: *Module.Struct,
|
||||
field_src: LazySrcLoc,
|
||||
field_name: []const u8,
|
||||
) InnerError {
|
||||
const mod = sema.mod;
|
||||
const gpa = sema.gpa;
|
||||
|
||||
const fqn = try struct_obj.getFullyQualifiedName(gpa);
|
||||
defer gpa.free(fqn);
|
||||
|
||||
const msg = msg: {
|
||||
const msg = try mod.errMsg(
|
||||
&block.base,
|
||||
field_src,
|
||||
"no field named '{s}' in struct '{s}'",
|
||||
.{ field_name, fqn },
|
||||
);
|
||||
errdefer msg.destroy(gpa);
|
||||
try mod.errNoteNonLazy(struct_obj.srcLoc(), msg, "'{s}' declared here", .{fqn});
|
||||
break :msg msg;
|
||||
};
|
||||
return mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
}
|
||||
|
||||
fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
|
||||
@ -4245,12 +4333,8 @@ fn analyzeStructFieldPtr(
|
||||
|
||||
const struct_obj = elem_ty.castTag(.@"struct").?.data;
|
||||
|
||||
const field_index = struct_obj.fields.getIndex(field_name) orelse {
|
||||
// TODO note: struct S declared here
|
||||
return mod.fail(&block.base, field_name_src, "no field named '{s}' in struct '{}'", .{
|
||||
field_name, elem_ty,
|
||||
});
|
||||
};
|
||||
const field_index = struct_obj.fields.getIndex(field_name) orelse
|
||||
return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
|
||||
const field = struct_obj.fields.entries.items[field_index].value;
|
||||
const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One);
|
||||
// TODO comptime field access
|
||||
|
||||
@ -660,6 +660,8 @@ pub const Inst = struct {
|
||||
/// Given a set of `field_ptr` instructions, assumes they are all part of a struct
|
||||
/// initialization expression, and emits compile errors for duplicate fields
|
||||
/// as well as missing fields, if applicable.
|
||||
/// This instruction asserts that there is at least one field_ptr instruction,
|
||||
/// because it must use one of them to find out the struct type.
|
||||
/// Uses the `pl_node` field. Payload is `Block`.
|
||||
validate_struct_init_ptr,
|
||||
/// A struct literal with a specified type, with no fields.
|
||||
|
||||
@ -481,6 +481,51 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exeFromCompiledC("structs", .{});
|
||||
case.addError(
|
||||
\\const Point = struct { x: i32, y: i32 };
|
||||
\\export fn main() c_int {
|
||||
\\ var p: Point = .{
|
||||
\\ .y = 24,
|
||||
\\ .x = 12,
|
||||
\\ .y = 24,
|
||||
\\ };
|
||||
\\ return p.y - p.x - p.x;
|
||||
\\}
|
||||
, &.{
|
||||
":6:10: error: duplicate field",
|
||||
":4:10: note: other field here",
|
||||
});
|
||||
case.addError(
|
||||
\\const Point = struct { x: i32, y: i32 };
|
||||
\\export fn main() c_int {
|
||||
\\ var p: Point = .{
|
||||
\\ .y = 24,
|
||||
\\ };
|
||||
\\ return p.y - p.x - p.x;
|
||||
\\}
|
||||
, &.{
|
||||
":3:21: error: mising struct field: x",
|
||||
":1:15: note: 'Point' declared here",
|
||||
});
|
||||
case.addError(
|
||||
\\const Point = struct { x: i32, y: i32 };
|
||||
\\export fn main() c_int {
|
||||
\\ var p: Point = .{
|
||||
\\ .x = 12,
|
||||
\\ .y = 24,
|
||||
\\ .z = 48,
|
||||
\\ };
|
||||
\\ return p.y - p.x - p.x;
|
||||
\\}
|
||||
, &.{
|
||||
":6:10: error: no field named 'z' in struct 'Point'",
|
||||
":1:15: note: 'Point' declared here",
|
||||
});
|
||||
}
|
||||
|
||||
ctx.c("empty start function", linux_x64,
|
||||
\\export fn _start() noreturn {
|
||||
\\ unreachable;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user