| ... | @@ -244,9 +244,9 @@ pub const Decl = struct { | ... | @@ -244,9 +244,9 @@ pub const Decl = struct { |
| 244 | } | 244 | } |
| 245 | | 245 | |
| 246 | pub fn src(self: Decl) usize { | 246 | pub fn src(self: Decl) usize { |
| 247 | const tree = self.container.file_scope.contents.tree; | 247 | const tree = &self.container.file_scope.tree; |
| 248 | const decl_node = tree.root_node.decls()[self.src_index]; | 248 | const decl_node = tree.rootDecls()[self.src_index]; |
| 249 | return tree.token_locs[decl_node.firstToken()].start; | 249 | return tree.tokens.items(.start)[tree.firstToken(decl_node)]; |
| 250 | } | 250 | } |
| 251 | | 251 | |
| 252 | pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash { | 252 | pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash { |
| ... | @@ -536,6 +536,12 @@ pub const Scope = struct { | ... | @@ -536,6 +536,12 @@ pub const Scope = struct { |
| 536 | pub const File = struct { | 536 | pub const File = struct { |
| 537 | pub const base_tag: Tag = .file; | 537 | pub const base_tag: Tag = .file; |
| 538 | base: Scope = Scope{ .tag = base_tag }, | 538 | base: Scope = Scope{ .tag = base_tag }, |
| | 539 | status: enum { |
| | 540 | never_loaded, |
| | 541 | unloaded_success, |
| | 542 | unloaded_parse_failure, |
| | 543 | loaded_success, |
| | 544 | }, |
| 539 | | 545 | |
| 540 | /// Relative to the owning package's root_src_dir. | 546 | /// Relative to the owning package's root_src_dir. |
| 541 | /// Reference to external memory, not owned by File. | 547 | /// Reference to external memory, not owned by File. |
| ... | @@ -544,16 +550,8 @@ pub const Scope = struct { | ... | @@ -544,16 +550,8 @@ pub const Scope = struct { |
| 544 | unloaded: void, | 550 | unloaded: void, |
| 545 | bytes: [:0]const u8, | 551 | bytes: [:0]const u8, |
| 546 | }, | 552 | }, |
| 547 | contents: union { | 553 | /// Whether this is populated or not depends on `status`. |
| 548 | not_available: void, | 554 | tree: ast.Tree, |
| 549 | tree: *ast.Tree, | | |
| 550 | }, | | |
| 551 | status: enum { | | |
| 552 | never_loaded, | | |
| 553 | unloaded_success, | | |
| 554 | unloaded_parse_failure, | | |
| 555 | loaded_success, | | |
| 556 | }, | | |
| 557 | /// Package that this file is a part of, managed externally. | 555 | /// Package that this file is a part of, managed externally. |
| 558 | pkg: *Package, | 556 | pkg: *Package, |
| 559 | | 557 | |
| ... | @@ -567,7 +565,7 @@ pub const Scope = struct { | ... | @@ -567,7 +565,7 @@ pub const Scope = struct { |
| 567 | => {}, | 565 | => {}, |
| 568 | | 566 | |
| 569 | .loaded_success => { | 567 | .loaded_success => { |
| 570 | self.contents.tree.deinit(); | 568 | self.tree.deinit(gpa); |
| 571 | self.status = .unloaded_success; | 569 | self.status = .unloaded_success; |
| 572 | }, | 570 | }, |
| 573 | } | 571 | } |
| ... | @@ -905,7 +903,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { | ... | @@ -905,7 +903,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 905 | .unreferenced => false, | 903 | .unreferenced => false, |
| 906 | }; | 904 | }; |
| 907 | | 905 | |
| 908 | const type_changed = mod.astGenAndAnalyzeDecl(decl) catch |err| switch (err) { | 906 | const type_changed = mod.astgenAndSemaDecl(decl) catch |err| switch (err) { |
| 909 | error.OutOfMemory => return error.OutOfMemory, | 907 | error.OutOfMemory => return error.OutOfMemory, |
| 910 | error.AnalysisFail => return error.AnalysisFail, | 908 | error.AnalysisFail => return error.AnalysisFail, |
| 911 | else => { | 909 | else => { |
| ... | @@ -947,129 +945,69 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { | ... | @@ -947,129 +945,69 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 947 | } | 945 | } |
| 948 | } | 946 | } |
| 949 | | 947 | |
| 950 | fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | 948 | /// Returns `true` if the Decl type changed. |
| | 949 | /// Returns `true` if this is the first time analyzing the Decl. |
| | 950 | /// Returns `false` otherwise. |
| | 951 | fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { |
| 951 | const tracy = trace(@src()); | 952 | const tracy = trace(@src()); |
| 952 | defer tracy.end(); | 953 | defer tracy.end(); |
| 953 | | 954 | |
| 954 | const tree = try self.getAstTree(decl.container.file_scope); | 955 | const tree = try mod.getAstTree(decl.container.file_scope); |
| 955 | const ast_node = tree.root_node.decls()[decl.src_index]; | 956 | const node_tags = tree.nodes.items(.tag); |
| 956 | switch (ast_node.tag) { | 957 | const node_datas = tree.nodes.items(.data); |
| 957 | .FnProto => { | 958 | const decl_node = tree.rootDecls()[decl.src_index]; |
| 958 | const fn_proto = ast_node.castTag(.FnProto).?; | 959 | switch (node_tags[decl_node]) { |
| | 960 | .fn_decl => { |
| | 961 | const fn_proto = node_datas[decl_node].lhs; |
| | 962 | const body = node_datas[decl_node].rhs; |
| | 963 | switch (node_tags[fn_proto]) { |
| | 964 | .fn_proto_simple => { |
| | 965 | var params: [1]ast.Node.Index = undefined; |
| | 966 | return mod.astgenAndSemaFn(decl, tree, body, tree.fnProtoSimple(&params, fn_proto)); |
| | 967 | }, |
| | 968 | .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree, body, tree.fnProtoMulti(fn_proto)), |
| | 969 | .fn_proto_one => { |
| | 970 | var params: [1]ast.Node.Index = undefined; |
| | 971 | return mod.astgenAndSemaFn(decl, tree, body, tree.fnProtoOne(&params, fn_proto)); |
| | 972 | }, |
| | 973 | .fn_proto => return mod.astgenAndSemaFn(decl, tree, body, tree.fnProto(fn_proto)), |
| | 974 | else => unreachable, |
| | 975 | } |
| | 976 | }, |
| | 977 | .fn_proto_simple => { |
| | 978 | var params: [1]ast.Node.Index = undefined; |
| | 979 | return mod.astgenAndSemaFn(decl, tree, null, tree.fnProtoSimple(&params, decl_node)); |
| | 980 | }, |
| | 981 | .fn_proto_multi => return mod.astgenAndSemaFn(decl, tree, null, tree.fnProtoMulti(decl_node)), |
| | 982 | .fn_proto_one => { |
| | 983 | var params: [1]ast.Node.Index = undefined; |
| | 984 | return mod.astgenAndSemaFn(decl, tree, null, tree.fnProtoOne(&params, decl_node)); |
| | 985 | }, |
| | 986 | .fn_proto => return mod.astgenAndSemaFn(decl, tree, null, tree.fnProto(decl_node)), |
| 959 | | 987 | |
| | 988 | .global_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.globalVarDecl(decl_node)), |
| | 989 | .local_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.localVarDecl(decl_node)), |
| | 990 | .simple_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.simpleVarDecl(decl_node)), |
| | 991 | .aligned_var_decl => return mod.astgenAndSemaVarDecl(decl, tree, tree.alignedVarDecl(decl_node)), |
| | 992 | |
| | 993 | .@"comptime" => { |
| 960 | decl.analysis = .in_progress; | 994 | decl.analysis = .in_progress; |
| 961 | | 995 | |
| 962 | // This arena allocator's memory is discarded at the end of this function. It is used | 996 | // A comptime decl does not store any value so we can just deinit this arena after analysis is done. |
| 963 | // to determine the type of the function, and hence the type of the decl, which is needed | 997 | var analysis_arena = std.heap.ArenaAllocator.init(self.gpa); |
| 964 | // to complete the Decl analysis. | 998 | defer analysis_arena.deinit(); |
| 965 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | 999 | var gen_scope: Scope.GenZIR = .{ |
| 966 | defer fn_type_scope_arena.deinit(); | | |
| 967 | var fn_type_scope: Scope.GenZIR = .{ | | |
| 968 | .decl = decl, | 1000 | .decl = decl, |
| 969 | .arena = &fn_type_scope_arena.allocator, | 1001 | .arena = &analysis_arena.allocator, |
| 970 | .parent = &decl.container.base, | 1002 | .parent = &decl.container.base, |
| 971 | }; | 1003 | }; |
| 972 | defer fn_type_scope.instructions.deinit(self.gpa); | 1004 | defer gen_scope.instructions.deinit(self.gpa); |
| 973 | | | |
| 974 | decl.is_pub = fn_proto.getVisibToken() != null; | | |
| 975 | | | |
| 976 | const param_decls = fn_proto.params(); | | |
| 977 | const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len); | | |
| 978 | | | |
| 979 | const fn_src = tree.token_locs[fn_proto.fn_token].start; | | |
| 980 | const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ | | |
| 981 | .ty = Type.initTag(.type), | | |
| 982 | .val = Value.initTag(.type_type), | | |
| 983 | }); | | |
| 984 | const type_type_rl: astgen.ResultLoc = .{ .ty = type_type }; | | |
| 985 | for (param_decls) |param_decl, i| { | | |
| 986 | const param_type_node = switch (param_decl.param_type) { | | |
| 987 | .any_type => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement anytype parameter", .{}), | | |
| 988 | .type_expr => |node| node, | | |
| 989 | }; | | |
| 990 | param_types[i] = try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node); | | |
| 991 | } | | |
| 992 | if (fn_proto.getVarArgsToken()) |var_args_token| { | | |
| 993 | return self.failTok(&fn_type_scope.base, var_args_token, "TODO implement var args", .{}); | | |
| 994 | } | | |
| 995 | if (fn_proto.getLibName()) |lib_name| blk: { | | |
| 996 | const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name.firstToken()), "\""); // TODO: call identifierTokenString | | |
| 997 | log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str}); | | |
| 998 | const target = self.comp.getTarget(); | | |
| 999 | if (target_util.is_libc_lib_name(target, lib_name_str)) { | | |
| 1000 | if (!self.comp.bin_file.options.link_libc) { | | |
| 1001 | return self.failNode( | | |
| 1002 | &fn_type_scope.base, | | |
| 1003 | lib_name, | | |
| 1004 | "dependency on libc must be explicitly specified in the build command", | | |
| 1005 | .{}, | | |
| 1006 | ); | | |
| 1007 | } | | |
| 1008 | break :blk; | | |
| 1009 | } | | |
| 1010 | if (target_util.is_libcpp_lib_name(target, lib_name_str)) { | | |
| 1011 | if (!self.comp.bin_file.options.link_libcpp) { | | |
| 1012 | return self.failNode( | | |
| 1013 | &fn_type_scope.base, | | |
| 1014 | lib_name, | | |
| 1015 | "dependency on libc++ must be explicitly specified in the build command", | | |
| 1016 | .{}, | | |
| 1017 | ); | | |
| 1018 | } | | |
| 1019 | break :blk; | | |
| 1020 | } | | |
| 1021 | if (!target.isWasm() and !self.comp.bin_file.options.pic) { | | |
| 1022 | return self.failNode( | | |
| 1023 | &fn_type_scope.base, | | |
| 1024 | lib_name, | | |
| 1025 | "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", | | |
| 1026 | .{ lib_name, lib_name }, | | |
| 1027 | ); | | |
| 1028 | } | | |
| 1029 | self.comp.stage1AddLinkLib(lib_name_str) catch |err| { | | |
| 1030 | return self.failNode( | | |
| 1031 | &fn_type_scope.base, | | |
| 1032 | lib_name, | | |
| 1033 | "unable to add link lib '{s}': {s}", | | |
| 1034 | .{ lib_name, @errorName(err) }, | | |
| 1035 | ); | | |
| 1036 | }; | | |
| 1037 | } | | |
| 1038 | if (fn_proto.getAlignExpr()) |align_expr| { | | |
| 1039 | return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{}); | | |
| 1040 | } | | |
| 1041 | if (fn_proto.getSectionExpr()) |sect_expr| { | | |
| 1042 | return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{}); | | |
| 1043 | } | | |
| 1044 | if (fn_proto.getCallconvExpr()) |callconv_expr| { | | |
| 1045 | return self.failNode( | | |
| 1046 | &fn_type_scope.base, | | |
| 1047 | callconv_expr, | | |
| 1048 | "TODO implement function calling convention expression", | | |
| 1049 | .{}, | | |
| 1050 | ); | | |
| 1051 | } | | |
| 1052 | const return_type_expr = switch (fn_proto.return_type) { | | |
| 1053 | .Explicit => |node| node, | | |
| 1054 | .InferErrorSet => |node| return self.failNode(&fn_type_scope.base, node, "TODO implement inferred error sets", .{}), | | |
| 1055 | .Invalid => |tok| return self.failTok(&fn_type_scope.base, tok, "unable to parse return type", .{}), | | |
| 1056 | }; | | |
| 1057 | | | |
| 1058 | const return_type_inst = try astgen.expr(self, &fn_type_scope.base, type_type_rl, return_type_expr); | | |
| 1059 | const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{ | | |
| 1060 | .return_type = return_type_inst, | | |
| 1061 | .param_types = param_types, | | |
| 1062 | }, .{}); | | |
| 1063 | | 1005 | |
| | 1006 | _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr); |
| 1064 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { | 1007 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { |
| 1065 | zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; | 1008 | zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {}; |
| 1066 | } | 1009 | } |
| 1067 | | 1010 | |
| 1068 | // We need the memory for the Type to go into the arena for the Decl | | |
| 1069 | var decl_arena = std.heap.ArenaAllocator.init(self.gpa); | | |
| 1070 | errdefer decl_arena.deinit(); | | |
| 1071 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | | |
| 1072 | | | |
| 1073 | var inst_table = Scope.Block.InstTable.init(self.gpa); | 1011 | var inst_table = Scope.Block.InstTable.init(self.gpa); |
| 1074 | defer inst_table.deinit(); | 1012 | defer inst_table.deinit(); |
| 1075 | | 1013 | |
| ... | @@ -1082,426 +1020,561 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | ... | @@ -1082,426 +1020,561 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1082 | .owner_decl = decl, | 1020 | .owner_decl = decl, |
| 1083 | .src_decl = decl, | 1021 | .src_decl = decl, |
| 1084 | .instructions = .{}, | 1022 | .instructions = .{}, |
| 1085 | .arena = &decl_arena.allocator, | 1023 | .arena = &analysis_arena.allocator, |
| 1086 | .inlining = null, | 1024 | .inlining = null, |
| 1087 | .is_comptime = false, | 1025 | .is_comptime = true, |
| 1088 | .branch_quota = &branch_quota, | 1026 | .branch_quota = &branch_quota, |
| 1089 | }; | 1027 | }; |
| 1090 | defer block_scope.instructions.deinit(self.gpa); | 1028 | defer block_scope.instructions.deinit(self.gpa); |
| 1091 | | 1029 | |
| 1092 | const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{ | 1030 | _ = try zir_sema.analyzeBody(self, &block_scope, .{ |
| 1093 | .instructions = fn_type_scope.instructions.items, | 1031 | .instructions = gen_scope.instructions.items, |
| 1094 | }); | 1032 | }); |
| 1095 | const body_node = fn_proto.getBodyNode() orelse { | | |
| 1096 | // Extern function. | | |
| 1097 | var type_changed = true; | | |
| 1098 | if (decl.typedValueManaged()) |tvm| { | | |
| 1099 | type_changed = !tvm.typed_value.ty.eql(fn_type); | | |
| 1100 | | 1033 | |
| 1101 | tvm.deinit(self.gpa); | 1034 | decl.analysis = .complete; |
| 1102 | } | 1035 | decl.generation = self.generation; |
| 1103 | const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl); | 1036 | return true; |
| | 1037 | }, |
| | 1038 | .UsingNamespace => @panic("TODO usingnamespace decl"), |
| | 1039 | else => unreachable, |
| | 1040 | } |
| | 1041 | } |
| 1104 | | 1042 | |
| 1105 | decl_arena_state.* = decl_arena.state; | 1043 | fn astgenAndSemaFn( |
| 1106 | decl.typed_value = .{ | 1044 | mod: *Module, |
| 1107 | .most_recent = .{ | 1045 | decl: *Decl, |
| 1108 | .typed_value = .{ .ty = fn_type, .val = fn_val }, | 1046 | tree: ast.Tree, |
| 1109 | .arena = decl_arena_state, | 1047 | body_node: ast.Node.Index, |
| 1110 | }, | 1048 | fn_proto: ast.full.FnProto, |
| 1111 | }; | 1049 | ) !bool { |
| 1112 | decl.analysis = .complete; | 1050 | const tracy = trace(@src()); |
| 1113 | decl.generation = self.generation; | 1051 | defer tracy.end(); |
| 1114 | | 1052 | |
| 1115 | try self.comp.bin_file.allocateDeclIndexes(decl); | 1053 | decl.analysis = .in_progress; |
| 1116 | try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); | | |
| 1117 | | 1054 | |
| 1118 | if (type_changed and self.emit_h != null) { | 1055 | const token_starts = tree.tokens.items(.start); |
| 1119 | try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); | | |
| 1120 | } | | |
| 1121 | | 1056 | |
| 1122 | return type_changed; | 1057 | // This arena allocator's memory is discarded at the end of this function. It is used |
| 1123 | }; | 1058 | // to determine the type of the function, and hence the type of the decl, which is needed |
| | 1059 | // to complete the Decl analysis. |
| | 1060 | var fn_type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); |
| | 1061 | defer fn_type_scope_arena.deinit(); |
| | 1062 | var fn_type_scope: Scope.GenZIR = .{ |
| | 1063 | .decl = decl, |
| | 1064 | .arena = &fn_type_scope_arena.allocator, |
| | 1065 | .parent = &decl.container.base, |
| | 1066 | }; |
| | 1067 | defer fn_type_scope.instructions.deinit(self.gpa); |
| 1124 | | 1068 | |
| 1125 | const new_func = try decl_arena.allocator.create(Fn); | 1069 | decl.is_pub = fn_proto.visib_token != null; |
| 1126 | const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); | | |
| 1127 | | 1070 | |
| 1128 | const fn_zir: zir.Body = blk: { | 1071 | // The AST params array does not contain anytype and ... parameters. |
| 1129 | // We put the ZIR inside the Decl arena. | 1072 | // We must iterate to count how many param types to allocate. |
| 1130 | var gen_scope: Scope.GenZIR = .{ | 1073 | const param_count = blk: { |
| 1131 | .decl = decl, | 1074 | var count: usize = 0; |
| 1132 | .arena = &decl_arena.allocator, | 1075 | var it = fn_proto.iterate(tree); |
| 1133 | .parent = &decl.container.base, | 1076 | while (it.next()) |_| { |
| 1134 | }; | 1077 | count += 1; |
| 1135 | defer gen_scope.instructions.deinit(self.gpa); | 1078 | } |
| 1136 | | 1079 | break :blk count; |
| 1137 | // We need an instruction for each parameter, and they must be first in the body. | 1080 | }; |
| 1138 | try gen_scope.instructions.resize(self.gpa, fn_proto.params_len); | 1081 | const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_count); |
| 1139 | var params_scope = &gen_scope.base; | 1082 | const fn_src = token_starts[fn_proto.ast.fn_token]; |
| 1140 | for (fn_proto.params()) |param, i| { | 1083 | const type_type = try astgen.addZIRInstConst(self, &fn_type_scope.base, fn_src, .{ |
| 1141 | const name_token = param.name_token.?; | 1084 | .ty = Type.initTag(.type), |
| 1142 | const src = tree.token_locs[name_token].start; | 1085 | .val = Value.initTag(.type_type), |
| 1143 | const param_name = try self.identifierTokenString(&gen_scope.base, name_token); | 1086 | }); |
| 1144 | const arg = try decl_arena.allocator.create(zir.Inst.Arg); | 1087 | const type_type_rl: astgen.ResultLoc = .{ .ty = type_type }; |
| 1145 | arg.* = .{ | 1088 | |
| 1146 | .base = .{ | 1089 | { |
| 1147 | .tag = .arg, | 1090 | var param_type_i: usize = 0; |
| 1148 | .src = src, | 1091 | var it = fn_proto.iterate(tree); |
| 1149 | }, | 1092 | while (it.next()) |param| : (param_type_i += 1) { |
| 1150 | .positionals = .{ | 1093 | if (param.anytype_ellipsis3) |token| { |
| 1151 | .name = param_name, | 1094 | switch (token_tags[token]) { |
| 1152 | }, | 1095 | .keyword_anytype => return self.failTok( |
| 1153 | .kw_args = .{}, | 1096 | &fn_type_scope.base, |
| 1154 | }; | 1097 | tok_i, |
| 1155 | gen_scope.instructions.items[i] = &arg.base; | 1098 | "TODO implement anytype parameter", |
| 1156 | const sub_scope = try decl_arena.allocator.create(Scope.LocalVal); | 1099 | .{}, |
| 1157 | sub_scope.* = .{ | 1100 | ), |
| 1158 | .parent = params_scope, | 1101 | .ellipsis3 => return self.failTok( |
| 1159 | .gen_zir = &gen_scope, | 1102 | &fn_type_scope.base, |
| 1160 | .name = param_name, | 1103 | token, |
| 1161 | .inst = &arg.base, | 1104 | "TODO implement var args", |
| 1162 | }; | 1105 | .{}, |
| 1163 | params_scope = &sub_scope.base; | 1106 | ), |
| | 1107 | else => unreachable, |
| 1164 | } | 1108 | } |
| | 1109 | } |
| | 1110 | const param_type_node = param.type_expr; |
| | 1111 | assert(param_type_node != 0); |
| | 1112 | param_types[param_type_i] = |
| | 1113 | try astgen.expr(self, &fn_type_scope.base, type_type_rl, param_type_node); |
| | 1114 | } |
| | 1115 | assert(param_type_i == param_count); |
| | 1116 | } |
| | 1117 | if (fn_proto.lib_name) |lib_name| blk: { |
| | 1118 | // TODO call std.zig.parseStringLiteral |
| | 1119 | const lib_name_str = mem.trim(u8, tree.tokenSlice(lib_name), "\""); |
| | 1120 | log.debug("extern fn symbol expected in lib '{s}'", .{lib_name_str}); |
| | 1121 | const target = self.comp.getTarget(); |
| | 1122 | if (target_util.is_libc_lib_name(target, lib_name_str)) { |
| | 1123 | if (!self.comp.bin_file.options.link_libc) { |
| | 1124 | return self.failTok( |
| | 1125 | &fn_type_scope.base, |
| | 1126 | lib_name, |
| | 1127 | "dependency on libc must be explicitly specified in the build command", |
| | 1128 | .{}, |
| | 1129 | ); |
| | 1130 | } |
| | 1131 | break :blk; |
| | 1132 | } |
| | 1133 | if (target_util.is_libcpp_lib_name(target, lib_name_str)) { |
| | 1134 | if (!self.comp.bin_file.options.link_libcpp) { |
| | 1135 | return self.failTok( |
| | 1136 | &fn_type_scope.base, |
| | 1137 | lib_name, |
| | 1138 | "dependency on libc++ must be explicitly specified in the build command", |
| | 1139 | .{}, |
| | 1140 | ); |
| | 1141 | } |
| | 1142 | break :blk; |
| | 1143 | } |
| | 1144 | if (!target.isWasm() and !self.comp.bin_file.options.pic) { |
| | 1145 | return self.failTok( |
| | 1146 | &fn_type_scope.base, |
| | 1147 | lib_name, |
| | 1148 | "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", |
| | 1149 | .{ lib_name, lib_name }, |
| | 1150 | ); |
| | 1151 | } |
| | 1152 | self.comp.stage1AddLinkLib(lib_name_str) catch |err| { |
| | 1153 | return self.failTok( |
| | 1154 | &fn_type_scope.base, |
| | 1155 | lib_name, |
| | 1156 | "unable to add link lib '{s}': {s}", |
| | 1157 | .{ lib_name, @errorName(err) }, |
| | 1158 | ); |
| | 1159 | }; |
| | 1160 | } |
| | 1161 | if (fn_proto.ast.align_expr) |align_expr| { |
| | 1162 | return self.failNode(&fn_type_scope.base, align_expr, "TODO implement function align expression", .{}); |
| | 1163 | } |
| | 1164 | if (fn_proto.ast.section_expr) |sect_expr| { |
| | 1165 | return self.failNode(&fn_type_scope.base, sect_expr, "TODO implement function section expression", .{}); |
| | 1166 | } |
| | 1167 | if (fn_proto.ast.callconv_expr) |callconv_expr| { |
| | 1168 | return self.failNode( |
| | 1169 | &fn_type_scope.base, |
| | 1170 | callconv_expr, |
| | 1171 | "TODO implement function calling convention expression", |
| | 1172 | .{}, |
| | 1173 | ); |
| | 1174 | } |
| | 1175 | const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; |
| | 1176 | if (token_tags[maybe_bang] == .bang) { |
| | 1177 | return self.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{}); |
| | 1178 | } |
| | 1179 | const return_type_inst = try astgen.expr( |
| | 1180 | self, |
| | 1181 | &fn_type_scope.base, |
| | 1182 | type_type_rl, |
| | 1183 | fn_proto.ast.return_type, |
| | 1184 | ); |
| | 1185 | const fn_type_inst = try astgen.addZIRInst(self, &fn_type_scope.base, fn_src, zir.Inst.FnType, .{ |
| | 1186 | .return_type = return_type_inst, |
| | 1187 | .param_types = param_types, |
| | 1188 | }, .{}); |
| 1165 | | 1189 | |
| 1166 | const body_block = body_node.cast(ast.Node.Block).?; | 1190 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { |
| | 1191 | zir.dumpZir(self.gpa, "fn_type", decl.name, fn_type_scope.instructions.items) catch {}; |
| | 1192 | } |
| 1167 | | 1193 | |
| 1168 | try astgen.blockExpr(self, params_scope, body_block); | 1194 | // We need the memory for the Type to go into the arena for the Decl |
| | 1195 | var decl_arena = std.heap.ArenaAllocator.init(self.gpa); |
| | 1196 | errdefer decl_arena.deinit(); |
| | 1197 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 1169 | | 1198 | |
| 1170 | if (gen_scope.instructions.items.len == 0 or | 1199 | var inst_table = Scope.Block.InstTable.init(self.gpa); |
| 1171 | !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) | 1200 | defer inst_table.deinit(); |
| 1172 | { | | |
| 1173 | const src = tree.token_locs[body_block.rbrace].start; | | |
| 1174 | _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid); | | |
| 1175 | } | | |
| 1176 | | 1201 | |
| 1177 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { | 1202 | var branch_quota: u32 = default_eval_branch_quota; |
| 1178 | zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {}; | | |
| 1179 | } | | |
| 1180 | | 1203 | |
| 1181 | break :blk .{ | 1204 | var block_scope: Scope.Block = .{ |
| 1182 | .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), | 1205 | .parent = null, |
| 1183 | }; | 1206 | .inst_table = &inst_table, |
| 1184 | }; | 1207 | .func = null, |
| | 1208 | .owner_decl = decl, |
| | 1209 | .src_decl = decl, |
| | 1210 | .instructions = .{}, |
| | 1211 | .arena = &decl_arena.allocator, |
| | 1212 | .inlining = null, |
| | 1213 | .is_comptime = false, |
| | 1214 | .branch_quota = &branch_quota, |
| | 1215 | }; |
| | 1216 | defer block_scope.instructions.deinit(self.gpa); |
| 1185 | | 1217 | |
| 1186 | const is_inline = blk: { | 1218 | const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{ |
| 1187 | if (fn_proto.getExternExportInlineToken()) |maybe_inline_token| { | 1219 | .instructions = fn_type_scope.instructions.items, |
| 1188 | if (tree.token_ids[maybe_inline_token] == .Keyword_inline) { | 1220 | }); |
| 1189 | break :blk true; | 1221 | if (body_node == 0) { |
| 1190 | } | 1222 | // Extern function. |
| 1191 | } | 1223 | var type_changed = true; |
| 1192 | break :blk false; | 1224 | if (decl.typedValueManaged()) |tvm| { |
| 1193 | }; | 1225 | type_changed = !tvm.typed_value.ty.eql(fn_type); |
| 1194 | const anal_state = ([2]Fn.Analysis{ .queued, .inline_only })[@boolToInt(is_inline)]; | | |
| 1195 | | 1226 | |
| 1196 | new_func.* = .{ | 1227 | tvm.deinit(self.gpa); |
| 1197 | .state = anal_state, | 1228 | } |
| 1198 | .zir = fn_zir, | 1229 | const fn_val = try Value.Tag.extern_fn.create(&decl_arena.allocator, decl); |
| 1199 | .body = undefined, | | |
| 1200 | .owner_decl = decl, | | |
| 1201 | }; | | |
| 1202 | fn_payload.* = .{ | | |
| 1203 | .base = .{ .tag = .function }, | | |
| 1204 | .data = new_func, | | |
| 1205 | }; | | |
| 1206 | | 1230 | |
| 1207 | var prev_type_has_bits = false; | 1231 | decl_arena_state.* = decl_arena.state; |
| 1208 | var prev_is_inline = false; | 1232 | decl.typed_value = .{ |
| 1209 | var type_changed = true; | 1233 | .most_recent = .{ |
| | 1234 | .typed_value = .{ .ty = fn_type, .val = fn_val }, |
| | 1235 | .arena = decl_arena_state, |
| | 1236 | }, |
| | 1237 | }; |
| | 1238 | decl.analysis = .complete; |
| | 1239 | decl.generation = self.generation; |
| 1210 | | 1240 | |
| 1211 | if (decl.typedValueManaged()) |tvm| { | 1241 | try self.comp.bin_file.allocateDeclIndexes(decl); |
| 1212 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); | 1242 | try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); |
| 1213 | type_changed = !tvm.typed_value.ty.eql(fn_type); | | |
| 1214 | if (tvm.typed_value.val.castTag(.function)) |payload| { | | |
| 1215 | const prev_func = payload.data; | | |
| 1216 | prev_is_inline = prev_func.state == .inline_only; | | |
| 1217 | } | | |
| 1218 | | 1243 | |
| 1219 | tvm.deinit(self.gpa); | 1244 | if (type_changed and self.emit_h != null) { |
| 1220 | } | 1245 | try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); |
| | 1246 | } |
| 1221 | | 1247 | |
| 1222 | decl_arena_state.* = decl_arena.state; | 1248 | return type_changed; |
| 1223 | decl.typed_value = .{ | 1249 | } |
| 1224 | .most_recent = .{ | 1250 | |
| 1225 | .typed_value = .{ | 1251 | const new_func = try decl_arena.allocator.create(Fn); |
| 1226 | .ty = fn_type, | 1252 | const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); |
| 1227 | .val = Value.initPayload(&fn_payload.base), | 1253 | |
| 1228 | }, | 1254 | const fn_zir: zir.Body = blk: { |
| 1229 | .arena = decl_arena_state, | 1255 | // We put the ZIR inside the Decl arena. |
| | 1256 | var gen_scope: Scope.GenZIR = .{ |
| | 1257 | .decl = decl, |
| | 1258 | .arena = &decl_arena.allocator, |
| | 1259 | .parent = &decl.container.base, |
| | 1260 | }; |
| | 1261 | defer gen_scope.instructions.deinit(self.gpa); |
| | 1262 | |
| | 1263 | // We need an instruction for each parameter, and they must be first in the body. |
| | 1264 | try gen_scope.instructions.resize(self.gpa, param_count); |
| | 1265 | var params_scope = &gen_scope.base; |
| | 1266 | var i: usize = 0; |
| | 1267 | var it = fn_proto.iterate(tree); |
| | 1268 | while (it.next()) |param| : (i += 1) { |
| | 1269 | const name_token = param.name_token.?; |
| | 1270 | const src = token_starts[name_token]; |
| | 1271 | const param_name = try self.identifierTokenString(&gen_scope.base, name_token); |
| | 1272 | const arg = try decl_arena.allocator.create(zir.Inst.NoOp); |
| | 1273 | arg.* = .{ |
| | 1274 | .base = .{ |
| | 1275 | .tag = .arg, |
| | 1276 | .src = src, |
| 1230 | }, | 1277 | }, |
| | 1278 | .positionals = .{}, |
| | 1279 | .kw_args = .{}, |
| 1231 | }; | 1280 | }; |
| 1232 | decl.analysis = .complete; | 1281 | gen_scope.instructions.items[i] = &arg.base; |
| 1233 | decl.generation = self.generation; | 1282 | const sub_scope = try decl_arena.allocator.create(Scope.LocalVal); |
| | 1283 | sub_scope.* = .{ |
| | 1284 | .parent = params_scope, |
| | 1285 | .gen_zir = &gen_scope, |
| | 1286 | .name = param_name, |
| | 1287 | .inst = &arg.base, |
| | 1288 | }; |
| | 1289 | params_scope = &sub_scope.base; |
| | 1290 | } |
| 1234 | | 1291 | |
| 1235 | if (!is_inline and fn_type.hasCodeGenBits()) { | 1292 | try astgen.blockExpr(self, params_scope, body_node); |
| 1236 | // We don't fully codegen the decl until later, but we do need to reserve a global | | |
| 1237 | // offset table index for it. This allows us to codegen decls out of dependency order, | | |
| 1238 | // increasing how many computations can be done in parallel. | | |
| 1239 | try self.comp.bin_file.allocateDeclIndexes(decl); | | |
| 1240 | try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); | | |
| 1241 | if (type_changed and self.emit_h != null) { | | |
| 1242 | try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); | | |
| 1243 | } | | |
| 1244 | } else if (!prev_is_inline and prev_type_has_bits) { | | |
| 1245 | self.comp.bin_file.freeDecl(decl); | | |
| 1246 | } | | |
| 1247 | | 1293 | |
| 1248 | if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { | 1294 | if (gen_scope.instructions.items.len == 0 or |
| 1249 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | 1295 | !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) |
| 1250 | if (is_inline) { | 1296 | { |
| 1251 | return self.failTok( | 1297 | const src = token_starts[tree.lastToken(body_node)]; |
| 1252 | &block_scope.base, | 1298 | _ = try astgen.addZIRNoOp(self, &gen_scope.base, src, .returnvoid); |
| 1253 | maybe_export_token, | 1299 | } |
| 1254 | "export of inline function", | | |
| 1255 | .{}, | | |
| 1256 | ); | | |
| 1257 | } | | |
| 1258 | const export_src = tree.token_locs[maybe_export_token].start; | | |
| 1259 | const name_loc = tree.token_locs[fn_proto.getNameToken().?]; | | |
| 1260 | const name = tree.tokenSliceLoc(name_loc); | | |
| 1261 | // The scope needs to have the decl in it. | | |
| 1262 | try self.analyzeExport(&block_scope.base, export_src, name, decl); | | |
| 1263 | } | | |
| 1264 | } | | |
| 1265 | return type_changed or is_inline != prev_is_inline; | | |
| 1266 | }, | | |
| 1267 | .VarDecl => { | | |
| 1268 | const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", ast_node); | | |
| 1269 | | 1300 | |
| 1270 | decl.analysis = .in_progress; | 1301 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { |
| | 1302 | zir.dumpZir(self.gpa, "fn_body", decl.name, gen_scope.instructions.items) catch {}; |
| | 1303 | } |
| 1271 | | 1304 | |
| 1272 | // We need the memory for the Type to go into the arena for the Decl | 1305 | break :blk .{ |
| 1273 | var decl_arena = std.heap.ArenaAllocator.init(self.gpa); | 1306 | .instructions = try gen_scope.arena.dupe(*zir.Inst, gen_scope.instructions.items), |
| 1274 | errdefer decl_arena.deinit(); | 1307 | }; |
| 1275 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); | 1308 | }; |
| 1276 | | 1309 | |
| 1277 | var decl_inst_table = Scope.Block.InstTable.init(self.gpa); | 1310 | const is_inline = fn_type.fnCallingConvention() == .Inline; |
| 1278 | defer decl_inst_table.deinit(); | 1311 | const anal_state: Fn.Analysis = if (is_inline) .inline_only else .queued; |
| 1279 | | 1312 | |
| 1280 | var branch_quota: u32 = default_eval_branch_quota; | 1313 | new_func.* = .{ |
| | 1314 | .state = anal_state, |
| | 1315 | .zir = fn_zir, |
| | 1316 | .body = undefined, |
| | 1317 | .owner_decl = decl, |
| | 1318 | }; |
| | 1319 | fn_payload.* = .{ |
| | 1320 | .base = .{ .tag = .function }, |
| | 1321 | .data = new_func, |
| | 1322 | }; |
| 1281 | | 1323 | |
| 1282 | var block_scope: Scope.Block = .{ | 1324 | var prev_type_has_bits = false; |
| 1283 | .parent = null, | 1325 | var prev_is_inline = false; |
| 1284 | .inst_table = &decl_inst_table, | 1326 | var type_changed = true; |
| 1285 | .func = null, | | |
| 1286 | .owner_decl = decl, | | |
| 1287 | .src_decl = decl, | | |
| 1288 | .instructions = .{}, | | |
| 1289 | .arena = &decl_arena.allocator, | | |
| 1290 | .inlining = null, | | |
| 1291 | .is_comptime = true, | | |
| 1292 | .branch_quota = &branch_quota, | | |
| 1293 | }; | | |
| 1294 | defer block_scope.instructions.deinit(self.gpa); | | |
| 1295 | | 1327 | |
| 1296 | decl.is_pub = var_decl.getVisibToken() != null; | 1328 | if (decl.typedValueManaged()) |tvm| { |
| 1297 | const is_extern = blk: { | 1329 | prev_type_has_bits = tvm.typed_value.ty.hasCodeGenBits(); |
| 1298 | const maybe_extern_token = var_decl.getExternExportToken() orelse | 1330 | type_changed = !tvm.typed_value.ty.eql(fn_type); |
| 1299 | break :blk false; | 1331 | if (tvm.typed_value.val.castTag(.function)) |payload| { |
| 1300 | if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false; | 1332 | const prev_func = payload.data; |
| 1301 | if (var_decl.getInitNode()) |some| { | 1333 | prev_is_inline = prev_func.state == .inline_only; |
| 1302 | return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{}); | 1334 | } |
| 1303 | } | | |
| 1304 | break :blk true; | | |
| 1305 | }; | | |
| 1306 | if (var_decl.getLibName()) |lib_name| { | | |
| 1307 | assert(is_extern); | | |
| 1308 | return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{}); | | |
| 1309 | } | | |
| 1310 | const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var; | | |
| 1311 | const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: { | | |
| 1312 | if (!is_mutable) { | | |
| 1313 | return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{}); | | |
| 1314 | } | | |
| 1315 | break :blk true; | | |
| 1316 | } else false; | | |
| 1317 | assert(var_decl.getComptimeToken() == null); | | |
| 1318 | if (var_decl.getAlignNode()) |align_expr| { | | |
| 1319 | return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{}); | | |
| 1320 | } | | |
| 1321 | if (var_decl.getSectionNode()) |sect_expr| { | | |
| 1322 | return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{}); | | |
| 1323 | } | | |
| 1324 | | 1335 | |
| 1325 | const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: { | 1336 | tvm.deinit(self.gpa); |
| 1326 | var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | 1337 | } |
| 1327 | defer gen_scope_arena.deinit(); | | |
| 1328 | var gen_scope: Scope.GenZIR = .{ | | |
| 1329 | .decl = decl, | | |
| 1330 | .arena = &gen_scope_arena.allocator, | | |
| 1331 | .parent = &decl.container.base, | | |
| 1332 | }; | | |
| 1333 | defer gen_scope.instructions.deinit(self.gpa); | | |
| 1334 | | 1338 | |
| 1335 | const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: { | 1339 | decl_arena_state.* = decl_arena.state; |
| 1336 | const src = tree.token_locs[type_node.firstToken()].start; | 1340 | decl.typed_value = .{ |
| 1337 | const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{ | 1341 | .most_recent = .{ |
| 1338 | .ty = Type.initTag(.type), | 1342 | .typed_value = .{ |
| 1339 | .val = Value.initTag(.type_type), | 1343 | .ty = fn_type, |
| 1340 | }); | 1344 | .val = Value.initPayload(&fn_payload.base), |
| 1341 | const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node); | 1345 | }, |
| 1342 | break :rl .{ .ty = var_type }; | 1346 | .arena = decl_arena_state, |
| 1343 | } else .none; | 1347 | }, |
| | 1348 | }; |
| | 1349 | decl.analysis = .complete; |
| | 1350 | decl.generation = self.generation; |
| | 1351 | |
| | 1352 | if (!is_inline and fn_type.hasCodeGenBits()) { |
| | 1353 | // We don't fully codegen the decl until later, but we do need to reserve a global |
| | 1354 | // offset table index for it. This allows us to codegen decls out of dependency order, |
| | 1355 | // increasing how many computations can be done in parallel. |
| | 1356 | try self.comp.bin_file.allocateDeclIndexes(decl); |
| | 1357 | try self.comp.work_queue.writeItem(.{ .codegen_decl = decl }); |
| | 1358 | if (type_changed and self.emit_h != null) { |
| | 1359 | try self.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); |
| | 1360 | } |
| | 1361 | } else if (!prev_is_inline and prev_type_has_bits) { |
| | 1362 | self.comp.bin_file.freeDecl(decl); |
| | 1363 | } |
| 1344 | | 1364 | |
| 1345 | const init_inst = try astgen.comptimeExpr(self, &gen_scope.base, init_result_loc, init_node); | 1365 | if (fn_proto.extern_export_token) |maybe_export_token| { |
| 1346 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { | 1366 | if (token_tags[maybe_export_token] == .Keyword_export) { |
| 1347 | zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {}; | 1367 | if (is_inline) { |
| 1348 | } | 1368 | return self.failTok( |
| | 1369 | &block_scope.base, |
| | 1370 | maybe_export_token, |
| | 1371 | "export of inline function", |
| | 1372 | .{}, |
| | 1373 | ); |
| | 1374 | } |
| | 1375 | const export_src = token_starts[maybe_export_token]; |
| | 1376 | const name = tree.tokenSlice(fn_proto.name_token.?); // TODO identifierTokenString |
| | 1377 | // The scope needs to have the decl in it. |
| | 1378 | try self.analyzeExport(&block_scope.base, export_src, name, decl); |
| | 1379 | } |
| | 1380 | } |
| | 1381 | return type_changed or is_inline != prev_is_inline; |
| | 1382 | } |
| 1349 | | 1383 | |
| 1350 | var var_inst_table = Scope.Block.InstTable.init(self.gpa); | 1384 | fn astgenAndSemaVarDecl( |
| 1351 | defer var_inst_table.deinit(); | 1385 | mod: *Module, |
| 1352 | | 1386 | decl: *Decl, |
| 1353 | var branch_quota_vi: u32 = default_eval_branch_quota; | 1387 | tree: ast.Tree, |
| 1354 | var inner_block: Scope.Block = .{ | 1388 | var_decl: ast.full.VarDecl, |
| 1355 | .parent = null, | 1389 | ) !bool { |
| 1356 | .inst_table = &var_inst_table, | 1390 | const tracy = trace(@src()); |
| 1357 | .func = null, | 1391 | defer tracy.end(); |
| 1358 | .owner_decl = decl, | | |
| 1359 | .src_decl = decl, | | |
| 1360 | .instructions = .{}, | | |
| 1361 | .arena = &gen_scope_arena.allocator, | | |
| 1362 | .inlining = null, | | |
| 1363 | .is_comptime = true, | | |
| 1364 | .branch_quota = &branch_quota_vi, | | |
| 1365 | }; | | |
| 1366 | defer inner_block.instructions.deinit(self.gpa); | | |
| 1367 | try zir_sema.analyzeBody(self, &inner_block, .{ | | |
| 1368 | .instructions = gen_scope.instructions.items, | | |
| 1369 | }); | | |
| 1370 | | 1392 | |
| 1371 | // The result location guarantees the type coercion. | 1393 | decl.analysis = .in_progress; |
| 1372 | const analyzed_init_inst = var_inst_table.get(init_inst).?; | | |
| 1373 | // The is_comptime in the Scope.Block guarantees the result is comptime-known. | | |
| 1374 | const val = analyzed_init_inst.value().?; | | |
| 1375 | | 1394 | |
| 1376 | const ty = try analyzed_init_inst.ty.copy(block_scope.arena); | 1395 | const token_starts = tree.tokens.items(.start); |
| 1377 | break :vi .{ | | |
| 1378 | .ty = ty, | | |
| 1379 | .val = try val.copy(block_scope.arena), | | |
| 1380 | }; | | |
| 1381 | } else if (!is_extern) { | | |
| 1382 | return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{}); | | |
| 1383 | } else if (var_decl.getTypeNode()) |type_node| vi: { | | |
| 1384 | // Temporary arena for the zir instructions. | | |
| 1385 | var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); | | |
| 1386 | defer type_scope_arena.deinit(); | | |
| 1387 | var type_scope: Scope.GenZIR = .{ | | |
| 1388 | .decl = decl, | | |
| 1389 | .arena = &type_scope_arena.allocator, | | |
| 1390 | .parent = &decl.container.base, | | |
| 1391 | }; | | |
| 1392 | defer type_scope.instructions.deinit(self.gpa); | | |
| 1393 | | 1396 | |
| 1394 | const var_type = try astgen.typeExpr(self, &type_scope.base, type_node); | 1397 | // We need the memory for the Type to go into the arena for the Decl |
| 1395 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { | 1398 | var decl_arena = std.heap.ArenaAllocator.init(self.gpa); |
| 1396 | zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {}; | 1399 | errdefer decl_arena.deinit(); |
| 1397 | } | 1400 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 1398 | | 1401 | |
| 1399 | const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{ | 1402 | var decl_inst_table = Scope.Block.InstTable.init(self.gpa); |
| 1400 | .instructions = type_scope.instructions.items, | 1403 | defer decl_inst_table.deinit(); |
| 1401 | }); | | |
| 1402 | break :vi .{ | | |
| 1403 | .ty = ty, | | |
| 1404 | .val = null, | | |
| 1405 | }; | | |
| 1406 | } else { | | |
| 1407 | return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{}); | | |
| 1408 | }; | | |
| 1409 | | 1404 | |
| 1410 | if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { | 1405 | var branch_quota: u32 = default_eval_branch_quota; |
| 1411 | return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty}); | | |
| 1412 | } | | |
| 1413 | | 1406 | |
| 1414 | var type_changed = true; | 1407 | var block_scope: Scope.Block = .{ |
| 1415 | if (decl.typedValueManaged()) |tvm| { | 1408 | .parent = null, |
| 1416 | type_changed = !tvm.typed_value.ty.eql(var_info.ty); | 1409 | .inst_table = &decl_inst_table, |
| | 1410 | .func = null, |
| | 1411 | .owner_decl = decl, |
| | 1412 | .src_decl = decl, |
| | 1413 | .instructions = .{}, |
| | 1414 | .arena = &decl_arena.allocator, |
| | 1415 | .inlining = null, |
| | 1416 | .is_comptime = true, |
| | 1417 | .branch_quota = &branch_quota, |
| | 1418 | }; |
| | 1419 | defer block_scope.instructions.deinit(self.gpa); |
| | 1420 | |
| | 1421 | decl.is_pub = var_decl.getVisibToken() != null; |
| | 1422 | const is_extern = blk: { |
| | 1423 | const maybe_extern_token = var_decl.getExternExportToken() orelse |
| | 1424 | break :blk false; |
| | 1425 | if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false; |
| | 1426 | if (var_decl.getInitNode()) |some| { |
| | 1427 | return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{}); |
| | 1428 | } |
| | 1429 | break :blk true; |
| | 1430 | }; |
| | 1431 | if (var_decl.getLibName()) |lib_name| { |
| | 1432 | assert(is_extern); |
| | 1433 | return self.failNode(&block_scope.base, lib_name, "TODO implement function library name", .{}); |
| | 1434 | } |
| | 1435 | const is_mutable = tree.token_ids[var_decl.mut_token] == .Keyword_var; |
| | 1436 | const is_threadlocal = if (var_decl.getThreadLocalToken()) |some| blk: { |
| | 1437 | if (!is_mutable) { |
| | 1438 | return self.failTok(&block_scope.base, some, "threadlocal variable cannot be constant", .{}); |
| | 1439 | } |
| | 1440 | break :blk true; |
| | 1441 | } else false; |
| | 1442 | assert(var_decl.getComptimeToken() == null); |
| | 1443 | if (var_decl.getAlignNode()) |align_expr| { |
| | 1444 | return self.failNode(&block_scope.base, align_expr, "TODO implement function align expression", .{}); |
| | 1445 | } |
| | 1446 | if (var_decl.getSectionNode()) |sect_expr| { |
| | 1447 | return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{}); |
| | 1448 | } |
| | 1449 | |
| | 1450 | const var_info: struct { ty: Type, val: ?Value } = if (var_decl.getInitNode()) |init_node| vi: { |
| | 1451 | var gen_scope_arena = std.heap.ArenaAllocator.init(self.gpa); |
| | 1452 | defer gen_scope_arena.deinit(); |
| | 1453 | var gen_scope: Scope.GenZIR = .{ |
| | 1454 | .decl = decl, |
| | 1455 | .arena = &gen_scope_arena.allocator, |
| | 1456 | .parent = &decl.container.base, |
| | 1457 | }; |
| | 1458 | defer gen_scope.instructions.deinit(self.gpa); |
| 1417 | | 1459 | |
| 1418 | tvm.deinit(self.gpa); | 1460 | const init_result_loc: astgen.ResultLoc = if (var_decl.getTypeNode()) |type_node| rl: { |
| 1419 | } | 1461 | const src = token_starts[type_node.firstToken()]; |
| | 1462 | const type_type = try astgen.addZIRInstConst(self, &gen_scope.base, src, .{ |
| | 1463 | .ty = Type.initTag(.type), |
| | 1464 | .val = Value.initTag(.type_type), |
| | 1465 | }); |
| | 1466 | const var_type = try astgen.expr(self, &gen_scope.base, .{ .ty = type_type }, type_node); |
| | 1467 | break :rl .{ .ty = var_type }; |
| | 1468 | } else .none; |
| 1420 | | 1469 | |
| 1421 | const new_variable = try decl_arena.allocator.create(Var); | 1470 | const init_inst = try astgen.comptimeExpr(self, &gen_scope.base, init_result_loc, init_node); |
| 1422 | new_variable.* = .{ | 1471 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { |
| 1423 | .owner_decl = decl, | 1472 | zir.dumpZir(self.gpa, "var_init", decl.name, gen_scope.instructions.items) catch {}; |
| 1424 | .init = var_info.val orelse undefined, | 1473 | } |
| 1425 | .is_extern = is_extern, | | |
| 1426 | .is_mutable = is_mutable, | | |
| 1427 | .is_threadlocal = is_threadlocal, | | |
| 1428 | }; | | |
| 1429 | const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable); | | |
| 1430 | | | |
| 1431 | decl_arena_state.* = decl_arena.state; | | |
| 1432 | decl.typed_value = .{ | | |
| 1433 | .most_recent = .{ | | |
| 1434 | .typed_value = .{ | | |
| 1435 | .ty = var_info.ty, | | |
| 1436 | .val = var_val, | | |
| 1437 | }, | | |
| 1438 | .arena = decl_arena_state, | | |
| 1439 | }, | | |
| 1440 | }; | | |
| 1441 | decl.analysis = .complete; | | |
| 1442 | decl.generation = self.generation; | | |
| 1443 | | 1474 | |
| 1444 | if (var_decl.getExternExportToken()) |maybe_export_token| { | 1475 | var var_inst_table = Scope.Block.InstTable.init(self.gpa); |
| 1445 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | 1476 | defer var_inst_table.deinit(); |
| 1446 | const export_src = tree.token_locs[maybe_export_token].start; | 1477 | |
| 1447 | const name_loc = tree.token_locs[var_decl.name_token]; | 1478 | var branch_quota_vi: u32 = default_eval_branch_quota; |
| 1448 | const name = tree.tokenSliceLoc(name_loc); | 1479 | var inner_block: Scope.Block = .{ |
| 1449 | // The scope needs to have the decl in it. | 1480 | .parent = null, |
| 1450 | try self.analyzeExport(&block_scope.base, export_src, name, decl); | 1481 | .inst_table = &var_inst_table, |
| 1451 | } | 1482 | .func = null, |
| 1452 | } | 1483 | .owner_decl = decl, |
| 1453 | return type_changed; | 1484 | .src_decl = decl, |
| 1454 | }, | 1485 | .instructions = .{}, |
| 1455 | .Comptime => { | 1486 | .arena = &gen_scope_arena.allocator, |
| 1456 | const comptime_decl = @fieldParentPtr(ast.Node.Comptime, "base", ast_node); | 1487 | .inlining = null, |
| | 1488 | .is_comptime = true, |
| | 1489 | .branch_quota = &branch_quota_vi, |
| | 1490 | }; |
| | 1491 | defer inner_block.instructions.deinit(self.gpa); |
| | 1492 | try zir_sema.analyzeBody(self, &inner_block, .{ |
| | 1493 | .instructions = gen_scope.instructions.items, |
| | 1494 | }); |
| 1457 | | 1495 | |
| 1458 | decl.analysis = .in_progress; | 1496 | // The result location guarantees the type coercion. |
| | 1497 | const analyzed_init_inst = var_inst_table.get(init_inst).?; |
| | 1498 | // The is_comptime in the Scope.Block guarantees the result is comptime-known. |
| | 1499 | const val = analyzed_init_inst.value().?; |
| 1459 | | 1500 | |
| 1460 | // A comptime decl does not store any value so we can just deinit this arena after analysis is done. | 1501 | const ty = try analyzed_init_inst.ty.copy(block_scope.arena); |
| 1461 | var analysis_arena = std.heap.ArenaAllocator.init(self.gpa); | 1502 | break :vi .{ |
| 1462 | defer analysis_arena.deinit(); | 1503 | .ty = ty, |
| 1463 | var gen_scope: Scope.GenZIR = .{ | 1504 | .val = try val.copy(block_scope.arena), |
| 1464 | .decl = decl, | 1505 | }; |
| 1465 | .arena = &analysis_arena.allocator, | 1506 | } else if (!is_extern) { |
| 1466 | .parent = &decl.container.base, | 1507 | return self.failTok(&block_scope.base, var_decl.firstToken(), "variables must be initialized", .{}); |
| 1467 | }; | 1508 | } else if (var_decl.getTypeNode()) |type_node| vi: { |
| 1468 | defer gen_scope.instructions.deinit(self.gpa); | 1509 | // Temporary arena for the zir instructions. |
| | 1510 | var type_scope_arena = std.heap.ArenaAllocator.init(self.gpa); |
| | 1511 | defer type_scope_arena.deinit(); |
| | 1512 | var type_scope: Scope.GenZIR = .{ |
| | 1513 | .decl = decl, |
| | 1514 | .arena = &type_scope_arena.allocator, |
| | 1515 | .parent = &decl.container.base, |
| | 1516 | }; |
| | 1517 | defer type_scope.instructions.deinit(self.gpa); |
| 1469 | | 1518 | |
| 1470 | _ = try astgen.comptimeExpr(self, &gen_scope.base, .none, comptime_decl.expr); | 1519 | const var_type = try astgen.typeExpr(self, &type_scope.base, type_node); |
| 1471 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { | 1520 | if (std.builtin.mode == .Debug and self.comp.verbose_ir) { |
| 1472 | zir.dumpZir(self.gpa, "comptime_block", decl.name, gen_scope.instructions.items) catch {}; | 1521 | zir.dumpZir(self.gpa, "var_type", decl.name, type_scope.instructions.items) catch {}; |
| 1473 | } | 1522 | } |
| 1474 | | 1523 | |
| 1475 | var inst_table = Scope.Block.InstTable.init(self.gpa); | 1524 | const ty = try zir_sema.analyzeBodyValueAsType(self, &block_scope, var_type, .{ |
| 1476 | defer inst_table.deinit(); | 1525 | .instructions = type_scope.instructions.items, |
| | 1526 | }); |
| | 1527 | break :vi .{ |
| | 1528 | .ty = ty, |
| | 1529 | .val = null, |
| | 1530 | }; |
| | 1531 | } else { |
| | 1532 | return self.failTok(&block_scope.base, var_decl.firstToken(), "unable to infer variable type", .{}); |
| | 1533 | }; |
| 1477 | | 1534 | |
| 1478 | var branch_quota: u32 = default_eval_branch_quota; | 1535 | if (is_mutable and !var_info.ty.isValidVarType(is_extern)) { |
| | 1536 | return self.failTok(&block_scope.base, var_decl.firstToken(), "variable of type '{}' must be const", .{var_info.ty}); |
| | 1537 | } |
| 1479 | | 1538 | |
| 1480 | var block_scope: Scope.Block = .{ | 1539 | var type_changed = true; |
| 1481 | .parent = null, | 1540 | if (decl.typedValueManaged()) |tvm| { |
| 1482 | .inst_table = &inst_table, | 1541 | type_changed = !tvm.typed_value.ty.eql(var_info.ty); |
| 1483 | .func = null, | | |
| 1484 | .owner_decl = decl, | | |
| 1485 | .src_decl = decl, | | |
| 1486 | .instructions = .{}, | | |
| 1487 | .arena = &analysis_arena.allocator, | | |
| 1488 | .inlining = null, | | |
| 1489 | .is_comptime = true, | | |
| 1490 | .branch_quota = &branch_quota, | | |
| 1491 | }; | | |
| 1492 | defer block_scope.instructions.deinit(self.gpa); | | |
| 1493 | | 1542 | |
| 1494 | _ = try zir_sema.analyzeBody(self, &block_scope, .{ | 1543 | tvm.deinit(self.gpa); |
| 1495 | .instructions = gen_scope.instructions.items, | 1544 | } |
| 1496 | }); | | |
| 1497 | | 1545 | |
| 1498 | decl.analysis = .complete; | 1546 | const new_variable = try decl_arena.allocator.create(Var); |
| 1499 | decl.generation = self.generation; | 1547 | new_variable.* = .{ |
| 1500 | return true; | 1548 | .owner_decl = decl, |
| | 1549 | .init = var_info.val orelse undefined, |
| | 1550 | .is_extern = is_extern, |
| | 1551 | .is_mutable = is_mutable, |
| | 1552 | .is_threadlocal = is_threadlocal, |
| | 1553 | }; |
| | 1554 | const var_val = try Value.Tag.variable.create(&decl_arena.allocator, new_variable); |
| | 1555 | |
| | 1556 | decl_arena_state.* = decl_arena.state; |
| | 1557 | decl.typed_value = .{ |
| | 1558 | .most_recent = .{ |
| | 1559 | .typed_value = .{ |
| | 1560 | .ty = var_info.ty, |
| | 1561 | .val = var_val, |
| | 1562 | }, |
| | 1563 | .arena = decl_arena_state, |
| 1501 | }, | 1564 | }, |
| 1502 | .Use => @panic("TODO usingnamespace decl"), | 1565 | }; |
| 1503 | else => unreachable, | 1566 | decl.analysis = .complete; |
| | 1567 | decl.generation = self.generation; |
| | 1568 | |
| | 1569 | if (var_decl.getExternExportToken()) |maybe_export_token| { |
| | 1570 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| | 1571 | const export_src = token_starts[maybe_export_token]; |
| | 1572 | const name = tree.tokenSlice(var_decl.name_token); // TODO identifierTokenString |
| | 1573 | // The scope needs to have the decl in it. |
| | 1574 | try self.analyzeExport(&block_scope.base, export_src, name, decl); |
| | 1575 | } |
| 1504 | } | 1576 | } |
| | 1577 | return type_changed; |
| 1505 | } | 1578 | } |
| 1506 | | 1579 | |
| 1507 | fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void { | 1580 | fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void { |
| ... | @@ -1512,7 +1585,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void | ... | @@ -1512,7 +1585,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void |
| 1512 | dependee.dependants.putAssumeCapacity(depender, {}); | 1585 | dependee.dependants.putAssumeCapacity(depender, {}); |
| 1513 | } | 1586 | } |
| 1514 | | 1587 | |
| 1515 | pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { | 1588 | pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*const ast.Tree { |
| 1516 | const tracy = trace(@src()); | 1589 | const tracy = trace(@src()); |
| 1517 | defer tracy.end(); | 1590 | defer tracy.end(); |
| 1518 | | 1591 | |
| ... | @@ -1523,8 +1596,10 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { | ... | @@ -1523,8 +1596,10 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1523 | const source = try root_scope.getSource(self); | 1596 | const source = try root_scope.getSource(self); |
| 1524 | | 1597 | |
| 1525 | var keep_tree = false; | 1598 | var keep_tree = false; |
| 1526 | const tree = try std.zig.parse(self.gpa, source); | 1599 | root_scope.tree = try std.zig.parse(self.gpa, source); |
| 1527 | defer if (!keep_tree) tree.deinit(); | 1600 | defer if (!keep_tree) root_scope.tree.deinit(self.gpa); |
| | 1601 | |
| | 1602 | const tree = &root_scope.tree; |
| 1528 | | 1603 | |
| 1529 | if (tree.errors.len != 0) { | 1604 | if (tree.errors.len != 0) { |
| 1530 | const parse_err = tree.errors[0]; | 1605 | const parse_err = tree.errors[0]; |
| ... | @@ -1532,12 +1607,12 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { | ... | @@ -1532,12 +1607,12 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1532 | var msg = std.ArrayList(u8).init(self.gpa); | 1607 | var msg = std.ArrayList(u8).init(self.gpa); |
| 1533 | defer msg.deinit(); | 1608 | defer msg.deinit(); |
| 1534 | | 1609 | |
| 1535 | try parse_err.render(tree.token_ids, msg.writer()); | 1610 | try tree.renderError(parse_err, msg.writer()); |
| 1536 | const err_msg = try self.gpa.create(ErrorMsg); | 1611 | const err_msg = try self.gpa.create(ErrorMsg); |
| 1537 | err_msg.* = .{ | 1612 | err_msg.* = .{ |
| 1538 | .src_loc = .{ | 1613 | .src_loc = .{ |
| 1539 | .file_scope = root_scope, | 1614 | .file_scope = root_scope, |
| 1540 | .byte_offset = tree.token_locs[parse_err.loc()].start, | 1615 | .byte_offset = tree.tokens.items(.start)[parse_err.loc()], |
| 1541 | }, | 1616 | }, |
| 1542 | .msg = msg.toOwnedSlice(), | 1617 | .msg = msg.toOwnedSlice(), |
| 1543 | }; | 1618 | }; |
| ... | @@ -1548,7 +1623,6 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { | ... | @@ -1548,7 +1623,6 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1548 | } | 1623 | } |
| 1549 | | 1624 | |
| 1550 | root_scope.status = .loaded_success; | 1625 | root_scope.status = .loaded_success; |
| 1551 | root_scope.contents = .{ .tree = tree }; | | |
| 1552 | keep_tree = true; | 1626 | keep_tree = true; |
| 1553 | | 1627 | |
| 1554 | return tree; | 1628 | return tree; |
| ... | @@ -1556,144 +1630,336 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { | ... | @@ -1556,144 +1630,336 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1556 | | 1630 | |
| 1557 | .unloaded_parse_failure => return error.AnalysisFail, | 1631 | .unloaded_parse_failure => return error.AnalysisFail, |
| 1558 | | 1632 | |
| 1559 | .loaded_success => return root_scope.contents.tree, | 1633 | .loaded_success => return &root_scope.tree, |
| 1560 | } | 1634 | } |
| 1561 | } | 1635 | } |
| 1562 | | 1636 | |
| 1563 | pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void { | 1637 | pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 1564 | const tracy = trace(@src()); | 1638 | const tracy = trace(@src()); |
| 1565 | defer tracy.end(); | 1639 | defer tracy.end(); |
| 1566 | | 1640 | |
| 1567 | // We may be analyzing it for the first time, or this may be | 1641 | // We may be analyzing it for the first time, or this may be |
| 1568 | // an incremental update. This code handles both cases. | 1642 | // an incremental update. This code handles both cases. |
| 1569 | const tree = try self.getAstTree(container_scope.file_scope); | 1643 | const tree = try mod.getAstTree(container_scope.file_scope); |
| 1570 | const decls = tree.root_node.decls(); | 1644 | const node_tags = tree.nodes.items(.tag); |
| | 1645 | const node_datas = tree.nodes.items(.data); |
| | 1646 | const decls = tree.rootDecls(); |
| 1571 | | 1647 | |
| 1572 | try self.comp.work_queue.ensureUnusedCapacity(decls.len); | 1648 | try mod.comp.work_queue.ensureUnusedCapacity(decls.len); |
| 1573 | try container_scope.decls.ensureCapacity(self.gpa, decls.len); | 1649 | try container_scope.decls.ensureCapacity(mod.gpa, decls.len); |
| 1574 | | 1650 | |
| 1575 | // Keep track of the decls that we expect to see in this file so that | 1651 | // Keep track of the decls that we expect to see in this file so that |
| 1576 | // we know which ones have been deleted. | 1652 | // we know which ones have been deleted. |
| 1577 | var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa); | 1653 | var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa); |
| 1578 | defer deleted_decls.deinit(); | 1654 | defer deleted_decls.deinit(); |
| 1579 | try deleted_decls.ensureCapacity(container_scope.decls.items().len); | 1655 | try deleted_decls.ensureCapacity(container_scope.decls.items().len); |
| 1580 | for (container_scope.decls.items()) |entry| { | 1656 | for (container_scope.decls.items()) |entry| { |
| 1581 | deleted_decls.putAssumeCapacityNoClobber(entry.key, {}); | 1657 | deleted_decls.putAssumeCapacityNoClobber(entry.key, {}); |
| 1582 | } | 1658 | } |
| 1583 | | 1659 | |
| 1584 | for (decls) |src_decl, decl_i| { | 1660 | for (decls) |decl_node, decl_i| switch (node_tags[decl_node]) { |
| 1585 | if (src_decl.cast(ast.Node.FnProto)) |fn_proto| { | 1661 | .fn_decl => { |
| 1586 | // We will create a Decl for it regardless of analysis status. | 1662 | const fn_proto = node_datas[decl_node].lhs; |
| 1587 | const name_tok = fn_proto.getNameToken() orelse { | 1663 | const body = node_datas[decl_node].rhs; |
| 1588 | @panic("TODO missing function name"); | 1664 | switch (node_tags[fn_proto]) { |
| 1589 | }; | 1665 | .fn_proto_simple => { |
| 1590 | | 1666 | var params: [1]ast.Node.Index = undefined; |
| 1591 | const name_loc = tree.token_locs[name_tok]; | 1667 | try mod.semaContainerFn( |
| 1592 | const name = tree.tokenSliceLoc(name_loc); | 1668 | container_scope, |
| 1593 | const name_hash = container_scope.fullyQualifiedNameHash(name); | 1669 | &deleted_decls, |
| 1594 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | 1670 | decl_node, |
| 1595 | if (self.decl_table.get(name_hash)) |decl| { | 1671 | decl_i, |
| 1596 | // Update the AST Node index of the decl, even if its contents are unchanged, it may | 1672 | tree.*, |
| 1597 | // have been re-ordered. | 1673 | body, |
| 1598 | decl.src_index = decl_i; | 1674 | tree.fnProtoSimple(&params, fn_proto), |
| 1599 | if (deleted_decls.swapRemove(decl) == null) { | 1675 | ); |
| 1600 | decl.analysis = .sema_failure; | 1676 | }, |
| 1601 | const msg = try ErrorMsg.create(self.gpa, .{ | 1677 | .fn_proto_multi => try mod.semaContainerFn( |
| 1602 | .file_scope = container_scope.file_scope, | 1678 | container_scope, |
| 1603 | .byte_offset = tree.token_locs[name_tok].start, | 1679 | &deleted_decls, |
| 1604 | }, "redefinition of '{s}'", .{decl.name}); | 1680 | decl_node, |
| 1605 | errdefer msg.destroy(self.gpa); | 1681 | decl_i, |
| 1606 | try self.failed_decls.putNoClobber(self.gpa, decl, msg); | 1682 | tree.*, |
| 1607 | } else { | 1683 | body, |
| 1608 | if (!srcHashEql(decl.contents_hash, contents_hash)) { | 1684 | tree.fnProtoMulti(fn_proto), |
| 1609 | try self.markOutdatedDecl(decl); | 1685 | ), |
| 1610 | decl.contents_hash = contents_hash; | 1686 | .fn_proto_one => { |
| 1611 | } else switch (self.comp.bin_file.tag) { | 1687 | var params: [1]ast.Node.Index = undefined; |
| 1612 | .coff => { | 1688 | try mod.semaContainerFn( |
| 1613 | // TODO Implement for COFF | 1689 | container_scope, |
| 1614 | }, | 1690 | &deleted_decls, |
| 1615 | .elf => if (decl.fn_link.elf.len != 0) { | 1691 | decl_node, |
| 1616 | // TODO Look into detecting when this would be unnecessary by storing enough state | 1692 | decl_i, |
| 1617 | // in `Decl` to notice that the line number did not change. | 1693 | tree.*, |
| 1618 | self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); | 1694 | body, |
| 1619 | }, | 1695 | tree.fnProtoOne(&params, fn_proto), |
| 1620 | .macho => if (decl.fn_link.macho.len != 0) { | 1696 | ); |
| 1621 | // TODO Look into detecting when this would be unnecessary by storing enough state | 1697 | }, |
| 1622 | // in `Decl` to notice that the line number did not change. | 1698 | .fn_proto => try mod.semaContainerFn( |
| 1623 | self.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); | 1699 | container_scope, |
| 1624 | }, | 1700 | &deleted_decls, |
| 1625 | .c, .wasm => {}, | 1701 | decl_node, |
| 1626 | } | 1702 | decl_i, |
| 1627 | } | 1703 | tree.*, |
| 1628 | } else { | 1704 | body, |
| 1629 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | 1705 | tree.fnProto(fn_proto), |
| 1630 | container_scope.decls.putAssumeCapacity(new_decl, {}); | 1706 | ), |
| 1631 | if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { | 1707 | else => unreachable, |
| 1632 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | | |
| 1633 | self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | | |
| 1634 | } | | |
| 1635 | } | | |
| 1636 | } | | |
| 1637 | } else if (src_decl.castTag(.VarDecl)) |var_decl| { | | |
| 1638 | const name_loc = tree.token_locs[var_decl.name_token]; | | |
| 1639 | const name = tree.tokenSliceLoc(name_loc); | | |
| 1640 | const name_hash = container_scope.fullyQualifiedNameHash(name); | | |
| 1641 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | | |
| 1642 | if (self.decl_table.get(name_hash)) |decl| { | | |
| 1643 | // Update the AST Node index of the decl, even if its contents are unchanged, it may | | |
| 1644 | // have been re-ordered. | | |
| 1645 | decl.src_index = decl_i; | | |
| 1646 | if (deleted_decls.swapRemove(decl) == null) { | | |
| 1647 | decl.analysis = .sema_failure; | | |
| 1648 | const err_msg = try ErrorMsg.create(self.gpa, .{ | | |
| 1649 | .file_scope = container_scope.file_scope, | | |
| 1650 | .byte_offset = name_loc.start, | | |
| 1651 | }, "redefinition of '{s}'", .{decl.name}); | | |
| 1652 | errdefer err_msg.destroy(self.gpa); | | |
| 1653 | try self.failed_decls.putNoClobber(self.gpa, decl, err_msg); | | |
| 1654 | } else if (!srcHashEql(decl.contents_hash, contents_hash)) { | | |
| 1655 | try self.markOutdatedDecl(decl); | | |
| 1656 | decl.contents_hash = contents_hash; | | |
| 1657 | } | | |
| 1658 | } else { | | |
| 1659 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | | |
| 1660 | container_scope.decls.putAssumeCapacity(new_decl, {}); | | |
| 1661 | if (var_decl.getExternExportToken()) |maybe_export_token| { | | |
| 1662 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { | | |
| 1663 | self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | | |
| 1664 | } | | |
| 1665 | } | | |
| 1666 | } | 1708 | } |
| 1667 | } else if (src_decl.castTag(.Comptime)) |comptime_node| { | 1709 | }, |
| 1668 | const name_index = self.getNextAnonNameIndex(); | 1710 | .fn_proto_simple => { |
| 1669 | const name = try std.fmt.allocPrint(self.gpa, "__comptime_{d}", .{name_index}); | 1711 | var params: [1]ast.Node.Index = undefined; |
| 1670 | defer self.gpa.free(name); | 1712 | try mod.semaContainerFn( |
| | 1713 | container_scope, |
| | 1714 | &deleted_decls, |
| | 1715 | decl_node, |
| | 1716 | decl_i, |
| | 1717 | tree.*, |
| | 1718 | null, |
| | 1719 | tree.fnProtoSimple(&params, decl_node), |
| | 1720 | ); |
| | 1721 | }, |
| | 1722 | .fn_proto_multi => try mod.semaContainerFn( |
| | 1723 | container_scope, |
| | 1724 | &deleted_decls, |
| | 1725 | decl_node, |
| | 1726 | decl_i, |
| | 1727 | tree.*, |
| | 1728 | null, |
| | 1729 | tree.fnProtoMulti(decl_node), |
| | 1730 | ), |
| | 1731 | .fn_proto_one => { |
| | 1732 | var params: [1]ast.Node.Index = undefined; |
| | 1733 | try mod.semaContainerFn( |
| | 1734 | container_scope, |
| | 1735 | &deleted_decls, |
| | 1736 | decl_node, |
| | 1737 | decl_i, |
| | 1738 | tree.*, |
| | 1739 | null, |
| | 1740 | tree.fnProtoOne(&params, decl_node), |
| | 1741 | ); |
| | 1742 | }, |
| | 1743 | .fn_proto => try mod.semaContainerFn( |
| | 1744 | container_scope, |
| | 1745 | &deleted_decls, |
| | 1746 | decl_node, |
| | 1747 | decl_i, |
| | 1748 | tree.*, |
| | 1749 | null, |
| | 1750 | tree.fnProto(decl_node), |
| | 1751 | ), |
| | 1752 | |
| | 1753 | .global_var_decl => try mod.semaContainerVar( |
| | 1754 | container_scope, |
| | 1755 | &deleted_decls, |
| | 1756 | decl_node, |
| | 1757 | decl_i, |
| | 1758 | tree.*, |
| | 1759 | tree.globalVarDecl(decl_node), |
| | 1760 | ), |
| | 1761 | .local_var_decl => try mod.semaContainerVar( |
| | 1762 | container_scope, |
| | 1763 | &deleted_decls, |
| | 1764 | decl_node, |
| | 1765 | decl_i, |
| | 1766 | tree.*, |
| | 1767 | tree.localVarDecl(decl_node), |
| | 1768 | ), |
| | 1769 | .simple_var_decl => try mod.semaContainerVar( |
| | 1770 | container_scope, |
| | 1771 | &deleted_decls, |
| | 1772 | decl_node, |
| | 1773 | decl_i, |
| | 1774 | tree.*, |
| | 1775 | tree.simpleVarDecl(decl_node), |
| | 1776 | ), |
| | 1777 | .aligned_var_decl => try mod.semaContainerVar( |
| | 1778 | container_scope, |
| | 1779 | &deleted_decls, |
| | 1780 | decl_node, |
| | 1781 | decl_i, |
| | 1782 | tree.*, |
| | 1783 | tree.alignedVarDecl(decl_node), |
| | 1784 | ), |
| | 1785 | |
| | 1786 | .@"comptime" => { |
| | 1787 | const name_index = mod.getNextAnonNameIndex(); |
| | 1788 | const name = try std.fmt.allocPrint(mod.gpa, "__comptime_{d}", .{name_index}); |
| | 1789 | defer mod.gpa.free(name); |
| 1671 | | 1790 | |
| 1672 | const name_hash = container_scope.fullyQualifiedNameHash(name); | 1791 | const name_hash = container_scope.fullyQualifiedNameHash(name); |
| 1673 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | 1792 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); |
| 1674 | | 1793 | |
| 1675 | const new_decl = try self.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | 1794 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); |
| 1676 | container_scope.decls.putAssumeCapacity(new_decl, {}); | 1795 | container_scope.decls.putAssumeCapacity(new_decl, {}); |
| 1677 | self.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); | 1796 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 1678 | } else if (src_decl.castTag(.ContainerField)) |container_field| { | 1797 | }, |
| 1679 | log.err("TODO: analyze container field", .{}); | 1798 | |
| 1680 | } else if (src_decl.castTag(.TestDecl)) |test_decl| { | 1799 | .container_field_init => try mod.semaContainerField( |
| | 1800 | container_scope, |
| | 1801 | &deleted_decls, |
| | 1802 | decl_node, |
| | 1803 | decl_i, |
| | 1804 | tree.*, |
| | 1805 | tree.containerFieldInit(decl), |
| | 1806 | ), |
| | 1807 | .container_field_align => try mod.semaContainerField( |
| | 1808 | container_scope, |
| | 1809 | &deleted_decls, |
| | 1810 | decl_node, |
| | 1811 | decl_i, |
| | 1812 | tree.*, |
| | 1813 | tree.containerFieldAlign(decl), |
| | 1814 | ), |
| | 1815 | .container_field => try mod.semaContainerField( |
| | 1816 | container_scope, |
| | 1817 | &deleted_decls, |
| | 1818 | decl_node, |
| | 1819 | decl_i, |
| | 1820 | tree.*, |
| | 1821 | tree.containerField(decl), |
| | 1822 | ), |
| | 1823 | |
| | 1824 | .test_decl => { |
| 1681 | log.err("TODO: analyze test decl", .{}); | 1825 | log.err("TODO: analyze test decl", .{}); |
| 1682 | } else if (src_decl.castTag(.Use)) |use_decl| { | 1826 | }, |
| | 1827 | .@"usingnamespace" => { |
| 1683 | log.err("TODO: analyze usingnamespace decl", .{}); | 1828 | log.err("TODO: analyze usingnamespace decl", .{}); |
| 1684 | } else { | 1829 | }, |
| 1685 | unreachable; | 1830 | else => unreachable, |
| 1686 | } | 1831 | }; |
| 1687 | } | | |
| 1688 | // Handle explicitly deleted decls from the source code. Not to be confused | 1832 | // Handle explicitly deleted decls from the source code. Not to be confused |
| 1689 | // with when we delete decls because they are no longer referenced. | 1833 | // with when we delete decls because they are no longer referenced. |
| 1690 | for (deleted_decls.items()) |entry| { | 1834 | for (deleted_decls.items()) |entry| { |
| 1691 | log.debug("noticed '{s}' deleted from source\n", .{entry.key.name}); | 1835 | log.debug("noticed '{s}' deleted from source\n", .{entry.key.name}); |
| 1692 | try self.deleteDecl(entry.key); | 1836 | try mod.deleteDecl(entry.key); |
| | 1837 | } |
| | 1838 | } |
| | 1839 | |
| | 1840 | fn semaContainerFn( |
| | 1841 | mod: *Module, |
| | 1842 | container_scope: *Scope.Container, |
| | 1843 | deleted_decls: *std.AutoArrayHashMap(*Decl, void), |
| | 1844 | decl_node: ast.Node.Index, |
| | 1845 | decl_i: usize, |
| | 1846 | tree: ast.Tree, |
| | 1847 | body_node: ast.Node.Index, |
| | 1848 | fn_proto: ast.full.FnProto, |
| | 1849 | ) !void { |
| | 1850 | const tracy = trace(@src()); |
| | 1851 | defer tracy.end(); |
| | 1852 | |
| | 1853 | const token_starts = tree.tokens.items(.start); |
| | 1854 | const token_tags = tree.tokens.items(.tag); |
| | 1855 | |
| | 1856 | // We will create a Decl for it regardless of analysis status. |
| | 1857 | const name_tok = fn_proto.name_token orelse { |
| | 1858 | @panic("TODO missing function name"); |
| | 1859 | }; |
| | 1860 | const name = tree.tokenSlice(name_tok); // TODO use identifierTokenString |
| | 1861 | const name_hash = container_scope.fullyQualifiedNameHash(name); |
| | 1862 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); |
| | 1863 | if (mod.decl_table.get(name_hash)) |decl| { |
| | 1864 | // Update the AST Node index of the decl, even if its contents are unchanged, it may |
| | 1865 | // have been re-ordered. |
| | 1866 | decl.src_index = decl_i; |
| | 1867 | if (deleted_decls.swapRemove(decl) == null) { |
| | 1868 | decl.analysis = .sema_failure; |
| | 1869 | const msg = try ErrorMsg.create(mod.gpa, .{ |
| | 1870 | .file_scope = container_scope.file_scope, |
| | 1871 | .byte_offset = token_starts[name_tok], |
| | 1872 | }, "redefinition of '{s}'", .{decl.name}); |
| | 1873 | errdefer msg.destroy(mod.gpa); |
| | 1874 | try mod.failed_decls.putNoClobber(mod.gpa, decl, msg); |
| | 1875 | } else { |
| | 1876 | if (!srcHashEql(decl.contents_hash, contents_hash)) { |
| | 1877 | try mod.markOutdatedDecl(decl); |
| | 1878 | decl.contents_hash = contents_hash; |
| | 1879 | } else switch (mod.comp.bin_file.tag) { |
| | 1880 | .coff => { |
| | 1881 | // TODO Implement for COFF |
| | 1882 | }, |
| | 1883 | .elf => if (decl.fn_link.elf.len != 0) { |
| | 1884 | // TODO Look into detecting when this would be unnecessary by storing enough state |
| | 1885 | // in `Decl` to notice that the line number did not change. |
| | 1886 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); |
| | 1887 | }, |
| | 1888 | .macho => if (decl.fn_link.macho.len != 0) { |
| | 1889 | // TODO Look into detecting when this would be unnecessary by storing enough state |
| | 1890 | // in `Decl` to notice that the line number did not change. |
| | 1891 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl }); |
| | 1892 | }, |
| | 1893 | .c, .wasm => {}, |
| | 1894 | } |
| | 1895 | } |
| | 1896 | } else { |
| | 1897 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); |
| | 1898 | container_scope.decls.putAssumeCapacity(new_decl, {}); |
| | 1899 | if (fn_proto.getExternExportInlineToken()) |maybe_export_token| { |
| | 1900 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| | 1901 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| | 1902 | } |
| | 1903 | } |
| | 1904 | } |
| | 1905 | } |
| | 1906 | |
| | 1907 | fn semaContainerVar( |
| | 1908 | mod: *Module, |
| | 1909 | container_scope: *Scope.Container, |
| | 1910 | deleted_decls: *std.AutoArrayHashMap(*Decl, void), |
| | 1911 | decl_node: ast.Node.Index, |
| | 1912 | decl_i: usize, |
| | 1913 | tree: ast.Tree, |
| | 1914 | var_decl: ast.full.VarDecl, |
| | 1915 | ) !void { |
| | 1916 | const tracy = trace(@src()); |
| | 1917 | defer tracy.end(); |
| | 1918 | |
| | 1919 | const token_starts = tree.tokens.items(.start); |
| | 1920 | |
| | 1921 | const name_src = token_starts[var_decl.name_token]; |
| | 1922 | const name = tree.tokenSlice(var_decl.name_token); // TODO identifierTokenString |
| | 1923 | const name_hash = container_scope.fullyQualifiedNameHash(name); |
| | 1924 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); |
| | 1925 | if (mod.decl_table.get(name_hash)) |decl| { |
| | 1926 | // Update the AST Node index of the decl, even if its contents are unchanged, it may |
| | 1927 | // have been re-ordered. |
| | 1928 | decl.src_index = decl_i; |
| | 1929 | if (deleted_decls.swapRemove(decl) == null) { |
| | 1930 | decl.analysis = .sema_failure; |
| | 1931 | const err_msg = try ErrorMsg.create(mod.gpa, .{ |
| | 1932 | .file_scope = container_scope.file_scope, |
| | 1933 | .byte_offset = name_src, |
| | 1934 | }, "redefinition of '{s}'", .{decl.name}); |
| | 1935 | errdefer err_msg.destroy(mod.gpa); |
| | 1936 | try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg); |
| | 1937 | } else if (!srcHashEql(decl.contents_hash, contents_hash)) { |
| | 1938 | try mod.markOutdatedDecl(decl); |
| | 1939 | decl.contents_hash = contents_hash; |
| | 1940 | } |
| | 1941 | } else { |
| | 1942 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); |
| | 1943 | container_scope.decls.putAssumeCapacity(new_decl, {}); |
| | 1944 | if (var_decl.getExternExportToken()) |maybe_export_token| { |
| | 1945 | if (tree.token_ids[maybe_export_token] == .Keyword_export) { |
| | 1946 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| | 1947 | } |
| | 1948 | } |
| 1693 | } | 1949 | } |
| 1694 | } | 1950 | } |
| 1695 | | 1951 | |
| | 1952 | fn semaContainerField() void { |
| | 1953 | const tracy = trace(@src()); |
| | 1954 | defer tracy.end(); |
| | 1955 | |
| | 1956 | log.err("TODO: analyze container field", .{}); |
| | 1957 | } |
| | 1958 | |
| 1696 | pub fn deleteDecl(self: *Module, decl: *Decl) !void { | 1959 | pub fn deleteDecl(self: *Module, decl: *Decl) !void { |
| | 1960 | const tracy = trace(@src()); |
| | 1961 | defer tracy.end(); |
| | 1962 | |
| 1697 | try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len); | 1963 | try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len); |
| 1698 | | 1964 | |
| 1699 | // Remove from the namespace it resides in. In the case of an anonymous Decl it will | 1965 | // Remove from the namespace it resides in. In the case of an anonymous Decl it will |
| ... | @@ -2338,15 +2604,16 @@ pub fn createContainerDecl( | ... | @@ -2338,15 +2604,16 @@ pub fn createContainerDecl( |
| 2338 | fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 { | 2604 | fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 { |
| 2339 | // TODO add namespaces, generic function signatrues | 2605 | // TODO add namespaces, generic function signatrues |
| 2340 | const tree = scope.tree(); | 2606 | const tree = scope.tree(); |
| 2341 | const base_name = switch (tree.token_ids[base_token]) { | 2607 | const token_tags = tree.tokens.items(.tag); |
| 2342 | .Keyword_struct => "struct", | 2608 | const base_name = switch (token_tags[base_token]) { |
| 2343 | .Keyword_enum => "enum", | 2609 | .keyword_struct => "struct", |
| 2344 | .Keyword_union => "union", | 2610 | .keyword_enum => "enum", |
| 2345 | .Keyword_opaque => "opaque", | 2611 | .keyword_union => "union", |
| | 2612 | .keyword_opaque => "opaque", |
| 2346 | else => unreachable, | 2613 | else => unreachable, |
| 2347 | }; | 2614 | }; |
| 2348 | const loc = tree.tokenLocationLoc(0, tree.token_locs[base_token]); | 2615 | const loc = tree.tokenLocation(0, base_token); |
| 2349 | return std.fmt.allocPrint(self.gpa, "{}:{}:{}", .{ base_name, loc.line, loc.column }); | 2616 | return std.fmt.allocPrint(self.gpa, "{s}:{d}:{d}", .{ base_name, loc.line, loc.column }); |
| 2350 | } | 2617 | } |
| 2351 | | 2618 | |
| 2352 | fn getNextAnonNameIndex(self: *Module) usize { | 2619 | fn getNextAnonNameIndex(self: *Module) usize { |
| ... | @@ -3092,7 +3359,7 @@ pub fn failTok( | ... | @@ -3092,7 +3359,7 @@ pub fn failTok( |
| 3092 | comptime format: []const u8, | 3359 | comptime format: []const u8, |
| 3093 | args: anytype, | 3360 | args: anytype, |
| 3094 | ) InnerError { | 3361 | ) InnerError { |
| 3095 | const src = scope.tree().token_locs[token_index].start; | 3362 | const src = scope.tree().tokens.items(.start)[token_index]; |
| 3096 | return self.fail(scope, src, format, args); | 3363 | return self.fail(scope, src, format, args); |
| 3097 | } | 3364 | } |
| 3098 | | 3365 | |
| ... | @@ -3103,7 +3370,7 @@ pub fn failNode( | ... | @@ -3103,7 +3370,7 @@ pub fn failNode( |
| 3103 | comptime format: []const u8, | 3370 | comptime format: []const u8, |
| 3104 | args: anytype, | 3371 | args: anytype, |
| 3105 | ) InnerError { | 3372 | ) InnerError { |
| 3106 | const src = scope.tree().token_locs[ast_node.firstToken()].start; | 3373 | const src = scope.tree().tokens.items(.start)[ast_node.firstToken()]; |
| 3107 | return self.fail(scope, src, format, args); | 3374 | return self.fail(scope, src, format, args); |
| 3108 | } | 3375 | } |
| 3109 | | 3376 | |
| ... | @@ -3537,6 +3804,7 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void | ... | @@ -3537,6 +3804,7 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void |
| 3537 | /// Identifier token -> String (allocated in scope.arena()) | 3804 | /// Identifier token -> String (allocated in scope.arena()) |
| 3538 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { | 3805 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { |
| 3539 | const tree = scope.tree(); | 3806 | const tree = scope.tree(); |
| | 3807 | const token_starts = tree.tokens.items(.start); |
| 3540 | | 3808 | |
| 3541 | const ident_name = tree.tokenSlice(token); | 3809 | const ident_name = tree.tokenSlice(token); |
| 3542 | if (mem.startsWith(u8, ident_name, "@")) { | 3810 | if (mem.startsWith(u8, ident_name, "@")) { |
| ... | @@ -3545,7 +3813,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) | ... | @@ -3545,7 +3813,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) |
| 3545 | return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { | 3813 | return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) { |
| 3546 | error.InvalidCharacter => { | 3814 | error.InvalidCharacter => { |
| 3547 | const bad_byte = raw_string[bad_index]; | 3815 | const bad_byte = raw_string[bad_index]; |
| 3548 | const src = tree.token_locs[token].start; | 3816 | const src = token_starts[token]; |
| 3549 | return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte}); | 3817 | return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte}); |
| 3550 | }, | 3818 | }, |
| 3551 | else => |e| return e, | 3819 | else => |e| return e, |