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 {...@@ -364,7 +364,7 @@ pub const Struct = struct {
364 /// Represents the declarations inside this struct.364 /// Represents the declarations inside this struct.
365 container: Scope.Container,365 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.
368 node_offset: i32,368 node_offset: i32,
369369
370 pub const Field = struct {370 pub const Field = struct {
...@@ -374,9 +374,16 @@ pub const Struct = struct {...@@ -374,9 +374,16 @@ pub const Struct = struct {
374 default_val: Value,374 default_val: Value,
375 };375 };
376376
377 pub fn getFullyQualifiedName(struct_obj: *Struct, gpa: *Allocator) ![]u8 {377 pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![]u8 {
378 // TODO this should return e.g. "std.fs.Dir.OpenOptions"378 // 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 };
380 }387 }
381};388};
382389
...@@ -1580,6 +1587,7 @@ pub const SrcLoc = struct {...@@ -1580,6 +1587,7 @@ pub const SrcLoc = struct {
1580 .byte_offset,1587 .byte_offset,
1581 .token_offset,1588 .token_offset,
1582 .node_offset,1589 .node_offset,
1590 .node_offset_back2tok,
1583 .node_offset_var_decl_ty,1591 .node_offset_var_decl_ty,
1584 .node_offset_for_cond,1592 .node_offset_for_cond,
1585 .node_offset_builtin_call_arg0,1593 .node_offset_builtin_call_arg0,
...@@ -1641,6 +1649,14 @@ pub const SrcLoc = struct {...@@ -1641,6 +1649,14 @@ pub const SrcLoc = struct {
1641 const token_starts = tree.tokens.items(.start);1649 const token_starts = tree.tokens.items(.start);
1642 return token_starts[tok_index];1650 return token_starts[tok_index];
1643 },1651 },
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 },
1644 .node_offset_var_decl_ty => |node_off| {1660 .node_offset_var_decl_ty => |node_off| {
1645 const decl = src_loc.container.decl;1661 const decl = src_loc.container.decl;
1646 const node = decl.relativeToNodeIndex(node_off);1662 const node = decl.relativeToNodeIndex(node_off);
...@@ -1755,7 +1771,10 @@ pub const SrcLoc = struct {...@@ -1755,7 +1771,10 @@ pub const SrcLoc = struct {
1755 const node_datas = tree.nodes.items(.data);1771 const node_datas = tree.nodes.items(.data);
1756 const node_tags = tree.nodes.items(.tag);1772 const node_tags = tree.nodes.items(.tag);
1757 const node = decl.relativeToNodeIndex(node_off);1773 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 };
1759 const token_starts = tree.tokens.items(.start);1778 const token_starts = tree.tokens.items(.start);
1760 return token_starts[tok_index];1779 return token_starts[tok_index];
1761 },1780 },
...@@ -1996,6 +2015,10 @@ pub const LazySrcLoc = union(enum) {...@@ -1996,6 +2015,10 @@ pub const LazySrcLoc = union(enum) {
1996 /// from its containing Decl node AST index.2015 /// from its containing Decl node AST index.
1997 /// The Decl is determined contextually.2016 /// The Decl is determined contextually.
1998 node_offset: i32,2017 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,
1999 /// The source location points to a variable declaration type expression,2022 /// The source location points to a variable declaration type expression,
2000 /// found by taking this AST node index offset from the containing2023 /// found by taking this AST node index offset from the containing
2001 /// Decl AST node, which points to a variable declaration AST node. Next, navigate2024 /// Decl AST node, which points to a variable declaration AST node. Next, navigate
...@@ -2034,10 +2057,10 @@ pub const LazySrcLoc = union(enum) {...@@ -2034,10 +2057,10 @@ pub const LazySrcLoc = union(enum) {
2034 /// to the callee expression.2057 /// to the callee expression.
2035 /// The Decl is determined contextually.2058 /// The Decl is determined contextually.
2036 node_offset_call_func: i32,2059 node_offset_call_func: i32,
2037 /// The source location points to the field name of a field access expression,2060 /// The payload is offset from the containing Decl AST node.
2038 /// found by taking this AST node index offset from the containing2061 /// The source location points to the field name of:
2039 /// Decl AST node, which points to a field access AST node. Next, navigate2062 /// * a field access expression (`a.b`), or
2040 /// to the field name token.2063 /// * the operand ("b" node) of a field initialization expression (`.a = b`)
2041 /// The Decl is determined contextually.2064 /// The Decl is determined contextually.
2042 node_offset_field_name: i32,2065 node_offset_field_name: i32,
2043 /// The source location points to the pointer of a pointer deref expression,2066 /// The source location points to the pointer of a pointer deref expression,
...@@ -2122,6 +2145,7 @@ pub const LazySrcLoc = union(enum) {...@@ -2122,6 +2145,7 @@ pub const LazySrcLoc = union(enum) {
2122 .byte_offset,2145 .byte_offset,
2123 .token_offset,2146 .token_offset,
2124 .node_offset,2147 .node_offset,
2148 .node_offset_back2tok,
2125 .node_offset_var_decl_ty,2149 .node_offset_var_decl_ty,
2126 .node_offset_for_cond,2150 .node_offset_for_cond,
2127 .node_offset_builtin_call_arg0,2151 .node_offset_builtin_call_arg0,
...@@ -2164,6 +2188,7 @@ pub const LazySrcLoc = union(enum) {...@@ -2164,6 +2188,7 @@ pub const LazySrcLoc = union(enum) {
2164 .byte_offset,2188 .byte_offset,
2165 .token_offset,2189 .token_offset,
2166 .node_offset,2190 .node_offset,
2191 .node_offset_back2tok,
2167 .node_offset_var_decl_ty,2192 .node_offset_var_decl_ty,
2168 .node_offset_for_cond,2193 .node_offset_for_cond,
2169 .node_offset_builtin_call_arg0,2194 .node_offset_builtin_call_arg0,
...@@ -4011,13 +4036,23 @@ pub fn errNote(...@@ -4011,13 +4036,23 @@ pub fn errNote(
4011 parent: *ErrorMsg,4036 parent: *ErrorMsg,
4012 comptime format: []const u8,4037 comptime format: []const u8,
4013 args: anytype,4038 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,
4014) error{OutOfMemory}!void {4049) error{OutOfMemory}!void {
4015 const msg = try std.fmt.allocPrint(mod.gpa, format, args);4050 const msg = try std.fmt.allocPrint(mod.gpa, format, args);
4016 errdefer mod.gpa.free(msg);4051 errdefer mod.gpa.free(msg);
40174052
4018 parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1);4053 parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1);
4019 parent.notes[parent.notes.len - 1] = .{4054 parent.notes[parent.notes.len - 1] = .{
4020 .src_loc = src.toSrcLoc(scope),4055 .src_loc = src_loc,
4021 .msg = msg,4056 .msg = msg,
4022 };4057 };
4023}4058}
src/Sema.zig+95-11
...@@ -838,12 +838,100 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Ind...@@ -838,12 +838,100 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Ind
838 const tracy = trace(@src());838 const tracy = trace(@src());
839 defer tracy.end();839 defer tracy.end();
840840
841 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;841 const gpa = sema.gpa;
842 const src = inst_data.src();842 const mod = sema.mod;
843 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);843 const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;
844 const instrs = sema.code.extra[extra.end..][0..extra.data.body_len];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);
847}935}
848936
849fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {937fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void {
...@@ -4245,12 +4333,8 @@ fn analyzeStructFieldPtr(...@@ -4245,12 +4333,8 @@ fn analyzeStructFieldPtr(
42454333
4246 const struct_obj = elem_ty.castTag(.@"struct").?.data;4334 const struct_obj = elem_ty.castTag(.@"struct").?.data;
42474335
4248 const field_index = struct_obj.fields.getIndex(field_name) orelse {4336 const field_index = struct_obj.fields.getIndex(field_name) orelse
4249 // TODO note: struct S declared here4337 return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name);
4250 return mod.fail(&block.base, field_name_src, "no field named '{s}' in struct '{}'", .{
4251 field_name, elem_ty,
4252 });
4253 };
4254 const field = struct_obj.fields.entries.items[field_index].value;4338 const field = struct_obj.fields.entries.items[field_index].value;
4255 const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One);4339 const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One);
4256 // TODO comptime field access4340 // TODO comptime field access
src/zir.zig+2
...@@ -660,6 +660,8 @@ pub const Inst = struct {...@@ -660,6 +660,8 @@ pub const Inst = struct {
660 /// Given a set of `field_ptr` instructions, assumes they are all part of a struct660 /// Given a set of `field_ptr` instructions, assumes they are all part of a struct
661 /// initialization expression, and emits compile errors for duplicate fields661 /// initialization expression, and emits compile errors for duplicate fields
662 /// as well as missing fields, if applicable.662 /// 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.
663 /// Uses the `pl_node` field. Payload is `Block`.665 /// Uses the `pl_node` field. Payload is `Block`.
664 validate_struct_init_ptr,666 validate_struct_init_ptr,
665 /// A struct literal with a specified type, with no fields.667 /// 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 {...@@ -481,6 +481,51 @@ pub fn addCases(ctx: *TestContext) !void {
481 \\}481 \\}
482 , "");482 , "");
483 }483 }
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
484 ctx.c("empty start function", linux_x64,529 ctx.c("empty start function", linux_x64,
485 \\export fn _start() noreturn {530 \\export fn _start() noreturn {
486 \\ unreachable;531 \\ unreachable;