| ... | ... | @@ -154,9 +154,7 @@ pub const DeclPlusEmitH = struct { |
| 154 | 154 | }; |
| 155 | 155 | |
| 156 | 156 | pub const Decl = struct { |
| 157 | | /// For declarations that have corresponding source code, this is identical to |
| 158 | | /// `getName().?`. For anonymous declarations this is allocated with Module's |
| 159 | | /// allocator. |
| 157 | /// Allocated with Module's allocator; outlives the ZIR code. |
| 160 | 158 | name: [*:0]const u8, |
| 161 | 159 | /// The most recent Type of the Decl after a successful semantic analysis. |
| 162 | 160 | /// Populated when `has_tv`. |
| ... | ... | @@ -270,13 +268,7 @@ pub const Decl = struct { |
| 270 | 268 | ); |
| 271 | 269 | |
| 272 | 270 | pub fn clearName(decl: *Decl, gpa: *Allocator) void { |
| 273 | | // name could be allocated in the ZIR or it could be owned by gpa. |
| 274 | | const file = decl.namespace.file_scope; |
| 275 | | const string_table_start = @ptrToInt(file.zir.string_bytes.ptr); |
| 276 | | const string_table_end = string_table_start + file.zir.string_bytes.len; |
| 277 | | if (@ptrToInt(decl.name) < string_table_start or @ptrToInt(decl.name) >= string_table_end) { |
| 278 | | gpa.free(mem.spanZ(decl.name)); |
| 279 | | } |
| 271 | gpa.free(mem.spanZ(decl.name)); |
| 280 | 272 | decl.name = undefined; |
| 281 | 273 | } |
| 282 | 274 | |
| ... | ... | @@ -285,7 +277,7 @@ pub const Decl = struct { |
| 285 | 277 | log.debug("destroy {*} ({s})", .{ decl, decl.name }); |
| 286 | 278 | decl.clearName(gpa); |
| 287 | 279 | if (decl.has_tv) { |
| 288 | | if (decl.val.getTypeNamespace()) |namespace| { |
| 280 | if (decl.getInnerNamespace()) |namespace| { |
| 289 | 281 | if (namespace.getDecl() == decl) { |
| 290 | 282 | namespace.clearDecls(module); |
| 291 | 283 | } |
| ... | ... | @@ -308,6 +300,9 @@ pub const Decl = struct { |
| 308 | 300 | func.deinit(gpa); |
| 309 | 301 | gpa.destroy(func); |
| 310 | 302 | } |
| 303 | if (decl.getVariable()) |variable| { |
| 304 | gpa.destroy(variable); |
| 305 | } |
| 311 | 306 | if (decl.value_arena) |arena_state| { |
| 312 | 307 | arena_state.promote(gpa).deinit(); |
| 313 | 308 | decl.value_arena = null; |
| ... | ... | @@ -472,6 +467,47 @@ pub const Decl = struct { |
| 472 | 467 | return func; |
| 473 | 468 | } |
| 474 | 469 | |
| 470 | pub fn getVariable(decl: *Decl) ?*Var { |
| 471 | if (!decl.has_tv) return null; |
| 472 | const variable = (decl.val.castTag(.variable) orelse return null).data; |
| 473 | if (variable.owner_decl != decl) return null; |
| 474 | return variable; |
| 475 | } |
| 476 | |
| 477 | /// Gets the namespace that this Decl creates by being a struct, union, |
| 478 | /// enum, or opaque. |
| 479 | /// Only returns it if the Decl is the owner. |
| 480 | pub fn getInnerNamespace(decl: *Decl) ?*Scope.Namespace { |
| 481 | if (!decl.has_tv) return null; |
| 482 | const ty = (decl.val.castTag(.ty) orelse return null).data; |
| 483 | switch (ty.tag()) { |
| 484 | .@"struct" => { |
| 485 | const struct_obj = ty.castTag(.@"struct").?.data; |
| 486 | if (struct_obj.owner_decl != decl) return null; |
| 487 | return &struct_obj.namespace; |
| 488 | }, |
| 489 | .enum_full => { |
| 490 | const enum_obj = ty.castTag(.enum_full).?.data; |
| 491 | if (enum_obj.owner_decl != decl) return null; |
| 492 | return &enum_obj.namespace; |
| 493 | }, |
| 494 | .empty_struct => { |
| 495 | // design flaw, can't verify the owner is this decl |
| 496 | @panic("TODO can't implement getInnerNamespace for this type"); |
| 497 | }, |
| 498 | .@"opaque" => { |
| 499 | @panic("TODO opaque types"); |
| 500 | }, |
| 501 | .@"union", .union_tagged => { |
| 502 | const union_obj = ty.cast(Type.Payload.Union).?.data; |
| 503 | if (union_obj.owner_decl != decl) return null; |
| 504 | return &union_obj.namespace; |
| 505 | }, |
| 506 | |
| 507 | else => return null, |
| 508 | } |
| 509 | } |
| 510 | |
| 475 | 511 | pub fn dump(decl: *Decl) void { |
| 476 | 512 | const loc = std.zig.findLineColumn(decl.scope.source.bytes, decl.src); |
| 477 | 513 | std.debug.print("{s}:{d}:{d} name={s} status={s}", .{ |
| ... | ... | @@ -504,6 +540,23 @@ pub const Decl = struct { |
| 504 | 540 | fn removeDependency(decl: *Decl, other: *Decl) void { |
| 505 | 541 | decl.dependencies.removeAssertDiscard(other); |
| 506 | 542 | } |
| 543 | |
| 544 | fn hasLinkAllocation(decl: Decl) bool { |
| 545 | return switch (decl.analysis) { |
| 546 | .unreferenced, |
| 547 | .in_progress, |
| 548 | .dependency_failure, |
| 549 | .sema_failure, |
| 550 | .sema_failure_retryable, |
| 551 | .codegen_failure, |
| 552 | .codegen_failure_retryable, |
| 553 | => false, |
| 554 | |
| 555 | .complete, |
| 556 | .outdated, |
| 557 | => true, |
| 558 | }; |
| 559 | } |
| 507 | 560 | }; |
| 508 | 561 | |
| 509 | 562 | /// This state is attached to every Decl when Module emit_h is non-null. |
| ... | ... | @@ -831,9 +884,8 @@ pub const Scope = struct { |
| 831 | 884 | /// Direct children of the namespace. Used during an update to detect |
| 832 | 885 | /// which decls have been added/removed from source. |
| 833 | 886 | /// Declaration order is preserved via entry order. |
| 834 | | /// Key memory references the string table of the containing `File` ZIR. |
| 887 | /// Key memory is owned by `decl.name`. |
| 835 | 888 | /// TODO save memory with https://github.com/ziglang/zig/issues/8619. |
| 836 | | /// Does not contain anonymous decls. |
| 837 | 889 | decls: std.StringArrayHashMapUnmanaged(*Decl) = .{}, |
| 838 | 890 | |
| 839 | 891 | pub fn deinit(ns: *Namespace, mod: *Module) void { |
| ... | ... | @@ -2468,8 +2520,6 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node |
| 2468 | 2520 | /// * Decl.zir_index |
| 2469 | 2521 | /// * Fn.zir_body_inst |
| 2470 | 2522 | /// * Decl.zir_decl_index |
| 2471 | | /// * Decl.name |
| 2472 | | /// * Namespace.decl keys |
| 2473 | 2523 | fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void { |
| 2474 | 2524 | const new_zir = file.zir; |
| 2475 | 2525 | |
| ... | ... | @@ -2484,18 +2534,6 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void { |
| 2484 | 2534 | |
| 2485 | 2535 | try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map, &extra_map); |
| 2486 | 2536 | |
| 2487 | | // Build string table for new ZIR. |
| 2488 | | var string_table: std.StringHashMapUnmanaged(u32) = .{}; |
| 2489 | | defer string_table.deinit(gpa); |
| 2490 | | { |
| 2491 | | var i: usize = 2; |
| 2492 | | while (i < new_zir.string_bytes.len) { |
| 2493 | | const string = new_zir.nullTerminatedString(i); |
| 2494 | | try string_table.put(gpa, string, @intCast(u32, i)); |
| 2495 | | i += string.len + 1; |
| 2496 | | } |
| 2497 | | } |
| 2498 | | |
| 2499 | 2537 | // Walk the Decl graph, updating ZIR indexes, strings, and populating |
| 2500 | 2538 | // the deleted and outdated lists. |
| 2501 | 2539 | |
| ... | ... | @@ -2523,12 +2561,6 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void { |
| 2523 | 2561 | try file.deleted_decls.append(gpa, decl); |
| 2524 | 2562 | continue; |
| 2525 | 2563 | }; |
| 2526 | | const new_name_index = string_table.get(mem.spanZ(decl.name)) orelse { |
| 2527 | | try file.deleted_decls.append(gpa, decl); |
| 2528 | | continue; |
| 2529 | | }; |
| 2530 | | decl.name = new_zir.nullTerminatedString(new_name_index).ptr; |
| 2531 | | |
| 2532 | 2564 | const new_hash = decl.contentsHashZir(new_zir); |
| 2533 | 2565 | if (!std.zig.srcHashEql(old_hash, new_hash)) { |
| 2534 | 2566 | try file.outdated_decls.append(gpa, decl); |
| ... | ... | @@ -2558,16 +2590,9 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void { |
| 2558 | 2590 | }; |
| 2559 | 2591 | } |
| 2560 | 2592 | |
| 2561 | | if (decl.val.getTypeNamespace()) |namespace| { |
| 2593 | if (decl.getInnerNamespace()) |namespace| { |
| 2562 | 2594 | for (namespace.decls.items()) |*entry| { |
| 2563 | 2595 | const sub_decl = entry.value; |
| 2564 | | if (sub_decl.zir_decl_index != 0) { |
| 2565 | | const new_key_index = string_table.get(entry.key) orelse { |
| 2566 | | try file.deleted_decls.append(gpa, sub_decl); |
| 2567 | | continue; |
| 2568 | | }; |
| 2569 | | entry.key = new_zir.nullTerminatedString(new_key_index); |
| 2570 | | } |
| 2571 | 2596 | try decl_stack.append(gpa, sub_decl); |
| 2572 | 2597 | } |
| 2573 | 2598 | } |
| ... | ... | @@ -2936,46 +2961,14 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 2936 | 2961 | } |
| 2937 | 2962 | return type_changed or is_inline != prev_is_inline; |
| 2938 | 2963 | } else { |
| 2939 | | const is_mutable = decl_tv.val.tag() == .variable; |
| 2940 | | |
| 2941 | | var is_threadlocal = false; // TODO implement threadlocal variables |
| 2942 | | var is_extern = false; // TODO implement extern variables |
| 2943 | | |
| 2944 | | if (is_mutable and !decl_tv.ty.isValidVarType(is_extern)) { |
| 2945 | | return mod.fail( |
| 2946 | | &block_scope.base, |
| 2947 | | src, // TODO point at the mut token |
| 2948 | | "variable of type '{}' must be const", |
| 2949 | | .{decl_tv.ty}, |
| 2950 | | ); |
| 2951 | | } |
| 2952 | | |
| 2953 | 2964 | var type_changed = true; |
| 2954 | 2965 | if (decl.has_tv) { |
| 2955 | 2966 | type_changed = !decl.ty.eql(decl_tv.ty); |
| 2956 | 2967 | decl.clearValues(gpa); |
| 2957 | 2968 | } |
| 2958 | 2969 | |
| 2959 | | const copied_val = try decl_tv.val.copy(&decl_arena.allocator); |
| 2960 | | const is_extern_fn = copied_val.tag() == .extern_fn; |
| 2961 | | |
| 2962 | | // TODO: also avoid allocating this Var structure if `!is_mutable`. |
| 2963 | | // I think this will require adjusting Sema to copy the value or something |
| 2964 | | // like that; otherwise it causes use of undefined value when freeing resources. |
| 2965 | | const decl_val: Value = if (is_extern_fn) copied_val else blk: { |
| 2966 | | const new_variable = try decl_arena.allocator.create(Var); |
| 2967 | | new_variable.* = .{ |
| 2968 | | .owner_decl = decl, |
| 2969 | | .init = copied_val, |
| 2970 | | .is_extern = is_extern, |
| 2971 | | .is_mutable = is_mutable, |
| 2972 | | .is_threadlocal = is_threadlocal, |
| 2973 | | }; |
| 2974 | | break :blk try Value.Tag.variable.create(&decl_arena.allocator, new_variable); |
| 2975 | | }; |
| 2976 | | |
| 2977 | 2970 | decl.ty = try decl_tv.ty.copy(&decl_arena.allocator); |
| 2978 | | decl.val = decl_val; |
| 2971 | decl.val = try decl_tv.val.copy(&decl_arena.allocator); |
| 2979 | 2972 | decl.align_val = try align_val.copy(&decl_arena.allocator); |
| 2980 | 2973 | decl.linksection_val = try linksection_val.copy(&decl_arena.allocator); |
| 2981 | 2974 | decl.has_tv = true; |
| ... | ... | @@ -3211,7 +3204,8 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo |
| 3211 | 3204 | const decl_node = iter.parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node); |
| 3212 | 3205 | |
| 3213 | 3206 | // Every Decl needs a name. |
| 3214 | | const raw_decl_name: [:0]const u8 = switch (decl_name_index) { |
| 3207 | var is_named_test = false; |
| 3208 | const decl_name: [:0]const u8 = switch (decl_name_index) { |
| 3215 | 3209 | 0 => name: { |
| 3216 | 3210 | if (is_exported) { |
| 3217 | 3211 | const i = iter.usingnamespace_index; |
| ... | ... | @@ -3228,24 +3222,28 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo |
| 3228 | 3222 | iter.unnamed_test_index += 1; |
| 3229 | 3223 | break :name try std.fmt.allocPrintZ(gpa, "test_{d}", .{i}); |
| 3230 | 3224 | }, |
| 3231 | | else => zir.nullTerminatedString(decl_name_index), |
| 3232 | | }; |
| 3233 | | const decl_name = if (raw_decl_name.len != 0) raw_decl_name else name: { |
| 3234 | | const test_name = zir.nullTerminatedString(decl_name_index + 1); |
| 3235 | | break :name try std.fmt.allocPrintZ(gpa, "test.{s}", .{test_name}); |
| 3225 | else => name: { |
| 3226 | const raw_name = zir.nullTerminatedString(decl_name_index); |
| 3227 | if (raw_name.len == 0) { |
| 3228 | is_named_test = true; |
| 3229 | const test_name = zir.nullTerminatedString(decl_name_index + 1); |
| 3230 | break :name try std.fmt.allocPrintZ(gpa, "test.{s}", .{test_name}); |
| 3231 | } else { |
| 3232 | break :name try gpa.dupeZ(u8, raw_name); |
| 3233 | } |
| 3234 | }, |
| 3236 | 3235 | }; |
| 3237 | 3236 | |
| 3238 | 3237 | // We create a Decl for it regardless of analysis status. |
| 3239 | 3238 | const gop = try namespace.decls.getOrPut(gpa, decl_name); |
| 3240 | 3239 | if (!gop.found_existing) { |
| 3241 | 3240 | const new_decl = try mod.allocateNewDecl(namespace, decl_node); |
| 3242 | | log.debug("scan new decl {*} ({s}) into {*}", .{ new_decl, decl_name, namespace }); |
| 3241 | log.debug("scan new {*} ({s}) into {*}", .{ new_decl, decl_name, namespace }); |
| 3243 | 3242 | new_decl.src_line = line; |
| 3244 | 3243 | new_decl.name = decl_name; |
| 3245 | 3244 | gop.entry.value = new_decl; |
| 3246 | 3245 | // Exported decls, comptime decls, usingnamespace decls, and |
| 3247 | 3246 | // test decls if in test mode, get analyzed. |
| 3248 | | const is_named_test = raw_decl_name.len == 0; |
| 3249 | 3247 | const want_analysis = is_exported or switch (decl_name_index) { |
| 3250 | 3248 | 0 => true, // comptime decl |
| 3251 | 3249 | 1 => mod.comp.bin_file.options.is_test, // test decl |
| ... | ... | @@ -3261,17 +3259,15 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) InnerError!vo |
| 3261 | 3259 | new_decl.zir_decl_index = @intCast(u32, decl_sub_index); |
| 3262 | 3260 | return; |
| 3263 | 3261 | } |
| 3262 | gpa.free(decl_name); |
| 3264 | 3263 | const decl = gop.entry.value; |
| 3265 | | log.debug("scan existing decl {*} ({s}) of {*}", .{ decl, decl_name, namespace }); |
| 3264 | log.debug("scan existing {*} ({s}) of {*}", .{ decl, decl_name, namespace }); |
| 3266 | 3265 | // Update the AST node of the decl; even if its contents are unchanged, it may |
| 3267 | 3266 | // have been re-ordered. |
| 3268 | 3267 | const prev_src_node = decl.src_node; |
| 3269 | 3268 | decl.src_node = decl_node; |
| 3270 | 3269 | decl.src_line = line; |
| 3271 | 3270 | |
| 3272 | | decl.clearName(gpa); |
| 3273 | | decl.name = decl_name; |
| 3274 | | |
| 3275 | 3271 | decl.is_pub = is_pub; |
| 3276 | 3272 | decl.is_exported = is_exported; |
| 3277 | 3273 | decl.has_align = has_align; |
| ... | ... | @@ -3305,14 +3301,13 @@ pub fn deleteDecl( |
| 3305 | 3301 | const tracy = trace(@src()); |
| 3306 | 3302 | defer tracy.end(); |
| 3307 | 3303 | |
| 3308 | | log.debug("deleting decl '{s}'", .{decl.name}); |
| 3304 | log.debug("deleting {*} ({s})", .{ decl, decl.name }); |
| 3309 | 3305 | |
| 3310 | 3306 | if (outdated_decls) |map| { |
| 3311 | 3307 | _ = map.swapRemove(decl); |
| 3312 | | try map.ensureCapacity(map.count() + decl.dependants.count()); |
| 3308 | try map.ensureUnusedCapacity(decl.dependants.count()); |
| 3313 | 3309 | } |
| 3314 | | try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.count() + |
| 3315 | | decl.dependencies.count()); |
| 3310 | try mod.deletion_set.ensureUnusedCapacity(mod.gpa, decl.dependencies.count()); |
| 3316 | 3311 | |
| 3317 | 3312 | // Remove from the namespace it resides in. |
| 3318 | 3313 | decl.namespace.removeDecl(decl); |
| ... | ... | @@ -3354,7 +3349,9 @@ pub fn deleteDecl( |
| 3354 | 3349 | } |
| 3355 | 3350 | _ = mod.compile_log_decls.swapRemove(decl); |
| 3356 | 3351 | mod.deleteDeclExports(decl); |
| 3357 | | mod.comp.bin_file.freeDecl(decl); |
| 3352 | if (decl.hasLinkAllocation()) { |
| 3353 | mod.comp.bin_file.freeDecl(decl); |
| 3354 | } |
| 3358 | 3355 | |
| 3359 | 3356 | decl.destroy(mod); |
| 3360 | 3357 | } |