diff --git a/src/AstGen.zig b/src/AstGen.zig index 79e5ad963e..1502b97017 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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; diff --git a/src/Module.zig b/src/Module.zig index 9410c4ea4a..c63fe43158 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -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 diff --git a/src/Sema.zig b/src/Sema.zig index 07f842751f..fb1638bc2a 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -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; diff --git a/src/arch/wasm/abi.zig b/src/arch/wasm/abi.zig index b1e99f4f92..d54965a50c 100644 --- a/src/arch/wasm/abi.zig +++ b/src/arch/wasm/abi.zig @@ -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; diff --git a/src/arch/x86_64/abi.zig b/src/arch/x86_64/abi.zig index 7e2025a23d..344fe235f3 100644 --- a/src/arch/x86_64/abi.zig +++ b/src/arch/x86_64/abi.zig @@ -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; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 45426c5ee0..004d152e1f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -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 { diff --git a/src/translate_c.zig b/src/translate_c.zig index b0fae81475..faa8a456f5 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -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)] }, diff --git a/src/type.zig b/src/type.zig index 339485c137..0d48c5e46a 100644 --- a/src/type.zig +++ b/src/type.zig @@ -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); } diff --git a/src/value.zig b/src/value.zig index a1961b40f7..50f86c7e79 100644 --- a/src/value.zig +++ b/src/value.zig @@ -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, } } diff --git a/test/behavior/enum.zig b/test/behavior/enum.zig index 517414780b..28c8785e64 100644 --- a/test/behavior/enum.zig +++ b/test/behavior/enum.zig @@ -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); +} diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index bd312e9cda..9834ba5f3d 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -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); +} diff --git a/test/behavior/sizeof_and_typeof.zig b/test/behavior/sizeof_and_typeof.zig index 83c5d977be..ab2d59bf83 100644 --- a/test/behavior/sizeof_and_typeof.zig +++ b/test/behavior/sizeof_and_typeof.zig @@ -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); + } +} diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 79bc1861e4..b94034adf4 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -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); +} diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c index 391e87fc67..439de8bbae 100644 --- a/test/c_abi/cfuncs.c +++ b/test/c_abi/cfuncs.c @@ -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() { diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index 145bbc384a..a34e4eda8f 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -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 { diff --git a/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig b/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig index 1b9118aa64..1472c7d5ba 100644 --- a/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig +++ b/test/cases/compile_errors/C_pointer_pointing_to_non_C_ABI_compatible_type_or_has_align_attr.zig @@ -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 diff --git a/test/cases/compile_errors/enum_backed_by_comptime_int_must_be_comptime.zig b/test/cases/compile_errors/enum_backed_by_comptime_int_must_be_comptime.zig new file mode 100644 index 0000000000..7dab294d4a --- /dev/null +++ b/test/cases/compile_errors/enum_backed_by_comptime_int_must_be_comptime.zig @@ -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 diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig index 0007a2014e..55ee277641 100644 --- a/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_struct_parameter.zig @@ -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 diff --git a/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig b/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig index 001d235e18..f848392c90 100644 --- a/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig +++ b/test/cases/compile_errors/function_with_non-extern_non-packed_union_parameter.zig @@ -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 diff --git a/test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig b/test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig deleted file mode 100644 index be9ffaa884..0000000000 --- a/test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig +++ /dev/null @@ -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 diff --git a/test/cases/compile_errors/noalias_param_coersion.zig b/test/cases/compile_errors/noalias_param_coersion.zig new file mode 100644 index 0000000000..e6dfae0370 --- /dev/null +++ b/test/cases/compile_errors/noalias_param_coersion.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig b/test/cases/compile_errors/overflow_in_enum_value_allocation.zig similarity index 50% rename from test/cases/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig rename to test/cases/compile_errors/overflow_in_enum_value_allocation.zig index c5dc5c1dcf..2a5b55e86d 100644 --- a/test/cases/compile_errors/stage1/obj/overflow_in_enum_value_allocation.zig +++ b/test/cases/compile_errors/overflow_in_enum_value_allocation.zig @@ -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' diff --git a/test/cases/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig b/test/cases/compile_errors/specify_enum_tag_type_that_is_too_small.zig similarity index 66% rename from test/cases/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig rename to test/cases/compile_errors/specify_enum_tag_type_that_is_too_small.zig index dc7077c8f8..d878bec18b 100644 --- a/test/cases/compile_errors/stage1/obj/specify_enum_tag_type_that_is_too_small.zig +++ b/test/cases/compile_errors/specify_enum_tag_type_that_is_too_small.zig @@ -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' diff --git a/test/cases/compile_errors/try_return.zig b/test/cases/compile_errors/try_return.zig new file mode 100644 index 0000000000..61e71e72b5 --- /dev/null +++ b/test/cases/compile_errors/try_return.zig @@ -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 diff --git a/test/cases/compile_errors/variadic_arg_validation.zig b/test/cases/compile_errors/variadic_arg_validation.zig new file mode 100644 index 0000000000..830d3a0877 --- /dev/null +++ b/test/cases/compile_errors/variadic_arg_validation.zig @@ -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' diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 4345625dc1..5aa72e2d1f 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -250,18 +250,20 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\} , ""); - cases.add("struct initializer - packed", - \\#define _NO_CRT_STDIO_INLINE 1 - \\#include - \\#include - \\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 + \\#include + \\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 diff --git a/test/standalone.zig b/test/standalone.zig index bfd683ec4c..c3fbad5377 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -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. diff --git a/test/standalone/issue_12706/build.zig b/test/standalone/issue_12706/build.zig new file mode 100644 index 0000000000..d84160a4f4 --- /dev/null +++ b/test/standalone/issue_12706/build.zig @@ -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); + } +} diff --git a/test/standalone/issue_12706/main.zig b/test/standalone/issue_12706/main.zig new file mode 100644 index 0000000000..b40c997561 --- /dev/null +++ b/test/standalone/issue_12706/main.zig @@ -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); +} diff --git a/test/standalone/issue_12706/test.c b/test/standalone/issue_12706/test.c new file mode 100644 index 0000000000..30b5c62697 --- /dev/null +++ b/test/standalone/issue_12706/test.c @@ -0,0 +1,11 @@ +#include + +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); +} \ No newline at end of file diff --git a/test/standalone/noreturn_call/as_arg.zig b/test/standalone/noreturn_call/as_arg.zig new file mode 100644 index 0000000000..08a4f0bd75 --- /dev/null +++ b/test/standalone/noreturn_call/as_arg.zig @@ -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")); +} diff --git a/test/standalone/noreturn_call/inline.zig b/test/standalone/noreturn_call/inline.zig new file mode 100644 index 0000000000..436d97896a --- /dev/null +++ b/test/standalone/noreturn_call/inline.zig @@ -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); +} diff --git a/test/translate_c.zig b/test/translate_c.zig index 637d491f49..54b4ad6081 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -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