Merge pull request #12723 from Vexu/stage2-fixes

Stage2 fixes
This commit is contained in:
Veikka Tuominen
2022-09-03 15:48:28 +03:00
committed by GitHub
33 changed files with 531 additions and 228 deletions

View File

@@ -5127,7 +5127,7 @@ fn tryExpr(
else => .none,
};
// This could be a pointer or value depending on the `rl` parameter.
const operand = try expr(parent_gz, scope, operand_rl, operand_node);
const operand = try reachableExpr(parent_gz, scope, operand_rl, operand_node, node);
const is_inline = parent_gz.force_comptime;
const is_inline_bit = @as(u2, @boolToInt(is_inline));
const is_ptr_bit = @as(u2, @boolToInt(operand_rl == .ref)) << 1;

View File

@@ -4635,7 +4635,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
decl.analysis = .complete;
decl.generation = mod.generation;
const has_runtime_bits = try sema.fnHasRuntimeBits(&block_scope, ty_src, decl.ty);
const has_runtime_bits = try sema.fnHasRuntimeBits(decl.ty);
if (has_runtime_bits) {
// We don't fully codegen the decl until later, but we do need to reserve a global

View File

@@ -2565,7 +2565,7 @@ fn zirEnumDecl(
}
}
if (small.nonexhaustive) {
if (small.nonexhaustive and enum_obj.tag_ty.zigTypeTag() != .ComptimeInt) {
if (fields_len > 1 and std.math.log2_int(u64, fields_len) == enum_obj.tag_ty.bitSize(sema.mod.getTarget())) {
return sema.fail(block, src, "non-exhaustive enum specifies every value", .{});
}
@@ -2586,6 +2586,7 @@ fn zirEnumDecl(
var cur_bit_bag: u32 = undefined;
var field_i: u32 = 0;
var last_tag_val: ?Value = null;
var tag_val_buf: Value.Payload.U64 = undefined;
while (field_i < fields_len) : (field_i += 1) {
if (field_i % 32 == 0) {
cur_bit_bag = sema.code.extra[bit_bag_index];
@@ -2641,6 +2642,21 @@ fn zirEnumDecl(
.ty = enum_obj.tag_ty,
.mod = mod,
});
} else {
tag_val_buf = .{
.base = .{ .tag = .int_u64 },
.data = field_i,
};
last_tag_val = Value.initPayload(&tag_val_buf.base);
}
if (!(try sema.intFitsInType(block, src, last_tag_val.?, enum_obj.tag_ty, null))) {
const tree = try sema.getAstTree(block);
const field_src = enumFieldSrcLoc(sema.mod.declPtr(block.src_decl), tree.*, src.node_offset.x, field_i);
const msg = try sema.errMsg(block, field_src, "enumeration value '{}' too large for type '{}'", .{
last_tag_val.?.fmtValue(enum_obj.tag_ty, mod), enum_obj.tag_ty.fmt(mod),
});
return sema.failWithOwnedErrorMsg(msg);
}
}
return decl_val;
@@ -2849,7 +2865,7 @@ fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
const inst_data = sema.code.instructions.items(.data)[inst].node;
const src = LazySrcLoc.nodeOffset(inst_data);
if (block.is_comptime or try sema.typeRequiresComptime(block, src, sema.fn_ret_ty)) {
if (block.is_comptime or try sema.typeRequiresComptime(sema.fn_ret_ty)) {
const fn_ret_ty = try sema.resolveTypeFields(block, src, sema.fn_ret_ty);
return sema.analyzeComptimeAlloc(block, fn_ret_ty, 0, src);
}
@@ -5040,7 +5056,7 @@ pub fn analyzeExport(
try mod.ensureDeclAnalyzed(exported_decl_index);
const exported_decl = mod.declPtr(exported_decl_index);
if (!sema.validateExternType(exported_decl.ty, .other)) {
if (!try sema.validateExternType(block, src, exported_decl.ty, .other)) {
const msg = msg: {
const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(sema.mod)});
errdefer msg.destroy(sema.gpa);
@@ -5569,7 +5585,11 @@ fn zirCall(
const param_ty_inst = try sema.addType(param_ty);
try sema.inst_map.put(sema.gpa, inst, param_ty_inst);
resolved_args[arg_index] = try sema.resolveBody(block, args_body[arg_start..arg_end], inst);
const resolved = try sema.resolveBody(block, args_body[arg_start..arg_end], inst);
if (sema.typeOf(resolved).zigTypeTag() == .NoReturn) {
return resolved;
}
resolved_args[arg_index] = resolved;
}
return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src);
@@ -5768,7 +5788,7 @@ fn analyzeCall(
var is_comptime_call = block.is_comptime or modifier == .compile_time;
var comptime_only_ret_ty = false;
if (!is_comptime_call) {
if (sema.typeRequiresComptime(block, func_src, func_ty_info.return_type)) |ct| {
if (sema.typeRequiresComptime(func_ty_info.return_type)) |ct| {
is_comptime_call = ct;
comptime_only_ret_ty = ct;
} else |err| switch (err) {
@@ -6047,7 +6067,7 @@ fn analyzeCall(
break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges);
};
if (!is_comptime_call) {
if (!is_comptime_call and sema.typeOf(result).zigTypeTag() != .NoReturn) {
try sema.emitDbgInline(
block,
module_fn,
@@ -6206,7 +6226,7 @@ fn analyzeInlineCallArg(
const param_ty = try sema.analyzeAsType(param_block, param_src, param_ty_inst);
new_fn_info.param_types[arg_i.*] = param_ty;
const uncasted_arg = uncasted_args[arg_i.*];
if (try sema.typeRequiresComptime(arg_block, arg_src, param_ty)) {
if (try sema.typeRequiresComptime(param_ty)) {
_ = sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "argument to parameter with comptime only type must be comptime known") catch |err| {
if (err == error.AnalysisFail and sema.err != null) {
try sema.addComptimeReturnTypeNote(arg_block, func, func_src, ret_ty, sema.err.?, comptime_only_ret_ty);
@@ -6308,7 +6328,7 @@ fn analyzeGenericCallArg(
) !void {
const is_runtime = comptime_arg.val.tag() == .generic_poison and
comptime_arg.ty.hasRuntimeBits() and
!(try sema.typeRequiresComptime(block, arg_src, comptime_arg.ty));
!(try sema.typeRequiresComptime(comptime_arg.ty));
if (is_runtime) {
const param_ty = new_fn_info.param_types[runtime_i.*];
const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src);
@@ -6573,7 +6593,7 @@ fn instantiateGenericCall(
}
} else if (is_anytype) {
const arg_ty = sema.typeOf(arg);
if (try sema.typeRequiresComptime(block, .unneeded, arg_ty)) {
if (try sema.typeRequiresComptime(arg_ty)) {
const arg_val = try sema.resolveConstValue(block, .unneeded, arg, undefined);
const child_arg = try child_sema.addConstant(arg_ty, arg_val);
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
@@ -6626,7 +6646,7 @@ fn instantiateGenericCall(
const arg = child_sema.inst_map.get(inst).?;
const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator);
if (try sema.typeRequiresComptime(block, .unneeded, copied_arg_ty)) {
if (try sema.typeRequiresComptime(copied_arg_ty)) {
is_comptime = true;
}
@@ -6657,7 +6677,7 @@ fn instantiateGenericCall(
// If the call evaluated to a return type that requires comptime, never mind
// our generic instantiation. Instead we need to perform a comptime call.
const new_fn_info = new_decl.ty.fnInfo();
if (try sema.typeRequiresComptime(block, call_src, new_fn_info.return_type)) {
if (try sema.typeRequiresComptime(new_fn_info.return_type)) {
return error.ComptimeReturn;
}
// Similarly, if the call evaluated to a generic type we need to instead
@@ -7838,7 +7858,7 @@ fn funcCommon(
}
var ret_ty_requires_comptime = false;
const ret_poison = if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| rp: {
const ret_poison = if (sema.typeRequiresComptime(bare_return_type)) |ret_comptime| rp: {
ret_ty_requires_comptime = ret_comptime;
break :rp bare_return_type.tag() == .generic_poison;
} else |err| switch (err) {
@@ -7876,7 +7896,7 @@ fn funcCommon(
};
return sema.failWithOwnedErrorMsg(msg);
}
if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !sema.validateExternType(return_type, .ret_ty)) {
if (!Type.fnCallingConventionAllowsZigTypes(cc_workaround) and !try sema.validateExternType(block, ret_ty_src, return_type, .ret_ty)) {
const msg = msg: {
const msg = try sema.errMsg(block, ret_ty_src, "return type '{}' not allowed in function with calling convention '{s}'", .{
return_type.fmt(sema.mod), @tagName(cc_workaround),
@@ -8072,7 +8092,7 @@ fn analyzeParameter(
cc: std.builtin.CallingConvention,
has_body: bool,
) !void {
const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty);
const requires_comptime = try sema.typeRequiresComptime(param.ty);
comptime_params[i] = param.is_comptime or requires_comptime;
const this_generic = param.ty.tag() == .generic_poison;
is_generic.* = is_generic.* or this_generic;
@@ -8095,7 +8115,7 @@ fn analyzeParameter(
};
return sema.failWithOwnedErrorMsg(msg);
}
if (!Type.fnCallingConventionAllowsZigTypes(cc) and !sema.validateExternType(param.ty, .param_ty)) {
if (!Type.fnCallingConventionAllowsZigTypes(cc) and !try sema.validateExternType(block, param_src, param.ty, .param_ty)) {
const msg = msg: {
const msg = try sema.errMsg(block, param_src, "parameter of type '{}' not allowed in function with calling convention '{s}'", .{
param.ty.fmt(sema.mod), @tagName(cc),
@@ -8177,7 +8197,7 @@ fn zirParam(
}
};
const is_comptime = comptime_syntax or
try sema.typeRequiresComptime(block, src, param_ty);
try sema.typeRequiresComptime(param_ty);
if (sema.inst_map.get(inst)) |arg| {
if (is_comptime) {
// We have a comptime value for this parameter so it should be elided from the
@@ -8237,7 +8257,7 @@ fn zirParamAnytype(
if (sema.inst_map.get(inst)) |air_ref| {
const param_ty = sema.typeOf(air_ref);
if (comptime_syntax or try sema.typeRequiresComptime(block, src, param_ty)) {
if (comptime_syntax or try sema.typeRequiresComptime(param_ty)) {
// We have a comptime value for this parameter so it should be elided from the
// function type of the function instruction in this block.
return;
@@ -15565,7 +15585,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
} else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) {
return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});
} else if (inst_data.size == .C) {
if (!sema.validateExternType(elem_ty, .other)) {
if (!try sema.validateExternType(block, elem_ty_src, elem_ty, .other)) {
const msg = msg: {
const msg = try sema.errMsg(block, elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
errdefer msg.destroy(sema.gpa);
@@ -16663,7 +16683,7 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
} else if (ptr_size == .Many and elem_ty.zigTypeTag() == .Opaque) {
return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{});
} else if (ptr_size == .C) {
if (!sema.validateExternType(elem_ty, .other)) {
if (!try sema.validateExternType(block, src, elem_ty, .other)) {
const msg = msg: {
const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
errdefer msg.destroy(sema.gpa);
@@ -17501,7 +17521,7 @@ fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
const target = sema.mod.getTarget();
const result_val = try val.intToFloat(sema.arena, operand_ty, dest_ty, target);
const result_val = try val.intToFloatAdvanced(sema.arena, operand_ty, dest_ty, target, sema.kit(block, operand_src));
return sema.addConstant(dest_ty, result_val);
} else if (dest_ty.zigTypeTag() == .ComptimeFloat) {
return sema.failWithNeededComptime(block, operand_src, "value being casted to 'comptime_float' must be comptime known");
@@ -20345,12 +20365,13 @@ fn validateRunTimeType(
.Int,
.Float,
.ErrorSet,
.Enum,
.Frame,
.AnyFrame,
.Void,
=> return true,
.Enum => return !(try sema.typeRequiresComptime(ty)),
.BoundFn,
.ComptimeFloat,
.ComptimeInt,
@@ -20383,7 +20404,7 @@ fn validateRunTimeType(
.Struct, .Union => {
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
const needs_comptime = try sema.typeRequiresComptime(block, src, resolved_ty);
const needs_comptime = try sema.typeRequiresComptime(resolved_ty);
return !needs_comptime;
},
};
@@ -20491,7 +20512,7 @@ fn explainWhyTypeIsComptimeInner(
.range = .type,
});
if (try sema.typeRequiresComptime(block, src, field.ty)) {
if (try sema.typeRequiresComptime(field.ty)) {
try mod.errNoteNonLazy(field_src_loc, msg, "struct requires comptime because of this field", .{});
try sema.explainWhyTypeIsComptimeInner(block, src, msg, field_src_loc, field.ty, type_set);
}
@@ -20511,7 +20532,7 @@ fn explainWhyTypeIsComptimeInner(
.range = .type,
});
if (try sema.typeRequiresComptime(block, src, field.ty)) {
if (try sema.typeRequiresComptime(field.ty)) {
try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{});
try sema.explainWhyTypeIsComptimeInner(block, src, msg, field_src_loc, field.ty, type_set);
}
@@ -20530,7 +20551,14 @@ const ExternPosition = enum {
/// Returns true if `ty` is allowed in extern types.
/// Does *NOT* require `ty` to be resolved in any way.
fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) bool {
/// Calls `resolveTypeLayout` for packed containers.
fn validateExternType(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
ty: Type,
position: ExternPosition,
) !bool {
switch (ty.zigTypeTag()) {
.Type,
.ComptimeFloat,
@@ -20558,17 +20586,25 @@ fn validateExternType(sema: *Sema, ty: Type, position: ExternPosition) bool {
.Fn => return !Type.fnCallingConventionAllowsZigTypes(ty.fnCallingConvention()),
.Enum => {
var buf: Type.Payload.Bits = undefined;
return sema.validateExternType(ty.intTagType(&buf), position);
return sema.validateExternType(block, src, ty.intTagType(&buf), position);
},
.Struct, .Union => switch (ty.containerLayout()) {
.Extern, .Packed => return true,
else => return false,
.Extern => return true,
.Packed => {
const target = sema.mod.getTarget();
const bit_size = try ty.bitSizeAdvanced(target, sema.kit(block, src));
switch (bit_size) {
8, 16, 32, 64, 128 => return true,
else => return false,
}
},
.Auto => return false,
},
.Array => {
if (position == .ret_ty or position == .param_ty) return false;
return sema.validateExternType(ty.elemType2(), .other);
return sema.validateExternType(block, src, ty.elemType2(), .other);
},
.Vector => return sema.validateExternType(ty.elemType2(), .other),
.Vector => return sema.validateExternType(block, src, ty.elemType2(), .other),
.Optional => return ty.isPtrLikeOptional(),
}
}
@@ -20620,8 +20656,8 @@ fn explainWhyTypeIsNotExtern(
try mod.errNoteNonLazy(src_loc, msg, "enum tag type '{}' is not extern compatible", .{tag_ty.fmt(sema.mod)});
try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position);
},
.Struct => try mod.errNoteNonLazy(src_loc, msg, "only structs with packed or extern layout are extern compatible", .{}),
.Union => try mod.errNoteNonLazy(src_loc, msg, "only unions with packed or extern layout are extern compatible", .{}),
.Struct => try mod.errNoteNonLazy(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}),
.Union => try mod.errNoteNonLazy(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}),
.Array => {
if (position == .ret_ty) {
return mod.errNoteNonLazy(src_loc, msg, "arrays are not allowed as a return type", .{});
@@ -23000,7 +23036,7 @@ fn coerceExtra(
}
break :int;
};
const result_val = try val.intToFloat(sema.arena, inst_ty, dest_ty, target);
const result_val = try val.intToFloatAdvanced(sema.arena, inst_ty, dest_ty, target, sema.kit(block, inst_src));
// TODO implement this compile error
//const int_again_val = try result_val.floatToInt(sema.arena, inst_ty);
//if (!int_again_val.eql(val, inst_ty, mod)) {
@@ -23424,8 +23460,11 @@ const InMemoryCoercionResult = union(enum) {
var index: u6 = 0;
var actual_noalias = false;
while (true) : (index += 1) {
if (param.actual << index != param.wanted << index) {
actual_noalias = (param.actual << index) == (1 << 31);
const actual = @truncate(u1, param.actual >> index);
const wanted = @truncate(u1, param.wanted >> index);
if (actual != wanted) {
actual_noalias = actual == 1;
break;
}
}
if (!actual_noalias) {
@@ -23919,7 +23958,7 @@ fn coerceInMemoryAllowedFns(
if (dest_info.noalias_bits != src_info.noalias_bits) {
return InMemoryCoercionResult{ .fn_param_noalias = .{
.actual = dest_info.noalias_bits,
.actual = src_info.noalias_bits,
.wanted = dest_info.noalias_bits,
} };
}
@@ -24077,16 +24116,40 @@ fn coerceVarArgParam(
inst: Air.Inst.Ref,
inst_src: LazySrcLoc,
) !Air.Inst.Ref {
const inst_ty = sema.typeOf(inst);
if (block.is_typeof) return inst;
switch (inst_ty.zigTypeTag()) {
const coerced = switch (sema.typeOf(inst).zigTypeTag()) {
// TODO consider casting to c_int/f64 if they fit
.ComptimeInt, .ComptimeFloat => return sema.fail(block, inst_src, "integer and float literals in var args function must be casted", .{}),
else => {},
.ComptimeInt, .ComptimeFloat => return sema.fail(
block,
inst_src,
"integer and float literals passed variadic function must be casted to a fixed-size number type",
.{},
),
.Fn => blk: {
const fn_val = try sema.resolveConstValue(block, .unneeded, inst, undefined);
const fn_decl = fn_val.pointerDecl().?;
break :blk try sema.analyzeDeclRef(fn_decl);
},
.Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
else => inst,
};
const coerced_ty = sema.typeOf(coerced);
if (!try sema.validateExternType(block, inst_src, coerced_ty, .other)) {
const msg = msg: {
const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)});
errdefer msg.destroy(sema.gpa);
const src_decl = sema.mod.declPtr(block.src_decl);
try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl), coerced_ty, .other);
try sema.addDeclaredHereNote(msg, coerced_ty);
break :msg msg;
};
return sema.failWithOwnedErrorMsg(msg);
}
// TODO implement more of this function.
return inst;
return coerced;
}
// TODO migrate callsites to use storePtr2 instead.
@@ -27581,7 +27644,7 @@ pub fn resolveTypeLayout(
// In case of querying the ABI alignment of this optional, we will ask
// for hasRuntimeBits() of the payload type, so we need "requires comptime"
// to be known already before this function returns.
_ = try sema.typeRequiresComptime(block, src, payload_ty);
_ = try sema.typeRequiresComptime(payload_ty);
return sema.resolveTypeLayout(block, src, payload_ty);
},
.ErrorUnion => {
@@ -27636,7 +27699,7 @@ fn resolveStructLayout(
// for hasRuntimeBits() of each field, so we need "requires comptime"
// to be known already before this function returns.
for (struct_obj.fields.values()) |field, i| {
_ = sema.typeRequiresComptime(block, src, field.ty) catch |err| switch (err) {
_ = sema.typeRequiresComptime(field.ty) catch |err| switch (err) {
error.AnalysisFail => {
const msg = sema.err orelse return err;
try sema.addFieldErrNote(block, ty, i, msg, "while checking this field", .{});
@@ -27868,7 +27931,7 @@ fn resolveStructFully(
}
// And let's not forget comptime-only status.
_ = try sema.typeRequiresComptime(block, src, ty);
_ = try sema.typeRequiresComptime(ty);
}
fn resolveUnionFully(
@@ -27901,7 +27964,7 @@ fn resolveUnionFully(
}
// And let's not forget comptime-only status.
_ = try sema.typeRequiresComptime(block, src, ty);
_ = try sema.typeRequiresComptime(ty);
}
pub fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type {
@@ -28275,7 +28338,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
};
return sema.failWithOwnedErrorMsg(msg);
}
if (struct_obj.layout == .Extern and !sema.validateExternType(field.ty, .other)) {
if (struct_obj.layout == .Extern and !try sema.validateExternType(&block_scope, src, field.ty, .other)) {
const msg = msg: {
const tree = try sema.getAstTree(&block_scope);
const fields_src = enumFieldSrcLoc(decl, tree.*, 0, i);
@@ -28612,7 +28675,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
};
return sema.failWithOwnedErrorMsg(msg);
}
if (union_obj.layout == .Extern and !sema.validateExternType(field_ty, .union_field)) {
if (union_obj.layout == .Extern and !try sema.validateExternType(&block_scope, src, field_ty, .union_field)) {
const msg = msg: {
const tree = try sema.getAstTree(&block_scope);
const field_src = enumFieldSrcLoc(decl, tree.*, 0, field_i);
@@ -29004,7 +29067,7 @@ pub fn typeHasOnePossibleValue(
},
.enum_nonexhaustive => {
const tag_ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty;
if (!(try sema.typeHasRuntimeBits(block, src, tag_ty))) {
if (tag_ty.zigTypeTag() != .ComptimeInt and !(try sema.typeHasRuntimeBits(block, src, tag_ty))) {
return Value.zero;
} else {
return null;
@@ -29536,7 +29599,7 @@ fn typePtrOrOptionalPtrTy(
/// TODO assert the return value matches `ty.comptimeOnly`
/// TODO merge these implementations together with the "advanced"/sema_kit pattern seen
/// elsewhere in value.zig
pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
return switch (ty.tag()) {
.u1,
.u8,
@@ -29627,7 +29690,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
.array,
.array_sentinel,
.vector,
=> return sema.typeRequiresComptime(block, src, ty.childType()),
=> return sema.typeRequiresComptime(ty.childType()),
.pointer,
.single_const_pointer,
@@ -29643,7 +29706,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
if (child_ty.zigTypeTag() == .Fn) {
return child_ty.fnInfo().is_generic;
} else {
return sema.typeRequiresComptime(block, src, child_ty);
return sema.typeRequiresComptime(child_ty);
}
},
@@ -29652,14 +29715,14 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
.optional_single_const_pointer,
=> {
var buf: Type.Payload.ElemType = undefined;
return sema.typeRequiresComptime(block, src, ty.optionalChild(&buf));
return sema.typeRequiresComptime(ty.optionalChild(&buf));
},
.tuple, .anon_struct => {
const tuple = ty.tupleFields();
for (tuple.types) |field_ty, i| {
const have_comptime_val = tuple.values[i].tag() != .unreachable_value;
if (!have_comptime_val and try sema.typeRequiresComptime(block, src, field_ty)) {
if (!have_comptime_val and try sema.typeRequiresComptime(field_ty)) {
return true;
}
}
@@ -29680,7 +29743,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
struct_obj.requires_comptime = .wip;
for (struct_obj.fields.values()) |field| {
if (field.is_comptime) continue;
if (try sema.typeRequiresComptime(block, src, field.ty)) {
if (try sema.typeRequiresComptime(field.ty)) {
struct_obj.requires_comptime = .yes;
return true;
}
@@ -29704,7 +29767,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
union_obj.requires_comptime = .wip;
for (union_obj.fields.values()) |field| {
if (try sema.typeRequiresComptime(block, src, field.ty)) {
if (try sema.typeRequiresComptime(field.ty)) {
union_obj.requires_comptime = .yes;
return true;
}
@@ -29715,18 +29778,18 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ
}
},
.error_union => return sema.typeRequiresComptime(block, src, ty.errorUnionPayload()),
.error_union => return sema.typeRequiresComptime(ty.errorUnionPayload()),
.anyframe_T => {
const child_ty = ty.castTag(.anyframe_T).?.data;
return sema.typeRequiresComptime(block, src, child_ty);
return sema.typeRequiresComptime(child_ty);
},
.enum_numbered => {
const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty;
return sema.typeRequiresComptime(block, src, 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(block, src, tag_ty);
return sema.typeRequiresComptime(tag_ty);
},
};
}
@@ -29764,7 +29827,7 @@ fn unionFieldAlignment(
}
/// Synchronize logic with `Type.isFnOrHasRuntimeBits`.
pub fn fnHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {
const fn_info = ty.fnInfo();
if (fn_info.is_generic) return false;
if (fn_info.is_var_args) return true;
@@ -29773,7 +29836,7 @@ pub fn fnHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) C
.Inline => return false,
else => {},
}
if (try sema.typeRequiresComptime(block, src, fn_info.return_type)) {
if (try sema.typeRequiresComptime(fn_info.return_type)) {
return false;
}
return true;

View File

@@ -23,6 +23,10 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
if (!ty.hasRuntimeBitsIgnoreComptime()) return none;
switch (ty.zigTypeTag()) {
.Struct => {
if (ty.containerLayout() == .Packed) {
if (ty.bitSize(target) <= 64) return direct;
return .{ .direct, .direct };
}
// When the struct type is non-scalar
if (ty.structFieldCount() > 1) return memory;
// When the struct's alignment is non-natural
@@ -57,6 +61,10 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
return direct;
},
.Union => {
if (ty.containerLayout() == .Packed) {
if (ty.bitSize(target) <= 64) return direct;
return .{ .direct, .direct };
}
const layout = ty.unionGetLayout(target);
std.debug.assert(layout.tag_size == 0);
if (ty.unionFields().count() > 1) return memory;

View File

@@ -5,7 +5,7 @@ const assert = std.debug.assert;
const Register = @import("bits.zig").Register;
const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none };
pub const Class = enum { integer, sse, sseup, x87, x87up, complex_x87, memory, none, win_i128 };
pub fn classifyWindows(ty: Type, target: Target) Class {
// https://docs.microsoft.com/en-gb/cpp/build/x64-calling-convention?view=vs-2017
@@ -34,7 +34,15 @@ pub fn classifyWindows(ty: Type, target: Target) Class {
=> switch (ty.abiSize(target)) {
0 => unreachable,
1, 2, 4, 8 => return .integer,
else => return .memory,
else => switch (ty.zigTypeTag()) {
.Int => return .win_i128,
.Struct, .Union => if (ty.containerLayout() == .Packed) {
return .win_i128;
} else {
return .memory;
},
else => return .memory,
},
},
.Float, .Vector => return .sse,
@@ -174,6 +182,12 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {
// "If the size of the aggregate exceeds a single eightbyte, each is classified
// separately.".
const ty_size = ty.abiSize(target);
if (ty.containerLayout() == .Packed) {
assert(ty_size <= 128);
result[0] = .integer;
if (ty_size > 64) result[1] = .integer;
return result;
}
if (ty_size > 64)
return memory_class;
@@ -284,6 +298,12 @@ pub fn classifySystemV(ty: Type, target: Target) [8]Class {
// "If the size of the aggregate exceeds a single eightbyte, each is classified
// separately.".
const ty_size = ty.abiSize(target);
if (ty.containerLayout() == .Packed) {
assert(ty_size <= 128);
result[0] = .integer;
if (ty_size > 64) result[1] = .integer;
return result;
}
if (ty_size > 64)
return memory_class;

View File

@@ -9051,7 +9051,7 @@ pub const FuncGen = struct {
}
},
},
.Union => return self.unionFieldPtr(inst, struct_ptr, struct_ty, field_index),
.Union => return self.unionFieldPtr(inst, struct_ptr, struct_ty),
else => unreachable,
}
}
@@ -9061,16 +9061,13 @@ pub const FuncGen = struct {
inst: Air.Inst.Index,
union_ptr: *const llvm.Value,
union_ty: Type,
field_index: c_uint,
) !?*const llvm.Value {
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
const field = &union_obj.fields.values()[field_index];
const result_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));
if (!field.ty.hasRuntimeBitsIgnoreComptime()) {
return null;
}
const target = self.dg.module.getTarget();
const layout = union_ty.unionGetLayout(target);
if (layout.payload_size == 0) {
return self.builder.buildBitCast(union_ptr, result_llvm_ty, "");
}
const payload_index = @boolToInt(layout.tag_align >= layout.payload_align);
const union_field_ptr = self.builder.buildStructGEP(union_ptr, payload_index, "");
return self.builder.buildBitCast(union_field_ptr, result_llvm_ty, "");
@@ -9677,22 +9674,7 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.
}
},
.C => {
const is_scalar = switch (fn_info.return_type.zigTypeTag()) {
.Void,
.Bool,
.NoReturn,
.Int,
.Float,
.Pointer,
.Optional,
.ErrorSet,
.Enum,
.AnyFrame,
.Vector,
=> true,
else => false,
};
const is_scalar = isScalar(fn_info.return_type);
switch (target.cpu.arch) {
.mips, .mipsel => return dg.lowerType(fn_info.return_type),
.x86_64 => switch (target.os.tag) {
@@ -9705,6 +9687,7 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.
return dg.context.intType(@intCast(c_uint, abi_size * 8));
}
},
.win_i128 => return dg.context.intType(64).vectorType(2),
.memory => return dg.context.voidType(),
.sse => return dg.lowerType(fn_info.return_type),
else => unreachable,
@@ -9745,6 +9728,7 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.
@panic("TODO");
},
.memory => unreachable, // handled above
.win_i128 => unreachable, // windows only
.none => break,
}
}
@@ -9840,22 +9824,7 @@ const ParamTypeIterator = struct {
@panic("TODO implement async function lowering in the LLVM backend");
},
.C => {
const is_scalar = switch (ty.zigTypeTag()) {
.Void,
.Bool,
.NoReturn,
.Int,
.Float,
.Pointer,
.Optional,
.ErrorSet,
.Enum,
.AnyFrame,
.Vector,
=> true,
else => false,
};
const is_scalar = isScalar(ty);
switch (it.target.cpu.arch) {
.riscv32, .riscv64 => {
it.zig_index += 1;
@@ -9884,6 +9853,11 @@ const ParamTypeIterator = struct {
return .abi_sized_int;
}
},
.win_i128 => {
it.zig_index += 1;
it.llvm_index += 1;
return .byref;
},
.memory => {
it.zig_index += 1;
it.llvm_index += 1;
@@ -9938,6 +9912,7 @@ const ParamTypeIterator = struct {
@panic("TODO");
},
.memory => unreachable, // handled above
.win_i128 => unreachable, // windows only
.none => break,
}
}
@@ -10109,6 +10084,27 @@ fn isByRef(ty: Type) bool {
}
}
fn isScalar(ty: Type) bool {
return switch (ty.zigTypeTag()) {
.Void,
.Bool,
.NoReturn,
.Int,
.Float,
.Pointer,
.Optional,
.ErrorSet,
.Enum,
.AnyFrame,
.Vector,
=> true,
.Struct => ty.containerLayout() == .Packed,
.Union => ty.containerLayout() == .Packed,
else => false,
};
}
/// This function returns true if we expect LLVM to lower x86_fp80 correctly
/// and false if we expect LLVM to crash if it counters an x86_fp80 type.
fn backendSupportsF80(target: std.Target) bool {

View File

@@ -1166,6 +1166,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
});
}
if (!c.zig_is_stage1 and is_packed) {
return failDecl(c, record_loc, bare_name, "cannot translate packed record union", .{});
}
const record_payload = try c.arena.create(ast.Payload.Record);
record_payload.* = .{
.base = .{ .tag = ([2]Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] },

View File

@@ -2042,6 +2042,9 @@ pub const Type = extern union {
try writer.writeAll("fn(");
for (fn_info.param_types) |param_ty, i| {
if (i != 0) try writer.writeAll(", ");
if (std.math.cast(u5, i)) |index| if (@truncate(u1, fn_info.noalias_bits >> index) != 0) {
try writer.writeAll("noalias ");
};
if (param_ty.tag() == .generic_poison) {
try writer.writeAll("anytype");
} else {
@@ -2398,7 +2401,7 @@ pub const Type = extern union {
} else if (ty.childType().zigTypeTag() == .Fn) {
return !ty.childType().fnInfo().is_generic;
} else if (sema_kit) |sk| {
return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty));
return !(try sk.sema.typeRequiresComptime(ty));
} else {
return !comptimeOnly(ty);
}
@@ -2437,7 +2440,7 @@ pub const Type = extern union {
if (ignore_comptime_only) {
return true;
} else if (sema_kit) |sk| {
return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, child_ty));
return !(try sk.sema.typeRequiresComptime(child_ty));
} else {
return !comptimeOnly(child_ty);
}

View File

@@ -2940,17 +2940,24 @@ pub const Value = extern union {
}
pub fn intToFloat(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, target: Target) !Value {
return intToFloatAdvanced(val, arena, int_ty, float_ty, target, null) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => unreachable,
};
}
pub fn intToFloatAdvanced(val: Value, arena: Allocator, int_ty: Type, float_ty: Type, target: Target, sema_kit: ?Module.WipAnalysis) !Value {
if (int_ty.zigTypeTag() == .Vector) {
const result_data = try arena.alloc(Value, int_ty.vectorLen());
for (result_data) |*scalar, i| {
scalar.* = try intToFloatScalar(val.indexVectorlike(i), arena, float_ty.scalarType(), target);
scalar.* = try intToFloatScalar(val.indexVectorlike(i), arena, float_ty.scalarType(), target, sema_kit);
}
return Value.Tag.aggregate.create(arena, result_data);
}
return intToFloatScalar(val, arena, float_ty, target);
return intToFloatScalar(val, arena, float_ty, target, sema_kit);
}
pub fn intToFloatScalar(val: Value, arena: Allocator, float_ty: Type, target: Target) !Value {
pub fn intToFloatScalar(val: Value, arena: Allocator, float_ty: Type, target: Target, sema_kit: ?Module.WipAnalysis) !Value {
switch (val.tag()) {
.undef, .zero, .one => return val,
.the_only_possible_value => return Value.initTag(.zero), // for i0, u0
@@ -2970,6 +2977,22 @@ pub const Value = extern union {
const float = bigIntToFloat(limbs, false);
return floatToValue(float, arena, float_ty, target);
},
.lazy_align => {
const ty = val.castTag(.lazy_align).?.data;
if (sema_kit) |sk| {
return intToFloatInner((try ty.abiAlignmentAdvanced(target, .{ .sema_kit = sk })).scalar, arena, float_ty, target);
} else {
return intToFloatInner(ty.abiAlignment(target), arena, float_ty, target);
}
},
.lazy_size => {
const ty = val.castTag(.lazy_size).?.data;
if (sema_kit) |sk| {
return intToFloatInner((try ty.abiSizeAdvanced(target, .{ .sema_kit = sk })).scalar, arena, float_ty, target);
} else {
return intToFloatInner(ty.abiSize(target), arena, float_ty, target);
}
},
else => unreachable,
}
}

View File

@@ -1175,3 +1175,10 @@ test "Non-exhaustive enum with nonstandard int size behaves correctly" {
const E = enum(u15) { _ };
try expect(@sizeOf(E) == @sizeOf(u15));
}
test "Non-exhaustive enum backed by comptime_int" {
const E = enum(comptime_int) { a, b, c, _ };
comptime var e: E = .a;
e = @intToEnum(E, 378089457309184723749);
try expect(@enumToInt(e) == 378089457309184723749);
}

View File

@@ -579,3 +579,29 @@ test "runtime init of unnamed packed struct type" {
}
}{ .x = z }).m();
}
test "packed struct passed to callconv(.C) function" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
const S = struct {
const Packed = packed struct {
a: u16,
b: bool = true,
c: bool = true,
d: u46 = 0,
};
fn foo(p: Packed, a1: u64, a2: u64, a3: u64, a4: u64, a5: u64) callconv(.C) bool {
return p.a == 12345 and p.b == true and p.c == true and p.d == 0 and a1 == 5 and a2 == 4 and a3 == 3 and a4 == 2 and a5 == 1;
}
};
const result = S.foo(S.Packed{
.a = 12345,
.b = true,
.c = true,
}, 5, 4, 3, 2, 1);
try expect(result);
}

View File

@@ -301,3 +301,14 @@ test "array access of generic param in typeof expression" {
try expect(S.first("a") == 'a');
comptime try expect(S.first("a") == 'a');
}
test "lazy size cast to float" {
{
const S = struct { a: u8 };
try expect(@intToFloat(f32, @sizeOf(S)) == 1.0);
}
{
const S = struct { a: u8 };
try expect(@as(f32, @sizeOf(S)) == 1.0);
}
}

View File

@@ -690,7 +690,7 @@ test "union with only 1 field casted to its enum type which has enum value speci
var e = Expr{ .Literal = Literal{ .Bool = true } };
comptime try expect(Tag(ExprTag) == comptime_int);
var t = @as(ExprTag, e);
comptime var t = @as(ExprTag, e);
try expect(t == Expr.Literal);
try expect(@enumToInt(t) == 33);
comptime try expect(@enumToInt(t) == 33);
@@ -1352,3 +1352,31 @@ test "@unionInit uses tag value instead of field index" {
}
try expect(@enumToInt(u) == 255);
}
test "union field ptr - zero sized payload" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const U = union {
foo: void,
bar: void,
fn bar(_: *void) void {}
};
var u: U = .{ .foo = {} };
U.bar(&u.foo);
}
test "union field ptr - zero sized field" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const U = union {
foo: void,
bar: u32,
fn bar(_: *void) void {}
};
var u: U = .{ .foo = {} };
U.bar(&u.foo);
}

View File

@@ -86,24 +86,8 @@ struct MedStructMixed {
void zig_med_struct_mixed(struct MedStructMixed);
struct MedStructMixed zig_ret_med_struct_mixed();
struct SmallPackedStruct {
uint8_t a: 2;
uint8_t b: 2;
uint8_t c: 2;
uint8_t d: 2;
uint8_t e: 1;
};
struct BigPackedStruct {
uint64_t a: 64;
uint64_t b: 64;
uint64_t c: 64;
uint64_t d: 64;
uint8_t e: 8;
};
//void zig_small_packed_struct(struct SmallPackedStruct); // #1481
void zig_big_packed_struct(struct BigPackedStruct);
void zig_small_packed_struct(uint8_t);
void zig_big_packed_struct(__int128);
struct SplitStructInts {
uint64_t a;
@@ -176,13 +160,19 @@ void run_c_tests(void) {
}
{
struct BigPackedStruct s = {1, 2, 3, 4, 5};
__int128 s = 0;
s |= 1 << 0;
s |= (__int128)2 << 64;
zig_big_packed_struct(s);
}
{
struct SmallPackedStruct s = {0, 1, 2, 3, 1};
//zig_small_packed_struct(s);
uint8_t s = 0;
s |= 0 << 0;
s |= 1 << 2;
s |= 2 << 4;
s |= 3 << 6;
zig_small_packed_struct(s);
}
{
@@ -378,42 +368,32 @@ void c_split_struct_mixed(struct SplitStructMixed x) {
assert_or_panic(y.c == 1337.0f);
}
struct SmallPackedStruct c_ret_small_packed_struct() {
struct SmallPackedStruct s = {
.a = 0,
.b = 1,
.c = 2,
.d = 3,
.e = 1,
};
uint8_t c_ret_small_packed_struct() {
uint8_t s = 0;
s |= 0 << 0;
s |= 1 << 2;
s |= 2 << 4;
s |= 3 << 6;
return s;
}
void c_small_packed_struct(struct SmallPackedStruct x) {
assert_or_panic(x.a == 0);
assert_or_panic(x.a == 1);
assert_or_panic(x.a == 2);
assert_or_panic(x.a == 3);
assert_or_panic(x.e == 1);
void c_small_packed_struct(uint8_t x) {
assert_or_panic(((x >> 0) & 0x3) == 0);
assert_or_panic(((x >> 2) & 0x3) == 1);
assert_or_panic(((x >> 4) & 0x3) == 2);
assert_or_panic(((x >> 6) & 0x3) == 3);
}
struct BigPackedStruct c_ret_big_packed_struct() {
struct BigPackedStruct s = {
.a = 1,
.b = 2,
.c = 3,
.d = 4,
.e = 5,
};
__int128 c_ret_big_packed_struct() {
__int128 s = 0;
s |= 1 << 0;
s |= (__int128)2 << 64;
return s;
}
void c_big_packed_struct(struct BigPackedStruct x) {
assert_or_panic(x.a == 1);
assert_or_panic(x.b == 2);
assert_or_panic(x.c == 3);
assert_or_panic(x.d == 4);
assert_or_panic(x.e == 5);
void c_big_packed_struct(__int128 x) {
assert_or_panic(((x >> 0) & 0xFFFFFFFFFFFFFFFF) == 1);
assert_or_panic(((x >> 64) & 0xFFFFFFFFFFFFFFFF) == 2);
}
struct SplitStructMixed c_ret_split_struct_mixed() {

View File

@@ -263,37 +263,30 @@ const SmallPackedStruct = packed struct {
b: u2,
c: u2,
d: u2,
e: bool,
};
const c_small_packed_struct: fn (SmallPackedStruct) callconv(.C) void = @compileError("TODO: #1481");
extern fn c_small_packed_struct(SmallPackedStruct) void;
extern fn c_ret_small_packed_struct() SmallPackedStruct;
// waiting on #1481
//export fn zig_small_packed_struct(x: SmallPackedStruct) void {
// expect(x.a == 0) catch @panic("test failure");
// expect(x.b == 1) catch @panic("test failure");
// expect(x.c == 2) catch @panic("test failure");
// expect(x.d == 3) catch @panic("test failure");
// expect(x.e) catch @panic("test failure");
//}
export fn zig_small_packed_struct(x: SmallPackedStruct) void {
expect(x.a == 0) catch @panic("test failure");
expect(x.b == 1) catch @panic("test failure");
expect(x.c == 2) catch @panic("test failure");
expect(x.d == 3) catch @panic("test failure");
}
test "C ABI small packed struct" {
var s = SmallPackedStruct{ .a = 0, .b = 1, .c = 2, .d = 3, .e = true };
_ = s; //c_small_packed_struct(s); // waiting on #1481
var s = SmallPackedStruct{ .a = 0, .b = 1, .c = 2, .d = 3 };
c_small_packed_struct(s);
var s2 = c_ret_small_packed_struct();
try expect(s2.a == 0);
try expect(s2.b == 1);
try expect(s2.c == 2);
try expect(s2.d == 3);
try expect(s2.e);
}
const BigPackedStruct = packed struct {
a: u64,
b: u64,
c: u64,
d: u64,
e: u8,
};
extern fn c_big_packed_struct(BigPackedStruct) void;
extern fn c_ret_big_packed_struct() BigPackedStruct;
@@ -301,20 +294,14 @@ extern fn c_ret_big_packed_struct() BigPackedStruct;
export fn zig_big_packed_struct(x: BigPackedStruct) void {
expect(x.a == 1) catch @panic("test failure");
expect(x.b == 2) catch @panic("test failure");
expect(x.c == 3) catch @panic("test failure");
expect(x.d == 4) catch @panic("test failure");
expect(x.e == 5) catch @panic("test failure");
}
test "C ABI big packed struct" {
var s = BigPackedStruct{ .a = 1, .b = 2, .c = 3, .d = 4, .e = 5 };
var s = BigPackedStruct{ .a = 1, .b = 2 };
c_big_packed_struct(s);
var s2 = c_ret_big_packed_struct();
try expect(s2.a == 1);
try expect(s2.b == 2);
try expect(s2.c == 3);
try expect(s2.d == 4);
try expect(s2.e == 5);
}
const SplitStructInt = extern struct {

View File

@@ -10,5 +10,5 @@ export fn a() void {
// target=native
//
// :3:19: error: C pointers cannot point to non-C-ABI-compatible type 'tmp.Foo'
// :3:19: note: only structs with packed or extern layout are extern compatible
// :3:19: note: only extern structs and ABI sized packed structs are extern compatible
// :1:13: note: struct declared here

View File

@@ -0,0 +1,11 @@
pub export fn entry() void {
const E = enum(comptime_int) { a, b, c, _ };
var e: E = .a;
_ = e;
}
// error
// backend=stage2
// target=native
//
// :3:12: error: variable of type 'tmp.entry.E' must be const or comptime

View File

@@ -10,5 +10,5 @@ export fn entry(foo: Foo) void { _ = foo; }
// target=native
//
// :6:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C'
// :6:17: note: only structs with packed or extern layout are extern compatible
// :6:17: note: only extern structs and ABI sized packed structs are extern compatible
// :1:13: note: struct declared here

View File

@@ -10,5 +10,5 @@ export fn entry(foo: Foo) void { _ = foo; }
// target=native
//
// :6:17: error: parameter of type 'tmp.Foo' not allowed in function with calling convention 'C'
// :6:17: note: only unions with packed or extern layout are extern compatible
// :6:17: note: only extern unions and ABI sized packed unions are extern compatible
// :1:13: note: union declared here

View File

@@ -1,11 +0,0 @@
extern fn printf([*:0]const u8, ...) c_int;
pub export fn entry() void {
_ = printf("%d %d %d %d\n", 1, 2, 3, 4);
}
// error
// backend=stage2
// target=native
//
// :4:33: error: integer and float literals in var args function must be casted

View File

@@ -0,0 +1,20 @@
pub export fn entry() void {
comptime var x: fn (noalias *i32, noalias *i32) void = undefined;
x = bar;
}
pub export fn entry1() void {
comptime var x: fn (*i32, *i32) void = undefined;
x = foo;
}
fn foo(noalias _: *i32, noalias _: *i32) void {}
fn bar(noalias _: *i32, _: *i32) void {}
// error
// backend=stage2
// target=native
//
// :3:9: error: expected type 'fn(noalias *i32, noalias *i32) void', found 'fn(noalias *i32, *i32) void'
// :3:9: note: regular parameter 1 cannot cast into a noalias parameter
// :7:9: error: expected type 'fn(*i32, *i32) void', found 'fn(noalias *i32, noalias *i32) void'
// :7:9: note: noalias parameter 0 cannot cast into a regular parameter

View File

@@ -2,13 +2,13 @@ const Moo = enum(u8) {
Last = 255,
Over,
};
pub fn main() void {
pub export fn entry() void {
var y = Moo.Last;
_ = y;
}
// error
// backend=stage1
// backend=stage2
// target=native
//
// tmp.zig:3:5: error: enumeration value 256 too large for type 'u8'
// :3:5: error: enumeration value '256' too large for type 'u8'

View File

@@ -12,7 +12,7 @@ export fn entry() void {
}
// error
// backend=stage1
// backend=stage2
// target=native
//
// tmp.zig:6:5: error: enumeration value 4 too large for type 'u2'
// :6:5: error: enumeration value '4' too large for type 'u2'

View File

@@ -0,0 +1,11 @@
pub fn foo() !void {
try return bar();
}
pub fn bar() !void {}
// error
// backend=stage2
// target=native
//
// :2:5: error: unreachable code
// :2:9: note: control flow is diverted here

View File

@@ -0,0 +1,29 @@
extern fn printf([*:0]const u8, ...) c_int;
pub export fn entry() void {
_ = printf("%d %d %d %d\n", 1, 2, 3, 4);
}
pub export fn entry1() void {
var arr: [2]u8 = undefined;
_ = printf("%d\n", arr);
}
pub export fn entry2() void {
_ = printf("%d\n", @as(u48, 2));
}
pub export fn entry3() void {
_ = printf("%d\n", {});
}
// error
// backend=stage2
// target=native
//
// :4:33: error: integer and float literals passed variadic function must be casted to a fixed-size number type
// :9:24: error: arrays must be passed by reference to variadic function
// :13:24: error: cannot pass 'u48' to variadic function
// :13:24: note: only integers with power of two bits are extern compatible
// :17:24: error: cannot pass 'void' to variadic function
// :17:24: note: 'void' is a zero bit type; for C 'void' use 'anyopaque'

View File

@@ -250,18 +250,20 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
\\}
, "");
cases.add("struct initializer - packed",
\\#define _NO_CRT_STDIO_INLINE 1
\\#include <stdint.h>
\\#include <stdlib.h>
\\struct s {uint8_t x,y;
\\ uint32_t z;} __attribute__((packed)) s0 = {1, 2};
\\int main() {
\\ /* sizeof nor offsetof currently supported */
\\ if (((intptr_t)&s0.z - (intptr_t)&s0.x) != 2) abort();
\\ return 0;
\\}
, "");
if (@import("builtin").zig_backend == .stage1) {
cases.add("struct initializer - packed",
\\#define _NO_CRT_STDIO_INLINE 1
\\#include <stdint.h>
\\#include <stdlib.h>
\\struct s {uint8_t x,y;
\\ uint32_t z;} __attribute__((packed)) s0 = {1, 2};
\\int main() {
\\ /* sizeof nor offsetof currently supported */
\\ if (((intptr_t)&s0.z - (intptr_t)&s0.x) != 2) abort();
\\ return 0;
\\}
, "");
}
cases.add("cast signed array index to unsigned",
\\#include <stdlib.h>

View File

@@ -13,6 +13,8 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
cases.add("test/standalone/guess_number/main.zig");
cases.add("test/standalone/main_return_error/error_u8.zig");
cases.add("test/standalone/main_return_error/error_u8_non_zero.zig");
cases.add("test/standalone/noreturn_call/inline.zig");
cases.add("test/standalone/noreturn_call/as_arg.zig");
cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{});
cases.addBuildFile("test/standalone/shared_library/build.zig", .{});
cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{});
@@ -66,6 +68,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
if (builtin.os.tag == .linux) {
cases.addBuildFile("test/standalone/pie/build.zig", .{});
}
cases.addBuildFile("test/standalone/issue_12706/build.zig", .{});
// Ensure the development tools are buildable.

View File

@@ -0,0 +1,39 @@
const std = @import("std");
const builtin = @import("builtin");
const Builder = std.build.Builder;
const CrossTarget = std.zig.CrossTarget;
// TODO integrate this with the std.build executor API
fn isRunnableTarget(t: CrossTarget) bool {
if (t.isNative()) return true;
return (t.getOsTag() == builtin.os.tag and
t.getCpuArch() == builtin.cpu.arch);
}
pub fn build(b: *Builder) void {
const mode = b.standardReleaseOptions();
const target = b.standardTargetOptions(.{});
const exe = b.addExecutable("main", "main.zig");
exe.setBuildMode(mode);
exe.install();
const c_sources = [_][]const u8{
"test.c",
};
exe.addCSourceFiles(&c_sources, &.{});
exe.linkLibC();
exe.setTarget(target);
b.default_step.dependOn(&exe.step);
const test_step = b.step("test", "Test the program");
if (isRunnableTarget(target)) {
const run_cmd = exe.run();
test_step.dependOn(&run_cmd.step);
} else {
test_step.dependOn(&exe.step);
}
}

View File

@@ -0,0 +1,12 @@
const std = @import("std");
extern fn testFnPtr(n: c_int, ...) void;
const val: c_int = 123;
fn func(a: c_int) callconv(.C) void {
std.debug.assert(a == val);
}
pub fn main() void {
testFnPtr(2, func, val);
}

View File

@@ -0,0 +1,11 @@
#include <stdarg.h>
void testFnPtr(int n, ...) {
va_list ap;
va_start(ap, n);
void (*fnPtr)(int) = va_arg(ap, void (*)(int));
int arg = va_arg(ap, int);
fnPtr(arg);
va_end(ap);
}

View File

@@ -0,0 +1,8 @@
const std = @import("std");
fn foo() noreturn {
std.process.exit(0);
}
fn bar(_: u8, _: u8) void {}
pub fn main() void {
bar(foo(), @compileError("bad"));
}

View File

@@ -0,0 +1,10 @@
pub fn main() void {
_ = bar();
}
inline fn bar() u8 {
noret();
}
const std = @import("std");
inline fn noret() noreturn {
std.process.exit(0);
}

View File

@@ -728,20 +728,22 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\}
});
cases.add("struct initializer - packed",
\\struct {int x,y,z;} __attribute__((packed)) s0 = {1, 2};
, &[_][]const u8{
\\const struct_unnamed_1 = packed struct {
\\ x: c_int,
\\ y: c_int,
\\ z: c_int,
\\};
\\pub export var s0: struct_unnamed_1 = struct_unnamed_1{
\\ .x = @as(c_int, 1),
\\ .y = @as(c_int, 2),
\\ .z = 0,
\\};
});
if (builtin.zig_backend == .stage1) {
cases.add("struct initializer - packed",
\\struct {int x,y,z;} __attribute__((packed)) s0 = {1, 2};
, &[_][]const u8{
\\const struct_unnamed_1 = packed struct {
\\ x: c_int,
\\ y: c_int,
\\ z: c_int,
\\};
\\pub export var s0: struct_unnamed_1 = struct_unnamed_1{
\\ .x = @as(c_int, 1),
\\ .y = @as(c_int, 2),
\\ .z = 0,
\\};
});
}
// Test case temporarily disabled:
// https://github.com/ziglang/zig/issues/12055