| 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,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, |
| 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 | node_offset: i32, | 368 | node_offset: i32, |
| 369 | 369 | ||
| 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 | }; |
| 376 | 376 | ||
| 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 | }; |
| 382 | 389 | ||
| ... | @@ -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 containing | 2023 | /// found by taking this AST node index offset from the containing |
| 2001 | /// Decl AST node, which points to a variable declaration AST node. Next, navigate | 2024 | /// 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 containing | 2061 | /// The source location points to the field name of: |
| 2039 | /// Decl AST node, which points to a field access AST node. Next, navigate | 2062 | /// * 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 | |||
| 4043 | pub 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); |
| 4017 | 4052 | ||
| 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(); |
| 840 | 840 | ||
| 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 | |||
| 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 | fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | 937 | fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { |
| ... | @@ -4245,12 +4333,8 @@ fn analyzeStructFieldPtr( | ... | @@ -4245,12 +4333,8 @@ fn analyzeStructFieldPtr( |
| 4245 | 4333 | ||
| 4246 | const struct_obj = elem_ty.castTag(.@"struct").?.data; | 4334 | const struct_obj = elem_ty.castTag(.@"struct").?.data; |
| 4247 | 4335 | ||
| 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 here | 4337 | 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 access | 4340 | // 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 struct | 660 | /// Given a set of `field_ptr` instructions, assumes they are all part of a struct |
| 661 | /// initialization expression, and emits compile errors for duplicate fields | 661 | /// 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; |