mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
Sema: fix error set merging creating references to invalid memory
Trying to use std.heap.page_allocator with stage2 now results in (incorrect) compile errors rather than UAF.
This commit is contained in:
parent
7355a20133
commit
8f9d857932
40
src/Sema.zig
40
src/Sema.zig
@ -4554,9 +4554,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
|
||||
if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) {
|
||||
return Air.Inst.Ref.anyerror_type;
|
||||
}
|
||||
// When we support inferred error sets, we'll want to use a data structure that can
|
||||
// represent a merged set of errors without forcing them to be resolved here. Until then
|
||||
// we re-use the same data structure that is used for explicit error set declarations.
|
||||
// Resolve both error sets now.
|
||||
var set: std.StringHashMapUnmanaged(void) = .{};
|
||||
defer set.deinit(sema.gpa);
|
||||
|
||||
@ -4565,6 +4563,12 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
|
||||
const name = lhs_ty.castTag(.error_set_single).?.data;
|
||||
try set.put(sema.gpa, name, {});
|
||||
},
|
||||
.error_set_merged => {
|
||||
const names = lhs_ty.castTag(.error_set_merged).?.data;
|
||||
for (names) |name| {
|
||||
try set.put(sema.gpa, name, {});
|
||||
}
|
||||
},
|
||||
.error_set => {
|
||||
const lhs_set = lhs_ty.castTag(.error_set).?.data;
|
||||
try set.ensureUnusedCapacity(sema.gpa, lhs_set.names_len);
|
||||
@ -4579,6 +4583,12 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
|
||||
const name = rhs_ty.castTag(.error_set_single).?.data;
|
||||
try set.put(sema.gpa, name, {});
|
||||
},
|
||||
.error_set_merged => {
|
||||
const names = rhs_ty.castTag(.error_set_merged).?.data;
|
||||
for (names) |name| {
|
||||
try set.put(sema.gpa, name, {});
|
||||
}
|
||||
},
|
||||
.error_set => {
|
||||
const rhs_set = rhs_ty.castTag(.error_set).?.data;
|
||||
try set.ensureUnusedCapacity(sema.gpa, rhs_set.names_len);
|
||||
@ -4589,22 +4599,25 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
const new_names = try sema.arena.alloc([]const u8, set.count());
|
||||
// TODO do we really want to create a Decl for this?
|
||||
// The reason we do it right now is for memory management.
|
||||
var anon_decl = try block.startAnonDecl();
|
||||
defer anon_decl.deinit();
|
||||
|
||||
const new_names = try anon_decl.arena().alloc([]const u8, set.count());
|
||||
var it = set.keyIterator();
|
||||
var i: usize = 0;
|
||||
while (it.next()) |key| : (i += 1) {
|
||||
new_names[i] = key.*;
|
||||
}
|
||||
|
||||
const new_error_set = try sema.arena.create(Module.ErrorSet);
|
||||
new_error_set.* = .{
|
||||
.owner_decl = sema.owner_decl,
|
||||
.node_offset = inst_data.src_node,
|
||||
.names_ptr = new_names.ptr,
|
||||
.names_len = @intCast(u32, new_names.len),
|
||||
};
|
||||
const error_set_ty = try Type.Tag.error_set.create(sema.arena, new_error_set);
|
||||
return sema.addConstant(Type.type, try Value.Tag.ty.create(sema.arena, error_set_ty));
|
||||
const err_set_ty = try Type.Tag.error_set_merged.create(anon_decl.arena(), new_names);
|
||||
const err_set_decl = try anon_decl.finish(
|
||||
Type.type,
|
||||
try Value.Tag.ty.create(anon_decl.arena(), err_set_ty),
|
||||
);
|
||||
try sema.mod.declareDeclDependency(sema.owner_decl, err_set_decl);
|
||||
return sema.addType(err_set_ty);
|
||||
}
|
||||
|
||||
fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
@ -14788,6 +14801,7 @@ fn typeHasOnePossibleValue(
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.error_set_inferred,
|
||||
.error_set_merged,
|
||||
.@"opaque",
|
||||
.var_args_param,
|
||||
.manyptr_u8,
|
||||
|
||||
52
src/type.zig
52
src/type.zig
@ -61,11 +61,17 @@ pub const Type = extern union {
|
||||
.c_longdouble,
|
||||
=> return .Float,
|
||||
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.anyerror,
|
||||
.error_set_inferred,
|
||||
.error_set_merged,
|
||||
=> return .ErrorSet,
|
||||
|
||||
.c_void, .@"opaque" => return .Opaque,
|
||||
.bool => return .Bool,
|
||||
.void => return .Void,
|
||||
.type => return .Type,
|
||||
.error_set, .error_set_single, .anyerror, .error_set_inferred => return .ErrorSet,
|
||||
.comptime_int => return .ComptimeInt,
|
||||
.comptime_float => return .ComptimeFloat,
|
||||
.noreturn => return .NoReturn,
|
||||
@ -608,6 +614,9 @@ pub const Type = extern union {
|
||||
return true;
|
||||
},
|
||||
.ErrorSet => {
|
||||
// TODO: revisit the language specification for how to evaluate equality
|
||||
// for error set types.
|
||||
|
||||
if (a.tag() == .anyerror and b.tag() == .anyerror) {
|
||||
return true;
|
||||
}
|
||||
@ -892,6 +901,14 @@ pub const Type = extern union {
|
||||
.payload = try payload.payload.copy(allocator),
|
||||
});
|
||||
},
|
||||
.error_set_merged => {
|
||||
const names = self.castTag(.error_set_merged).?.data;
|
||||
const duped_names = try allocator.alloc([]const u8, names.len);
|
||||
for (duped_names) |*name, i| {
|
||||
name.* = try allocator.dupe(u8, names[i]);
|
||||
}
|
||||
return Tag.error_set_merged.create(allocator, duped_names);
|
||||
},
|
||||
.error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
|
||||
.error_set_inferred => return self.copyPayloadShallow(allocator, Payload.ErrorSetInferred),
|
||||
.error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
|
||||
@ -1185,6 +1202,16 @@ pub const Type = extern union {
|
||||
const func = ty.castTag(.error_set_inferred).?.data.func;
|
||||
return writer.print("(inferred error set of {s})", .{func.owner_decl.name});
|
||||
},
|
||||
.error_set_merged => {
|
||||
const names = ty.castTag(.error_set_merged).?.data;
|
||||
try writer.writeAll("error{");
|
||||
for (names) |name, i| {
|
||||
if (i != 0) try writer.writeByte(',');
|
||||
try writer.writeAll(name);
|
||||
}
|
||||
try writer.writeAll("}");
|
||||
return;
|
||||
},
|
||||
.error_set_single => {
|
||||
const name = ty.castTag(.error_set_single).?.data;
|
||||
return writer.print("error{{{s}}}", .{name});
|
||||
@ -1365,6 +1392,7 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.error_set_inferred,
|
||||
.error_set_merged,
|
||||
.@"opaque",
|
||||
.generic_poison,
|
||||
.array_u8,
|
||||
@ -1525,6 +1553,7 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.error_set_inferred,
|
||||
.error_set_merged,
|
||||
.manyptr_u8,
|
||||
.manyptr_const_u8,
|
||||
.atomic_order,
|
||||
@ -1783,6 +1812,7 @@ pub const Type = extern union {
|
||||
.anyerror_void_error_union,
|
||||
.anyerror,
|
||||
.error_set_inferred,
|
||||
.error_set_merged,
|
||||
=> return 2, // TODO revisit this when we have the concept of the error tag type
|
||||
|
||||
.array, .array_sentinel => return self.elemType().abiAlignment(target),
|
||||
@ -2021,6 +2051,7 @@ pub const Type = extern union {
|
||||
.anyerror_void_error_union,
|
||||
.anyerror,
|
||||
.error_set_inferred,
|
||||
.error_set_merged,
|
||||
=> return 2, // TODO revisit this when we have the concept of the error tag type
|
||||
|
||||
.int_signed, .int_unsigned => {
|
||||
@ -2199,6 +2230,7 @@ pub const Type = extern union {
|
||||
.anyerror_void_error_union,
|
||||
.anyerror,
|
||||
.error_set_inferred,
|
||||
.error_set_merged,
|
||||
=> return 16, // TODO revisit this when we have the concept of the error tag type
|
||||
|
||||
.int_signed, .int_unsigned => self.cast(Payload.Bits).?.data,
|
||||
@ -2961,7 +2993,7 @@ pub const Type = extern union {
|
||||
return .{ .signedness = .unsigned, .bits = smallestUnsignedBits(field_count - 1) };
|
||||
},
|
||||
|
||||
.error_set, .error_set_single, .anyerror, .error_set_inferred => {
|
||||
.error_set, .error_set_single, .anyerror, .error_set_inferred, .error_set_merged => {
|
||||
// TODO revisit this when error sets support custom int types
|
||||
return .{ .signedness = .unsigned, .bits = 16 };
|
||||
},
|
||||
@ -3250,6 +3282,7 @@ pub const Type = extern union {
|
||||
.error_set,
|
||||
.error_set_single,
|
||||
.error_set_inferred,
|
||||
.error_set_merged,
|
||||
.@"opaque",
|
||||
.var_args_param,
|
||||
.manyptr_u8,
|
||||
@ -3882,6 +3915,7 @@ pub const Type = extern union {
|
||||
error_set_single,
|
||||
/// The type is the inferred error set of a specific function.
|
||||
error_set_inferred,
|
||||
error_set_merged,
|
||||
empty_struct,
|
||||
@"opaque",
|
||||
@"struct",
|
||||
@ -3986,6 +4020,7 @@ pub const Type = extern union {
|
||||
|
||||
.error_set => Payload.ErrorSet,
|
||||
.error_set_inferred => Payload.ErrorSetInferred,
|
||||
.error_set_merged => Payload.ErrorSetMerged,
|
||||
|
||||
.array, .vector => Payload.Array,
|
||||
.array_sentinel => Payload.ArraySentinel,
|
||||
@ -4090,6 +4125,13 @@ pub const Type = extern union {
|
||||
data: *Module.ErrorSet,
|
||||
};
|
||||
|
||||
pub const ErrorSetMerged = struct {
|
||||
pub const base_tag = Tag.error_set_merged;
|
||||
|
||||
base: Payload = Payload{ .tag = base_tag },
|
||||
data: []const []const u8,
|
||||
};
|
||||
|
||||
pub const ErrorSetInferred = struct {
|
||||
pub const base_tag = Tag.error_set_inferred;
|
||||
|
||||
@ -4125,6 +4167,12 @@ pub const Type = extern union {
|
||||
try self.map.put(gpa, entry.key_ptr.*, {});
|
||||
}
|
||||
},
|
||||
.error_set_merged => {
|
||||
const names = err_set_ty.castTag(.error_set_merged).?.data;
|
||||
for (names) |name| {
|
||||
try self.map.put(gpa, name, {});
|
||||
}
|
||||
},
|
||||
.anyerror => {
|
||||
self.is_anyerror = true;
|
||||
},
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user