Sema: add error for failed assumption about struct having runtime bits

This commit is contained in:
Veikka Tuominen 2022-12-02 18:46:59 +02:00
parent 86e6acb37b
commit 59dad43de2
5 changed files with 67 additions and 7 deletions

View File

@ -940,6 +940,7 @@ pub const Struct = struct {
requires_comptime: PropertyBoolean = .unknown,
have_field_inits: bool = false,
is_tuple: bool,
assumed_runtime_bits: bool = false,
pub const Fields = std.StringArrayHashMapUnmanaged(Field);
@ -1205,6 +1206,7 @@ pub const Union = struct {
fully_resolved,
},
requires_comptime: PropertyBoolean = .unknown,
assumed_runtime_bits: bool = false,
pub const Field = struct {
/// undefined until `status` is `have_field_types` or `have_layout`.

View File

@ -29237,6 +29237,16 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
struct_obj.status = .have_layout;
_ = try sema.resolveTypeRequiresComptime(resolved_ty);
if (struct_obj.assumed_runtime_bits and !resolved_ty.hasRuntimeBits()) {
const msg = try Module.ErrorMsg.create(
sema.gpa,
struct_obj.srcLoc(sema.mod),
"struct layout depends on it having runtime bits",
.{},
);
return sema.failWithOwnedErrorMsg(msg);
}
}
// otherwise it's a tuple; no need to resolve anything
}
@ -29401,6 +29411,16 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
}
union_obj.status = .have_layout;
_ = try sema.resolveTypeRequiresComptime(resolved_ty);
if (union_obj.assumed_runtime_bits and !resolved_ty.hasRuntimeBits()) {
const msg = try Module.ErrorMsg.create(
sema.gpa,
union_obj.srcLoc(sema.mod),
"union layout depends on it having runtime bits",
.{},
);
return sema.failWithOwnedErrorMsg(msg);
}
}
// In case of querying the ABI alignment of this struct, we will ask

View File

@ -2459,6 +2459,7 @@ pub const Type = extern union {
if (struct_obj.status == .field_types_wip) {
// In this case, we guess that hasRuntimeBits() for this type is true,
// and then later if our guess was incorrect, we emit a compile error.
struct_obj.assumed_runtime_bits = true;
return true;
}
switch (strat) {
@ -2491,6 +2492,12 @@ pub const Type = extern union {
.@"union" => {
const union_obj = ty.castTag(.@"union").?.data;
if (union_obj.status == .field_types_wip) {
// In this case, we guess that hasRuntimeBits() for this type is true,
// and then later if our guess was incorrect, we emit a compile error.
union_obj.assumed_runtime_bits = true;
return true;
}
switch (strat) {
.sema => |sema| _ = try sema.resolveTypeFields(ty),
.eager => assert(union_obj.haveFieldTypes()),
@ -3027,8 +3034,9 @@ pub const Type = extern union {
const struct_obj = ty.castTag(.@"struct").?.data;
if (opt_sema) |sema| {
if (struct_obj.status == .field_types_wip) {
// We'll guess "pointer-aligned" and if we guess wrong, emit
// a compile error later.
// We'll guess "pointer-aligned", if the struct has an
// underaligned pointer field then some allocations
// might require explicit alignment.
return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) };
}
_ = try sema.resolveTypeFields(ty);
@ -3153,8 +3161,9 @@ pub const Type = extern union {
};
if (opt_sema) |sema| {
if (union_obj.status == .field_types_wip) {
// We'll guess "pointer-aligned" and if we guess wrong, emit
// a compile error later.
// We'll guess "pointer-aligned", if the union has an
// underaligned pointer field then some allocations
// might require explicit alignment.
return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) };
}
_ = try sema.resolveTypeFields(ty);
@ -5234,7 +5243,12 @@ pub const Type = extern union {
.@"struct" => {
const struct_obj = ty.castTag(.@"struct").?.data;
switch (struct_obj.requires_comptime) {
.wip, .unknown => unreachable, // This function asserts types already resolved.
.wip, .unknown => {
// Return false to avoid incorrect dependency loops.
// This will be handled correctly once merged with
// `Sema.typeRequiresComptime`.
return false;
},
.no => return false,
.yes => return true,
}
@ -5243,7 +5257,12 @@ pub const Type = extern union {
.@"union", .union_safety_tagged, .union_tagged => {
const union_obj = ty.cast(Type.Payload.Union).?.data;
switch (union_obj.requires_comptime) {
.wip, .unknown => unreachable, // This function asserts types already resolved.
.wip, .unknown => {
// Return false to avoid incorrect dependency loops.
// This will be handled correctly once merged with
// `Sema.typeRequiresComptime`.
return false;
},
.no => return false,
.yes => return true,
}

View File

@ -187,7 +187,7 @@ pub const Value = extern union {
bound_fn,
/// The ABI alignment of the payload type.
lazy_align,
/// The ABI alignment of the payload type.
/// The ABI size of the payload type.
lazy_size,
pub const last_no_payload_tag = Tag.empty_array;

View File

@ -0,0 +1,19 @@
comptime {
const S = struct {
const Foo = struct {
y: Bar,
};
const Bar = struct {
y: if (@sizeOf(Foo) == 0) u64 else void,
};
};
_ = @sizeOf(S.Foo) + 1;
}
// error
// backend=stage2
// target=native
//
// :6:21: error: struct layout depends on it having runtime bits
// :4:13: note: while checking this field