InternPool: make more use of NullTerminatedString.Slice

This should avoid the random pointer invalidation crashes.

Closes #18954
This commit is contained in:
Jacob Young 2024-02-16 03:13:25 +01:00 committed by Andrew Kelley
parent 0183b44bb1
commit 6f08e17229
9 changed files with 70 additions and 61 deletions

View File

@ -12619,8 +12619,9 @@ fn analyzeSwitchRuntimeBlock(
operand_ty.fmt(mod), operand_ty.fmt(mod),
}); });
} }
for (0..operand_ty.errorSetNames(mod).len) |i| { const error_names = operand_ty.errorSetNames(mod);
const error_name = operand_ty.errorSetNames(mod)[i]; for (0..error_names.len) |name_index| {
const error_name = error_names.get(ip)[name_index];
if (seen_errors.contains(error_name)) continue; if (seen_errors.contains(error_name)) continue;
cases_len += 1; cases_len += 1;
@ -22362,8 +22363,9 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
if (!operand_ty.isAnyError(mod) and operand_ty.errorSetIsEmpty(mod)) break :disjoint true; if (!operand_ty.isAnyError(mod) and operand_ty.errorSetIsEmpty(mod)) break :disjoint true;
if (dest_ty.isAnyError(mod)) break :disjoint false; if (dest_ty.isAnyError(mod)) break :disjoint false;
if (operand_ty.isAnyError(mod)) break :disjoint false; if (operand_ty.isAnyError(mod)) break :disjoint false;
for (dest_ty.errorSetNames(mod)) |dest_err_name| { const dest_err_names = dest_ty.errorSetNames(mod);
if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name)) for (0..dest_err_names.len) |dest_err_index| {
if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_names.get(ip)[dest_err_index]))
break :disjoint false; break :disjoint false;
} }
@ -22375,8 +22377,8 @@ fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData
_ = try sema.resolveInferredErrorSetTy(block, src, dest_ty.toIntern()); _ = try sema.resolveInferredErrorSetTy(block, src, dest_ty.toIntern());
_ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty.toIntern()); _ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_ty.toIntern());
for (dest_ty.errorSetNames(mod)) |dest_err_name| { for (0..dest_err_names.len) |dest_err_index| {
if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_name)) if (Type.errorSetHasFieldIp(ip, operand_ty.toIntern(), dest_err_names.get(ip)[dest_err_index]))
break :disjoint false; break :disjoint false;
} }
@ -38780,17 +38782,18 @@ fn elemPtrType(sema: *Sema, ptr_ty: Type, offset: ?usize) !Type {
/// Asserts that lhs and rhs are both error sets and are resolved. /// Asserts that lhs and rhs are both error sets and are resolved.
fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type { fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type {
const mod = sema.mod; const mod = sema.mod;
const ip = &mod.intern_pool;
const arena = sema.arena; const arena = sema.arena;
const lhs_names = lhs.errorSetNames(mod); const lhs_names = lhs.errorSetNames(mod);
const rhs_names = rhs.errorSetNames(mod); const rhs_names = rhs.errorSetNames(mod);
var names: InferredErrorSet.NameMap = .{}; var names: InferredErrorSet.NameMap = .{};
try names.ensureUnusedCapacity(arena, lhs_names.len); try names.ensureUnusedCapacity(arena, lhs_names.len);
for (lhs_names) |name| { for (0..lhs_names.len) |lhs_index| {
names.putAssumeCapacityNoClobber(name, {}); names.putAssumeCapacityNoClobber(lhs_names.get(ip)[lhs_index], {});
} }
for (rhs_names) |name| { for (0..rhs_names.len) |rhs_index| {
try names.put(arena, name, {}); try names.put(arena, rhs_names.get(ip)[rhs_index], {});
} }
return mod.errorSetFromUnsortedNames(names.keys()); return mod.errorSetFromUnsortedNames(names.keys());

View File

@ -7216,13 +7216,14 @@ fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
const mod = func.bin_file.base.comp.module.?; const mod = func.bin_file.base.comp.module.?;
const ip = &mod.intern_pool;
const enum_decl_index = enum_ty.getOwnerDecl(mod); const enum_decl_index = enum_ty.getOwnerDecl(mod);
var arena_allocator = std.heap.ArenaAllocator.init(func.gpa); var arena_allocator = std.heap.ArenaAllocator.init(func.gpa);
defer arena_allocator.deinit(); defer arena_allocator.deinit();
const arena = arena_allocator.allocator(); const arena = arena_allocator.allocator();
const fqn = mod.intern_pool.stringToSlice(try mod.declPtr(enum_decl_index).getFullyQualifiedName(mod)); const fqn = ip.stringToSlice(try mod.declPtr(enum_decl_index).getFullyQualifiedName(mod));
const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn}); const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn});
// check if we already generated code for this. // check if we already generated code for this.
@ -7252,9 +7253,9 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
// TODO: Make switch implementation generic so we can use a jump table for this when the tags are not sparse. // TODO: Make switch implementation generic so we can use a jump table for this when the tags are not sparse.
// generate an if-else chain for each tag value as well as constant. // generate an if-else chain for each tag value as well as constant.
for (enum_ty.enumFields(mod), 0..) |tag_name_ip, field_index_usize| { const tag_names = enum_ty.enumFields(mod);
const field_index = @as(u32, @intCast(field_index_usize)); for (0..tag_names.len) |tag_index| {
const tag_name = mod.intern_pool.stringToSlice(tag_name_ip); const tag_name = ip.stringToSlice(tag_names.get(ip)[tag_index]);
// for each tag name, create an unnamed const, // for each tag name, create an unnamed const,
// and then get a pointer to its value. // and then get a pointer to its value.
const name_ty = try mod.arrayType(.{ const name_ty = try mod.arrayType(.{
@ -7279,7 +7280,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
try writer.writeByte(std.wasm.opcode(.local_get)); try writer.writeByte(std.wasm.opcode(.local_get));
try leb.writeULEB128(writer, @as(u32, 1)); try leb.writeULEB128(writer, @as(u32, 1));
const tag_val = try mod.enumValueFieldIndex(enum_ty, field_index); const tag_val = try mod.enumValueFieldIndex(enum_ty, @intCast(tag_index));
const tag_value = try func.lowerConstant(tag_val, enum_ty); const tag_value = try func.lowerConstant(tag_val, enum_ty);
switch (tag_value) { switch (tag_value) {
@ -7372,6 +7373,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const mod = func.bin_file.base.comp.module.?; const mod = func.bin_file.base.comp.module.?;
const ip = &mod.intern_pool;
const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
const operand = try func.resolveInst(ty_op.operand); const operand = try func.resolveInst(ty_op.operand);
@ -7384,8 +7386,8 @@ fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
var lowest: ?u32 = null; var lowest: ?u32 = null;
var highest: ?u32 = null; var highest: ?u32 = null;
for (names) |name| { for (0..names.len) |name_index| {
const err_int = @as(Module.ErrorInt, @intCast(mod.global_error_set.getIndex(name).?)); const err_int: Module.ErrorInt = @intCast(mod.global_error_set.getIndex(names.get(ip)[name_index]).?);
if (lowest) |*l| { if (lowest) |*l| {
if (err_int < l.*) { if (err_int < l.*) {
l.* = err_int; l.* = err_int;

View File

@ -2187,6 +2187,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
const mod = self.bin_file.comp.module.?; const mod = self.bin_file.comp.module.?;
const ip = &mod.intern_pool;
switch (lazy_sym.ty.zigTypeTag(mod)) { switch (lazy_sym.ty.zigTypeTag(mod)) {
.Enum => { .Enum => {
const enum_ty = lazy_sym.ty; const enum_ty = lazy_sym.ty;
@ -2209,10 +2210,10 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
try self.genLazySymbolRef(.lea, data_reg, .{ .kind = .const_data, .ty = enum_ty }); try self.genLazySymbolRef(.lea, data_reg, .{ .kind = .const_data, .ty = enum_ty });
var data_off: i32 = 0; var data_off: i32 = 0;
for (exitlude_jump_relocs, 0..) |*exitlude_jump_reloc, index_usize| { const tag_names = enum_ty.enumFields(mod);
const index: u32 = @intCast(index_usize); for (exitlude_jump_relocs, 0..) |*exitlude_jump_reloc, tag_index| {
const tag_name = mod.intern_pool.stringToSlice(enum_ty.enumFields(mod)[index_usize]); const tag_name_len = ip.stringToSlice(tag_names.get(ip)[tag_index]).len;
const tag_val = try mod.enumValueFieldIndex(enum_ty, index); const tag_val = try mod.enumValueFieldIndex(enum_ty, @intCast(tag_index));
const tag_mcv = try self.genTypedValue(.{ .ty = enum_ty, .val = tag_val }); const tag_mcv = try self.genTypedValue(.{ .ty = enum_ty, .val = tag_val });
try self.genBinOpMir(.{ ._, .cmp }, enum_ty, enum_mcv, tag_mcv); try self.genBinOpMir(.{ ._, .cmp }, enum_ty, enum_mcv, tag_mcv);
const skip_reloc = try self.asmJccReloc(.ne, undefined); const skip_reloc = try self.asmJccReloc(.ne, undefined);
@ -2228,14 +2229,14 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void {
.{ .reg = ret_reg }, .{ .reg = ret_reg },
8, 8,
Type.usize, Type.usize,
.{ .immediate = tag_name.len }, .{ .immediate = tag_name_len },
.{}, .{},
); );
exitlude_jump_reloc.* = try self.asmJmpReloc(undefined); exitlude_jump_reloc.* = try self.asmJmpReloc(undefined);
self.performReloc(skip_reloc); self.performReloc(skip_reloc);
data_off += @intCast(tag_name.len + 1); data_off += @intCast(tag_name_len + 1);
} }
try self.airTrap(); try self.airTrap();

View File

@ -119,6 +119,7 @@ pub fn generateLazySymbol(
const comp = bin_file.comp; const comp = bin_file.comp;
const zcu = comp.module.?; const zcu = comp.module.?;
const ip = &zcu.intern_pool;
const target = comp.root_mod.resolved_target.result; const target = comp.root_mod.resolved_target.result;
const endian = target.cpu.arch.endian(); const endian = target.cpu.arch.endian();
const gpa = comp.gpa; const gpa = comp.gpa;
@ -151,8 +152,9 @@ pub fn generateLazySymbol(
return Result.ok; return Result.ok;
} else if (lazy_sym.ty.zigTypeTag(zcu) == .Enum) { } else if (lazy_sym.ty.zigTypeTag(zcu) == .Enum) {
alignment.* = .@"1"; alignment.* = .@"1";
for (lazy_sym.ty.enumFields(zcu)) |tag_name_ip| { const tag_names = lazy_sym.ty.enumFields(zcu);
const tag_name = zcu.intern_pool.stringToSlice(tag_name_ip); for (0..tag_names.len) |tag_index| {
const tag_name = zcu.intern_pool.stringToSlice(tag_names.get(ip)[tag_index]);
try code.ensureUnusedCapacity(tag_name.len + 1); try code.ensureUnusedCapacity(tag_name.len + 1);
code.appendSliceAssumeCapacity(tag_name); code.appendSliceAssumeCapacity(tag_name);
code.appendAssumeCapacity(0); code.appendAssumeCapacity(0);

View File

@ -2595,6 +2595,7 @@ pub fn genGlobalAsm(mod: *Module, writer: anytype) !void {
pub fn genErrDecls(o: *Object) !void { pub fn genErrDecls(o: *Object) !void {
const mod = o.dg.module; const mod = o.dg.module;
const ip = &mod.intern_pool;
const writer = o.writer(); const writer = o.writer();
var max_name_len: usize = 0; var max_name_len: usize = 0;
@ -2603,7 +2604,7 @@ pub fn genErrDecls(o: *Object) !void {
try writer.writeAll("enum {\n"); try writer.writeAll("enum {\n");
o.indent_writer.pushIndent(); o.indent_writer.pushIndent();
for (mod.global_error_set.keys()[1..], 1..) |name_nts, value| { for (mod.global_error_set.keys()[1..], 1..) |name_nts, value| {
const name = mod.intern_pool.stringToSlice(name_nts); const name = ip.stringToSlice(name_nts);
max_name_len = @max(name.len, max_name_len); max_name_len = @max(name.len, max_name_len);
const err_val = try mod.intern(.{ .err = .{ const err_val = try mod.intern(.{ .err = .{
.ty = .anyerror_type, .ty = .anyerror_type,
@ -2621,8 +2622,8 @@ pub fn genErrDecls(o: *Object) !void {
defer o.dg.gpa.free(name_buf); defer o.dg.gpa.free(name_buf);
@memcpy(name_buf[0..name_prefix.len], name_prefix); @memcpy(name_buf[0..name_prefix.len], name_prefix);
for (mod.global_error_set.keys()) |name_nts| { for (mod.global_error_set.keys()) |name_ip| {
const name = mod.intern_pool.stringToSlice(name_nts); const name = ip.stringToSlice(name_ip);
@memcpy(name_buf[name_prefix.len..][0..name.len], name); @memcpy(name_buf[name_prefix.len..][0..name.len], name);
const identifier = name_buf[0 .. name_prefix.len + name.len]; const identifier = name_buf[0 .. name_prefix.len + name.len];
@ -2652,7 +2653,7 @@ pub fn genErrDecls(o: *Object) !void {
try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = array_identifier }, Const, .none, .complete); try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = array_identifier }, Const, .none, .complete);
try writer.writeAll(" = {"); try writer.writeAll(" = {");
for (mod.global_error_set.keys(), 0..) |name_nts, value| { for (mod.global_error_set.keys(), 0..) |name_nts, value| {
const name = mod.intern_pool.stringToSlice(name_nts); const name = ip.stringToSlice(name_nts);
if (value != 0) try writer.writeByte(','); if (value != 0) try writer.writeByte(',');
const len_val = try mod.intValue(Type.usize, name.len); const len_val = try mod.intValue(Type.usize, name.len);
@ -2730,6 +2731,7 @@ fn genExports(o: *Object) !void {
pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
const mod = o.dg.module; const mod = o.dg.module;
const ip = &mod.intern_pool;
const w = o.writer(); const w = o.writer();
const key = lazy_fn.key_ptr.*; const key = lazy_fn.key_ptr.*;
const val = lazy_fn.value_ptr; const val = lazy_fn.value_ptr;
@ -2747,23 +2749,23 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void {
try w.writeByte('('); try w.writeByte('(');
try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, .none, .complete); try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, .none, .complete);
try w.writeAll(") {\n switch (tag) {\n"); try w.writeAll(") {\n switch (tag) {\n");
for (enum_ty.enumFields(mod), 0..) |name_ip, index_usize| { const tag_names = enum_ty.enumFields(mod);
const index = @as(u32, @intCast(index_usize)); for (0..tag_names.len) |tag_index| {
const name = mod.intern_pool.stringToSlice(name_ip); const tag_name = ip.stringToSlice(tag_names.get(ip)[tag_index]);
const tag_val = try mod.enumValueFieldIndex(enum_ty, index); const tag_val = try mod.enumValueFieldIndex(enum_ty, @intCast(tag_index));
const int_val = try tag_val.intFromEnum(enum_ty, mod); const int_val = try tag_val.intFromEnum(enum_ty, mod);
const name_ty = try mod.arrayType(.{ const name_ty = try mod.arrayType(.{
.len = name.len, .len = tag_name.len,
.child = .u8_type, .child = .u8_type,
.sentinel = .zero_u8, .sentinel = .zero_u8,
}); });
const name_val = try mod.intern(.{ .aggregate = .{ const name_val = try mod.intern(.{ .aggregate = .{
.ty = name_ty.toIntern(), .ty = name_ty.toIntern(),
.storage = .{ .bytes = name }, .storage = .{ .bytes = tag_name },
} }); } });
const len_val = try mod.intValue(Type.usize, name.len); const len_val = try mod.intValue(Type.usize, tag_name.len);
try w.print(" case {}: {{\n static ", .{ try w.print(" case {}: {{\n static ", .{
try o.dg.fmtIntLiteral(enum_ty, int_val, .Other), try o.dg.fmtIntLiteral(enum_ty, int_val, .Other),

View File

@ -9574,6 +9574,7 @@ pub const FuncGen = struct {
fn airErrorSetHasValue(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { fn airErrorSetHasValue(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
const o = self.dg.object; const o = self.dg.object;
const mod = o.module; const mod = o.module;
const ip = &mod.intern_pool;
const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
const operand = try self.resolveInst(ty_op.operand); const operand = try self.resolveInst(ty_op.operand);
const error_set_ty = ty_op.ty.toType(); const error_set_ty = ty_op.ty.toType();
@ -9585,8 +9586,8 @@ pub const FuncGen = struct {
var wip_switch = try self.wip.@"switch"(operand, invalid_block, @intCast(names.len)); var wip_switch = try self.wip.@"switch"(operand, invalid_block, @intCast(names.len));
defer wip_switch.finish(&self.wip); defer wip_switch.finish(&self.wip);
for (names) |name| { for (0..names.len) |name_index| {
const err_int = mod.global_error_set.getIndex(name).?; const err_int = mod.global_error_set.getIndex(names.get(ip)[name_index]).?;
const this_tag_int_value = try o.builder.intConst(try o.errorIntType(), err_int); const this_tag_int_value = try o.builder.intConst(try o.errorIntType(), err_int);
try wip_switch.addCase(this_tag_int_value, valid_block, &self.wip); try wip_switch.addCase(this_tag_int_value, valid_block, &self.wip);
} }

View File

@ -2828,7 +2828,7 @@ fn addDbgInfoErrorSet(
target: std.Target, target: std.Target,
dbg_info_buffer: *std.ArrayList(u8), dbg_info_buffer: *std.ArrayList(u8),
) !void { ) !void {
return addDbgInfoErrorSetNames(mod, ty, ty.errorSetNames(mod), target, dbg_info_buffer); return addDbgInfoErrorSetNames(mod, ty, ty.errorSetNames(mod).get(&mod.intern_pool), target, dbg_info_buffer);
} }
fn addDbgInfoErrorSetNames( fn addDbgInfoErrorSetNames(

View File

@ -2908,22 +2908,21 @@ pub const Type = struct {
// Asserts that `ty` is an error set and not `anyerror`. // Asserts that `ty` is an error set and not `anyerror`.
// Asserts that `ty` is resolved if it is an inferred error set. // Asserts that `ty` is resolved if it is an inferred error set.
pub fn errorSetNames(ty: Type, mod: *Module) []const InternPool.NullTerminatedString { pub fn errorSetNames(ty: Type, mod: *Module) InternPool.NullTerminatedString.Slice {
const ip = &mod.intern_pool; const ip = &mod.intern_pool;
return switch (ip.indexToKey(ty.toIntern())) { return switch (ip.indexToKey(ty.toIntern())) {
.error_set_type => |x| x.names.get(ip), .error_set_type => |x| x.names,
.inferred_error_set_type => |i| switch (ip.funcIesResolved(i).*) { .inferred_error_set_type => |i| switch (ip.funcIesResolved(i).*) {
.none => unreachable, // unresolved inferred error set .none => unreachable, // unresolved inferred error set
.anyerror_type => unreachable, .anyerror_type => unreachable,
else => |t| ip.indexToKey(t).error_set_type.names.get(ip), else => |t| ip.indexToKey(t).error_set_type.names,
}, },
else => unreachable, else => unreachable,
}; };
} }
pub fn enumFields(ty: Type, mod: *Module) []const InternPool.NullTerminatedString { pub fn enumFields(ty: Type, mod: *Module) InternPool.NullTerminatedString.Slice {
const ip = &mod.intern_pool; return mod.intern_pool.indexToKey(ty.toIntern()).enum_type.names;
return ip.indexToKey(ty.toIntern()).enum_type.names.get(ip);
} }
pub fn enumFieldCount(ty: Type, mod: *Module) usize { pub fn enumFieldCount(ty: Type, mod: *Module) usize {

View File

@ -219,23 +219,22 @@ pub fn addCases(ctx: *Cases, b: *std.Build) !void {
, ""); , "");
} }
// https://github.com/ziglang/zig/issues/18954 {
//{ var case = ctx.exeFromCompiledC("inferred local const and var", .{}, b);
// var case = ctx.exeFromCompiledC("inferred local const and var", .{}, b);
// case.addCompareOutput( case.addCompareOutput(
// \\fn add(a: i32, b: i32) i32 { \\fn add(a: i32, b: i32) i32 {
// \\ return a + b; \\ return a + b;
// \\} \\}
// \\ \\
// \\pub export fn main() c_int { \\pub export fn main() c_int {
// \\ const x = add(1, 2); \\ const x = add(1, 2);
// \\ var y = add(3, 0); \\ var y = add(3, 0);
// \\ y -= x; \\ y -= x;
// \\ return y; \\ return y;
// \\} \\}
// , ""); , "");
//} }
{ {
var case = ctx.exeFromCompiledC("control flow", .{}, b); var case = ctx.exeFromCompiledC("control flow", .{}, b);