authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-02 21:06:09-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-02 21:06:09-07:00
logd47f0abd5b5ba95bacd2d573aeabcc53db8c8fc3
tree18e901d51e99848a038e0e81eb412829b1069978
parent97d7fddfb78d17132749cae59fea36fe661bf642

stage2: Sema: implement validate_struct_init_ptr


4 files changed, 186 insertions(+), 20 deletions(-)

src/Module.zig+44-9
......@@ -364,7 +364,7 @@ pub const Struct = struct {
364364 /// Represents the declarations inside this struct.
365365 container: Scope.Container,
366366
367 /// Offset from Decl node index, points to the struct AST node.
367 /// Offset from `owner_decl`, points to the struct AST node.
368368 node_offset: i32,
369369
370370 pub const Field = struct {
......@@ -374,9 +374,16 @@ pub const Struct = struct {
374374 default_val: Value,
375375 };
376376
377 pub fn getFullyQualifiedName(struct_obj: *Struct, gpa: *Allocator) ![]u8 {
377 pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![]u8 {
378378 // TODO this should return e.g. "std.fs.Dir.OpenOptions"
379 return gpa.dupe(u8, mem.spanZ(struct_obj.owner_decl.name));
379 return gpa.dupe(u8, mem.spanZ(s.owner_decl.name));
380 }
381
382 pub fn srcLoc(s: Struct) SrcLoc {
383 return .{
384 .container = .{ .decl = s.owner_decl },
385 .lazy = .{ .node_offset = s.node_offset },
386 };
380387 }
381388};
382389
......@@ -1580,6 +1587,7 @@ pub const SrcLoc = struct {
15801587 .byte_offset,
15811588 .token_offset,
15821589 .node_offset,
1590 .node_offset_back2tok,
15831591 .node_offset_var_decl_ty,
15841592 .node_offset_for_cond,
15851593 .node_offset_builtin_call_arg0,
......@@ -1641,6 +1649,14 @@ pub const SrcLoc = struct {
16411649 const token_starts = tree.tokens.items(.start);
16421650 return token_starts[tok_index];
16431651 },
1652 .node_offset_back2tok => |node_off| {
1653 const decl = src_loc.container.decl;
1654 const node = decl.relativeToNodeIndex(node_off);
1655 const tree = decl.container.file_scope.base.tree();
1656 const tok_index = tree.firstToken(node) - 2;
1657 const token_starts = tree.tokens.items(.start);
1658 return token_starts[tok_index];
1659 },
16441660 .node_offset_var_decl_ty => |node_off| {
16451661 const decl = src_loc.container.decl;
16461662 const node = decl.relativeToNodeIndex(node_off);
......@@ -1755,7 +1771,10 @@ pub const SrcLoc = struct {
17551771 const node_datas = tree.nodes.items(.data);
17561772 const node_tags = tree.nodes.items(.tag);
17571773 const node = decl.relativeToNodeIndex(node_off);
1758 const tok_index = node_datas[node].rhs;
1774 const tok_index = switch (node_tags[node]) {
1775 .field_access => node_datas[node].rhs,
1776 else => tree.firstToken(node) - 2,
1777 };
17591778 const token_starts = tree.tokens.items(.start);
17601779 return token_starts[tok_index];
17611780 },
......@@ -1996,6 +2015,10 @@ pub const LazySrcLoc = union(enum) {
19962015 /// from its containing Decl node AST index.
19972016 /// The Decl is determined contextually.
19982017 node_offset: i32,
2018 /// The source location points to two tokens left of the first token of an AST node,
2019 /// which is this value offset from its containing Decl node AST index.
2020 /// The Decl is determined contextually.
2021 node_offset_back2tok: i32,
19992022 /// The source location points to a variable declaration type expression,
20002023 /// found by taking this AST node index offset from the containing
20012024 /// Decl AST node, which points to a variable declaration AST node. Next, navigate
......@@ -2034,10 +2057,10 @@ pub const LazySrcLoc = union(enum) {
20342057 /// to the callee expression.
20352058 /// The Decl is determined contextually.
20362059 node_offset_call_func: i32,
2037 /// The source location points to the field name of a field access expression,
2038 /// found by taking this AST node index offset from the containing
2039 /// Decl AST node, which points to a field access AST node. Next, navigate
2040 /// to the field name token.
2060 /// The payload is offset from the containing Decl AST node.
2061 /// The source location points to the field name of:
2062 /// * a field access expression (`a.b`), or
2063 /// * the operand ("b" node) of a field initialization expression (`.a = b`)
20412064 /// The Decl is determined contextually.
20422065 node_offset_field_name: i32,
20432066 /// The source location points to the pointer of a pointer deref expression,
......@@ -2122,6 +2145,7 @@ pub const LazySrcLoc = union(enum) {
21222145 .byte_offset,
21232146 .token_offset,
21242147 .node_offset,
2148 .node_offset_back2tok,
21252149 .node_offset_var_decl_ty,
21262150 .node_offset_for_cond,
21272151 .node_offset_builtin_call_arg0,
......@@ -2164,6 +2188,7 @@ pub const LazySrcLoc = union(enum) {
21642188 .byte_offset,
21652189 .token_offset,
21662190 .node_offset,
2191 .node_offset_back2tok,
21672192 .node_offset_var_decl_ty,
21682193 .node_offset_for_cond,
21692194 .node_offset_builtin_call_arg0,
......@@ -4011,13 +4036,23 @@ pub fn errNote(
40114036 parent: *ErrorMsg,
40124037 comptime format: []const u8,
40134038 args: anytype,
4039) error{OutOfMemory}!void {
4040 return mod.errNoteNonLazy(src.toSrcLoc(scope), parent, format, args);
4041}
4042
4043pub fn errNoteNonLazy(
4044 mod: *Module,
4045 src_loc: SrcLoc,
4046 parent: *ErrorMsg,
4047 comptime format: []const u8,
4048 args: anytype,
40144049) error{OutOfMemory}!void {
40154050 const msg = try std.fmt.allocPrint(mod.gpa, format, args);
40164051 errdefer mod.gpa.free(msg);
40174052
40184053 parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1);
40194054 parent.notes[parent.notes.len - 1] = .{
4020 .src_loc = src.toSrcLoc(scope),
4055 .src_loc = src_loc,
40214056 .msg = msg,
40224057 };
40234058}
src/Sema.zig+95-11
......@@ -838,12 +838,100 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Ind
838838 const tracy = trace(@src());
839839 defer tracy.end();
840840
841 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
842 const src = inst_data.src();
843 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
844 const instrs = sema.code.extra[extra.end..][0..extra.data.body_len];
841 const gpa = sema.gpa;
842 const mod = sema.mod;
843 const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;
844 const struct_init_src = validate_inst.src();
845 const validate_extra = sema.code.extraData(zir.Inst.Block, validate_inst.payload_index);
846 const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
847
848 const struct_obj: *Module.Struct = s: {
849 const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
850 const field_ptr_extra = sema.code.extraData(zir.Inst.Field, field_ptr_data.payload_index).data;
851 const object_ptr = try sema.resolveInst(field_ptr_extra.lhs);
852 break :s object_ptr.ty.elemType().castTag(.@"struct").?.data;
853 };
854
855 // Maps field index to field_ptr index of where it was already initialized.
856 const found_fields = try gpa.alloc(zir.Inst.Index, struct_obj.fields.entries.items.len);
857 defer gpa.free(found_fields);
858
859 mem.set(zir.Inst.Index, found_fields, 0);
860
861 for (instrs) |field_ptr| {
862 const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node;
863 const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_ptr_data.src_node };
864 const field_ptr_extra = sema.code.extraData(zir.Inst.Field, field_ptr_data.payload_index).data;
865 const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start);
866 const field_index = struct_obj.fields.getIndex(field_name) orelse
867 return sema.failWithBadFieldAccess(block, struct_obj, field_src, field_name);
868 if (found_fields[field_index] != 0) {
869 const other_field_ptr = found_fields[field_index];
870 const other_field_ptr_data = sema.code.instructions.items(.data)[other_field_ptr].pl_node;
871 const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_ptr_data.src_node };
872 const msg = msg: {
873 const msg = try mod.errMsg(&block.base, field_src, "duplicate field", .{});
874 errdefer msg.destroy(gpa);
875 try mod.errNote(&block.base, other_field_src, msg, "other field here", .{});
876 break :msg msg;
877 };
878 return mod.failWithOwnedErrorMsg(&block.base, msg);
879 }
880 found_fields[field_index] = field_ptr;
881 }
882
883 var root_msg: ?*Module.ErrorMsg = null;
884
885 for (found_fields) |field_ptr, i| {
886 if (field_ptr != 0) continue;
887
888 const field_name = struct_obj.fields.entries.items[i].key;
889 const template = "mising struct field: {s}";
890 const args = .{field_name};
891 if (root_msg) |msg| {
892 try mod.errNote(&block.base, struct_init_src, msg, template, args);
893 } else {
894 root_msg = try mod.errMsg(&block.base, struct_init_src, template, args);
895 }
896 }
897 if (root_msg) |msg| {
898 const fqn = try struct_obj.getFullyQualifiedName(gpa);
899 defer gpa.free(fqn);
900 try mod.errNoteNonLazy(
901 struct_obj.srcLoc(),
902 msg,
903 "'{s}' declared here",
904 .{fqn},
905 );
906 return mod.failWithOwnedErrorMsg(&block.base, msg);
907 }
908}
909
910fn failWithBadFieldAccess(
911 sema: *Sema,
912 block: *Scope.Block,
913 struct_obj: *Module.Struct,
914 field_src: LazySrcLoc,
915 field_name: []const u8,
916) InnerError {
917 const mod = sema.mod;
918 const gpa = sema.gpa;
845919
846 log.warn("TODO implement zirValidateStructInitPtr (compile errors for missing/dupe fields)", .{});
920 const fqn = try struct_obj.getFullyQualifiedName(gpa);
921 defer gpa.free(fqn);
922
923 const msg = msg: {
924 const msg = try mod.errMsg(
925 &block.base,
926 field_src,
927 "no field named '{s}' in struct '{s}'",
928 .{ field_name, fqn },
929 );
930 errdefer msg.destroy(gpa);
931 try mod.errNoteNonLazy(struct_obj.srcLoc(), msg, "'{s}' declared here", .{fqn});
932 break :msg msg;
933 };
934 return mod.failWithOwnedErrorMsg(&block.base, msg);
847935}
848936
849937fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
......@@ -4245,12 +4333,8 @@ fn analyzeStructFieldPtr(
42454333
42464334 const struct_obj = elem_ty.castTag(.@"struct").?.data;
42474335
4248 const field_index = struct_obj.fields.getIndex(field_name) orelse {
4249 // TODO note: struct S declared here
4250 return mod.fail(&block.base, field_name_src, "no field named '{s}' in struct '{}'", .{
4251 field_name, elem_ty,
4252 });
4253 };
4336 const field_index = struct_obj.fields.getIndex(field_name) orelse
4337 return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
42544338 const field = struct_obj.fields.entries.items[field_index].value;
42554339 const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One);
42564340 // TODO comptime field access
src/zir.zig+2
......@@ -660,6 +660,8 @@ pub const Inst = struct {
660660 /// Given a set of `field_ptr` instructions, assumes they are all part of a struct
661661 /// initialization expression, and emits compile errors for duplicate fields
662662 /// as well as missing fields, if applicable.
663 /// This instruction asserts that there is at least one field_ptr instruction,
664 /// because it must use one of them to find out the struct type.
663665 /// Uses the `pl_node` field. Payload is `Block`.
664666 validate_struct_init_ptr,
665667 /// A struct literal with a specified type, with no fields.
test/stage2/cbe.zig+45
......@@ -481,6 +481,51 @@ pub fn addCases(ctx: *TestContext) !void {
481481 \\}
482482 , "");
483483 }
484
485 {
486 var case = ctx.exeFromCompiledC("structs", .{});
487 case.addError(
488 \\const Point = struct { x: i32, y: i32 };
489 \\export fn main() c_int {
490 \\ var p: Point = .{
491 \\ .y = 24,
492 \\ .x = 12,
493 \\ .y = 24,
494 \\ };
495 \\ return p.y - p.x - p.x;
496 \\}
497 , &.{
498 ":6:10: error: duplicate field",
499 ":4:10: note: other field here",
500 });
501 case.addError(
502 \\const Point = struct { x: i32, y: i32 };
503 \\export fn main() c_int {
504 \\ var p: Point = .{
505 \\ .y = 24,
506 \\ };
507 \\ return p.y - p.x - p.x;
508 \\}
509 , &.{
510 ":3:21: error: mising struct field: x",
511 ":1:15: note: 'Point' declared here",
512 });
513 case.addError(
514 \\const Point = struct { x: i32, y: i32 };
515 \\export fn main() c_int {
516 \\ var p: Point = .{
517 \\ .x = 12,
518 \\ .y = 24,
519 \\ .z = 48,
520 \\ };
521 \\ return p.y - p.x - p.x;
522 \\}
523 , &.{
524 ":6:10: error: no field named 'z' in struct 'Point'",
525 ":1:15: note: 'Point' declared here",
526 });
527 }
528
484529 ctx.c("empty start function", linux_x64,
485530 \\export fn _start() noreturn {
486531 \\ unreachable;