| author | |
| committer | |
| log | d47f0abd5b5ba95bacd2d573aeabcc53db8c8fc3 |
| tree | 18e901d51e99848a038e0e81eb412829b1069978 |
| parent | 97d7fddfb78d17132749cae59fea36fe661bf642 |
4 files changed, 186 insertions(+), 20 deletions(-)
src/Module.zig+44-9| ... | ... | @@ -364,7 +364,7 @@ pub const Struct = struct { |
| 364 | 364 | /// Represents the declarations inside this struct. |
| 365 | 365 | container: Scope.Container, |
| 366 | 366 | |
| 367 | /// Offset from Decl node index, points to the struct AST node. | |
| 367 | /// Offset from `owner_decl`, points to the struct AST node. | |
| 368 | 368 | node_offset: i32, |
| 369 | 369 | |
| 370 | 370 | pub const Field = struct { |
| ... | ... | @@ -374,9 +374,16 @@ pub const Struct = struct { |
| 374 | 374 | default_val: Value, |
| 375 | 375 | }; |
| 376 | 376 | |
| 377 | pub fn getFullyQualifiedName(struct_obj: *Struct, gpa: *Allocator) ![]u8 { | |
| 377 | pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![]u8 { | |
| 378 | 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 | }; |
| 382 | 389 | |
| ... | ... | @@ -1580,6 +1587,7 @@ pub const SrcLoc = struct { |
| 1580 | 1587 | .byte_offset, |
| 1581 | 1588 | .token_offset, |
| 1582 | 1589 | .node_offset, |
| 1590 | .node_offset_back2tok, | |
| 1583 | 1591 | .node_offset_var_decl_ty, |
| 1584 | 1592 | .node_offset_for_cond, |
| 1585 | 1593 | .node_offset_builtin_call_arg0, |
| ... | ... | @@ -1641,6 +1649,14 @@ pub const SrcLoc = struct { |
| 1641 | 1649 | const token_starts = tree.tokens.items(.start); |
| 1642 | 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 | 1660 | .node_offset_var_decl_ty => |node_off| { |
| 1645 | 1661 | const decl = src_loc.container.decl; |
| 1646 | 1662 | const node = decl.relativeToNodeIndex(node_off); |
| ... | ... | @@ -1755,7 +1771,10 @@ pub const SrcLoc = struct { |
| 1755 | 1771 | const node_datas = tree.nodes.items(.data); |
| 1756 | 1772 | const node_tags = tree.nodes.items(.tag); |
| 1757 | 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 | 1778 | const token_starts = tree.tokens.items(.start); |
| 1760 | 1779 | return token_starts[tok_index]; |
| 1761 | 1780 | }, |
| ... | ... | @@ -1996,6 +2015,10 @@ pub const LazySrcLoc = union(enum) { |
| 1996 | 2015 | /// from its containing Decl node AST index. |
| 1997 | 2016 | /// The Decl is determined contextually. |
| 1998 | 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 | 2022 | /// The source location points to a variable declaration type expression, |
| 2000 | 2023 | /// found by taking this AST node index offset from the containing |
| 2001 | 2024 | /// Decl AST node, which points to a variable declaration AST node. Next, navigate |
| ... | ... | @@ -2034,10 +2057,10 @@ pub const LazySrcLoc = union(enum) { |
| 2034 | 2057 | /// to the callee expression. |
| 2035 | 2058 | /// The Decl is determined contextually. |
| 2036 | 2059 | 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`) | |
| 2041 | 2064 | /// The Decl is determined contextually. |
| 2042 | 2065 | node_offset_field_name: i32, |
| 2043 | 2066 | /// The source location points to the pointer of a pointer deref expression, |
| ... | ... | @@ -2122,6 +2145,7 @@ pub const LazySrcLoc = union(enum) { |
| 2122 | 2145 | .byte_offset, |
| 2123 | 2146 | .token_offset, |
| 2124 | 2147 | .node_offset, |
| 2148 | .node_offset_back2tok, | |
| 2125 | 2149 | .node_offset_var_decl_ty, |
| 2126 | 2150 | .node_offset_for_cond, |
| 2127 | 2151 | .node_offset_builtin_call_arg0, |
| ... | ... | @@ -2164,6 +2188,7 @@ pub const LazySrcLoc = union(enum) { |
| 2164 | 2188 | .byte_offset, |
| 2165 | 2189 | .token_offset, |
| 2166 | 2190 | .node_offset, |
| 2191 | .node_offset_back2tok, | |
| 2167 | 2192 | .node_offset_var_decl_ty, |
| 2168 | 2193 | .node_offset_for_cond, |
| 2169 | 2194 | .node_offset_builtin_call_arg0, |
| ... | ... | @@ -4011,13 +4036,23 @@ pub fn errNote( |
| 4011 | 4036 | parent: *ErrorMsg, |
| 4012 | 4037 | comptime format: []const u8, |
| 4013 | 4038 | args: anytype, |
| 4039 | ) error{OutOfMemory}!void { | |
| 4040 | return mod.errNoteNonLazy(src.toSrcLoc(scope), parent, format, args); | |
| 4041 | } | |
| 4042 | ||
| 4043 | pub fn errNoteNonLazy( | |
| 4044 | mod: *Module, | |
| 4045 | src_loc: SrcLoc, | |
| 4046 | parent: *ErrorMsg, | |
| 4047 | comptime format: []const u8, | |
| 4048 | args: anytype, | |
| 4014 | 4049 | ) error{OutOfMemory}!void { |
| 4015 | 4050 | const msg = try std.fmt.allocPrint(mod.gpa, format, args); |
| 4016 | 4051 | errdefer mod.gpa.free(msg); |
| 4017 | 4052 | |
| 4018 | 4053 | parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1); |
| 4019 | 4054 | parent.notes[parent.notes.len - 1] = .{ |
| 4020 | .src_loc = src.toSrcLoc(scope), | |
| 4055 | .src_loc = src_loc, | |
| 4021 | 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 | 838 | const tracy = trace(@src()); |
| 839 | 839 | defer tracy.end(); |
| 840 | 840 | |
| 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 | ||
| 910 | fn 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; | |
| 845 | 919 | |
| 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 | } |
| 848 | 936 | |
| 849 | 937 | fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { |
| ... | ... | @@ -4245,12 +4333,8 @@ fn analyzeStructFieldPtr( |
| 4245 | 4333 | |
| 4246 | 4334 | const struct_obj = elem_ty.castTag(.@"struct").?.data; |
| 4247 | 4335 | |
| 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); | |
| 4254 | 4338 | const field = struct_obj.fields.entries.items[field_index].value; |
| 4255 | 4339 | const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One); |
| 4256 | 4340 | // TODO comptime field access |
src/zir.zig+2| ... | ... | @@ -660,6 +660,8 @@ pub const Inst = struct { |
| 660 | 660 | /// Given a set of `field_ptr` instructions, assumes they are all part of a struct |
| 661 | 661 | /// initialization expression, and emits compile errors for duplicate fields |
| 662 | 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 | 665 | /// Uses the `pl_node` field. Payload is `Block`. |
| 664 | 666 | validate_struct_init_ptr, |
| 665 | 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 | 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 | 529 | ctx.c("empty start function", linux_x64, |
| 485 | 530 | \\export fn _start() noreturn { |
| 486 | 531 | \\ unreachable; |