Commit 3116477dcc
Changed files (5)
src
src/codegen/c.zig
@@ -1127,8 +1127,19 @@ pub const DeclGen = struct {
try writer.writeByte('}');
return;
},
+ .empty_struct => {
+ const ai = ty.arrayInfo(mod);
+ try writer.writeByte('{');
+ if (ai.sentinel) |s| {
+ try dg.renderValue(writer, ai.elem_type, s, initializer_type);
+ } else {
+ try writer.writeByte('0');
+ }
+ try writer.writeByte('}');
+ return;
+ },
.none => switch (val.tag()) {
- .empty_struct_value, .empty_array => {
+ .empty_array => {
const ai = ty.arrayInfo(mod);
try writer.writeByte('{');
if (ai.sentinel) |s| {
src/Sema.zig
@@ -12615,7 +12615,7 @@ fn analyzeTupleCat(
const dest_fields = lhs_len + rhs_len;
if (dest_fields == 0) {
- return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
+ return sema.addConstant(Type.empty_struct_literal, Value.empty_struct);
}
if (lhs_len == 0) {
return rhs;
@@ -12943,7 +12943,7 @@ fn analyzeTupleMul(
return sema.fail(block, rhs_src, "operation results in overflow", .{});
if (final_len_u64 == 0) {
- return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value));
+ return sema.addConstant(Type.empty_struct_literal, Value.empty_struct);
}
const final_len = try sema.usizeCast(block, rhs_src, final_len_u64);
@@ -21860,7 +21860,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
const args = try sema.resolveInst(extra.args);
const args_ty = sema.typeOf(args);
- if (!args_ty.isTuple() and args_ty.tag() != .empty_struct_literal) {
+ if (!args_ty.isTuple() and args_ty.ip_index != .empty_struct_type) {
return sema.fail(block, args_src, "expected a tuple, found '{}'", .{args_ty.fmt(sema.mod)});
}
@@ -24780,37 +24780,41 @@ fn structFieldVal(
assert(unresolved_struct_ty.zigTypeTag(mod) == .Struct);
const struct_ty = try sema.resolveTypeFields(unresolved_struct_ty);
- switch (struct_ty.tag()) {
- .tuple, .empty_struct_literal => return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty),
- .anon_struct => {
- const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);
- return sema.tupleFieldValByIndex(block, src, struct_byval, field_index, struct_ty);
- },
- .@"struct" => {
- const struct_obj = struct_ty.castTag(.@"struct").?.data;
- if (struct_obj.is_tuple) return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty);
-
- const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
- return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
- const field_index = @intCast(u32, field_index_usize);
- const field = struct_obj.fields.values()[field_index];
+ switch (struct_ty.ip_index) {
+ .empty_struct_type => return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty),
+ .none => switch (struct_ty.tag()) {
+ .tuple => return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty),
+ .anon_struct => {
+ const field_index = try sema.anonStructFieldIndex(block, struct_ty, field_name, field_name_src);
+ return sema.tupleFieldValByIndex(block, src, struct_byval, field_index, struct_ty);
+ },
+ .@"struct" => {
+ const struct_obj = struct_ty.castTag(.@"struct").?.data;
+ if (struct_obj.is_tuple) return sema.tupleFieldVal(block, src, struct_byval, field_name, field_name_src, struct_ty);
- if (field.is_comptime) {
- return sema.addConstant(field.ty, field.default_val);
- }
+ const field_index_usize = struct_obj.fields.getIndex(field_name) orelse
+ return sema.failWithBadStructFieldAccess(block, struct_obj, field_name_src, field_name);
+ const field_index = @intCast(u32, field_index_usize);
+ const field = struct_obj.fields.values()[field_index];
- if (try sema.resolveMaybeUndefVal(struct_byval)) |struct_val| {
- if (struct_val.isUndef()) return sema.addConstUndef(field.ty);
- if ((try sema.typeHasOnePossibleValue(field.ty))) |opv| {
- return sema.addConstant(field.ty, opv);
+ if (field.is_comptime) {
+ return sema.addConstant(field.ty, field.default_val);
}
- const field_values = struct_val.castTag(.aggregate).?.data;
- return sema.addConstant(field.ty, field_values[field_index]);
- }
+ if (try sema.resolveMaybeUndefVal(struct_byval)) |struct_val| {
+ if (struct_val.isUndef()) return sema.addConstUndef(field.ty);
+ if ((try sema.typeHasOnePossibleValue(field.ty))) |opv| {
+ return sema.addConstant(field.ty, opv);
+ }
+
+ const field_values = struct_val.castTag(.aggregate).?.data;
+ return sema.addConstant(field.ty, field_values[field_index]);
+ }
- try sema.requireRuntimeBlock(block, src, null);
- return block.addStructFieldVal(struct_byval, field_index, field.ty);
+ try sema.requireRuntimeBlock(block, src, null);
+ return block.addStructFieldVal(struct_byval, field_index, field.ty);
+ },
+ else => unreachable,
},
else => unreachable,
}
@@ -27848,6 +27852,19 @@ fn beginComptimePtrMutation(
else => unreachable,
}
},
+ .empty_struct => {
+ const duped = try sema.arena.create(Value);
+ duped.* = Value.initTag(.the_only_possible_value);
+ return beginComptimePtrMutationInner(
+ sema,
+ block,
+ src,
+ parent.ty.structFieldType(field_index),
+ duped,
+ ptr_elem_ty,
+ parent.decl_ref_mut,
+ );
+ },
.none => switch (val_ptr.tag()) {
.aggregate => return beginComptimePtrMutationInner(
sema,
@@ -27901,20 +27918,6 @@ fn beginComptimePtrMutation(
else => unreachable,
},
- .empty_struct_value => {
- const duped = try sema.arena.create(Value);
- duped.* = Value.initTag(.the_only_possible_value);
- return beginComptimePtrMutationInner(
- sema,
- block,
- src,
- parent.ty.structFieldType(field_index),
- duped,
- ptr_elem_ty,
- parent.decl_ref_mut,
- );
- },
-
else => unreachable,
},
else => unreachable,
@@ -31502,174 +31505,174 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
const mod = sema.mod;
- if (ty.ip_index != .none) return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
- .int_type => false,
- .ptr_type => |ptr_type| {
- const child_ty = ptr_type.elem_type.toType();
- if (child_ty.zigTypeTag(mod) == .Fn) {
- return child_ty.fnInfo().is_generic;
- } else {
- return sema.resolveTypeRequiresComptime(child_ty);
- }
- },
- .array_type => |array_type| return sema.resolveTypeRequiresComptime(array_type.child.toType()),
- .vector_type => |vector_type| return sema.resolveTypeRequiresComptime(vector_type.child.toType()),
- .opt_type => |child| return sema.resolveTypeRequiresComptime(child.toType()),
- .error_union_type => |error_union_type| return sema.resolveTypeRequiresComptime(error_union_type.payload_type.toType()),
- .simple_type => |t| switch (t) {
- .f16,
- .f32,
- .f64,
- .f80,
- .f128,
- .usize,
- .isize,
- .c_char,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .anyopaque,
- .bool,
- .void,
- .anyerror,
- .@"anyframe",
- .noreturn,
- .generic_poison,
- .atomic_order,
- .atomic_rmw_op,
- .calling_convention,
- .address_space,
- .float_mode,
- .reduce_op,
- .call_modifier,
- .prefetch_options,
- .export_options,
- .extern_options,
+ return switch (ty.ip_index) {
+ .empty_struct_type => false,
+ .none => switch (ty.tag()) {
+ .empty_struct,
+ .error_set,
+ .error_set_single,
+ .error_set_inferred,
+ .error_set_merged,
+ .@"opaque",
+ .enum_simple,
=> false,
- .type,
- .comptime_int,
- .comptime_float,
- .null,
- .undefined,
- .enum_literal,
- .type_info,
- => true,
-
- .var_args_param => unreachable,
- },
- .struct_type => @panic("TODO"),
- .union_type => @panic("TODO"),
- .simple_value => unreachable,
- .extern_func => unreachable,
- .int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
- };
-
- return switch (ty.tag()) {
- .empty_struct_literal,
- .empty_struct,
- .error_set,
- .error_set_single,
- .error_set_inferred,
- .error_set_merged,
- .@"opaque",
- .enum_simple,
- => false,
+ .function => true,
- .function => true,
+ .inferred_alloc_mut => unreachable,
+ .inferred_alloc_const => unreachable,
- .inferred_alloc_mut => unreachable,
- .inferred_alloc_const => unreachable,
+ .array,
+ .array_sentinel,
+ => return sema.resolveTypeRequiresComptime(ty.childType(mod)),
- .array,
- .array_sentinel,
- => return sema.resolveTypeRequiresComptime(ty.childType(mod)),
+ .pointer => {
+ const child_ty = ty.childType(mod);
+ if (child_ty.zigTypeTag(mod) == .Fn) {
+ return child_ty.fnInfo().is_generic;
+ } else {
+ return sema.resolveTypeRequiresComptime(child_ty);
+ }
+ },
- .pointer => {
- const child_ty = ty.childType(mod);
- if (child_ty.zigTypeTag(mod) == .Fn) {
- return child_ty.fnInfo().is_generic;
- } else {
- return sema.resolveTypeRequiresComptime(child_ty);
- }
- },
+ .optional => {
+ return sema.resolveTypeRequiresComptime(ty.optionalChild(mod));
+ },
- .optional => {
- return sema.resolveTypeRequiresComptime(ty.optionalChild(mod));
- },
+ .tuple, .anon_struct => {
+ const tuple = ty.tupleFields();
+ for (tuple.types, 0..) |field_ty, i| {
+ const have_comptime_val = tuple.values[i].ip_index != .unreachable_value;
+ if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty)) {
+ return true;
+ }
+ }
+ return false;
+ },
- .tuple, .anon_struct => {
- const tuple = ty.tupleFields();
- for (tuple.types, 0..) |field_ty, i| {
- const have_comptime_val = tuple.values[i].ip_index != .unreachable_value;
- if (!have_comptime_val and try sema.resolveTypeRequiresComptime(field_ty)) {
- return true;
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ switch (struct_obj.requires_comptime) {
+ .no, .wip => return false,
+ .yes => return true,
+ .unknown => {
+ var requires_comptime = false;
+ struct_obj.requires_comptime = .wip;
+ for (struct_obj.fields.values()) |field| {
+ if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true;
+ }
+ if (requires_comptime) {
+ struct_obj.requires_comptime = .yes;
+ } else {
+ struct_obj.requires_comptime = .no;
+ }
+ return requires_comptime;
+ },
}
- }
- return false;
- },
+ },
- .@"struct" => {
- const struct_obj = ty.castTag(.@"struct").?.data;
- switch (struct_obj.requires_comptime) {
- .no, .wip => return false,
- .yes => return true,
- .unknown => {
- var requires_comptime = false;
- struct_obj.requires_comptime = .wip;
- for (struct_obj.fields.values()) |field| {
- if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true;
- }
- if (requires_comptime) {
- struct_obj.requires_comptime = .yes;
- } else {
- struct_obj.requires_comptime = .no;
- }
- return requires_comptime;
- },
- }
- },
+ .@"union", .union_safety_tagged, .union_tagged => {
+ const union_obj = ty.cast(Type.Payload.Union).?.data;
+ switch (union_obj.requires_comptime) {
+ .no, .wip => return false,
+ .yes => return true,
+ .unknown => {
+ var requires_comptime = false;
+ union_obj.requires_comptime = .wip;
+ for (union_obj.fields.values()) |field| {
+ if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true;
+ }
+ if (requires_comptime) {
+ union_obj.requires_comptime = .yes;
+ } else {
+ union_obj.requires_comptime = .no;
+ }
+ return requires_comptime;
+ },
+ }
+ },
- .@"union", .union_safety_tagged, .union_tagged => {
- const union_obj = ty.cast(Type.Payload.Union).?.data;
- switch (union_obj.requires_comptime) {
- .no, .wip => return false,
- .yes => return true,
- .unknown => {
- var requires_comptime = false;
- union_obj.requires_comptime = .wip;
- for (union_obj.fields.values()) |field| {
- if (try sema.resolveTypeRequiresComptime(field.ty)) requires_comptime = true;
- }
- if (requires_comptime) {
- union_obj.requires_comptime = .yes;
- } else {
- union_obj.requires_comptime = .no;
- }
- return requires_comptime;
- },
- }
+ .error_union => return sema.resolveTypeRequiresComptime(ty.errorUnionPayload()),
+ .anyframe_T => {
+ const child_ty = ty.castTag(.anyframe_T).?.data;
+ return sema.resolveTypeRequiresComptime(child_ty);
+ },
+ .enum_numbered => {
+ const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty;
+ return sema.resolveTypeRequiresComptime(tag_ty);
+ },
+ .enum_full, .enum_nonexhaustive => {
+ const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty;
+ return sema.resolveTypeRequiresComptime(tag_ty);
+ },
},
+ else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+ .int_type => false,
+ .ptr_type => |ptr_type| {
+ const child_ty = ptr_type.elem_type.toType();
+ if (child_ty.zigTypeTag(mod) == .Fn) {
+ return child_ty.fnInfo().is_generic;
+ } else {
+ return sema.resolveTypeRequiresComptime(child_ty);
+ }
+ },
+ .array_type => |array_type| return sema.resolveTypeRequiresComptime(array_type.child.toType()),
+ .vector_type => |vector_type| return sema.resolveTypeRequiresComptime(vector_type.child.toType()),
+ .opt_type => |child| return sema.resolveTypeRequiresComptime(child.toType()),
+ .error_union_type => |error_union_type| return sema.resolveTypeRequiresComptime(error_union_type.payload_type.toType()),
+ .simple_type => |t| switch (t) {
+ .f16,
+ .f32,
+ .f64,
+ .f80,
+ .f128,
+ .usize,
+ .isize,
+ .c_char,
+ .c_short,
+ .c_ushort,
+ .c_int,
+ .c_uint,
+ .c_long,
+ .c_ulong,
+ .c_longlong,
+ .c_ulonglong,
+ .c_longdouble,
+ .anyopaque,
+ .bool,
+ .void,
+ .anyerror,
+ .@"anyframe",
+ .noreturn,
+ .generic_poison,
+ .var_args_param,
+ .atomic_order,
+ .atomic_rmw_op,
+ .calling_convention,
+ .address_space,
+ .float_mode,
+ .reduce_op,
+ .call_modifier,
+ .prefetch_options,
+ .export_options,
+ .extern_options,
+ => false,
- .error_union => return sema.resolveTypeRequiresComptime(ty.errorUnionPayload()),
- .anyframe_T => {
- const child_ty = ty.castTag(.anyframe_T).?.data;
- return sema.resolveTypeRequiresComptime(child_ty);
- },
- .enum_numbered => {
- const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty;
- return sema.resolveTypeRequiresComptime(tag_ty);
- },
- .enum_full, .enum_nonexhaustive => {
- const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty;
- return sema.resolveTypeRequiresComptime(tag_ty);
+ .type,
+ .comptime_int,
+ .comptime_float,
+ .null,
+ .undefined,
+ .enum_literal,
+ .type_info,
+ => true,
+ },
+ .struct_type => @panic("TODO"),
+ .union_type => @panic("TODO"),
+ .simple_value => unreachable,
+ .extern_func => unreachable,
+ .int => unreachable,
+ .enum_tag => unreachable, // it's a value, not a type
},
};
}
@@ -32957,237 +32960,240 @@ fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type {
pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
const mod = sema.mod;
- if (ty.ip_index != .none) switch (mod.intern_pool.indexToKey(ty.ip_index)) {
- .int_type => |int_type| {
- if (int_type.bits == 0) {
- return try mod.intValue(ty, 0);
- } else {
- return null;
- }
- },
- .ptr_type => return null,
- .array_type => |array_type| {
- if (array_type.len == 0)
- return Value.initTag(.empty_array);
- if ((try sema.typeHasOnePossibleValue(array_type.child.toType())) != null) {
- return Value.initTag(.the_only_possible_value);
- }
- return null;
- },
- .vector_type => |vector_type| {
- if (vector_type.len == 0) return Value.initTag(.empty_array);
- if (try sema.typeHasOnePossibleValue(vector_type.child.toType())) |v| return v;
- return null;
- },
- .opt_type => |child| {
- if (child.toType().isNoReturn()) {
- return Value.null;
- } else {
- return null;
- }
- },
- .error_union_type => return null,
- .simple_type => |t| switch (t) {
- .f16,
- .f32,
- .f64,
- .f80,
- .f128,
- .usize,
- .isize,
- .c_char,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .anyopaque,
- .bool,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .@"anyframe",
- .enum_literal,
- .atomic_order,
- .atomic_rmw_op,
- .calling_convention,
- .address_space,
- .float_mode,
- .reduce_op,
- .call_modifier,
- .prefetch_options,
- .export_options,
- .extern_options,
- .type_info,
- => return null,
+ switch (ty.ip_index) {
+ .empty_struct_type => return Value.empty_struct,
- .void => return Value.void,
- .noreturn => return Value.@"unreachable",
- .null => return Value.null,
- .undefined => return Value.undef,
+ .none => switch (ty.tag()) {
+ .error_set_single,
+ .error_set,
+ .error_set_merged,
+ .error_union,
+ .function,
+ .array_sentinel,
+ .error_set_inferred,
+ .@"opaque",
+ .anyframe_T,
+ .pointer,
+ => return null,
- .generic_poison => return error.GenericPoison,
- .var_args_param => unreachable,
- },
- .struct_type => @panic("TODO"),
- .union_type => @panic("TODO"),
- .simple_value => unreachable,
- .extern_func => unreachable,
- .int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
- };
+ .optional => {
+ const child_ty = ty.optionalChild(mod);
+ if (child_ty.isNoReturn()) {
+ return Value.null;
+ } else {
+ return null;
+ }
+ },
- switch (ty.tag()) {
- .error_set_single,
- .error_set,
- .error_set_merged,
- .error_union,
- .function,
- .array_sentinel,
- .error_set_inferred,
- .@"opaque",
- .anyframe_T,
- .pointer,
- => return null,
+ .@"struct" => {
+ const resolved_ty = try sema.resolveTypeFields(ty);
+ const s = resolved_ty.castTag(.@"struct").?.data;
+ for (s.fields.values(), 0..) |field, i| {
+ if (field.is_comptime) continue;
+ if (field.ty.eql(resolved_ty, sema.mod)) {
+ const msg = try Module.ErrorMsg.create(
+ sema.gpa,
+ s.srcLoc(sema.mod),
+ "struct '{}' depends on itself",
+ .{ty.fmt(sema.mod)},
+ );
+ try sema.addFieldErrNote(resolved_ty, i, msg, "while checking this field", .{});
+ return sema.failWithOwnedErrorMsg(msg);
+ }
+ if ((try sema.typeHasOnePossibleValue(field.ty)) == null) {
+ return null;
+ }
+ }
+ return Value.empty_struct;
+ },
- .optional => {
- const child_ty = ty.optionalChild(mod);
- if (child_ty.isNoReturn()) {
- return Value.null;
- } else {
- return null;
- }
- },
+ .tuple, .anon_struct => {
+ const tuple = ty.tupleFields();
+ for (tuple.values, 0..) |val, i| {
+ const is_comptime = val.ip_index != .unreachable_value;
+ if (is_comptime) continue;
+ if ((try sema.typeHasOnePossibleValue(tuple.types[i])) != null) continue;
+ return null;
+ }
+ return Value.empty_struct;
+ },
- .@"struct" => {
- const resolved_ty = try sema.resolveTypeFields(ty);
- const s = resolved_ty.castTag(.@"struct").?.data;
- for (s.fields.values(), 0..) |field, i| {
- if (field.is_comptime) continue;
- if (field.ty.eql(resolved_ty, sema.mod)) {
+ .enum_numbered => {
+ const resolved_ty = try sema.resolveTypeFields(ty);
+ const enum_obj = resolved_ty.castTag(.enum_numbered).?.data;
+ // An explicit tag type is always provided for enum_numbered.
+ if (!(try sema.typeHasRuntimeBits(enum_obj.tag_ty))) {
+ return null;
+ }
+ if (enum_obj.fields.count() == 1) {
+ if (enum_obj.values.count() == 0) {
+ return try mod.intValue(ty, 0); // auto-numbered
+ } else {
+ return enum_obj.values.keys()[0];
+ }
+ } else {
+ return null;
+ }
+ },
+ .enum_full => {
+ const resolved_ty = try sema.resolveTypeFields(ty);
+ const enum_obj = resolved_ty.castTag(.enum_full).?.data;
+ if (!(try sema.typeHasRuntimeBits(enum_obj.tag_ty))) {
+ return null;
+ }
+ switch (enum_obj.fields.count()) {
+ 0 => return Value.@"unreachable",
+ 1 => if (enum_obj.values.count() == 0) {
+ return try mod.intValue(ty, 0); // auto-numbered
+ } else {
+ return enum_obj.values.keys()[0];
+ },
+ else => return null,
+ }
+ },
+ .enum_simple => {
+ const resolved_ty = try sema.resolveTypeFields(ty);
+ const enum_simple = resolved_ty.castTag(.enum_simple).?.data;
+ switch (enum_simple.fields.count()) {
+ 0 => return Value.@"unreachable",
+ 1 => return try mod.intValue(ty, 0),
+ else => return null,
+ }
+ },
+ .enum_nonexhaustive => {
+ const tag_ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty;
+ if (tag_ty.zigTypeTag(mod) != .ComptimeInt and !(try sema.typeHasRuntimeBits(tag_ty))) {
+ return try mod.intValue(ty, 0);
+ } else {
+ return null;
+ }
+ },
+ .@"union", .union_safety_tagged, .union_tagged => {
+ const resolved_ty = try sema.resolveTypeFields(ty);
+ const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
+ const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse
+ return null;
+ const fields = union_obj.fields.values();
+ if (fields.len == 0) return Value.@"unreachable";
+ const only_field = fields[0];
+ if (only_field.ty.eql(resolved_ty, sema.mod)) {
const msg = try Module.ErrorMsg.create(
sema.gpa,
- s.srcLoc(sema.mod),
- "struct '{}' depends on itself",
+ union_obj.srcLoc(sema.mod),
+ "union '{}' depends on itself",
.{ty.fmt(sema.mod)},
);
- try sema.addFieldErrNote(resolved_ty, i, msg, "while checking this field", .{});
+ try sema.addFieldErrNote(resolved_ty, 0, msg, "while checking this field", .{});
return sema.failWithOwnedErrorMsg(msg);
}
- if ((try sema.typeHasOnePossibleValue(field.ty)) == null) {
+ const val_val = (try sema.typeHasOnePossibleValue(only_field.ty)) orelse
return null;
- }
- }
- return Value.initTag(.empty_struct_value);
- },
+ // TODO make this not allocate.
+ return try Value.Tag.@"union".create(sema.arena, .{
+ .tag = tag_val,
+ .val = val_val,
+ });
+ },
+
+ .empty_struct => return Value.empty_struct,
- .tuple, .anon_struct => {
- const tuple = ty.tupleFields();
- for (tuple.values, 0..) |val, i| {
- const is_comptime = val.ip_index != .unreachable_value;
- if (is_comptime) continue;
- if ((try sema.typeHasOnePossibleValue(tuple.types[i])) != null) continue;
+ .array => {
+ if (ty.arrayLen(mod) == 0)
+ return Value.initTag(.empty_array);
+ if ((try sema.typeHasOnePossibleValue(ty.childType(mod))) != null) {
+ return Value.initTag(.the_only_possible_value);
+ }
return null;
- }
- return Value.initTag(.empty_struct_value);
+ },
+
+ .inferred_alloc_const => unreachable,
+ .inferred_alloc_mut => unreachable,
},
- .enum_numbered => {
- const resolved_ty = try sema.resolveTypeFields(ty);
- const enum_obj = resolved_ty.castTag(.enum_numbered).?.data;
- // An explicit tag type is always provided for enum_numbered.
- if (!(try sema.typeHasRuntimeBits(enum_obj.tag_ty))) {
- return null;
- }
- if (enum_obj.fields.count() == 1) {
- if (enum_obj.values.count() == 0) {
- return try mod.intValue(ty, 0); // auto-numbered
+ else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+ .int_type => |int_type| {
+ if (int_type.bits == 0) {
+ return try mod.intValue(ty, 0);
} else {
- return enum_obj.values.keys()[0];
+ return null;
+ }
+ },
+ .ptr_type => return null,
+ .array_type => |array_type| {
+ if (array_type.len == 0)
+ return Value.initTag(.empty_array);
+ if ((try sema.typeHasOnePossibleValue(array_type.child.toType())) != null) {
+ return Value.initTag(.the_only_possible_value);
}
- } else {
return null;
- }
- },
- .enum_full => {
- const resolved_ty = try sema.resolveTypeFields(ty);
- const enum_obj = resolved_ty.castTag(.enum_full).?.data;
- if (!(try sema.typeHasRuntimeBits(enum_obj.tag_ty))) {
+ },
+ .vector_type => |vector_type| {
+ if (vector_type.len == 0) return Value.initTag(.empty_array);
+ if (try sema.typeHasOnePossibleValue(vector_type.child.toType())) |v| return v;
return null;
- }
- switch (enum_obj.fields.count()) {
- 0 => return Value.@"unreachable",
- 1 => if (enum_obj.values.count() == 0) {
- return try mod.intValue(ty, 0); // auto-numbered
+ },
+ .opt_type => |child| {
+ if (child.toType().isNoReturn()) {
+ return Value.null;
} else {
- return enum_obj.values.keys()[0];
- },
- else => return null,
- }
- },
- .enum_simple => {
- const resolved_ty = try sema.resolveTypeFields(ty);
- const enum_simple = resolved_ty.castTag(.enum_simple).?.data;
- switch (enum_simple.fields.count()) {
- 0 => return Value.@"unreachable",
- 1 => return try mod.intValue(ty, 0),
- else => return null,
- }
- },
- .enum_nonexhaustive => {
- const tag_ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty;
- if (tag_ty.zigTypeTag(mod) != .ComptimeInt and !(try sema.typeHasRuntimeBits(tag_ty))) {
- return try mod.intValue(ty, 0);
- } else {
- return null;
- }
- },
- .@"union", .union_safety_tagged, .union_tagged => {
- const resolved_ty = try sema.resolveTypeFields(ty);
- const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
- const tag_val = (try sema.typeHasOnePossibleValue(union_obj.tag_ty)) orelse
- return null;
- const fields = union_obj.fields.values();
- if (fields.len == 0) return Value.@"unreachable";
- const only_field = fields[0];
- if (only_field.ty.eql(resolved_ty, sema.mod)) {
- const msg = try Module.ErrorMsg.create(
- sema.gpa,
- union_obj.srcLoc(sema.mod),
- "union '{}' depends on itself",
- .{ty.fmt(sema.mod)},
- );
- try sema.addFieldErrNote(resolved_ty, 0, msg, "while checking this field", .{});
- return sema.failWithOwnedErrorMsg(msg);
- }
- const val_val = (try sema.typeHasOnePossibleValue(only_field.ty)) orelse
- return null;
- // TODO make this not allocate. The function in `Type.onePossibleValue`
- // currently returns `empty_struct_value` and we should do that here too.
- return try Value.Tag.@"union".create(sema.arena, .{
- .tag = tag_val,
- .val = val_val,
- });
- },
+ return null;
+ }
+ },
+ .error_union_type => return null,
+ .simple_type => |t| switch (t) {
+ .f16,
+ .f32,
+ .f64,
+ .f80,
+ .f128,
+ .usize,
+ .isize,
+ .c_char,
+ .c_short,
+ .c_ushort,
+ .c_int,
+ .c_uint,
+ .c_long,
+ .c_ulong,
+ .c_longlong,
+ .c_ulonglong,
+ .c_longdouble,
+ .anyopaque,
+ .bool,
+ .type,
+ .anyerror,
+ .comptime_int,
+ .comptime_float,
+ .@"anyframe",
+ .enum_literal,
+ .atomic_order,
+ .atomic_rmw_op,
+ .calling_convention,
+ .address_space,
+ .float_mode,
+ .reduce_op,
+ .call_modifier,
+ .prefetch_options,
+ .export_options,
+ .extern_options,
+ .type_info,
+ => return null,
- .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),
+ .void => return Value.void,
+ .noreturn => return Value.@"unreachable",
+ .null => return Value.null,
+ .undefined => return Value.undef,
- .array => {
- if (ty.arrayLen(mod) == 0)
- return Value.initTag(.empty_array);
- if ((try sema.typeHasOnePossibleValue(ty.childType(mod))) != null) {
- return Value.initTag(.the_only_possible_value);
- }
- return null;
+ .generic_poison => return error.GenericPoison,
+ .var_args_param => unreachable,
+ },
+ .struct_type => @panic("TODO"),
+ .union_type => @panic("TODO"),
+ .simple_value => unreachable,
+ .extern_func => unreachable,
+ .int => unreachable,
+ .enum_tag => unreachable, // it's a value, not a type
},
-
- .inferred_alloc_const => unreachable,
- .inferred_alloc_mut => unreachable,
}
}
@@ -33567,8 +33573,116 @@ fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type {
/// elsewhere in value.zig
pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
const mod = sema.mod;
- if (ty.ip_index != .none) {
- switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+ return switch (ty.ip_index) {
+ .empty_struct_type => false,
+
+ .none => switch (ty.tag()) {
+ .empty_struct,
+ .error_set,
+ .error_set_single,
+ .error_set_inferred,
+ .error_set_merged,
+ .@"opaque",
+ .enum_simple,
+ => false,
+
+ .function => true,
+
+ .inferred_alloc_mut => unreachable,
+ .inferred_alloc_const => unreachable,
+
+ .array,
+ .array_sentinel,
+ => return sema.typeRequiresComptime(ty.childType(mod)),
+
+ .pointer => {
+ const child_ty = ty.childType(mod);
+ if (child_ty.zigTypeTag(mod) == .Fn) {
+ return child_ty.fnInfo().is_generic;
+ } else {
+ return sema.typeRequiresComptime(child_ty);
+ }
+ },
+
+ .optional => {
+ return sema.typeRequiresComptime(ty.optionalChild(mod));
+ },
+
+ .tuple, .anon_struct => {
+ const tuple = ty.tupleFields();
+ for (tuple.types, 0..) |field_ty, i| {
+ const have_comptime_val = tuple.values[i].ip_index != .unreachable_value;
+ if (!have_comptime_val and try sema.typeRequiresComptime(field_ty)) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ switch (struct_obj.requires_comptime) {
+ .no, .wip => return false,
+ .yes => return true,
+ .unknown => {
+ if (struct_obj.status == .field_types_wip)
+ return false;
+
+ try sema.resolveTypeFieldsStruct(ty, struct_obj);
+
+ struct_obj.requires_comptime = .wip;
+ for (struct_obj.fields.values()) |field| {
+ if (field.is_comptime) continue;
+ if (try sema.typeRequiresComptime(field.ty)) {
+ struct_obj.requires_comptime = .yes;
+ return true;
+ }
+ }
+ struct_obj.requires_comptime = .no;
+ return false;
+ },
+ }
+ },
+
+ .@"union", .union_safety_tagged, .union_tagged => {
+ const union_obj = ty.cast(Type.Payload.Union).?.data;
+ switch (union_obj.requires_comptime) {
+ .no, .wip => return false,
+ .yes => return true,
+ .unknown => {
+ if (union_obj.status == .field_types_wip)
+ return false;
+
+ try sema.resolveTypeFieldsUnion(ty, union_obj);
+
+ union_obj.requires_comptime = .wip;
+ for (union_obj.fields.values()) |field| {
+ if (try sema.typeRequiresComptime(field.ty)) {
+ union_obj.requires_comptime = .yes;
+ return true;
+ }
+ }
+ union_obj.requires_comptime = .no;
+ return false;
+ },
+ }
+ },
+
+ .error_union => return sema.typeRequiresComptime(ty.errorUnionPayload()),
+ .anyframe_T => {
+ const child_ty = ty.castTag(.anyframe_T).?.data;
+ return sema.typeRequiresComptime(child_ty);
+ },
+ .enum_numbered => {
+ const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty;
+ return sema.typeRequiresComptime(tag_ty);
+ },
+ .enum_full, .enum_nonexhaustive => {
+ const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty;
+ return sema.typeRequiresComptime(tag_ty);
+ },
+ },
+ else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
.int_type => return false,
.ptr_type => |ptr_type| {
const child_ty = ptr_type.elem_type.toType();
@@ -33638,113 +33752,6 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
.extern_func => unreachable,
.int => unreachable,
.enum_tag => unreachable, // it's a value, not a type
- }
- }
- return switch (ty.tag()) {
- .empty_struct_literal,
- .empty_struct,
- .error_set,
- .error_set_single,
- .error_set_inferred,
- .error_set_merged,
- .@"opaque",
- .enum_simple,
- => false,
-
- .function => true,
-
- .inferred_alloc_mut => unreachable,
- .inferred_alloc_const => unreachable,
-
- .array,
- .array_sentinel,
- => return sema.typeRequiresComptime(ty.childType(mod)),
-
- .pointer => {
- const child_ty = ty.childType(mod);
- if (child_ty.zigTypeTag(mod) == .Fn) {
- return child_ty.fnInfo().is_generic;
- } else {
- return sema.typeRequiresComptime(child_ty);
- }
- },
-
- .optional => {
- return sema.typeRequiresComptime(ty.optionalChild(mod));
- },
-
- .tuple, .anon_struct => {
- const tuple = ty.tupleFields();
- for (tuple.types, 0..) |field_ty, i| {
- const have_comptime_val = tuple.values[i].ip_index != .unreachable_value;
- if (!have_comptime_val and try sema.typeRequiresComptime(field_ty)) {
- return true;
- }
- }
- return false;
- },
-
- .@"struct" => {
- const struct_obj = ty.castTag(.@"struct").?.data;
- switch (struct_obj.requires_comptime) {
- .no, .wip => return false,
- .yes => return true,
- .unknown => {
- if (struct_obj.status == .field_types_wip)
- return false;
-
- try sema.resolveTypeFieldsStruct(ty, struct_obj);
-
- struct_obj.requires_comptime = .wip;
- for (struct_obj.fields.values()) |field| {
- if (field.is_comptime) continue;
- if (try sema.typeRequiresComptime(field.ty)) {
- struct_obj.requires_comptime = .yes;
- return true;
- }
- }
- struct_obj.requires_comptime = .no;
- return false;
- },
- }
- },
-
- .@"union", .union_safety_tagged, .union_tagged => {
- const union_obj = ty.cast(Type.Payload.Union).?.data;
- switch (union_obj.requires_comptime) {
- .no, .wip => return false,
- .yes => return true,
- .unknown => {
- if (union_obj.status == .field_types_wip)
- return false;
-
- try sema.resolveTypeFieldsUnion(ty, union_obj);
-
- union_obj.requires_comptime = .wip;
- for (union_obj.fields.values()) |field| {
- if (try sema.typeRequiresComptime(field.ty)) {
- union_obj.requires_comptime = .yes;
- return true;
- }
- }
- union_obj.requires_comptime = .no;
- return false;
- },
- }
- },
-
- .error_union => return sema.typeRequiresComptime(ty.errorUnionPayload()),
- .anyframe_T => {
- const child_ty = ty.castTag(.anyframe_T).?.data;
- return sema.typeRequiresComptime(child_ty);
- },
- .enum_numbered => {
- const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty;
- return sema.typeRequiresComptime(tag_ty);
- },
- .enum_full, .enum_nonexhaustive => {
- const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty;
- return sema.typeRequiresComptime(tag_ty);
},
};
}
src/type.zig
@@ -34,8 +34,51 @@ pub const Type = struct {
}
pub fn zigTypeTagOrPoison(ty: Type, mod: *const Module) error{GenericPoison}!std.builtin.TypeId {
- if (ty.ip_index != .none) {
- switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+ switch (ty.ip_index) {
+ .none => switch (ty.tag()) {
+ .error_set,
+ .error_set_single,
+ .error_set_inferred,
+ .error_set_merged,
+ => return .ErrorSet,
+
+ .@"opaque" => return .Opaque,
+
+ .function => return .Fn,
+
+ .array,
+ .array_sentinel,
+ => return .Array,
+
+ .pointer,
+ .inferred_alloc_const,
+ .inferred_alloc_mut,
+ => return .Pointer,
+
+ .optional => return .Optional,
+
+ .error_union => return .ErrorUnion,
+
+ .anyframe_T => return .AnyFrame,
+
+ .empty_struct,
+ .@"struct",
+ .tuple,
+ .anon_struct,
+ => return .Struct,
+
+ .enum_full,
+ .enum_nonexhaustive,
+ .enum_simple,
+ .enum_numbered,
+ => return .Enum,
+
+ .@"union",
+ .union_safety_tagged,
+ .union_tagged,
+ => return .Union,
+ },
+ else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
.int_type => return .Int,
.ptr_type => return .Pointer,
.array_type => return .Array,
@@ -104,51 +147,7 @@ pub const Type = struct {
.enum_tag,
.simple_value,
=> unreachable, // it's a value, not a type
- }
- }
- switch (ty.tag()) {
- .error_set,
- .error_set_single,
- .error_set_inferred,
- .error_set_merged,
- => return .ErrorSet,
-
- .@"opaque" => return .Opaque,
-
- .function => return .Fn,
-
- .array,
- .array_sentinel,
- => return .Array,
-
- .pointer,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- => return .Pointer,
-
- .optional => return .Optional,
-
- .error_union => return .ErrorUnion,
-
- .anyframe_T => return .AnyFrame,
-
- .empty_struct,
- .empty_struct_literal,
- .@"struct",
- .tuple,
- .anon_struct,
- => return .Struct,
-
- .enum_full,
- .enum_nonexhaustive,
- .enum_simple,
- .enum_numbered,
- => return .Enum,
-
- .@"union",
- .union_safety_tagged,
- .union_tagged,
- => return .Union,
+ },
}
}
@@ -517,7 +516,7 @@ pub const Type = struct {
const b_struct_obj = (b.castTag(.@"struct") orelse return false).data;
return a_struct_obj == b_struct_obj;
},
- .tuple, .empty_struct_literal => {
+ .tuple => {
if (!b.isSimpleTuple()) return false;
const a_tuple = a.tupleFields();
@@ -741,7 +740,7 @@ pub const Type = struct {
const struct_obj: *const Module.Struct = ty.castTag(.@"struct").?.data;
std.hash.autoHash(hasher, struct_obj);
},
- .tuple, .empty_struct_literal => {
+ .tuple => {
std.hash.autoHash(hasher, std.builtin.TypeId.Struct);
const tuple = ty.tupleFields();
@@ -837,7 +836,6 @@ pub const Type = struct {
} else switch (self.legacy.ptr_otherwise.tag) {
.inferred_alloc_const,
.inferred_alloc_mut,
- .empty_struct_literal,
=> unreachable,
.optional,
@@ -1047,7 +1045,7 @@ pub const Type = struct {
while (true) {
const t = ty.tag();
switch (t) {
- .empty_struct, .empty_struct_literal => return writer.writeAll("struct {}"),
+ .empty_struct => return writer.writeAll("struct {}"),
.@"struct" => {
const struct_obj = ty.castTag(.@"struct").?.data;
@@ -1266,327 +1264,328 @@ pub const Type = struct {
/// Prints a name suitable for `@typeName`.
pub fn print(ty: Type, writer: anytype, mod: *Module) @TypeOf(writer).Error!void {
- if (ty.ip_index != .none) switch (mod.intern_pool.indexToKey(ty.ip_index)) {
- .int_type => |int_type| {
- const sign_char: u8 = switch (int_type.signedness) {
- .signed => 'i',
- .unsigned => 'u',
- };
- return writer.print("{c}{d}", .{ sign_char, int_type.bits });
- },
- .ptr_type => {
- const info = ty.ptrInfo(mod);
-
- if (info.sentinel) |s| switch (info.size) {
- .One, .C => unreachable,
- .Many => try writer.print("[*:{}]", .{s.fmtValue(info.pointee_type, mod)}),
- .Slice => try writer.print("[:{}]", .{s.fmtValue(info.pointee_type, mod)}),
- } else switch (info.size) {
- .One => try writer.writeAll("*"),
- .Many => try writer.writeAll("[*]"),
- .C => try writer.writeAll("[*c]"),
- .Slice => try writer.writeAll("[]"),
- }
- if (info.@"align" != 0 or info.host_size != 0 or info.vector_index != .none) {
- if (info.@"align" != 0) {
- try writer.print("align({d}", .{info.@"align"});
- } else {
- const alignment = info.pointee_type.abiAlignment(mod);
- try writer.print("align({d}", .{alignment});
- }
-
- if (info.bit_offset != 0 or info.host_size != 0) {
- try writer.print(":{d}:{d}", .{ info.bit_offset, info.host_size });
- }
- if (info.vector_index == .runtime) {
- try writer.writeAll(":?");
- } else if (info.vector_index != .none) {
- try writer.print(":{d}", .{@enumToInt(info.vector_index)});
- }
- try writer.writeAll(") ");
- }
- if (info.@"addrspace" != .generic) {
- try writer.print("addrspace(.{s}) ", .{@tagName(info.@"addrspace")});
- }
- if (!info.mutable) try writer.writeAll("const ");
- if (info.@"volatile") try writer.writeAll("volatile ");
- if (info.@"allowzero" and info.size != .C) try writer.writeAll("allowzero ");
-
- try print(info.pointee_type, writer, mod);
- return;
- },
- .array_type => |array_type| {
- if (array_type.sentinel == .none) {
- try writer.print("[{d}]", .{array_type.len});
- try print(array_type.child.toType(), writer, mod);
- } else {
- try writer.print("[{d}:{}]", .{
- array_type.len,
- array_type.sentinel.toValue().fmtValue(array_type.child.toType(), mod),
- });
- try print(array_type.child.toType(), writer, mod);
- }
- return;
- },
- .vector_type => |vector_type| {
- try writer.print("@Vector({d}, ", .{vector_type.len});
- try print(vector_type.child.toType(), writer, mod);
- try writer.writeAll(")");
- return;
- },
- .opt_type => |child| {
- try writer.writeByte('?');
- try print(child.toType(), writer, mod);
- return;
- },
- .error_union_type => |error_union_type| {
- try print(error_union_type.error_set_type.toType(), writer, mod);
- try writer.writeByte('!');
- try print(error_union_type.payload_type.toType(), writer, mod);
- return;
- },
- .simple_type => |s| return writer.writeAll(@tagName(s)),
- .struct_type => @panic("TODO"),
- .union_type => @panic("TODO"),
- .simple_value => unreachable,
- .extern_func => unreachable,
- .int => unreachable,
- .enum_tag => unreachable,
- };
- const t = ty.tag();
- switch (t) {
- .inferred_alloc_const => unreachable,
- .inferred_alloc_mut => unreachable,
+ switch (ty.ip_index) {
+ .empty_struct_type => try writer.writeAll("@TypeOf(.{})"),
- .empty_struct_literal => try writer.writeAll("@TypeOf(.{})"),
+ .none => switch (ty.tag()) {
+ .inferred_alloc_const => unreachable,
+ .inferred_alloc_mut => unreachable,
- .empty_struct => {
- const namespace = ty.castTag(.empty_struct).?.data;
- try namespace.renderFullyQualifiedName(mod, "", writer);
- },
+ .empty_struct => {
+ const namespace = ty.castTag(.empty_struct).?.data;
+ try namespace.renderFullyQualifiedName(mod, "", writer);
+ },
- .@"struct" => {
- const struct_obj = ty.castTag(.@"struct").?.data;
- const decl = mod.declPtr(struct_obj.owner_decl);
- try decl.renderFullyQualifiedName(mod, writer);
- },
- .@"union", .union_safety_tagged, .union_tagged => {
- const union_obj = ty.cast(Payload.Union).?.data;
- const decl = mod.declPtr(union_obj.owner_decl);
- try decl.renderFullyQualifiedName(mod, writer);
- },
- .enum_full, .enum_nonexhaustive => {
- const enum_full = ty.cast(Payload.EnumFull).?.data;
- const decl = mod.declPtr(enum_full.owner_decl);
- try decl.renderFullyQualifiedName(mod, writer);
- },
- .enum_simple => {
- const enum_simple = ty.castTag(.enum_simple).?.data;
- const decl = mod.declPtr(enum_simple.owner_decl);
- try decl.renderFullyQualifiedName(mod, writer);
- },
- .enum_numbered => {
- const enum_numbered = ty.castTag(.enum_numbered).?.data;
- const decl = mod.declPtr(enum_numbered.owner_decl);
- try decl.renderFullyQualifiedName(mod, writer);
- },
- .@"opaque" => {
- const opaque_obj = ty.cast(Payload.Opaque).?.data;
- const decl = mod.declPtr(opaque_obj.owner_decl);
- try decl.renderFullyQualifiedName(mod, writer);
- },
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ const decl = mod.declPtr(struct_obj.owner_decl);
+ try decl.renderFullyQualifiedName(mod, writer);
+ },
+ .@"union", .union_safety_tagged, .union_tagged => {
+ const union_obj = ty.cast(Payload.Union).?.data;
+ const decl = mod.declPtr(union_obj.owner_decl);
+ try decl.renderFullyQualifiedName(mod, writer);
+ },
+ .enum_full, .enum_nonexhaustive => {
+ const enum_full = ty.cast(Payload.EnumFull).?.data;
+ const decl = mod.declPtr(enum_full.owner_decl);
+ try decl.renderFullyQualifiedName(mod, writer);
+ },
+ .enum_simple => {
+ const enum_simple = ty.castTag(.enum_simple).?.data;
+ const decl = mod.declPtr(enum_simple.owner_decl);
+ try decl.renderFullyQualifiedName(mod, writer);
+ },
+ .enum_numbered => {
+ const enum_numbered = ty.castTag(.enum_numbered).?.data;
+ const decl = mod.declPtr(enum_numbered.owner_decl);
+ try decl.renderFullyQualifiedName(mod, writer);
+ },
+ .@"opaque" => {
+ const opaque_obj = ty.cast(Payload.Opaque).?.data;
+ const decl = mod.declPtr(opaque_obj.owner_decl);
+ try decl.renderFullyQualifiedName(mod, writer);
+ },
- .error_set_inferred => {
- const func = ty.castTag(.error_set_inferred).?.data.func;
+ .error_set_inferred => {
+ const func = ty.castTag(.error_set_inferred).?.data.func;
- try writer.writeAll("@typeInfo(@typeInfo(@TypeOf(");
- const owner_decl = mod.declPtr(func.owner_decl);
- try owner_decl.renderFullyQualifiedName(mod, writer);
- try writer.writeAll(")).Fn.return_type.?).ErrorUnion.error_set");
- },
+ try writer.writeAll("@typeInfo(@typeInfo(@TypeOf(");
+ const owner_decl = mod.declPtr(func.owner_decl);
+ try owner_decl.renderFullyQualifiedName(mod, writer);
+ try writer.writeAll(")).Fn.return_type.?).ErrorUnion.error_set");
+ },
- .function => {
- const fn_info = ty.fnInfo();
- if (fn_info.is_noinline) {
- try writer.writeAll("noinline ");
- }
- try writer.writeAll("fn(");
- for (fn_info.param_types, 0..) |param_ty, i| {
- if (i != 0) try writer.writeAll(", ");
- if (fn_info.paramIsComptime(i)) {
- try writer.writeAll("comptime ");
+ .function => {
+ const fn_info = ty.fnInfo();
+ if (fn_info.is_noinline) {
+ try writer.writeAll("noinline ");
}
- if (std.math.cast(u5, i)) |index| if (@truncate(u1, fn_info.noalias_bits >> index) != 0) {
- try writer.writeAll("noalias ");
- };
- if (param_ty.isGenericPoison()) {
- try writer.writeAll("anytype");
- } else {
- try print(param_ty, writer, mod);
+ try writer.writeAll("fn(");
+ for (fn_info.param_types, 0..) |param_ty, i| {
+ if (i != 0) try writer.writeAll(", ");
+ if (fn_info.paramIsComptime(i)) {
+ try writer.writeAll("comptime ");
+ }
+ if (std.math.cast(u5, i)) |index| if (@truncate(u1, fn_info.noalias_bits >> index) != 0) {
+ try writer.writeAll("noalias ");
+ };
+ if (param_ty.isGenericPoison()) {
+ try writer.writeAll("anytype");
+ } else {
+ try print(param_ty, writer, mod);
+ }
}
- }
- if (fn_info.is_var_args) {
- if (fn_info.param_types.len != 0) {
- try writer.writeAll(", ");
+ if (fn_info.is_var_args) {
+ if (fn_info.param_types.len != 0) {
+ try writer.writeAll(", ");
+ }
+ try writer.writeAll("...");
}
- try writer.writeAll("...");
- }
- try writer.writeAll(") ");
- if (fn_info.alignment != 0) {
- try writer.print("align({d}) ", .{fn_info.alignment});
- }
- if (fn_info.cc != .Unspecified) {
- try writer.writeAll("callconv(.");
- try writer.writeAll(@tagName(fn_info.cc));
try writer.writeAll(") ");
- }
- if (fn_info.return_type.isGenericPoison()) {
- try writer.writeAll("anytype");
- } else {
- try print(fn_info.return_type, writer, mod);
- }
- },
+ if (fn_info.alignment != 0) {
+ try writer.print("align({d}) ", .{fn_info.alignment});
+ }
+ if (fn_info.cc != .Unspecified) {
+ try writer.writeAll("callconv(.");
+ try writer.writeAll(@tagName(fn_info.cc));
+ try writer.writeAll(") ");
+ }
+ if (fn_info.return_type.isGenericPoison()) {
+ try writer.writeAll("anytype");
+ } else {
+ try print(fn_info.return_type, writer, mod);
+ }
+ },
- .error_union => {
- const error_union = ty.castTag(.error_union).?.data;
- try print(error_union.error_set, writer, mod);
- try writer.writeAll("!");
- try print(error_union.payload, writer, mod);
- },
+ .error_union => {
+ const error_union = ty.castTag(.error_union).?.data;
+ try print(error_union.error_set, writer, mod);
+ try writer.writeAll("!");
+ try print(error_union.payload, writer, mod);
+ },
- .array => {
- const payload = ty.castTag(.array).?.data;
- try writer.print("[{d}]", .{payload.len});
- try print(payload.elem_type, writer, mod);
- },
- .array_sentinel => {
- const payload = ty.castTag(.array_sentinel).?.data;
- try writer.print("[{d}:{}]", .{
- payload.len,
- payload.sentinel.fmtValue(payload.elem_type, mod),
- });
- try print(payload.elem_type, writer, mod);
- },
- .tuple => {
- const tuple = ty.castTag(.tuple).?.data;
+ .array => {
+ const payload = ty.castTag(.array).?.data;
+ try writer.print("[{d}]", .{payload.len});
+ try print(payload.elem_type, writer, mod);
+ },
+ .array_sentinel => {
+ const payload = ty.castTag(.array_sentinel).?.data;
+ try writer.print("[{d}:{}]", .{
+ payload.len,
+ payload.sentinel.fmtValue(payload.elem_type, mod),
+ });
+ try print(payload.elem_type, writer, mod);
+ },
+ .tuple => {
+ const tuple = ty.castTag(.tuple).?.data;
- try writer.writeAll("tuple{");
- for (tuple.types, 0..) |field_ty, i| {
- if (i != 0) try writer.writeAll(", ");
- const val = tuple.values[i];
- if (val.ip_index != .unreachable_value) {
- try writer.writeAll("comptime ");
- }
- try print(field_ty, writer, mod);
- if (val.ip_index != .unreachable_value) {
- try writer.print(" = {}", .{val.fmtValue(field_ty, mod)});
+ try writer.writeAll("tuple{");
+ for (tuple.types, 0..) |field_ty, i| {
+ if (i != 0) try writer.writeAll(", ");
+ const val = tuple.values[i];
+ if (val.ip_index != .unreachable_value) {
+ try writer.writeAll("comptime ");
+ }
+ try print(field_ty, writer, mod);
+ if (val.ip_index != .unreachable_value) {
+ try writer.print(" = {}", .{val.fmtValue(field_ty, mod)});
+ }
}
- }
- try writer.writeAll("}");
- },
- .anon_struct => {
- const anon_struct = ty.castTag(.anon_struct).?.data;
+ try writer.writeAll("}");
+ },
+ .anon_struct => {
+ const anon_struct = ty.castTag(.anon_struct).?.data;
- try writer.writeAll("struct{");
- for (anon_struct.types, 0..) |field_ty, i| {
- if (i != 0) try writer.writeAll(", ");
- const val = anon_struct.values[i];
- if (val.ip_index != .unreachable_value) {
- try writer.writeAll("comptime ");
- }
- try writer.writeAll(anon_struct.names[i]);
- try writer.writeAll(": ");
+ try writer.writeAll("struct{");
+ for (anon_struct.types, 0..) |field_ty, i| {
+ if (i != 0) try writer.writeAll(", ");
+ const val = anon_struct.values[i];
+ if (val.ip_index != .unreachable_value) {
+ try writer.writeAll("comptime ");
+ }
+ try writer.writeAll(anon_struct.names[i]);
+ try writer.writeAll(": ");
- try print(field_ty, writer, mod);
+ try print(field_ty, writer, mod);
- if (val.ip_index != .unreachable_value) {
- try writer.print(" = {}", .{val.fmtValue(field_ty, mod)});
+ if (val.ip_index != .unreachable_value) {
+ try writer.print(" = {}", .{val.fmtValue(field_ty, mod)});
+ }
}
- }
- try writer.writeAll("}");
- },
+ try writer.writeAll("}");
+ },
- .pointer => {
- const info = ty.ptrInfo(mod);
+ .pointer => {
+ const info = ty.ptrInfo(mod);
- if (info.sentinel) |s| switch (info.size) {
- .One, .C => unreachable,
- .Many => try writer.print("[*:{}]", .{s.fmtValue(info.pointee_type, mod)}),
- .Slice => try writer.print("[:{}]", .{s.fmtValue(info.pointee_type, mod)}),
- } else switch (info.size) {
- .One => try writer.writeAll("*"),
- .Many => try writer.writeAll("[*]"),
- .C => try writer.writeAll("[*c]"),
- .Slice => try writer.writeAll("[]"),
- }
- if (info.@"align" != 0 or info.host_size != 0 or info.vector_index != .none) {
- if (info.@"align" != 0) {
- try writer.print("align({d}", .{info.@"align"});
- } else {
- const alignment = info.pointee_type.abiAlignment(mod);
- try writer.print("align({d}", .{alignment});
+ if (info.sentinel) |s| switch (info.size) {
+ .One, .C => unreachable,
+ .Many => try writer.print("[*:{}]", .{s.fmtValue(info.pointee_type, mod)}),
+ .Slice => try writer.print("[:{}]", .{s.fmtValue(info.pointee_type, mod)}),
+ } else switch (info.size) {
+ .One => try writer.writeAll("*"),
+ .Many => try writer.writeAll("[*]"),
+ .C => try writer.writeAll("[*c]"),
+ .Slice => try writer.writeAll("[]"),
}
+ if (info.@"align" != 0 or info.host_size != 0 or info.vector_index != .none) {
+ if (info.@"align" != 0) {
+ try writer.print("align({d}", .{info.@"align"});
+ } else {
+ const alignment = info.pointee_type.abiAlignment(mod);
+ try writer.print("align({d}", .{alignment});
+ }
- if (info.bit_offset != 0 or info.host_size != 0) {
- try writer.print(":{d}:{d}", .{ info.bit_offset, info.host_size });
+ if (info.bit_offset != 0 or info.host_size != 0) {
+ try writer.print(":{d}:{d}", .{ info.bit_offset, info.host_size });
+ }
+ if (info.vector_index == .runtime) {
+ try writer.writeAll(":?");
+ } else if (info.vector_index != .none) {
+ try writer.print(":{d}", .{@enumToInt(info.vector_index)});
+ }
+ try writer.writeAll(") ");
}
- if (info.vector_index == .runtime) {
- try writer.writeAll(":?");
- } else if (info.vector_index != .none) {
- try writer.print(":{d}", .{@enumToInt(info.vector_index)});
+ if (info.@"addrspace" != .generic) {
+ try writer.print("addrspace(.{s}) ", .{@tagName(info.@"addrspace")});
}
- try writer.writeAll(") ");
- }
- if (info.@"addrspace" != .generic) {
- try writer.print("addrspace(.{s}) ", .{@tagName(info.@"addrspace")});
- }
- if (!info.mutable) try writer.writeAll("const ");
- if (info.@"volatile") try writer.writeAll("volatile ");
- if (info.@"allowzero" and info.size != .C) try writer.writeAll("allowzero ");
-
- try print(info.pointee_type, writer, mod);
- },
+ if (!info.mutable) try writer.writeAll("const ");
+ if (info.@"volatile") try writer.writeAll("volatile ");
+ if (info.@"allowzero" and info.size != .C) try writer.writeAll("allowzero ");
- .optional => {
- const child_type = ty.castTag(.optional).?.data;
- try writer.writeByte('?');
- try print(child_type, writer, mod);
- },
- .anyframe_T => {
- const return_type = ty.castTag(.anyframe_T).?.data;
- try writer.print("anyframe->", .{});
- try print(return_type, writer, mod);
- },
- .error_set => {
- const names = ty.castTag(.error_set).?.data.names.keys();
- try writer.writeAll("error{");
- for (names, 0..) |name, i| {
- if (i != 0) try writer.writeByte(',');
- try writer.writeAll(name);
- }
- try writer.writeAll("}");
- },
- .error_set_single => {
- const name = ty.castTag(.error_set_single).?.data;
- return writer.print("error{{{s}}}", .{name});
- },
- .error_set_merged => {
- const names = ty.castTag(.error_set_merged).?.data.keys();
- try writer.writeAll("error{");
- for (names, 0..) |name, i| {
- if (i != 0) try writer.writeByte(',');
- try writer.writeAll(name);
- }
- try writer.writeAll("}");
- },
- }
- }
+ try print(info.pointee_type, writer, mod);
+ },
- pub fn toValue(self: Type, allocator: Allocator) Allocator.Error!Value {
- if (self.ip_index != .none) return self.ip_index.toValue();
- switch (self.tag()) {
- .inferred_alloc_const => unreachable,
- .inferred_alloc_mut => unreachable,
+ .optional => {
+ const child_type = ty.castTag(.optional).?.data;
+ try writer.writeByte('?');
+ try print(child_type, writer, mod);
+ },
+ .anyframe_T => {
+ const return_type = ty.castTag(.anyframe_T).?.data;
+ try writer.print("anyframe->", .{});
+ try print(return_type, writer, mod);
+ },
+ .error_set => {
+ const names = ty.castTag(.error_set).?.data.names.keys();
+ try writer.writeAll("error{");
+ for (names, 0..) |name, i| {
+ if (i != 0) try writer.writeByte(',');
+ try writer.writeAll(name);
+ }
+ try writer.writeAll("}");
+ },
+ .error_set_single => {
+ const name = ty.castTag(.error_set_single).?.data;
+ return writer.print("error{{{s}}}", .{name});
+ },
+ .error_set_merged => {
+ const names = ty.castTag(.error_set_merged).?.data.keys();
+ try writer.writeAll("error{");
+ for (names, 0..) |name, i| {
+ if (i != 0) try writer.writeByte(',');
+ try writer.writeAll(name);
+ }
+ try writer.writeAll("}");
+ },
+ },
+ else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+ .int_type => |int_type| {
+ const sign_char: u8 = switch (int_type.signedness) {
+ .signed => 'i',
+ .unsigned => 'u',
+ };
+ return writer.print("{c}{d}", .{ sign_char, int_type.bits });
+ },
+ .ptr_type => {
+ const info = ty.ptrInfo(mod);
+
+ if (info.sentinel) |s| switch (info.size) {
+ .One, .C => unreachable,
+ .Many => try writer.print("[*:{}]", .{s.fmtValue(info.pointee_type, mod)}),
+ .Slice => try writer.print("[:{}]", .{s.fmtValue(info.pointee_type, mod)}),
+ } else switch (info.size) {
+ .One => try writer.writeAll("*"),
+ .Many => try writer.writeAll("[*]"),
+ .C => try writer.writeAll("[*c]"),
+ .Slice => try writer.writeAll("[]"),
+ }
+ if (info.@"align" != 0 or info.host_size != 0 or info.vector_index != .none) {
+ if (info.@"align" != 0) {
+ try writer.print("align({d}", .{info.@"align"});
+ } else {
+ const alignment = info.pointee_type.abiAlignment(mod);
+ try writer.print("align({d}", .{alignment});
+ }
+
+ if (info.bit_offset != 0 or info.host_size != 0) {
+ try writer.print(":{d}:{d}", .{ info.bit_offset, info.host_size });
+ }
+ if (info.vector_index == .runtime) {
+ try writer.writeAll(":?");
+ } else if (info.vector_index != .none) {
+ try writer.print(":{d}", .{@enumToInt(info.vector_index)});
+ }
+ try writer.writeAll(") ");
+ }
+ if (info.@"addrspace" != .generic) {
+ try writer.print("addrspace(.{s}) ", .{@tagName(info.@"addrspace")});
+ }
+ if (!info.mutable) try writer.writeAll("const ");
+ if (info.@"volatile") try writer.writeAll("volatile ");
+ if (info.@"allowzero" and info.size != .C) try writer.writeAll("allowzero ");
+
+ try print(info.pointee_type, writer, mod);
+ return;
+ },
+ .array_type => |array_type| {
+ if (array_type.sentinel == .none) {
+ try writer.print("[{d}]", .{array_type.len});
+ try print(array_type.child.toType(), writer, mod);
+ } else {
+ try writer.print("[{d}:{}]", .{
+ array_type.len,
+ array_type.sentinel.toValue().fmtValue(array_type.child.toType(), mod),
+ });
+ try print(array_type.child.toType(), writer, mod);
+ }
+ return;
+ },
+ .vector_type => |vector_type| {
+ try writer.print("@Vector({d}, ", .{vector_type.len});
+ try print(vector_type.child.toType(), writer, mod);
+ try writer.writeAll(")");
+ return;
+ },
+ .opt_type => |child| {
+ try writer.writeByte('?');
+ try print(child.toType(), writer, mod);
+ return;
+ },
+ .error_union_type => |error_union_type| {
+ try print(error_union_type.error_set_type.toType(), writer, mod);
+ try writer.writeByte('!');
+ try print(error_union_type.payload_type.toType(), writer, mod);
+ return;
+ },
+ .simple_type => |s| return writer.writeAll(@tagName(s)),
+ .struct_type => @panic("TODO"),
+ .union_type => @panic("TODO"),
+ .simple_value => unreachable,
+ .extern_func => unreachable,
+ .int => unreachable,
+ .enum_tag => unreachable,
+ },
+ }
+ }
+
+ pub fn toValue(self: Type, allocator: Allocator) Allocator.Error!Value {
+ if (self.ip_index != .none) return self.ip_index.toValue();
+ switch (self.tag()) {
+ .inferred_alloc_const => unreachable,
+ .inferred_alloc_mut => unreachable,
else => return Value.Tag.ty.create(allocator, self),
}
}
@@ -1610,240 +1609,244 @@ pub const Type = struct {
ignore_comptime_only: bool,
strat: AbiAlignmentAdvancedStrat,
) RuntimeBitsError!bool {
- if (ty.ip_index != .none) switch (mod.intern_pool.indexToKey(ty.ip_index)) {
- .int_type => |int_type| return int_type.bits != 0,
- .ptr_type => |ptr_type| {
+ switch (ty.ip_index) {
+ // False because it is a comptime-only type.
+ .empty_struct_type => return false,
+
+ .none => switch (ty.tag()) {
+ .error_set_inferred,
+
+ .@"opaque",
+ .error_set_single,
+ .error_union,
+ .error_set,
+ .error_set_merged,
+ => return true,
+
// Pointers to zero-bit types still have a runtime address; however, pointers
// to comptime-only types do not, with the exception of function pointers.
- if (ignore_comptime_only) return true;
- const child_ty = ptr_type.elem_type.toType();
- if (child_ty.zigTypeTag(mod) == .Fn) return !child_ty.fnInfo().is_generic;
- if (strat == .sema) return !(try strat.sema.typeRequiresComptime(ty));
- return !comptimeOnly(ty, mod);
- },
- .array_type => |array_type| {
- if (array_type.sentinel != .none) {
- return array_type.child.toType().hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat);
- } else {
- return array_type.len > 0 and
- try array_type.child.toType().hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat);
- }
- },
- .vector_type => |vector_type| {
- return vector_type.len > 0 and
- try vector_type.child.toType().hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat);
- },
- .opt_type => |child| {
- const child_ty = child.toType();
- if (child_ty.isNoReturn()) {
- // Then the optional is comptime-known to be null.
- return false;
- }
- if (ignore_comptime_only) {
- return true;
- } else if (strat == .sema) {
- return !(try strat.sema.typeRequiresComptime(child_ty));
- } else {
- return !comptimeOnly(child_ty, mod);
- }
- },
- .error_union_type => @panic("TODO"),
- .simple_type => |t| return switch (t) {
- .f16,
- .f32,
- .f64,
- .f80,
- .f128,
- .usize,
- .isize,
- .c_char,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .bool,
- .anyerror,
- .@"anyframe",
- .anyopaque,
- .atomic_order,
- .atomic_rmw_op,
- .calling_convention,
- .address_space,
- .float_mode,
- .reduce_op,
- .call_modifier,
- .prefetch_options,
- .export_options,
- .extern_options,
- => true,
+ .anyframe_T,
+ .pointer,
+ => {
+ if (ignore_comptime_only) {
+ return true;
+ } else if (ty.childType(mod).zigTypeTag(mod) == .Fn) {
+ return !ty.childType(mod).fnInfo().is_generic;
+ } else if (strat == .sema) {
+ return !(try strat.sema.typeRequiresComptime(ty));
+ } else {
+ return !comptimeOnly(ty, mod);
+ }
+ },
// These are false because they are comptime-only types.
- .void,
- .type,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .null,
- .undefined,
- .enum_literal,
- .type_info,
- => false,
+ .empty_struct,
+ // These are function *bodies*, not pointers.
+ // Special exceptions have to be made when emitting functions due to
+ // this returning false.
+ .function,
+ => return false,
- .generic_poison => unreachable,
- .var_args_param => unreachable,
- },
- .struct_type => @panic("TODO"),
- .union_type => @panic("TODO"),
- .simple_value => unreachable,
- .extern_func => unreachable,
- .int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
- };
- switch (ty.tag()) {
- .error_set_inferred,
-
- .@"opaque",
- .error_set_single,
- .error_union,
- .error_set,
- .error_set_merged,
- => return true,
-
- // Pointers to zero-bit types still have a runtime address; however, pointers
- // to comptime-only types do not, with the exception of function pointers.
- .anyframe_T,
- .pointer,
- => {
- if (ignore_comptime_only) {
- return true;
- } else if (ty.childType(mod).zigTypeTag(mod) == .Fn) {
- return !ty.childType(mod).fnInfo().is_generic;
- } else if (strat == .sema) {
- return !(try strat.sema.typeRequiresComptime(ty));
- } else {
- return !comptimeOnly(ty, mod);
- }
- },
+ .optional => {
+ const child_ty = ty.optionalChild(mod);
+ if (child_ty.isNoReturn()) {
+ // Then the optional is comptime-known to be null.
+ return false;
+ }
+ if (ignore_comptime_only) {
+ return true;
+ } else if (strat == .sema) {
+ return !(try strat.sema.typeRequiresComptime(child_ty));
+ } else {
+ return !comptimeOnly(child_ty, mod);
+ }
+ },
- // These are false because they are comptime-only types.
- .empty_struct,
- .empty_struct_literal,
- // These are function *bodies*, not pointers.
- // Special exceptions have to be made when emitting functions due to
- // this returning false.
- .function,
- => return false,
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ 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) {
+ .sema => |sema| _ = try sema.resolveTypeFields(ty),
+ .eager => assert(struct_obj.haveFieldTypes()),
+ .lazy => if (!struct_obj.haveFieldTypes()) return error.NeedLazy,
+ }
+ for (struct_obj.fields.values()) |field| {
+ if (field.is_comptime) continue;
+ if (try field.ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat))
+ return true;
+ } else {
+ return false;
+ }
+ },
- .optional => {
- const child_ty = ty.optionalChild(mod);
- if (child_ty.isNoReturn()) {
- // Then the optional is comptime-known to be null.
- return false;
- }
- if (ignore_comptime_only) {
- return true;
- } else if (strat == .sema) {
- return !(try strat.sema.typeRequiresComptime(child_ty));
- } else {
- return !comptimeOnly(child_ty, mod);
- }
- },
+ .enum_full => {
+ const enum_full = ty.castTag(.enum_full).?.data;
+ return enum_full.tag_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat);
+ },
+ .enum_simple => {
+ const enum_simple = ty.castTag(.enum_simple).?.data;
+ return enum_simple.fields.count() >= 2;
+ },
+ .enum_numbered, .enum_nonexhaustive => {
+ const int_tag_ty = try ty.intTagType(mod);
+ return int_tag_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat);
+ },
- .@"struct" => {
- const struct_obj = ty.castTag(.@"struct").?.data;
- 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) {
- .sema => |sema| _ = try sema.resolveTypeFields(ty),
- .eager => assert(struct_obj.haveFieldTypes()),
- .lazy => if (!struct_obj.haveFieldTypes()) return error.NeedLazy,
- }
- for (struct_obj.fields.values()) |field| {
- if (field.is_comptime) continue;
- if (try field.ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat))
+ .@"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;
- } else {
- return false;
- }
- },
+ }
+ switch (strat) {
+ .sema => |sema| _ = try sema.resolveTypeFields(ty),
+ .eager => assert(union_obj.haveFieldTypes()),
+ .lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy,
+ }
+ for (union_obj.fields.values()) |value| {
+ if (try value.ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat))
+ return true;
+ } else {
+ return false;
+ }
+ },
+ .union_safety_tagged, .union_tagged => {
+ const union_obj = ty.cast(Payload.Union).?.data;
+ if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) {
+ return true;
+ }
- .enum_full => {
- const enum_full = ty.castTag(.enum_full).?.data;
- return enum_full.tag_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat);
- },
- .enum_simple => {
- const enum_simple = ty.castTag(.enum_simple).?.data;
- return enum_simple.fields.count() >= 2;
- },
- .enum_numbered, .enum_nonexhaustive => {
- const int_tag_ty = try ty.intTagType(mod);
- return int_tag_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat);
- },
+ switch (strat) {
+ .sema => |sema| _ = try sema.resolveTypeFields(ty),
+ .eager => assert(union_obj.haveFieldTypes()),
+ .lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy,
+ }
+ for (union_obj.fields.values()) |value| {
+ if (try value.ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat))
+ return true;
+ } else {
+ return false;
+ }
+ },
- .@"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()),
- .lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy,
- }
- for (union_obj.fields.values()) |value| {
- if (try value.ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat))
- return true;
- } else {
- return false;
- }
- },
- .union_safety_tagged, .union_tagged => {
- const union_obj = ty.cast(Payload.Union).?.data;
- if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) {
- return true;
- }
+ .array => return ty.arrayLen(mod) != 0 and
+ try ty.childType(mod).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat),
+ .array_sentinel => return ty.childType(mod).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat),
- switch (strat) {
- .sema => |sema| _ = try sema.resolveTypeFields(ty),
- .eager => assert(union_obj.haveFieldTypes()),
- .lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy,
- }
- for (union_obj.fields.values()) |value| {
- if (try value.ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat))
- return true;
- } else {
+ .tuple, .anon_struct => {
+ const tuple = ty.tupleFields();
+ for (tuple.types, 0..) |field_ty, i| {
+ const val = tuple.values[i];
+ if (val.ip_index != .unreachable_value) continue; // comptime field
+ if (try field_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) return true;
+ }
return false;
- }
- },
-
- .array => return ty.arrayLen(mod) != 0 and
- try ty.childType(mod).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat),
- .array_sentinel => return ty.childType(mod).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat),
+ },
- .tuple, .anon_struct => {
- const tuple = ty.tupleFields();
- for (tuple.types, 0..) |field_ty, i| {
- const val = tuple.values[i];
- if (val.ip_index != .unreachable_value) continue; // comptime field
- if (try field_ty.hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat)) return true;
- }
- return false;
+ .inferred_alloc_const => unreachable,
+ .inferred_alloc_mut => unreachable,
+ },
+ else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+ .int_type => |int_type| return int_type.bits != 0,
+ .ptr_type => |ptr_type| {
+ // Pointers to zero-bit types still have a runtime address; however, pointers
+ // to comptime-only types do not, with the exception of function pointers.
+ if (ignore_comptime_only) return true;
+ const child_ty = ptr_type.elem_type.toType();
+ if (child_ty.zigTypeTag(mod) == .Fn) return !child_ty.fnInfo().is_generic;
+ if (strat == .sema) return !(try strat.sema.typeRequiresComptime(ty));
+ return !comptimeOnly(ty, mod);
+ },
+ .array_type => |array_type| {
+ if (array_type.sentinel != .none) {
+ return array_type.child.toType().hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat);
+ } else {
+ return array_type.len > 0 and
+ try array_type.child.toType().hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat);
+ }
+ },
+ .vector_type => |vector_type| {
+ return vector_type.len > 0 and
+ try vector_type.child.toType().hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat);
+ },
+ .opt_type => |child| {
+ const child_ty = child.toType();
+ if (child_ty.isNoReturn()) {
+ // Then the optional is comptime-known to be null.
+ return false;
+ }
+ if (ignore_comptime_only) {
+ return true;
+ } else if (strat == .sema) {
+ return !(try strat.sema.typeRequiresComptime(child_ty));
+ } else {
+ return !comptimeOnly(child_ty, mod);
+ }
+ },
+ .error_union_type => @panic("TODO"),
+ .simple_type => |t| return switch (t) {
+ .f16,
+ .f32,
+ .f64,
+ .f80,
+ .f128,
+ .usize,
+ .isize,
+ .c_char,
+ .c_short,
+ .c_ushort,
+ .c_int,
+ .c_uint,
+ .c_long,
+ .c_ulong,
+ .c_longlong,
+ .c_ulonglong,
+ .c_longdouble,
+ .bool,
+ .anyerror,
+ .@"anyframe",
+ .anyopaque,
+ .atomic_order,
+ .atomic_rmw_op,
+ .calling_convention,
+ .address_space,
+ .float_mode,
+ .reduce_op,
+ .call_modifier,
+ .prefetch_options,
+ .export_options,
+ .extern_options,
+ => true,
+
+ // These are false because they are comptime-only types.
+ .void,
+ .type,
+ .comptime_int,
+ .comptime_float,
+ .noreturn,
+ .null,
+ .undefined,
+ .enum_literal,
+ .type_info,
+ => false,
+
+ .generic_poison => unreachable,
+ .var_args_param => unreachable,
+ },
+ .struct_type => @panic("TODO"),
+ .union_type => @panic("TODO"),
+ .simple_value => unreachable,
+ .extern_func => unreachable,
+ .int => unreachable,
+ .enum_tag => unreachable, // it's a value, not a type
},
-
- .inferred_alloc_const => unreachable,
- .inferred_alloc_mut => unreachable,
}
}
@@ -1851,104 +1854,107 @@ pub const Type = struct {
/// readFrom/writeToMemory are supported only for types with a well-
/// defined memory layout
pub fn hasWellDefinedLayout(ty: Type, mod: *Module) bool {
- if (ty.ip_index != .none) return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
- .int_type => true,
- .ptr_type => true,
- .array_type => |array_type| array_type.child.toType().hasWellDefinedLayout(mod),
- .vector_type => true,
- .opt_type => |child| child.toType().isPtrLikeOptional(mod),
- .error_union_type => false,
- .simple_type => |t| switch (t) {
- .f16,
- .f32,
- .f64,
- .f80,
- .f128,
- .usize,
- .isize,
- .c_char,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .bool,
- .void,
+ return switch (ty.ip_index) {
+ .empty_struct_type => false,
+
+ .none => switch (ty.tag()) {
+ .pointer,
+ .enum_numbered,
=> true,
- .anyerror,
- .@"anyframe",
- .anyopaque,
- .atomic_order,
- .atomic_rmw_op,
- .calling_convention,
- .address_space,
- .float_mode,
- .reduce_op,
- .call_modifier,
- .prefetch_options,
- .export_options,
- .extern_options,
- .type,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .null,
- .undefined,
- .enum_literal,
- .type_info,
- .generic_poison,
+ .error_set,
+ .error_set_single,
+ .error_set_inferred,
+ .error_set_merged,
+ .@"opaque",
+ // These are function bodies, not function pointers.
+ .function,
+ .enum_simple,
+ .error_union,
+ .anyframe_T,
+ .tuple,
+ .anon_struct,
+ .empty_struct,
=> false,
- .var_args_param => unreachable,
- },
- .struct_type => @panic("TODO"),
- .union_type => @panic("TODO"),
- .simple_value => unreachable,
- .extern_func => unreachable,
- .int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
- };
- return switch (ty.tag()) {
- .pointer,
- .enum_numbered,
- => true,
+ .enum_full,
+ .enum_nonexhaustive,
+ => !ty.cast(Payload.EnumFull).?.data.tag_ty_inferred,
- .error_set,
- .error_set_single,
- .error_set_inferred,
- .error_set_merged,
- .@"opaque",
- // These are function bodies, not function pointers.
- .function,
- .enum_simple,
- .error_union,
- .anyframe_T,
- .tuple,
- .anon_struct,
- .empty_struct_literal,
- .empty_struct,
- => false,
+ .inferred_alloc_mut => unreachable,
+ .inferred_alloc_const => unreachable,
- .enum_full,
- .enum_nonexhaustive,
- => !ty.cast(Payload.EnumFull).?.data.tag_ty_inferred,
+ .array,
+ .array_sentinel,
+ => ty.childType(mod).hasWellDefinedLayout(mod),
- .inferred_alloc_mut => unreachable,
- .inferred_alloc_const => unreachable,
+ .optional => ty.isPtrLikeOptional(mod),
+ .@"struct" => ty.castTag(.@"struct").?.data.layout != .Auto,
+ .@"union", .union_safety_tagged => ty.cast(Payload.Union).?.data.layout != .Auto,
+ .union_tagged => false,
+ },
+ else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+ .int_type => true,
+ .ptr_type => true,
+ .array_type => |array_type| array_type.child.toType().hasWellDefinedLayout(mod),
+ .vector_type => true,
+ .opt_type => |child| child.toType().isPtrLikeOptional(mod),
+ .error_union_type => false,
+ .simple_type => |t| switch (t) {
+ .f16,
+ .f32,
+ .f64,
+ .f80,
+ .f128,
+ .usize,
+ .isize,
+ .c_char,
+ .c_short,
+ .c_ushort,
+ .c_int,
+ .c_uint,
+ .c_long,
+ .c_ulong,
+ .c_longlong,
+ .c_ulonglong,
+ .c_longdouble,
+ .bool,
+ .void,
+ => true,
- .array,
- .array_sentinel,
- => ty.childType(mod).hasWellDefinedLayout(mod),
+ .anyerror,
+ .@"anyframe",
+ .anyopaque,
+ .atomic_order,
+ .atomic_rmw_op,
+ .calling_convention,
+ .address_space,
+ .float_mode,
+ .reduce_op,
+ .call_modifier,
+ .prefetch_options,
+ .export_options,
+ .extern_options,
+ .type,
+ .comptime_int,
+ .comptime_float,
+ .noreturn,
+ .null,
+ .undefined,
+ .enum_literal,
+ .type_info,
+ .generic_poison,
+ => false,
- .optional => ty.isPtrLikeOptional(mod),
- .@"struct" => ty.castTag(.@"struct").?.data.layout != .Auto,
- .@"union", .union_safety_tagged => ty.cast(Payload.Union).?.data.layout != .Auto,
- .union_tagged => false,
+ .var_args_param => unreachable,
+ },
+ .struct_type => @panic("TODO"),
+ .union_type => @panic("TODO"),
+ .simple_value => unreachable,
+ .extern_func => unreachable,
+ .int => unreachable,
+ .enum_tag => unreachable, // it's a value, not a type
+ },
};
}
@@ -2120,232 +2126,232 @@ pub const Type = struct {
else => null,
};
- if (ty.ip_index != .none) switch (mod.intern_pool.indexToKey(ty.ip_index)) {
- .int_type => |int_type| {
- if (int_type.bits == 0) return AbiAlignmentAdvanced{ .scalar = 0 };
- return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(int_type.bits, target) };
- },
- .ptr_type => {
- return AbiAlignmentAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) };
- },
- .array_type => |array_type| {
- return array_type.child.toType().abiAlignmentAdvanced(mod, strat);
- },
- .vector_type => |vector_type| {
- const bits_u64 = try bitSizeAdvanced(vector_type.child.toType(), mod, opt_sema);
- const bits = @intCast(u32, bits_u64);
- const bytes = ((bits * vector_type.len) + 7) / 8;
- const alignment = std.math.ceilPowerOfTwoAssert(u32, bytes);
- return AbiAlignmentAdvanced{ .scalar = alignment };
- },
+ switch (ty.ip_index) {
+ .empty_struct_type => return AbiAlignmentAdvanced{ .scalar = 0 },
+ .none => switch (ty.tag()) {
+ .@"opaque" => return AbiAlignmentAdvanced{ .scalar = 1 },
- .opt_type => return abiAlignmentAdvancedOptional(ty, mod, strat),
- .error_union_type => return abiAlignmentAdvancedErrorUnion(ty, mod, strat),
- .simple_type => |t| switch (t) {
- .bool,
- .atomic_order,
- .atomic_rmw_op,
- .calling_convention,
- .address_space,
- .float_mode,
- .reduce_op,
- .call_modifier,
- .prefetch_options,
- .anyopaque,
- => return AbiAlignmentAdvanced{ .scalar = 1 },
+ // represents machine code; not a pointer
+ .function => {
+ const alignment = ty.castTag(.function).?.data.alignment;
+ if (alignment != 0) return AbiAlignmentAdvanced{ .scalar = alignment };
+ return AbiAlignmentAdvanced{ .scalar = target_util.defaultFunctionAlignment(target) };
+ },
- .usize,
- .isize,
- .export_options,
- .extern_options,
- .@"anyframe",
+ .pointer,
+ .anyframe_T,
=> return AbiAlignmentAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) },
- .c_char => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.char) },
- .c_short => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.short) },
- .c_ushort => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ushort) },
- .c_int => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.int) },
- .c_uint => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.uint) },
- .c_long => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.long) },
- .c_ulong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulong) },
- .c_longlong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longlong) },
- .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulonglong) },
- .c_longdouble => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
-
- .f16 => return AbiAlignmentAdvanced{ .scalar = 2 },
- .f32 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.float) },
- .f64 => switch (target.c_type_bit_size(.double)) {
- 64 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.double) },
- else => return AbiAlignmentAdvanced{ .scalar = 8 },
- },
- .f80 => switch (target.c_type_bit_size(.longdouble)) {
- 80 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
- else => {
- const u80_ty: Type = .{
- .ip_index = .u80_type,
- .legacy = undefined,
- };
- return AbiAlignmentAdvanced{ .scalar = abiAlignment(u80_ty, mod) };
- },
- },
- .f128 => switch (target.c_type_bit_size(.longdouble)) {
- 128 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
- else => return AbiAlignmentAdvanced{ .scalar = 16 },
- },
-
// TODO revisit this when we have the concept of the error tag type
- .anyerror => return AbiAlignmentAdvanced{ .scalar = 2 },
-
- .void,
- .type,
- .comptime_int,
- .comptime_float,
- .null,
- .undefined,
- .enum_literal,
- .type_info,
- => return AbiAlignmentAdvanced{ .scalar = 0 },
-
- .noreturn => unreachable,
- .generic_poison => unreachable,
- .var_args_param => unreachable,
- },
- .struct_type => @panic("TODO"),
- .union_type => @panic("TODO"),
- .simple_value => unreachable,
- .extern_func => unreachable,
- .int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
- };
-
- switch (ty.tag()) {
- .@"opaque" => return AbiAlignmentAdvanced{ .scalar = 1 },
-
- // represents machine code; not a pointer
- .function => {
- const alignment = ty.castTag(.function).?.data.alignment;
- if (alignment != 0) return AbiAlignmentAdvanced{ .scalar = alignment };
- return AbiAlignmentAdvanced{ .scalar = target_util.defaultFunctionAlignment(target) };
- },
-
- .pointer,
- .anyframe_T,
- => return AbiAlignmentAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) },
+ .error_set_inferred,
+ .error_set_single,
+ .error_set,
+ .error_set_merged,
+ => return AbiAlignmentAdvanced{ .scalar = 2 },
- // TODO revisit this when we have the concept of the error tag type
- .error_set_inferred,
- .error_set_single,
- .error_set,
- .error_set_merged,
- => return AbiAlignmentAdvanced{ .scalar = 2 },
-
- .array, .array_sentinel => return ty.childType(mod).abiAlignmentAdvanced(mod, strat),
+ .array, .array_sentinel => return ty.childType(mod).abiAlignmentAdvanced(mod, strat),
- .optional => return abiAlignmentAdvancedOptional(ty, mod, strat),
- .error_union => return abiAlignmentAdvancedErrorUnion(ty, mod, strat),
+ .optional => return abiAlignmentAdvancedOptional(ty, mod, strat),
+ .error_union => return abiAlignmentAdvancedErrorUnion(ty, mod, strat),
- .@"struct" => {
- const struct_obj = ty.castTag(.@"struct").?.data;
- if (opt_sema) |sema| {
- if (struct_obj.status == .field_types_wip) {
- // 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.ptrBitWidth(), 8) };
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ if (opt_sema) |sema| {
+ if (struct_obj.status == .field_types_wip) {
+ // 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.ptrBitWidth(), 8) };
+ }
+ _ = try sema.resolveTypeFields(ty);
}
- _ = try sema.resolveTypeFields(ty);
- }
- if (!struct_obj.haveFieldTypes()) switch (strat) {
- .eager => unreachable, // struct layout not resolved
- .sema => unreachable, // handled above
- .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
- };
- if (struct_obj.layout == .Packed) {
- switch (strat) {
- .sema => |sema| try sema.resolveTypeLayout(ty),
- .lazy => |arena| {
- if (!struct_obj.haveLayout()) {
- return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) };
- }
- },
- .eager => {},
+ if (!struct_obj.haveFieldTypes()) switch (strat) {
+ .eager => unreachable, // struct layout not resolved
+ .sema => unreachable, // handled above
+ .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
+ };
+ if (struct_obj.layout == .Packed) {
+ switch (strat) {
+ .sema => |sema| try sema.resolveTypeLayout(ty),
+ .lazy => |arena| {
+ if (!struct_obj.haveLayout()) {
+ return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) };
+ }
+ },
+ .eager => {},
+ }
+ assert(struct_obj.haveLayout());
+ return AbiAlignmentAdvanced{ .scalar = struct_obj.backing_int_ty.abiAlignment(mod) };
}
- assert(struct_obj.haveLayout());
- return AbiAlignmentAdvanced{ .scalar = struct_obj.backing_int_ty.abiAlignment(mod) };
- }
-
- const fields = ty.structFields();
- var big_align: u32 = 0;
- for (fields.values()) |field| {
- if (!(field.ty.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) {
- error.NeedLazy => return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(strat.lazy, ty) },
- else => |e| return e,
- })) continue;
- const field_align = if (field.abi_align != 0)
- field.abi_align
- else switch (try field.ty.abiAlignmentAdvanced(mod, strat)) {
- .scalar => |a| a,
- .val => switch (strat) {
- .eager => unreachable, // struct layout not resolved
- .sema => unreachable, // handled above
- .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
- },
- };
- big_align = @max(big_align, field_align);
+ const fields = ty.structFields();
+ var big_align: u32 = 0;
+ for (fields.values()) |field| {
+ if (!(field.ty.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) {
+ error.NeedLazy => return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(strat.lazy, ty) },
+ else => |e| return e,
+ })) continue;
+
+ const field_align = if (field.abi_align != 0)
+ field.abi_align
+ else switch (try field.ty.abiAlignmentAdvanced(mod, strat)) {
+ .scalar => |a| a,
+ .val => switch (strat) {
+ .eager => unreachable, // struct layout not resolved
+ .sema => unreachable, // handled above
+ .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
+ },
+ };
+ big_align = @max(big_align, field_align);
+
+ // This logic is duplicated in Module.Struct.Field.alignment.
+ if (struct_obj.layout == .Extern or target.ofmt == .c) {
+ if (field.ty.isAbiInt(mod) and field.ty.intInfo(mod).bits >= 128) {
+ // The C ABI requires 128 bit integer fields of structs
+ // to be 16-bytes aligned.
+ big_align = @max(big_align, 16);
+ }
+ }
+ }
+ return AbiAlignmentAdvanced{ .scalar = big_align };
+ },
- // This logic is duplicated in Module.Struct.Field.alignment.
- if (struct_obj.layout == .Extern or target.ofmt == .c) {
- if (field.ty.isAbiInt(mod) and field.ty.intInfo(mod).bits >= 128) {
- // The C ABI requires 128 bit integer fields of structs
- // to be 16-bytes aligned.
- big_align = @max(big_align, 16);
+ .tuple, .anon_struct => {
+ const tuple = ty.tupleFields();
+ var big_align: u32 = 0;
+ for (tuple.types, 0..) |field_ty, i| {
+ const val = tuple.values[i];
+ if (val.ip_index != .unreachable_value) continue; // comptime field
+ if (!(field_ty.hasRuntimeBits(mod))) continue;
+
+ switch (try field_ty.abiAlignmentAdvanced(mod, strat)) {
+ .scalar => |field_align| big_align = @max(big_align, field_align),
+ .val => switch (strat) {
+ .eager => unreachable, // field type alignment not resolved
+ .sema => unreachable, // passed to abiAlignmentAdvanced above
+ .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
+ },
}
}
- }
- return AbiAlignmentAdvanced{ .scalar = big_align };
- },
+ return AbiAlignmentAdvanced{ .scalar = big_align };
+ },
- .tuple, .anon_struct => {
- const tuple = ty.tupleFields();
- var big_align: u32 = 0;
- for (tuple.types, 0..) |field_ty, i| {
- const val = tuple.values[i];
- if (val.ip_index != .unreachable_value) continue; // comptime field
- if (!(field_ty.hasRuntimeBits(mod))) continue;
+ .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {
+ const int_tag_ty = try ty.intTagType(mod);
+ return AbiAlignmentAdvanced{ .scalar = int_tag_ty.abiAlignment(mod) };
+ },
+ .@"union" => {
+ const union_obj = ty.castTag(.@"union").?.data;
+ return abiAlignmentAdvancedUnion(ty, mod, strat, union_obj, false);
+ },
+ .union_safety_tagged, .union_tagged => {
+ const union_obj = ty.cast(Payload.Union).?.data;
+ return abiAlignmentAdvancedUnion(ty, mod, strat, union_obj, true);
+ },
+
+ .empty_struct => return AbiAlignmentAdvanced{ .scalar = 0 },
- switch (try field_ty.abiAlignmentAdvanced(mod, strat)) {
- .scalar => |field_align| big_align = @max(big_align, field_align),
- .val => switch (strat) {
- .eager => unreachable, // field type alignment not resolved
- .sema => unreachable, // passed to abiAlignmentAdvanced above
- .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) },
- },
- }
- }
- return AbiAlignmentAdvanced{ .scalar = big_align };
+ .inferred_alloc_const,
+ .inferred_alloc_mut,
+ => unreachable,
},
+ else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+ .int_type => |int_type| {
+ if (int_type.bits == 0) return AbiAlignmentAdvanced{ .scalar = 0 };
+ return AbiAlignmentAdvanced{ .scalar = intAbiAlignment(int_type.bits, target) };
+ },
+ .ptr_type => {
+ return AbiAlignmentAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) };
+ },
+ .array_type => |array_type| {
+ return array_type.child.toType().abiAlignmentAdvanced(mod, strat);
+ },
+ .vector_type => |vector_type| {
+ const bits_u64 = try bitSizeAdvanced(vector_type.child.toType(), mod, opt_sema);
+ const bits = @intCast(u32, bits_u64);
+ const bytes = ((bits * vector_type.len) + 7) / 8;
+ const alignment = std.math.ceilPowerOfTwoAssert(u32, bytes);
+ return AbiAlignmentAdvanced{ .scalar = alignment };
+ },
- .enum_full, .enum_nonexhaustive, .enum_simple, .enum_numbered => {
- const int_tag_ty = try ty.intTagType(mod);
- return AbiAlignmentAdvanced{ .scalar = int_tag_ty.abiAlignment(mod) };
- },
- .@"union" => {
- const union_obj = ty.castTag(.@"union").?.data;
- return abiAlignmentAdvancedUnion(ty, mod, strat, union_obj, false);
- },
- .union_safety_tagged, .union_tagged => {
- const union_obj = ty.cast(Payload.Union).?.data;
- return abiAlignmentAdvancedUnion(ty, mod, strat, union_obj, true);
- },
+ .opt_type => return abiAlignmentAdvancedOptional(ty, mod, strat),
+ .error_union_type => return abiAlignmentAdvancedErrorUnion(ty, mod, strat),
+ .simple_type => |t| switch (t) {
+ .bool,
+ .atomic_order,
+ .atomic_rmw_op,
+ .calling_convention,
+ .address_space,
+ .float_mode,
+ .reduce_op,
+ .call_modifier,
+ .prefetch_options,
+ .anyopaque,
+ => return AbiAlignmentAdvanced{ .scalar = 1 },
- .empty_struct,
- .empty_struct_literal,
- => return AbiAlignmentAdvanced{ .scalar = 0 },
+ .usize,
+ .isize,
+ .export_options,
+ .extern_options,
+ .@"anyframe",
+ => return AbiAlignmentAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) },
+
+ .c_char => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.char) },
+ .c_short => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.short) },
+ .c_ushort => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ushort) },
+ .c_int => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.int) },
+ .c_uint => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.uint) },
+ .c_long => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.long) },
+ .c_ulong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulong) },
+ .c_longlong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longlong) },
+ .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulonglong) },
+ .c_longdouble => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
+
+ .f16 => return AbiAlignmentAdvanced{ .scalar = 2 },
+ .f32 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.float) },
+ .f64 => switch (target.c_type_bit_size(.double)) {
+ 64 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.double) },
+ else => return AbiAlignmentAdvanced{ .scalar = 8 },
+ },
+ .f80 => switch (target.c_type_bit_size(.longdouble)) {
+ 80 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
+ else => {
+ const u80_ty: Type = .{
+ .ip_index = .u80_type,
+ .legacy = undefined,
+ };
+ return AbiAlignmentAdvanced{ .scalar = abiAlignment(u80_ty, mod) };
+ },
+ },
+ .f128 => switch (target.c_type_bit_size(.longdouble)) {
+ 128 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
+ else => return AbiAlignmentAdvanced{ .scalar = 16 },
+ },
- .inferred_alloc_const,
- .inferred_alloc_mut,
- => unreachable,
+ // TODO revisit this when we have the concept of the error tag type
+ .anyerror => return AbiAlignmentAdvanced{ .scalar = 2 },
+
+ .void,
+ .type,
+ .comptime_int,
+ .comptime_float,
+ .null,
+ .undefined,
+ .enum_literal,
+ .type_info,
+ => return AbiAlignmentAdvanced{ .scalar = 0 },
+
+ .noreturn => unreachable,
+ .generic_poison => unreachable,
+ .var_args_param => unreachable,
+ },
+ .struct_type => @panic("TODO"),
+ .union_type => @panic("TODO"),
+ .simple_value => unreachable,
+ .extern_func => unreachable,
+ .int => unreachable,
+ .enum_tag => unreachable, // it's a value, not a type
+ },
}
}
@@ -2506,255 +2512,256 @@ pub const Type = struct {
) Module.CompileError!AbiSizeAdvanced {
const target = mod.getTarget();
- if (ty.ip_index != .none) switch (mod.intern_pool.indexToKey(ty.ip_index)) {
- .int_type => |int_type| {
- if (int_type.bits == 0) return AbiSizeAdvanced{ .scalar = 0 };
- return AbiSizeAdvanced{ .scalar = intAbiSize(int_type.bits, target) };
- },
- .ptr_type => |ptr_type| switch (ptr_type.size) {
- .Slice => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) * 2 },
- else => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) },
- },
- .array_type => |array_type| {
- const len = array_type.len + @boolToInt(array_type.sentinel != .none);
- switch (try array_type.child.toType().abiSizeAdvanced(mod, strat)) {
- .scalar => |elem_size| return .{ .scalar = len * elem_size },
- .val => switch (strat) {
- .sema, .eager => unreachable,
- .lazy => |arena| return .{ .val = try Value.Tag.lazy_size.create(arena, ty) },
- },
- }
- },
- .vector_type => |vector_type| {
- const opt_sema = switch (strat) {
- .sema => |sema| sema,
- .eager => null,
- .lazy => |arena| return AbiSizeAdvanced{
- .val = try Value.Tag.lazy_size.create(arena, ty),
- },
- };
- const elem_bits_u64 = try vector_type.child.toType().bitSizeAdvanced(mod, opt_sema);
- const elem_bits = @intCast(u32, elem_bits_u64);
- const total_bits = elem_bits * vector_type.len;
- const total_bytes = (total_bits + 7) / 8;
- const alignment = switch (try ty.abiAlignmentAdvanced(mod, strat)) {
- .scalar => |x| x,
- .val => return AbiSizeAdvanced{
- .val = try Value.Tag.lazy_size.create(strat.lazy, ty),
- },
- };
- const result = std.mem.alignForwardGeneric(u32, total_bytes, alignment);
- return AbiSizeAdvanced{ .scalar = result };
- },
+ switch (ty.ip_index) {
+ .empty_struct_type => return AbiSizeAdvanced{ .scalar = 0 },
- .opt_type => return ty.abiSizeAdvancedOptional(mod, strat),
- .error_union_type => @panic("TODO"),
- .simple_type => |t| switch (t) {
- .bool,
- .atomic_order,
- .atomic_rmw_op,
- .calling_convention,
- .address_space,
- .float_mode,
- .reduce_op,
- .call_modifier,
- => return AbiSizeAdvanced{ .scalar = 1 },
-
- .f16 => return AbiSizeAdvanced{ .scalar = 2 },
- .f32 => return AbiSizeAdvanced{ .scalar = 4 },
- .f64 => return AbiSizeAdvanced{ .scalar = 8 },
- .f128 => return AbiSizeAdvanced{ .scalar = 16 },
- .f80 => switch (target.c_type_bit_size(.longdouble)) {
- 80 => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) },
+ .none => switch (ty.tag()) {
+ .function => unreachable, // represents machine code; not a pointer
+ .@"opaque" => unreachable, // no size available
+ .inferred_alloc_const => unreachable,
+ .inferred_alloc_mut => unreachable,
+
+ .empty_struct => return AbiSizeAdvanced{ .scalar = 0 },
+
+ .@"struct", .tuple, .anon_struct => switch (ty.containerLayout()) {
+ .Packed => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ switch (strat) {
+ .sema => |sema| try sema.resolveTypeLayout(ty),
+ .lazy => |arena| {
+ if (!struct_obj.haveLayout()) {
+ return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) };
+ }
+ },
+ .eager => {},
+ }
+ assert(struct_obj.haveLayout());
+ return AbiSizeAdvanced{ .scalar = struct_obj.backing_int_ty.abiSize(mod) };
+ },
else => {
- const u80_ty: Type = .{
- .ip_index = .u80_type,
- .legacy = undefined,
- };
- return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, mod) };
+ switch (strat) {
+ .sema => |sema| try sema.resolveTypeLayout(ty),
+ .lazy => |arena| {
+ if (ty.castTag(.@"struct")) |payload| {
+ const struct_obj = payload.data;
+ if (!struct_obj.haveLayout()) {
+ return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) };
+ }
+ }
+ },
+ .eager => {},
+ }
+ const field_count = ty.structFieldCount();
+ if (field_count == 0) {
+ return AbiSizeAdvanced{ .scalar = 0 };
+ }
+ return AbiSizeAdvanced{ .scalar = ty.structFieldOffset(field_count, mod) };
},
},
- .usize,
- .isize,
- .@"anyframe",
- => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) },
-
- .c_char => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.char) },
- .c_short => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.short) },
- .c_ushort => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ushort) },
- .c_int => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.int) },
- .c_uint => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.uint) },
- .c_long => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.long) },
- .c_ulong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulong) },
- .c_longlong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longlong) },
- .c_ulonglong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulonglong) },
- .c_longdouble => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) },
-
- .anyopaque,
- .void,
- .type,
- .comptime_int,
- .comptime_float,
- .null,
- .undefined,
- .enum_literal,
- => return AbiSizeAdvanced{ .scalar = 0 },
-
- // TODO revisit this when we have the concept of the error tag type
- .anyerror => return AbiSizeAdvanced{ .scalar = 2 },
-
- .prefetch_options => unreachable, // missing call to resolveTypeFields
- .export_options => unreachable, // missing call to resolveTypeFields
- .extern_options => unreachable, // missing call to resolveTypeFields
-
- .type_info => unreachable,
- .noreturn => unreachable,
- .generic_poison => unreachable,
- .var_args_param => unreachable,
- },
- .struct_type => @panic("TODO"),
- .union_type => @panic("TODO"),
- .simple_value => unreachable,
- .extern_func => unreachable,
- .int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
- };
-
- switch (ty.tag()) {
- .function => unreachable, // represents machine code; not a pointer
- .@"opaque" => unreachable, // no size available
- .inferred_alloc_const => unreachable,
- .inferred_alloc_mut => unreachable,
-
- .empty_struct_literal,
- .empty_struct,
- => return AbiSizeAdvanced{ .scalar = 0 },
+ .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
+ const int_tag_ty = try ty.intTagType(mod);
+ return AbiSizeAdvanced{ .scalar = int_tag_ty.abiSize(mod) };
+ },
+ .@"union" => {
+ const union_obj = ty.castTag(.@"union").?.data;
+ return abiSizeAdvancedUnion(ty, mod, strat, union_obj, false);
+ },
+ .union_safety_tagged, .union_tagged => {
+ const union_obj = ty.cast(Payload.Union).?.data;
+ return abiSizeAdvancedUnion(ty, mod, strat, union_obj, true);
+ },
- .@"struct", .tuple, .anon_struct => switch (ty.containerLayout()) {
- .Packed => {
- const struct_obj = ty.castTag(.@"struct").?.data;
- switch (strat) {
- .sema => |sema| try sema.resolveTypeLayout(ty),
- .lazy => |arena| {
- if (!struct_obj.haveLayout()) {
- return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) };
- }
+ .array => {
+ const payload = ty.castTag(.array).?.data;
+ switch (try payload.elem_type.abiSizeAdvanced(mod, strat)) {
+ .scalar => |elem_size| return AbiSizeAdvanced{ .scalar = payload.len * elem_size },
+ .val => switch (strat) {
+ .sema => unreachable,
+ .eager => unreachable,
+ .lazy => |arena| return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) },
},
- .eager => {},
}
- assert(struct_obj.haveLayout());
- return AbiSizeAdvanced{ .scalar = struct_obj.backing_int_ty.abiSize(mod) };
},
- else => {
- switch (strat) {
- .sema => |sema| try sema.resolveTypeLayout(ty),
- .lazy => |arena| {
- if (ty.castTag(.@"struct")) |payload| {
- const struct_obj = payload.data;
- if (!struct_obj.haveLayout()) {
- return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) };
- }
- }
+ .array_sentinel => {
+ const payload = ty.castTag(.array_sentinel).?.data;
+ switch (try payload.elem_type.abiSizeAdvanced(mod, strat)) {
+ .scalar => |elem_size| return AbiSizeAdvanced{ .scalar = (payload.len + 1) * elem_size },
+ .val => switch (strat) {
+ .sema => unreachable,
+ .eager => unreachable,
+ .lazy => |arena| return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) },
},
- .eager => {},
}
- const field_count = ty.structFieldCount();
- if (field_count == 0) {
- return AbiSizeAdvanced{ .scalar = 0 };
- }
- return AbiSizeAdvanced{ .scalar = ty.structFieldOffset(field_count, mod) };
},
- },
- .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => {
- const int_tag_ty = try ty.intTagType(mod);
- return AbiSizeAdvanced{ .scalar = int_tag_ty.abiSize(mod) };
- },
- .@"union" => {
- const union_obj = ty.castTag(.@"union").?.data;
- return abiSizeAdvancedUnion(ty, mod, strat, union_obj, false);
- },
- .union_safety_tagged, .union_tagged => {
- const union_obj = ty.cast(Payload.Union).?.data;
- return abiSizeAdvancedUnion(ty, mod, strat, union_obj, true);
- },
+ .anyframe_T => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) },
- .array => {
- const payload = ty.castTag(.array).?.data;
- switch (try payload.elem_type.abiSizeAdvanced(mod, strat)) {
- .scalar => |elem_size| return AbiSizeAdvanced{ .scalar = payload.len * elem_size },
- .val => switch (strat) {
- .sema => unreachable,
- .eager => unreachable,
- .lazy => |arena| return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) },
- },
- }
- },
- .array_sentinel => {
- const payload = ty.castTag(.array_sentinel).?.data;
- switch (try payload.elem_type.abiSizeAdvanced(mod, strat)) {
- .scalar => |elem_size| return AbiSizeAdvanced{ .scalar = (payload.len + 1) * elem_size },
- .val => switch (strat) {
- .sema => unreachable,
- .eager => unreachable,
- .lazy => |arena| return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) },
- },
- }
- },
+ .pointer => switch (ty.castTag(.pointer).?.data.size) {
+ .Slice => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) * 2 },
+ else => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) },
+ },
- .anyframe_T => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) },
+ // TODO revisit this when we have the concept of the error tag type
+ .error_set_inferred,
+ .error_set,
+ .error_set_merged,
+ .error_set_single,
+ => return AbiSizeAdvanced{ .scalar = 2 },
- .pointer => switch (ty.castTag(.pointer).?.data.size) {
- .Slice => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) * 2 },
- else => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) },
- },
+ .optional => return ty.abiSizeAdvancedOptional(mod, strat),
- // TODO revisit this when we have the concept of the error tag type
- .error_set_inferred,
- .error_set,
- .error_set_merged,
- .error_set_single,
- => return AbiSizeAdvanced{ .scalar = 2 },
+ .error_union => {
+ // This code needs to be kept in sync with the equivalent switch prong
+ // in abiAlignmentAdvanced.
+ const data = ty.castTag(.error_union).?.data;
+ const code_size = abiSize(Type.anyerror, mod);
+ if (!(data.payload.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) {
+ error.NeedLazy => return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(strat.lazy, ty) },
+ else => |e| return e,
+ })) {
+ // Same as anyerror.
+ return AbiSizeAdvanced{ .scalar = code_size };
+ }
+ const code_align = abiAlignment(Type.anyerror, mod);
+ const payload_align = abiAlignment(data.payload, mod);
+ const payload_size = switch (try data.payload.abiSizeAdvanced(mod, strat)) {
+ .scalar => |elem_size| elem_size,
+ .val => switch (strat) {
+ .sema => unreachable,
+ .eager => unreachable,
+ .lazy => |arena| return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) },
+ },
+ };
- .optional => return ty.abiSizeAdvancedOptional(mod, strat),
+ var size: u64 = 0;
+ if (code_align > payload_align) {
+ size += code_size;
+ size = std.mem.alignForwardGeneric(u64, size, payload_align);
+ size += payload_size;
+ size = std.mem.alignForwardGeneric(u64, size, code_align);
+ } else {
+ size += payload_size;
+ size = std.mem.alignForwardGeneric(u64, size, code_align);
+ size += code_size;
+ size = std.mem.alignForwardGeneric(u64, size, payload_align);
+ }
+ return AbiSizeAdvanced{ .scalar = size };
+ },
+ },
+ else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+ .int_type => |int_type| {
+ if (int_type.bits == 0) return AbiSizeAdvanced{ .scalar = 0 };
+ return AbiSizeAdvanced{ .scalar = intAbiSize(int_type.bits, target) };
+ },
+ .ptr_type => |ptr_type| switch (ptr_type.size) {
+ .Slice => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) * 2 },
+ else => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) },
+ },
+ .array_type => |array_type| {
+ const len = array_type.len + @boolToInt(array_type.sentinel != .none);
+ switch (try array_type.child.toType().abiSizeAdvanced(mod, strat)) {
+ .scalar => |elem_size| return .{ .scalar = len * elem_size },
+ .val => switch (strat) {
+ .sema, .eager => unreachable,
+ .lazy => |arena| return .{ .val = try Value.Tag.lazy_size.create(arena, ty) },
+ },
+ }
+ },
+ .vector_type => |vector_type| {
+ const opt_sema = switch (strat) {
+ .sema => |sema| sema,
+ .eager => null,
+ .lazy => |arena| return AbiSizeAdvanced{
+ .val = try Value.Tag.lazy_size.create(arena, ty),
+ },
+ };
+ const elem_bits_u64 = try vector_type.child.toType().bitSizeAdvanced(mod, opt_sema);
+ const elem_bits = @intCast(u32, elem_bits_u64);
+ const total_bits = elem_bits * vector_type.len;
+ const total_bytes = (total_bits + 7) / 8;
+ const alignment = switch (try ty.abiAlignmentAdvanced(mod, strat)) {
+ .scalar => |x| x,
+ .val => return AbiSizeAdvanced{
+ .val = try Value.Tag.lazy_size.create(strat.lazy, ty),
+ },
+ };
+ const result = std.mem.alignForwardGeneric(u32, total_bytes, alignment);
+ return AbiSizeAdvanced{ .scalar = result };
+ },
- .error_union => {
- // This code needs to be kept in sync with the equivalent switch prong
- // in abiAlignmentAdvanced.
- const data = ty.castTag(.error_union).?.data;
- const code_size = abiSize(Type.anyerror, mod);
- if (!(data.payload.hasRuntimeBitsAdvanced(mod, false, strat) catch |err| switch (err) {
- error.NeedLazy => return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(strat.lazy, ty) },
- else => |e| return e,
- })) {
- // Same as anyerror.
- return AbiSizeAdvanced{ .scalar = code_size };
- }
- const code_align = abiAlignment(Type.anyerror, mod);
- const payload_align = abiAlignment(data.payload, mod);
- const payload_size = switch (try data.payload.abiSizeAdvanced(mod, strat)) {
- .scalar => |elem_size| elem_size,
- .val => switch (strat) {
- .sema => unreachable,
- .eager => unreachable,
- .lazy => |arena| return AbiSizeAdvanced{ .val = try Value.Tag.lazy_size.create(arena, ty) },
+ .opt_type => return ty.abiSizeAdvancedOptional(mod, strat),
+ .error_union_type => @panic("TODO"),
+ .simple_type => |t| switch (t) {
+ .bool,
+ .atomic_order,
+ .atomic_rmw_op,
+ .calling_convention,
+ .address_space,
+ .float_mode,
+ .reduce_op,
+ .call_modifier,
+ => return AbiSizeAdvanced{ .scalar = 1 },
+
+ .f16 => return AbiSizeAdvanced{ .scalar = 2 },
+ .f32 => return AbiSizeAdvanced{ .scalar = 4 },
+ .f64 => return AbiSizeAdvanced{ .scalar = 8 },
+ .f128 => return AbiSizeAdvanced{ .scalar = 16 },
+ .f80 => switch (target.c_type_bit_size(.longdouble)) {
+ 80 => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) },
+ else => {
+ const u80_ty: Type = .{
+ .ip_index = .u80_type,
+ .legacy = undefined,
+ };
+ return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, mod) };
+ },
},
- };
- var size: u64 = 0;
- if (code_align > payload_align) {
- size += code_size;
- size = std.mem.alignForwardGeneric(u64, size, payload_align);
- size += payload_size;
- size = std.mem.alignForwardGeneric(u64, size, code_align);
- } else {
- size += payload_size;
- size = std.mem.alignForwardGeneric(u64, size, code_align);
- size += code_size;
- size = std.mem.alignForwardGeneric(u64, size, payload_align);
- }
- return AbiSizeAdvanced{ .scalar = size };
+ .usize,
+ .isize,
+ .@"anyframe",
+ => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) },
+
+ .c_char => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.char) },
+ .c_short => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.short) },
+ .c_ushort => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ushort) },
+ .c_int => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.int) },
+ .c_uint => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.uint) },
+ .c_long => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.long) },
+ .c_ulong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulong) },
+ .c_longlong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longlong) },
+ .c_ulonglong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulonglong) },
+ .c_longdouble => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) },
+
+ .anyopaque,
+ .void,
+ .type,
+ .comptime_int,
+ .comptime_float,
+ .null,
+ .undefined,
+ .enum_literal,
+ => return AbiSizeAdvanced{ .scalar = 0 },
+
+ // TODO revisit this when we have the concept of the error tag type
+ .anyerror => return AbiSizeAdvanced{ .scalar = 2 },
+
+ .prefetch_options => unreachable, // missing call to resolveTypeFields
+ .export_options => unreachable, // missing call to resolveTypeFields
+ .extern_options => unreachable, // missing call to resolveTypeFields
+
+ .type_info => unreachable,
+ .noreturn => unreachable,
+ .generic_poison => unreachable,
+ .var_args_param => unreachable,
+ },
+ .struct_type => @panic("TODO"),
+ .union_type => @panic("TODO"),
+ .simple_value => unreachable,
+ .extern_func => unreachable,
+ .int => unreachable,
+ .enum_tag => unreachable, // it's a value, not a type
},
}
}
@@ -2929,7 +2936,6 @@ pub const Type = struct {
switch (ty.tag()) {
.function => unreachable, // represents machine code; not a pointer
.empty_struct => unreachable,
- .empty_struct_literal => unreachable,
.inferred_alloc_const => unreachable,
.inferred_alloc_mut => unreachable,
.@"opaque" => unreachable,
@@ -3490,12 +3496,16 @@ pub const Type = struct {
}
pub fn containerLayout(ty: Type) std.builtin.Type.ContainerLayout {
- return switch (ty.tag()) {
- .tuple, .empty_struct_literal, .anon_struct => .Auto,
- .@"struct" => ty.castTag(.@"struct").?.data.layout,
- .@"union" => ty.castTag(.@"union").?.data.layout,
- .union_safety_tagged => ty.castTag(.union_safety_tagged).?.data.layout,
- .union_tagged => ty.castTag(.union_tagged).?.data.layout,
+ return switch (ty.ip_index) {
+ .empty_struct_type => .Auto,
+ .none => switch (ty.tag()) {
+ .tuple, .anon_struct => .Auto,
+ .@"struct" => ty.castTag(.@"struct").?.data.layout,
+ .@"union" => ty.castTag(.@"union").?.data.layout,
+ .union_safety_tagged => ty.castTag(.union_safety_tagged).?.data.layout,
+ .union_tagged => ty.castTag(.union_tagged).?.data.layout,
+ else => unreachable,
+ },
else => unreachable,
};
}
@@ -3610,13 +3620,14 @@ pub const Type = struct {
pub fn arrayLenIp(ty: Type, ip: InternPool) u64 {
return switch (ty.ip_index) {
+ .empty_struct_type => 0,
.none => switch (ty.tag()) {
.array => ty.castTag(.array).?.data.len,
.array_sentinel => ty.castTag(.array_sentinel).?.data.len,
.tuple => ty.castTag(.tuple).?.data.types.len,
.anon_struct => ty.castTag(.anon_struct).?.data.types.len,
.@"struct" => ty.castTag(.@"struct").?.data.fields.count(),
- .empty_struct, .empty_struct_literal => 0,
+ .empty_struct => 0,
else => unreachable,
},
@@ -3649,10 +3660,10 @@ pub const Type = struct {
/// Asserts the type is an array, pointer or vector.
pub fn sentinel(ty: Type, mod: *const Module) ?Value {
return switch (ty.ip_index) {
+ .empty_struct_type => null,
.none => switch (ty.tag()) {
.array,
.tuple,
- .empty_struct_literal,
.@"struct",
=> null,
@@ -3951,197 +3962,200 @@ pub const Type = struct {
pub fn onePossibleValue(starting_type: Type, mod: *Module) !?Value {
var ty = starting_type;
- if (ty.ip_index != .none) switch (mod.intern_pool.indexToKey(ty.ip_index)) {
- .int_type => |int_type| {
- if (int_type.bits == 0) {
- return try mod.intValue(ty, 0);
- } else {
- return null;
- }
- },
- .ptr_type => return null,
- .array_type => |array_type| {
- if (array_type.len == 0)
- return Value.initTag(.empty_array);
- if ((try array_type.child.toType().onePossibleValue(mod)) != null)
- return Value.initTag(.the_only_possible_value);
- return null;
- },
- .vector_type => |vector_type| {
- if (vector_type.len == 0) return Value.initTag(.empty_array);
- if (try vector_type.child.toType().onePossibleValue(mod)) |v| return v;
- return null;
- },
- .opt_type => |child| {
- if (child.toType().isNoReturn()) {
- return Value.null;
- } else {
- return null;
- }
- },
- .error_union_type => return null,
- .simple_type => |t| switch (t) {
- .f16,
- .f32,
- .f64,
- .f80,
- .f128,
- .usize,
- .isize,
- .c_char,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .anyopaque,
- .bool,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .@"anyframe",
- .enum_literal,
- .atomic_order,
- .atomic_rmw_op,
- .calling_convention,
- .address_space,
- .float_mode,
- .reduce_op,
- .call_modifier,
- .prefetch_options,
- .export_options,
- .extern_options,
- .type_info,
+ while (true) switch (ty.ip_index) {
+ .empty_struct_type => return Value.empty_struct,
+
+ .none => switch (ty.tag()) {
+ .error_union,
+ .error_set_single,
+ .error_set,
+ .error_set_merged,
+ .function,
+ .array_sentinel,
+ .error_set_inferred,
+ .@"opaque",
+ .anyframe_T,
+ .pointer,
=> return null,
- .void => return Value.void,
- .noreturn => return Value.@"unreachable",
- .null => return Value.null,
- .undefined => return Value.undef,
+ .optional => {
+ const child_ty = ty.optionalChild(mod);
+ if (child_ty.isNoReturn()) {
+ return Value.null;
+ } else {
+ return null;
+ }
+ },
- .generic_poison => unreachable,
- .var_args_param => unreachable,
- },
- .struct_type => @panic("TODO"),
- .union_type => @panic("TODO"),
- .simple_value => unreachable,
- .extern_func => unreachable,
- .int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
- };
+ .@"struct" => {
+ const s = ty.castTag(.@"struct").?.data;
+ assert(s.haveFieldTypes());
+ for (s.fields.values()) |field| {
+ if (field.is_comptime) continue;
+ if ((try field.ty.onePossibleValue(mod)) != null) continue;
+ return null;
+ }
+ return Value.empty_struct;
+ },
- while (true) switch (ty.tag()) {
- .error_union,
- .error_set_single,
- .error_set,
- .error_set_merged,
- .function,
- .array_sentinel,
- .error_set_inferred,
- .@"opaque",
- .anyframe_T,
- .pointer,
- => return null,
+ .tuple, .anon_struct => {
+ const tuple = ty.tupleFields();
+ for (tuple.values, 0..) |val, i| {
+ const is_comptime = val.ip_index != .unreachable_value;
+ if (is_comptime) continue;
+ if ((try tuple.types[i].onePossibleValue(mod)) != null) continue;
+ return null;
+ }
+ return Value.empty_struct;
+ },
+
+ .enum_numbered => {
+ const enum_numbered = ty.castTag(.enum_numbered).?.data;
+ // An explicit tag type is always provided for enum_numbered.
+ if (enum_numbered.tag_ty.hasRuntimeBits(mod)) {
+ return null;
+ }
+ assert(enum_numbered.fields.count() == 1);
+ return enum_numbered.values.keys()[0];
+ },
+ .enum_full => {
+ const enum_full = ty.castTag(.enum_full).?.data;
+ if (enum_full.tag_ty.hasRuntimeBits(mod)) {
+ return null;
+ }
+ switch (enum_full.fields.count()) {
+ 0 => return Value.@"unreachable",
+ 1 => if (enum_full.values.count() == 0) {
+ return try mod.intValue(ty, 0); // auto-numbered
+ } else {
+ return enum_full.values.keys()[0];
+ },
+ else => return null,
+ }
+ },
+ .enum_simple => {
+ const enum_simple = ty.castTag(.enum_simple).?.data;
+ switch (enum_simple.fields.count()) {
+ 0 => return Value.@"unreachable",
+ 1 => return try mod.intValue(ty, 0),
+ else => return null,
+ }
+ },
+ .enum_nonexhaustive => {
+ const tag_ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty;
+ if (!tag_ty.hasRuntimeBits(mod)) {
+ return try mod.intValue(ty, 0);
+ } else {
+ return null;
+ }
+ },
+ .@"union", .union_safety_tagged, .union_tagged => {
+ const union_obj = ty.cast(Payload.Union).?.data;
+ const tag_val = (try union_obj.tag_ty.onePossibleValue(mod)) orelse return null;
+ if (union_obj.fields.count() == 0) return Value.@"unreachable";
+ const only_field = union_obj.fields.values()[0];
+ const val_val = (try only_field.ty.onePossibleValue(mod)) orelse return null;
+ _ = tag_val;
+ _ = val_val;
+ return Value.empty_struct;
+ },
- .optional => {
- const child_ty = ty.optionalChild(mod);
- if (child_ty.isNoReturn()) {
- return Value.null;
- } else {
- return null;
- }
- },
+ .empty_struct => return Value.empty_struct,
- .@"struct" => {
- const s = ty.castTag(.@"struct").?.data;
- assert(s.haveFieldTypes());
- for (s.fields.values()) |field| {
- if (field.is_comptime) continue;
- if ((try field.ty.onePossibleValue(mod)) != null) continue;
+ .array => {
+ if (ty.arrayLen(mod) == 0)
+ return Value.initTag(.empty_array);
+ if ((try ty.childType(mod).onePossibleValue(mod)) != null)
+ return Value.initTag(.the_only_possible_value);
return null;
- }
- return Value.initTag(.empty_struct_value);
- },
+ },
- .tuple, .anon_struct => {
- const tuple = ty.tupleFields();
- for (tuple.values, 0..) |val, i| {
- const is_comptime = val.ip_index != .unreachable_value;
- if (is_comptime) continue;
- if ((try tuple.types[i].onePossibleValue(mod)) != null) continue;
- return null;
- }
- return Value.initTag(.empty_struct_value);
+ .inferred_alloc_const => unreachable,
+ .inferred_alloc_mut => unreachable,
},
-
- .enum_numbered => {
- const enum_numbered = ty.castTag(.enum_numbered).?.data;
- // An explicit tag type is always provided for enum_numbered.
- if (enum_numbered.tag_ty.hasRuntimeBits(mod)) {
+ else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+ .int_type => |int_type| {
+ if (int_type.bits == 0) {
+ return try mod.intValue(ty, 0);
+ } else {
+ return null;
+ }
+ },
+ .ptr_type => return null,
+ .array_type => |array_type| {
+ if (array_type.len == 0)
+ return Value.initTag(.empty_array);
+ if ((try array_type.child.toType().onePossibleValue(mod)) != null)
+ return Value.initTag(.the_only_possible_value);
return null;
- }
- assert(enum_numbered.fields.count() == 1);
- return enum_numbered.values.keys()[0];
- },
- .enum_full => {
- const enum_full = ty.castTag(.enum_full).?.data;
- if (enum_full.tag_ty.hasRuntimeBits(mod)) {
+ },
+ .vector_type => |vector_type| {
+ if (vector_type.len == 0) return Value.initTag(.empty_array);
+ if (try vector_type.child.toType().onePossibleValue(mod)) |v| return v;
return null;
- }
- switch (enum_full.fields.count()) {
- 0 => return Value.@"unreachable",
- 1 => if (enum_full.values.count() == 0) {
- return try mod.intValue(ty, 0); // auto-numbered
+ },
+ .opt_type => |child| {
+ if (child.toType().isNoReturn()) {
+ return Value.null;
} else {
- return enum_full.values.keys()[0];
- },
- else => return null,
- }
- },
- .enum_simple => {
- const enum_simple = ty.castTag(.enum_simple).?.data;
- switch (enum_simple.fields.count()) {
- 0 => return Value.@"unreachable",
- 1 => return try mod.intValue(ty, 0),
- else => return null,
- }
- },
- .enum_nonexhaustive => {
- const tag_ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty;
- if (!tag_ty.hasRuntimeBits(mod)) {
- return try mod.intValue(ty, 0);
- } else {
- return null;
- }
- },
- .@"union", .union_safety_tagged, .union_tagged => {
- const union_obj = ty.cast(Payload.Union).?.data;
- const tag_val = (try union_obj.tag_ty.onePossibleValue(mod)) orelse return null;
- if (union_obj.fields.count() == 0) return Value.@"unreachable";
- const only_field = union_obj.fields.values()[0];
- const val_val = (try only_field.ty.onePossibleValue(mod)) orelse return null;
- _ = tag_val;
- _ = val_val;
- return Value.initTag(.empty_struct_value);
- },
+ return null;
+ }
+ },
+ .error_union_type => return null,
+ .simple_type => |t| switch (t) {
+ .f16,
+ .f32,
+ .f64,
+ .f80,
+ .f128,
+ .usize,
+ .isize,
+ .c_char,
+ .c_short,
+ .c_ushort,
+ .c_int,
+ .c_uint,
+ .c_long,
+ .c_ulong,
+ .c_longlong,
+ .c_ulonglong,
+ .c_longdouble,
+ .anyopaque,
+ .bool,
+ .type,
+ .anyerror,
+ .comptime_int,
+ .comptime_float,
+ .@"anyframe",
+ .enum_literal,
+ .atomic_order,
+ .atomic_rmw_op,
+ .calling_convention,
+ .address_space,
+ .float_mode,
+ .reduce_op,
+ .call_modifier,
+ .prefetch_options,
+ .export_options,
+ .extern_options,
+ .type_info,
+ => return null,
- .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),
+ .void => return Value.void,
+ .noreturn => return Value.@"unreachable",
+ .null => return Value.null,
+ .undefined => return Value.undef,
- .array => {
- if (ty.arrayLen(mod) == 0)
- return Value.initTag(.empty_array);
- if ((try ty.childType(mod).onePossibleValue(mod)) != null)
- return Value.initTag(.the_only_possible_value);
- return null;
+ .generic_poison => unreachable,
+ .var_args_param => unreachable,
+ },
+ .struct_type => @panic("TODO"),
+ .union_type => @panic("TODO"),
+ .simple_value => unreachable,
+ .extern_func => unreachable,
+ .int => unreachable,
+ .enum_tag => unreachable, // it's a value, not a type
},
-
- .inferred_alloc_const => unreachable,
- .inferred_alloc_mut => unreachable,
};
}
@@ -4150,159 +4164,161 @@ pub const Type = struct {
/// TODO merge these implementations together with the "advanced" pattern seen
/// elsewhere in this file.
pub fn comptimeOnly(ty: Type, mod: *const Module) bool {
- if (ty.ip_index != .none) return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
- .int_type => false,
- .ptr_type => |ptr_type| {
- const child_ty = ptr_type.elem_type.toType();
- if (child_ty.zigTypeTag(mod) == .Fn) {
- return false;
- } else {
- return child_ty.comptimeOnly(mod);
- }
- },
- .array_type => |array_type| array_type.child.toType().comptimeOnly(mod),
- .vector_type => |vector_type| vector_type.child.toType().comptimeOnly(mod),
- .opt_type => |child| child.toType().comptimeOnly(mod),
- .error_union_type => |error_union_type| error_union_type.payload_type.toType().comptimeOnly(mod),
- .simple_type => |t| switch (t) {
- .f16,
- .f32,
- .f64,
- .f80,
- .f128,
- .usize,
- .isize,
- .c_char,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .anyopaque,
- .bool,
- .void,
- .anyerror,
- .@"anyframe",
- .noreturn,
- .generic_poison,
- .atomic_order,
- .atomic_rmw_op,
- .calling_convention,
- .address_space,
- .float_mode,
- .reduce_op,
- .call_modifier,
- .prefetch_options,
- .export_options,
- .extern_options,
- => false,
+ return switch (ty.ip_index) {
+ .empty_struct_type => false,
- .type,
- .comptime_int,
- .comptime_float,
- .null,
- .undefined,
- .enum_literal,
- .type_info,
- => true,
+ .none => switch (ty.tag()) {
+ .empty_struct,
+ .error_set,
+ .error_set_single,
+ .error_set_inferred,
+ .error_set_merged,
+ .@"opaque",
+ .enum_simple,
+ => false,
- .var_args_param => unreachable,
- },
- .struct_type => @panic("TODO"),
- .union_type => @panic("TODO"),
- .simple_value => unreachable,
- .extern_func => unreachable,
- .int => unreachable,
- .enum_tag => unreachable, // it's a value, not a type
- };
+ // These are function bodies, not function pointers.
+ .function => true,
- return switch (ty.tag()) {
- .empty_struct_literal,
- .empty_struct,
- .error_set,
- .error_set_single,
- .error_set_inferred,
- .error_set_merged,
- .@"opaque",
- .enum_simple,
- => false,
+ .inferred_alloc_mut => unreachable,
+ .inferred_alloc_const => unreachable,
- // These are function bodies, not function pointers.
- .function => true,
+ .array,
+ .array_sentinel,
+ => return ty.childType(mod).comptimeOnly(mod),
- .inferred_alloc_mut => unreachable,
- .inferred_alloc_const => unreachable,
+ .pointer => {
+ const child_ty = ty.childType(mod);
+ if (child_ty.zigTypeTag(mod) == .Fn) {
+ return false;
+ } else {
+ return child_ty.comptimeOnly(mod);
+ }
+ },
- .array,
- .array_sentinel,
- => return ty.childType(mod).comptimeOnly(mod),
+ .optional => {
+ return ty.optionalChild(mod).comptimeOnly(mod);
+ },
- .pointer => {
- const child_ty = ty.childType(mod);
- if (child_ty.zigTypeTag(mod) == .Fn) {
+ .tuple, .anon_struct => {
+ const tuple = ty.tupleFields();
+ for (tuple.types, 0..) |field_ty, i| {
+ const have_comptime_val = tuple.values[i].ip_index != .unreachable_value;
+ if (!have_comptime_val and field_ty.comptimeOnly(mod)) return true;
+ }
return false;
- } else {
- return child_ty.comptimeOnly(mod);
- }
- },
+ },
- .optional => {
- return ty.optionalChild(mod).comptimeOnly(mod);
- },
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ switch (struct_obj.requires_comptime) {
+ .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,
+ }
+ },
- .tuple, .anon_struct => {
- const tuple = ty.tupleFields();
- for (tuple.types, 0..) |field_ty, i| {
- const have_comptime_val = tuple.values[i].ip_index != .unreachable_value;
- if (!have_comptime_val and field_ty.comptimeOnly(mod)) return true;
- }
- return false;
- },
+ .@"union", .union_safety_tagged, .union_tagged => {
+ const union_obj = ty.cast(Type.Payload.Union).?.data;
+ switch (union_obj.requires_comptime) {
+ .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,
+ }
+ },
- .@"struct" => {
- const struct_obj = ty.castTag(.@"struct").?.data;
- switch (struct_obj.requires_comptime) {
- .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,
- }
+ .error_union => return ty.errorUnionPayload().comptimeOnly(mod),
+ .anyframe_T => {
+ const child_ty = ty.castTag(.anyframe_T).?.data;
+ return child_ty.comptimeOnly(mod);
+ },
+ .enum_numbered => {
+ const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty;
+ return tag_ty.comptimeOnly(mod);
+ },
+ .enum_full, .enum_nonexhaustive => {
+ const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty;
+ return tag_ty.comptimeOnly(mod);
+ },
},
-
- .@"union", .union_safety_tagged, .union_tagged => {
- const union_obj = ty.cast(Type.Payload.Union).?.data;
- switch (union_obj.requires_comptime) {
- .wip, .unknown => {
- // Return false to avoid incorrect dependency loops.
- // This will be handled correctly once merged with
- // `Sema.typeRequiresComptime`.
+ else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+ .int_type => false,
+ .ptr_type => |ptr_type| {
+ const child_ty = ptr_type.elem_type.toType();
+ if (child_ty.zigTypeTag(mod) == .Fn) {
return false;
- },
- .no => return false,
- .yes => return true,
- }
- },
+ } else {
+ return child_ty.comptimeOnly(mod);
+ }
+ },
+ .array_type => |array_type| array_type.child.toType().comptimeOnly(mod),
+ .vector_type => |vector_type| vector_type.child.toType().comptimeOnly(mod),
+ .opt_type => |child| child.toType().comptimeOnly(mod),
+ .error_union_type => |error_union_type| error_union_type.payload_type.toType().comptimeOnly(mod),
+ .simple_type => |t| switch (t) {
+ .f16,
+ .f32,
+ .f64,
+ .f80,
+ .f128,
+ .usize,
+ .isize,
+ .c_char,
+ .c_short,
+ .c_ushort,
+ .c_int,
+ .c_uint,
+ .c_long,
+ .c_ulong,
+ .c_longlong,
+ .c_ulonglong,
+ .c_longdouble,
+ .anyopaque,
+ .bool,
+ .void,
+ .anyerror,
+ .@"anyframe",
+ .noreturn,
+ .generic_poison,
+ .atomic_order,
+ .atomic_rmw_op,
+ .calling_convention,
+ .address_space,
+ .float_mode,
+ .reduce_op,
+ .call_modifier,
+ .prefetch_options,
+ .export_options,
+ .extern_options,
+ => false,
- .error_union => return ty.errorUnionPayload().comptimeOnly(mod),
- .anyframe_T => {
- const child_ty = ty.castTag(.anyframe_T).?.data;
- return child_ty.comptimeOnly(mod);
- },
- .enum_numbered => {
- const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty;
- return tag_ty.comptimeOnly(mod);
- },
- .enum_full, .enum_nonexhaustive => {
- const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty;
- return tag_ty.comptimeOnly(mod);
+ .type,
+ .comptime_int,
+ .comptime_float,
+ .null,
+ .undefined,
+ .enum_literal,
+ .type_info,
+ => true,
+
+ .var_args_param => unreachable,
+ },
+ .struct_type => @panic("TODO"),
+ .union_type => @panic("TODO"),
+ .simple_value => unreachable,
+ .extern_func => unreachable,
+ .int => unreachable,
+ .enum_tag => unreachable, // it's a value, not a type
},
};
}
@@ -4575,15 +4591,19 @@ pub const Type = struct {
}
pub fn structFields(ty: Type) Module.Struct.Fields {
- switch (ty.tag()) {
- .empty_struct, .empty_struct_literal => return .{},
- .@"struct" => {
- const struct_obj = ty.castTag(.@"struct").?.data;
- assert(struct_obj.haveFieldTypes());
- return struct_obj.fields;
+ return switch (ty.ip_index) {
+ .empty_struct_type => .{},
+ .none => switch (ty.tag()) {
+ .empty_struct => .{},
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ assert(struct_obj.haveFieldTypes());
+ return struct_obj.fields;
+ },
+ else => unreachable,
},
else => unreachable,
- }
+ };
}
pub fn structFieldName(ty: Type, field_index: usize) []const u8 {
@@ -4599,17 +4619,21 @@ pub const Type = struct {
}
pub fn structFieldCount(ty: Type) usize {
- switch (ty.tag()) {
- .@"struct" => {
- const struct_obj = ty.castTag(.@"struct").?.data;
- assert(struct_obj.haveFieldTypes());
- return struct_obj.fields.count();
+ return switch (ty.ip_index) {
+ .empty_struct_type => 0,
+ .none => switch (ty.tag()) {
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ assert(struct_obj.haveFieldTypes());
+ return struct_obj.fields.count();
+ },
+ .empty_struct => 0,
+ .tuple => ty.castTag(.tuple).?.data.types.len,
+ .anon_struct => ty.castTag(.anon_struct).?.data.types.len,
+ else => unreachable,
},
- .empty_struct, .empty_struct_literal => return 0,
- .tuple => return ty.castTag(.tuple).?.data.types.len,
- .anon_struct => return ty.castTag(.anon_struct).?.data.types.len,
else => unreachable,
- }
+ };
}
/// Supports structs and unions.
@@ -4927,10 +4951,6 @@ pub const Type = struct {
return ty.ip_index == .generic_poison_type;
}
- pub fn isVarArgsParam(ty: Type) bool {
- return ty.ip_index == .none and ty.tag() == .var_args_param;
- }
-
/// This enum does not directly correspond to `std.builtin.TypeId` because
/// it has extra enum tags in it, as a way of using less memory. For example,
/// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types
@@ -4938,8 +4958,6 @@ pub const Type = struct {
/// with different enum tags, because the the former requires more payload data than the latter.
/// See `zigTypeTag` for the function that corresponds to `std.builtin.TypeId`.
pub const Tag = enum(usize) {
- /// Same as `empty_struct` except it has an empty namespace.
- empty_struct_literal,
/// This is a special value that tracks a set of types that have been stored
/// to an inferred allocation. It does not support most of the normal type queries.
/// However it does respond to `isConstPtr`, `ptrSize`, `zigTypeTag`, etc.
@@ -4982,7 +5000,6 @@ pub const Type = struct {
return switch (t) {
.inferred_alloc_const,
.inferred_alloc_mut,
- .empty_struct_literal,
=> @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
.optional,
@@ -5038,33 +5055,36 @@ pub const Type = struct {
pub fn isTuple(ty: Type) bool {
return switch (ty.ip_index) {
+ .empty_struct_type => true,
.none => switch (ty.tag()) {
- .tuple, .empty_struct_literal => true,
+ .tuple => true,
.@"struct" => ty.castTag(.@"struct").?.data.is_tuple,
else => false,
},
- else => false, // TODO
+ else => false, // TODO struct
};
}
pub fn isAnonStruct(ty: Type) bool {
return switch (ty.ip_index) {
+ .empty_struct_type => true,
.none => switch (ty.tag()) {
- .anon_struct, .empty_struct_literal => true,
+ .anon_struct => true,
else => false,
},
- else => false, // TODO
+ else => false, // TODO struct
};
}
pub fn isTupleOrAnonStruct(ty: Type) bool {
return switch (ty.ip_index) {
+ .empty_struct_type => true,
.none => switch (ty.tag()) {
- .tuple, .empty_struct_literal, .anon_struct => true,
+ .tuple, .anon_struct => true,
.@"struct" => ty.castTag(.@"struct").?.data.is_tuple,
else => false,
},
- else => false, // TODO
+ else => false, // TODO struct
};
}
@@ -5072,7 +5092,7 @@ pub const Type = struct {
return switch (ty.ip_index) {
.empty_struct => true,
.none => switch (ty.tag()) {
- .tuple, .empty_struct_literal => true,
+ .tuple => true,
else => false,
},
else => false, // TODO
@@ -5083,7 +5103,7 @@ pub const Type = struct {
return switch (ty.ip_index) {
.empty_struct => true,
.none => switch (ty.tag()) {
- .tuple, .empty_struct_literal, .anon_struct => true,
+ .tuple, .anon_struct => true,
else => false,
},
else => false,
@@ -5100,7 +5120,6 @@ pub const Type = struct {
.types = ty.castTag(.anon_struct).?.data.types,
.values = ty.castTag(.anon_struct).?.data.values,
},
- .empty_struct_literal => .{ .types = &.{}, .values = &.{} },
else => unreachable,
},
else => unreachable,
@@ -5387,6 +5406,7 @@ pub const Type = struct {
.ip_index = .const_slice_u8_sentinel_0_type,
.legacy = undefined,
};
+ pub const empty_struct_literal: Type = .{ .ip_index = .empty_struct_type, .legacy = undefined };
pub const generic_poison: Type = .{ .ip_index = .generic_poison_type, .legacy = undefined };
src/TypedValue.zig
@@ -80,67 +80,9 @@ pub fn print(
return writer.writeAll("(variable)");
while (true) switch (val.ip_index) {
+ .empty_struct => return printAggregate(ty, val, writer, level, mod),
.none => switch (val.tag()) {
- .empty_struct_value, .aggregate => {
- if (level == 0) {
- return writer.writeAll(".{ ... }");
- }
- if (ty.zigTypeTag(mod) == .Struct) {
- try writer.writeAll(".{");
- const max_len = std.math.min(ty.structFieldCount(), max_aggregate_items);
-
- var i: u32 = 0;
- while (i < max_len) : (i += 1) {
- if (i != 0) try writer.writeAll(", ");
- switch (ty.tag()) {
- .anon_struct, .@"struct" => try writer.print(".{s} = ", .{ty.structFieldName(i)}),
- else => {},
- }
- try print(.{
- .ty = ty.structFieldType(i),
- .val = try val.fieldValue(ty, mod, i),
- }, writer, level - 1, mod);
- }
- if (ty.structFieldCount() > max_aggregate_items) {
- try writer.writeAll(", ...");
- }
- return writer.writeAll("}");
- } else {
- const elem_ty = ty.elemType2(mod);
- const len = ty.arrayLen(mod);
-
- if (elem_ty.eql(Type.u8, mod)) str: {
- const max_len = @intCast(usize, std.math.min(len, max_string_len));
- var buf: [max_string_len]u8 = undefined;
-
- var i: u32 = 0;
- while (i < max_len) : (i += 1) {
- const elem = try val.fieldValue(ty, mod, i);
- if (elem.isUndef()) break :str;
- buf[i] = std.math.cast(u8, elem.toUnsignedInt(mod)) orelse break :str;
- }
-
- const truncated = if (len > max_string_len) " (truncated)" else "";
- return writer.print("\"{}{s}\"", .{ std.zig.fmtEscapes(buf[0..max_len]), truncated });
- }
-
- try writer.writeAll(".{ ");
-
- const max_len = std.math.min(len, max_aggregate_items);
- var i: u32 = 0;
- while (i < max_len) : (i += 1) {
- if (i != 0) try writer.writeAll(", ");
- try print(.{
- .ty = elem_ty,
- .val = try val.fieldValue(ty, mod, i),
- }, writer, level - 1, mod);
- }
- if (len > max_aggregate_items) {
- try writer.writeAll(", ...");
- }
- return writer.writeAll(" }");
- }
- },
+ .aggregate => return printAggregate(ty, val, writer, level, mod),
.@"union" => {
if (level == 0) {
return writer.writeAll(".{ ... }");
@@ -426,3 +368,70 @@ pub fn print(
},
};
}
+
+fn printAggregate(
+ ty: Type,
+ val: Value,
+ writer: anytype,
+ level: u8,
+ mod: *Module,
+) (@TypeOf(writer).Error || Allocator.Error)!void {
+ if (level == 0) {
+ return writer.writeAll(".{ ... }");
+ }
+ if (ty.zigTypeTag(mod) == .Struct) {
+ try writer.writeAll(".{");
+ const max_len = std.math.min(ty.structFieldCount(), max_aggregate_items);
+
+ var i: u32 = 0;
+ while (i < max_len) : (i += 1) {
+ if (i != 0) try writer.writeAll(", ");
+ switch (ty.tag()) {
+ .anon_struct, .@"struct" => try writer.print(".{s} = ", .{ty.structFieldName(i)}),
+ else => {},
+ }
+ try print(.{
+ .ty = ty.structFieldType(i),
+ .val = try val.fieldValue(ty, mod, i),
+ }, writer, level - 1, mod);
+ }
+ if (ty.structFieldCount() > max_aggregate_items) {
+ try writer.writeAll(", ...");
+ }
+ return writer.writeAll("}");
+ } else {
+ const elem_ty = ty.elemType2(mod);
+ const len = ty.arrayLen(mod);
+
+ if (elem_ty.eql(Type.u8, mod)) str: {
+ const max_len = @intCast(usize, std.math.min(len, max_string_len));
+ var buf: [max_string_len]u8 = undefined;
+
+ var i: u32 = 0;
+ while (i < max_len) : (i += 1) {
+ const elem = try val.fieldValue(ty, mod, i);
+ if (elem.isUndef()) break :str;
+ buf[i] = std.math.cast(u8, elem.toUnsignedInt(mod)) orelse break :str;
+ }
+
+ const truncated = if (len > max_string_len) " (truncated)" else "";
+ return writer.print("\"{}{s}\"", .{ std.zig.fmtEscapes(buf[0..max_len]), truncated });
+ }
+
+ try writer.writeAll(".{ ");
+
+ const max_len = std.math.min(len, max_aggregate_items);
+ var i: u32 = 0;
+ while (i < max_len) : (i += 1) {
+ if (i != 0) try writer.writeAll(", ");
+ try print(.{
+ .ty = elem_ty,
+ .val = try val.fieldValue(ty, mod, i),
+ }, writer, level - 1, mod);
+ }
+ if (len > max_aggregate_items) {
+ try writer.writeAll(", ...");
+ }
+ return writer.writeAll(" }");
+ }
+}
src/value.zig
@@ -36,7 +36,6 @@ pub const Value = struct {
/// The only possible value for a particular type, which is stored externally.
the_only_possible_value,
- empty_struct_value,
empty_array, // See last_no_payload_tag below.
// After this, the tag requires a payload.
@@ -124,7 +123,6 @@ pub const Value = struct {
pub fn Type(comptime t: Tag) type {
return switch (t) {
.the_only_possible_value,
- .empty_struct_value,
.empty_array,
=> @compileError("Value Tag " ++ @tagName(t) ++ " has no payload"),
@@ -269,7 +267,6 @@ pub const Value = struct {
} else switch (self.legacy.ptr_otherwise.tag) {
.the_only_possible_value,
.empty_array,
- .empty_struct_value,
=> unreachable,
.ty, .lazy_align, .lazy_size => {
@@ -488,7 +485,6 @@ pub const Value = struct {
}
var val = start_val;
while (true) switch (val.tag()) {
- .empty_struct_value => return out_stream.writeAll("struct {}{}"),
.aggregate => {
return out_stream.writeAll("(aggregate)");
},
@@ -1914,7 +1910,7 @@ pub const Value = struct {
const a_tag = a.tag();
const b_tag = b.tag();
if (a_tag == b_tag) switch (a_tag) {
- .the_only_possible_value, .empty_struct_value => return true,
+ .the_only_possible_value => return true,
.enum_literal => {
const a_name = a.castTag(.enum_literal).?.data;
const b_name = b.castTag(.enum_literal).?.data;
@@ -2106,7 +2102,6 @@ pub const Value = struct {
},
.Struct => {
// A struct can be represented with one of:
- // .empty_struct_value,
// .the_one_possible_value,
// .aggregate,
// Note that we already checked above for matching tags, e.g. both .aggregate.
@@ -2254,7 +2249,6 @@ pub const Value = struct {
},
.Struct => {
switch (val.tag()) {
- .empty_struct_value => {},
.aggregate => {
const field_values = val.castTag(.aggregate).?.data;
for (field_values, 0..) |field_val, i| {
@@ -2587,7 +2581,6 @@ pub const Value = struct {
.none => switch (val.tag()) {
// This is the case of accessing an element of an undef array.
.empty_array => unreachable, // out of bounds array index
- .empty_struct_value => unreachable, // out of bounds array index
.empty_array_sentinel => {
assert(index == 0); // The only valid index for an empty array with sentinel.
@@ -2749,6 +2742,17 @@ pub const Value = struct {
pub fn fieldValue(val: Value, ty: Type, mod: *Module, index: usize) !Value {
switch (val.ip_index) {
.undef => return Value.undef,
+ .empty_struct => {
+ if (ty.isSimpleTupleOrAnonStruct()) {
+ const tuple = ty.tupleFields();
+ return tuple.values[index];
+ }
+ if (try ty.structFieldValueComptime(mod, index)) |some| {
+ return some;
+ }
+ unreachable;
+ },
+
.none => switch (val.tag()) {
.aggregate => {
const field_values = val.castTag(.aggregate).?.data;
@@ -2762,17 +2766,6 @@ pub const Value = struct {
.the_only_possible_value => return (try ty.onePossibleValue(mod)).?,
- .empty_struct_value => {
- if (ty.isSimpleTupleOrAnonStruct()) {
- const tuple = ty.tupleFields();
- return tuple.values[index];
- }
- if (try ty.structFieldValueComptime(mod, index)) |some| {
- return some;
- }
- unreachable;
- },
-
else => unreachable,
},
else => unreachable,
@@ -5189,6 +5182,7 @@ pub const Value = struct {
pub const generic_poison: Value = .{ .ip_index = .generic_poison, .legacy = undefined };
pub const generic_poison_type: Value = .{ .ip_index = .generic_poison_type, .legacy = undefined };
+ pub const empty_struct: Value = .{ .ip_index = .empty_struct, .legacy = undefined };
pub fn makeBool(x: bool) Value {
return if (x) Value.true else Value.false;