Commit e23b0a01e6

Jacob Young <jacobly0@users.noreply.github.com>
2023-06-03 00:49:40
InternPool: fix yet more key lifetime issues
1 parent 6a15fc8
src/arch/x86_64/CodeGen.zig
@@ -2026,13 +2026,9 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
             try self.genLazySymbolRef(.lea, data_reg, .{ .kind = .const_data, .ty = enum_ty });
 
             var data_off: i32 = 0;
-            for (
-                exitlude_jump_relocs,
-                enum_ty.enumFields(mod),
-                0..,
-            ) |*exitlude_jump_reloc, tag_name_ip, index_usize| {
+            for (exitlude_jump_relocs, 0..) |*exitlude_jump_reloc, index_usize| {
                 const index = @intCast(u32, index_usize);
-                const tag_name = mod.intern_pool.stringToSlice(tag_name_ip);
+                const tag_name = mod.intern_pool.stringToSlice(enum_ty.enumFields(mod)[index_usize]);
                 const tag_val = try mod.enumValueFieldIndex(enum_ty, index);
                 const tag_mcv = try self.genTypedValue(.{ .ty = enum_ty, .val = tag_val });
                 try self.genBinOpMir(.{ ._, .cmp }, enum_ty, enum_mcv, tag_mcv);
src/codegen.zig
@@ -517,7 +517,7 @@ pub fn generateSymbol(
                         const field_ty = field.ty;
                         if (!field_ty.hasRuntimeBits(mod)) continue;
 
-                        const field_val = switch (aggregate.storage) {
+                        const field_val = switch (mod.intern_pool.indexToKey(typed_value.val.toIntern()).aggregate.storage) {
                             .bytes => |bytes| try mod.intern_pool.get(mod.gpa, .{ .int = .{
                                 .ty = field_ty.toIntern(),
                                 .storage = .{ .u64 = bytes[index] },
src/Module.zig
@@ -5448,7 +5448,6 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
     defer comptime_mutable_decls.deinit();
 
     const fn_ty = decl.ty;
-    const fn_ty_info = mod.typeToFunc(fn_ty).?;
 
     var sema: Sema = .{
         .mod = mod,
@@ -5459,7 +5458,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
         .owner_decl_index = decl_index,
         .func = func,
         .func_index = func_index.toOptional(),
-        .fn_ret_ty = fn_ty_info.return_type.toType(),
+        .fn_ret_ty = mod.typeToFunc(fn_ty).?.return_type.toType(),
         .owner_func = func,
         .owner_func_index = func_index.toOptional(),
         .branch_quota = @max(func.branch_quota, Sema.default_branch_quota),
@@ -5499,7 +5498,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
     // This could be a generic function instantiation, however, in which case we need to
     // map the comptime parameters to constant values and only emit arg AIR instructions
     // for the runtime ones.
-    const runtime_params_len = @intCast(u32, fn_ty_info.param_types.len);
+    const runtime_params_len = @intCast(u32, mod.typeToFunc(fn_ty).?.param_types.len);
     try inner_block.instructions.ensureTotalCapacityPrecise(gpa, runtime_params_len);
     try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len * 2); // * 2 for the `addType`
     try sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
@@ -5525,7 +5524,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
             sema.inst_map.putAssumeCapacityNoClobber(inst, arg);
             total_param_index += 1;
             continue;
-        } else fn_ty_info.param_types[runtime_param_index].toType();
+        } else mod.typeToFunc(fn_ty).?.param_types[runtime_param_index].toType();
 
         const opt_opv = sema.typeHasOnePossibleValue(param_ty) catch |err| switch (err) {
             error.NeededSourceLocation => unreachable,
@@ -5623,7 +5622,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
     // Crucially, this happens *after* we set the function state to success above,
     // so that dependencies on the function body will now be satisfied rather than
     // result in circular dependency errors.
-    sema.resolveFnTypes(mod.typeToFunc(fn_ty).?) catch |err| switch (err) {
+    sema.resolveFnTypes(fn_ty) catch |err| switch (err) {
         error.NeededSourceLocation => unreachable,
         error.GenericPoison => unreachable,
         error.ComptimeReturn => unreachable,
@@ -6378,9 +6377,9 @@ pub fn populateTestFunctions(
 
         for (test_fn_vals, mod.test_functions.keys()) |*test_fn_val, test_decl_index| {
             const test_decl = mod.declPtr(test_decl_index);
-            // Protects test_decl_name from being invalidated during call to intern() below.
-            try ip.string_bytes.ensureUnusedCapacity(gpa, ip.stringToSlice(test_decl.name).len + 10);
-            const test_decl_name = ip.stringToSlice(test_decl.name);
+            // TODO: write something like getCoercedInts to avoid needing to dupe
+            const test_decl_name = try gpa.dupe(u8, ip.stringToSlice(test_decl.name));
+            defer gpa.free(test_decl_name);
             const test_name_decl_index = n: {
                 const test_name_decl_ty = try mod.arrayType(.{
                     .len = test_decl_name.len,
src/Sema.zig
@@ -5227,6 +5227,8 @@ fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
 fn addStrLit(sema: *Sema, block: *Block, bytes: []const u8) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
     const gpa = sema.gpa;
+    // TODO: write something like getCoercedInts to avoid needing to dupe
+    const duped_bytes = try sema.arena.dupe(u8, bytes);
     const ty = try mod.arrayType(.{
         .len = bytes.len,
         .child = .u8_type,
@@ -5234,7 +5236,7 @@ fn addStrLit(sema: *Sema, block: *Block, bytes: []const u8) CompileError!Air.Ins
     });
     const val = try mod.intern(.{ .aggregate = .{
         .ty = ty.toIntern(),
-        .storage = .{ .bytes = bytes },
+        .storage = .{ .bytes = duped_bytes },
     } });
     const gop = try mod.memoized_decls.getOrPut(gpa, val);
     if (!gop.found_existing) {
@@ -11478,7 +11480,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                         operand_ty.fmt(mod),
                     });
                 }
-                for (operand_ty.errorSetNames(mod)) |error_name_ip| {
+                for (0..operand_ty.errorSetNames(mod).len) |i| {
+                    const error_name_ip = operand_ty.errorSetNames(mod)[i];
                     const error_name = mod.intern_pool.stringToSlice(error_name_ip);
                     if (seen_errors.contains(error_name)) continue;
                     cases_len += 1;
@@ -15851,7 +15854,8 @@ fn zirBuiltinSrc(
     const func_name_val = blk: {
         var anon_decl = try block.startAnonDecl();
         defer anon_decl.deinit();
-        const name = mod.intern_pool.stringToSlice(fn_owner_decl.name);
+        // TODO: write something like getCoercedInts to avoid needing to dupe
+        const name = try sema.arena.dupe(u8, mod.intern_pool.stringToSlice(fn_owner_decl.name));
         const new_decl_ty = try mod.arrayType(.{
             .len = name.len,
             .child = .u8_type,
@@ -16287,7 +16291,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             const error_field_vals = if (ty.isAnyError(mod)) null else blk: {
                 const vals = try sema.arena.alloc(InternPool.Index, ty.errorSetNames(mod).len);
                 for (vals, 0..) |*field_val, i| {
-                    const name = ip.stringToSlice(ty.errorSetNames(mod)[i]);
+                    // TODO: write something like getCoercedInts to avoid needing to dupe
+                    const name = try sema.arena.dupe(u8, ip.stringToSlice(ty.errorSetNames(mod)[i]));
                     const name_val = v: {
                         var anon_decl = try block.startAnonDecl();
                         defer anon_decl.deinit();
@@ -16417,8 +16422,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
             const enum_field_vals = try sema.arena.alloc(InternPool.Index, enum_type.names.len);
             for (enum_field_vals, 0..) |*field_val, i| {
-                const name_ip = ip.indexToKey(ty.toIntern()).enum_type.names[i];
-                const name = ip.stringToSlice(name_ip);
+                // TODO: write something like getCoercedInts to avoid needing to dupe
+                const name = try sema.arena.dupe(u8, ip.stringToSlice(ip.indexToKey(ty.toIntern()).enum_type.names[i]));
                 const name_val = v: {
                     var anon_decl = try block.startAnonDecl();
                     defer anon_decl.deinit();
@@ -16556,7 +16561,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
             for (union_field_vals, 0..) |*field_val, i| {
                 const field = union_fields.values()[i];
-                const name = ip.stringToSlice(union_fields.keys()[i]);
+                // TODO: write something like getCoercedInts to avoid needing to dupe
+                const name = try sema.arena.dupe(u8, ip.stringToSlice(union_fields.keys()[i]));
                 const name_val = v: {
                     var anon_decl = try block.startAnonDecl();
                     defer anon_decl.deinit();
@@ -16714,9 +16720,10 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                             const name_val = v: {
                                 var anon_decl = try block.startAnonDecl();
                                 defer anon_decl.deinit();
+                                // TODO: write something like getCoercedInts to avoid needing to dupe
                                 const bytes = if (tuple.names.len != 0)
                                     // https://github.com/ziglang/zig/issues/15709
-                                    @as([]const u8, ip.stringToSlice(tuple.names[i]))
+                                    try sema.arena.dupe(u8, ip.stringToSlice(ip.indexToKey(struct_ty.toIntern()).anon_struct_type.names[i]))
                                 else
                                     try std.fmt.allocPrint(sema.arena, "{d}", .{i});
                                 const new_decl_ty = try mod.arrayType(.{
@@ -16771,7 +16778,8 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                     struct_obj.fields.keys(),
                     struct_obj.fields.values(),
                 ) |*field_val, name_nts, field| {
-                    const name = ip.stringToSlice(name_nts);
+                    // TODO: write something like getCoercedInts to avoid needing to dupe
+                    const name = try sema.arena.dupe(u8, ip.stringToSlice(name_nts));
                     const name_val = v: {
                         var anon_decl = try block.startAnonDecl();
                         defer anon_decl.deinit();
@@ -17020,9 +17028,8 @@ fn typeInfoNamespaceDecls(
         const name_val = v: {
             var anon_decl = try block.startAnonDecl();
             defer anon_decl.deinit();
-            // Protects the decl name slice from being invalidated at the call to intern().
-            try ip.string_bytes.ensureUnusedCapacity(sema.gpa, ip.stringToSlice(decl.name).len + 1);
-            const name = ip.stringToSlice(decl.name);
+            // TODO: write something like getCoercedInts to avoid needing to dupe
+            const name = try sema.arena.dupe(u8, ip.stringToSlice(decl.name));
             const new_decl_ty = try mod.arrayType(.{
                 .len = name.len,
                 .child = .u8_type,
@@ -19060,6 +19067,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
             };
             return sema.failWithOwnedErrorMsg(msg);
         };
+        // TODO: write something like getCoercedInts to avoid needing to dupe
         const field_name = enum_ty.enumFieldName(field_index, mod);
         return sema.addStrLit(block, ip.stringToSlice(field_name));
     }
@@ -19601,7 +19609,6 @@ fn zirReify(
             // Tag type
             const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
             var explicit_tags_seen: []bool = &.{};
-            var explicit_enum_info: ?InternPool.Key.EnumType = null;
             var enum_field_names: []InternPool.NullTerminatedString = &.{};
             if (tag_type_val.optionalValue(mod)) |payload_val| {
                 union_obj.tag_ty = payload_val.toType();
@@ -19611,7 +19618,6 @@ fn zirReify(
                     else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}),
                 };
 
-                explicit_enum_info = enum_type;
                 explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
                 @memset(explicit_tags_seen, false);
             } else {
@@ -19640,7 +19646,8 @@ fn zirReify(
                     enum_field_names[i] = field_name;
                 }
 
-                if (explicit_enum_info) |tag_info| {
+                if (explicit_tags_seen.len > 0) {
+                    const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type;
                     const enum_index = tag_info.nameIndex(ip, field_name) orelse {
                         const msg = msg: {
                             const msg = try sema.errMsg(block, src, "no field named '{s}' in enum '{}'", .{ ip.stringToSlice(field_name), union_obj.tag_ty.fmt(mod) });
@@ -19705,7 +19712,8 @@ fn zirReify(
                 }
             }
 
-            if (explicit_enum_info) |tag_info| {
+            if (explicit_tags_seen.len > 0) {
+                const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type;
                 if (tag_info.names.len > fields_len) {
                     const msg = msg: {
                         const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
@@ -31625,17 +31633,17 @@ fn resolvePeerTypes(
     return chosen_ty;
 }
 
-pub fn resolveFnTypes(sema: *Sema, fn_info: InternPool.Key.FuncType) CompileError!void {
+pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void {
     const mod = sema.mod;
-    try sema.resolveTypeFully(fn_info.return_type.toType());
+    try sema.resolveTypeFully(mod.typeToFunc(fn_ty).?.return_type.toType());
 
-    if (mod.comp.bin_file.options.error_return_tracing and fn_info.return_type.toType().isError(mod)) {
+    if (mod.comp.bin_file.options.error_return_tracing and mod.typeToFunc(fn_ty).?.return_type.toType().isError(mod)) {
         // Ensure the type exists so that backends can assume that.
         _ = try sema.getBuiltinType("StackTrace");
     }
 
-    for (fn_info.param_types) |param_ty| {
-        try sema.resolveTypeFully(param_ty.toType());
+    for (0..mod.typeToFunc(fn_ty).?.param_types.len) |i| {
+        try sema.resolveTypeFully(mod.typeToFunc(fn_ty).?.param_types[i].toType());
     }
 }
 
@@ -33077,7 +33085,6 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
     var enum_field_names: []InternPool.NullTerminatedString = &.{};
     var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .{};
     var explicit_tags_seen: []bool = &.{};
-    var explicit_enum_info: ?InternPool.Key.EnumType = null;
     if (tag_type_ref != .none) {
         const tag_ty_src: LazySrcLoc = .{ .node_offset_container_tag = src.node_offset.x };
         const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref);
@@ -33114,7 +33121,6 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
             };
             // The fields of the union must match the enum exactly.
             // A flag per field is used to check for missing and extraneous fields.
-            explicit_enum_info = enum_type;
             explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
             @memset(explicit_tags_seen, false);
         }
@@ -33256,7 +33262,8 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
             return sema.failWithOwnedErrorMsg(msg);
         }
 
-        if (explicit_enum_info) |tag_info| {
+        if (explicit_tags_seen.len > 0) {
+            const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type;
             const enum_index = tag_info.nameIndex(ip, field_name) orelse {
                 const msg = msg: {
                     const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{
@@ -33346,7 +33353,8 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
         }
     }
 
-    if (explicit_enum_info) |tag_info| {
+    if (explicit_tags_seen.len > 0) {
+        const tag_info = ip.indexToKey(union_obj.tag_ty.toIntern()).enum_type;
         if (tag_info.names.len > fields_len) {
             const msg = msg: {
                 const msg = try sema.errMsg(&block_scope, src, "enum field(s) missing in union", .{});
@@ -33706,9 +33714,10 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
                 }
                 // In this case the struct has all comptime-known fields and
                 // therefore has one possible value.
+                // TODO: write something like getCoercedInts to avoid needing to dupe
                 return (try mod.intern(.{ .aggregate = .{
                     .ty = ty.toIntern(),
-                    .storage = .{ .elems = tuple.values },
+                    .storage = .{ .elems = try sema.arena.dupe(InternPool.Index, tuple.values) },
                 } })).toValue();
             },
 
src/type.zig
@@ -2576,9 +2576,12 @@ pub const Type = struct {
                     }
                     // In this case the struct has all comptime-known fields and
                     // therefore has one possible value.
+                    // TODO: write something like getCoercedInts to avoid needing to dupe
+                    const duped_values = try mod.gpa.dupe(InternPool.Index, tuple.values);
+                    defer mod.gpa.free(duped_values);
                     return (try mod.intern(.{ .aggregate = .{
                         .ty = ty.toIntern(),
-                        .storage = .{ .elems = tuple.values },
+                        .storage = .{ .elems = duped_values },
                     } })).toValue();
                 },
 
src/value.zig
@@ -1904,6 +1904,7 @@ pub const Value = struct {
         start: usize,
         end: usize,
     ) error{OutOfMemory}!Value {
+        // TODO: write something like getCoercedInts to avoid needing to dupe
         return switch (val.ip_index) {
             .none => switch (val.tag()) {
                 .slice => val.castTag(.slice).?.data.ptr.sliceArray(mod, arena, start, end),
@@ -1937,8 +1938,8 @@ pub const Value = struct {
                         else => unreachable,
                     }.toIntern(),
                     .storage = switch (aggregate.storage) {
-                        .bytes => |bytes| .{ .bytes = bytes[start..end] },
-                        .elems => |elems| .{ .elems = elems[start..end] },
+                        .bytes => .{ .bytes = try arena.dupe(u8, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.bytes[start..end]) },
+                        .elems => .{ .elems = try arena.dupe(InternPool.Index, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.elems[start..end]) },
                         .repeated_elem => |elem| .{ .repeated_elem = elem },
                     },
                 } })).toValue(),