From e5a3eb9777ff165d936b0811f3825eabb8bcd6a4 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 16 Nov 2022 15:46:43 +0200 Subject: [PATCH 01/14] Type: make `hasRuntimeBitsAdvanced` take `AbiAlignmentAdvancedStrat` I wasn't able to create a reduced test case for this but the reasoning can be seen in `abiAlignmentAdvancedUnion` where if `strat` was lazy `hasRuntimeBitsAdvanced` would be given `null` instead of `sema` which would cause eager evaluation when it is not valid or desired. --- src/Sema.zig | 7 ++-- src/print_zir.zig | 18 ++++++++++- src/type.zig | 81 ++++++++++++++++++++++++++++------------------- src/value.zig | 12 +++++-- 4 files changed, 81 insertions(+), 37 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index a6811d37fd..b3f1dd1bf0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -128,7 +128,7 @@ pub const Block = struct { /// Shared among all child blocks. sema: *Sema, /// The namespace to use for lookups from this source block - /// When analyzing fields, this is different from src_decl.src_namepsace. + /// When analyzing fields, this is different from src_decl.src_namespace. namespace: *Namespace, /// The AIR instructions generated for this block. instructions: std.ArrayListUnmanaged(Air.Inst.Index), @@ -31298,7 +31298,10 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool { } pub fn typeHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool { - return ty.hasRuntimeBitsAdvanced(false, sema); + return ty.hasRuntimeBitsAdvanced(false, .{ .sema = sema }) catch |err| switch (err) { + error.NeedLazy => unreachable, + else => |e| return e, + }; } fn typeAbiSize(sema: *Sema, ty: Type) !u64 { diff --git a/src/print_zir.zig b/src/print_zir.zig index f1b1068920..d434abd439 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -262,9 +262,10 @@ const Writer = struct { => try self.writeBreak(stream, inst), .array_init, .array_init_ref, + => try self.writeArrayInit(stream, inst), .array_init_anon, .array_init_anon_ref, - => try self.writeArrayInit(stream, inst), + => try self.writeArrayInitAnon(stream, inst), .slice_start => try self.writeSliceStart(stream, inst), .slice_end => try self.writeSliceEnd(stream, inst), @@ -2316,6 +2317,21 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } + fn writeArrayInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + + const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); + const args = self.code.refSlice(extra.end, extra.data.operands_len); + + try stream.writeAll("{"); + for (args) |arg, i| { + if (i != 0) try stream.writeAll(", "); + try self.writeInstRef(stream, arg); + } + try stream.writeAll("}) "); + try self.writeSrc(stream, inst_data.src()); + } + fn writeArrayInitSent(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; diff --git a/src/type.zig b/src/type.zig index 710b2fe4a3..6afee8bc73 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2312,6 +2312,8 @@ pub const Type = extern union { } } + const RuntimeBitsError = Module.CompileError || error{NeedLazy}; + /// true if and only if the type takes up space in memory at runtime. /// There are two reasons a type will return false: /// * the type is a comptime-only type. For example, the type `type` itself. @@ -2326,8 +2328,8 @@ pub const Type = extern union { pub fn hasRuntimeBitsAdvanced( ty: Type, ignore_comptime_only: bool, - opt_sema: ?*Sema, - ) Module.CompileError!bool { + strat: AbiAlignmentAdvancedStrat, + ) RuntimeBitsError!bool { switch (ty.tag()) { .u1, .u8, @@ -2406,8 +2408,8 @@ pub const Type = extern union { return true; } else if (ty.childType().zigTypeTag() == .Fn) { return !ty.childType().fnInfo().is_generic; - } else if (opt_sema) |sema| { - return !(try sema.typeRequiresComptime(ty)); + } else if (strat == .sema) { + return !(try strat.sema.typeRequiresComptime(ty)); } else { return !comptimeOnly(ty); } @@ -2445,8 +2447,8 @@ pub const Type = extern union { } if (ignore_comptime_only) { return true; - } else if (opt_sema) |sema| { - return !(try sema.typeRequiresComptime(child_ty)); + } else if (strat == .sema) { + return !(try strat.sema.typeRequiresComptime(child_ty)); } else { return !comptimeOnly(child_ty); } @@ -2459,13 +2461,14 @@ pub const Type = extern union { // and then later if our guess was incorrect, we emit a compile error. return true; } - if (opt_sema) |sema| { - _ = try sema.resolveTypeFields(ty); + switch (strat) { + .sema => |sema| _ = try sema.resolveTypeFields(ty), + .eager => assert(struct_obj.haveFieldTypes()), + .lazy => if (!struct_obj.haveFieldTypes()) return error.NeedLazy, } - assert(struct_obj.haveFieldTypes()); for (struct_obj.fields.values()) |field| { if (field.is_comptime) continue; - if (try field.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema)) + if (try field.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat)) return true; } else { return false; @@ -2474,7 +2477,7 @@ pub const Type = extern union { .enum_full => { const enum_full = ty.castTag(.enum_full).?.data; - return enum_full.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema); + return enum_full.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat); }, .enum_simple => { const enum_simple = ty.castTag(.enum_simple).?.data; @@ -2483,17 +2486,18 @@ pub const Type = extern union { .enum_numbered, .enum_nonexhaustive => { var buffer: Payload.Bits = undefined; const int_tag_ty = ty.intTagType(&buffer); - return int_tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema); + return int_tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat); }, .@"union" => { const union_obj = ty.castTag(.@"union").?.data; - if (opt_sema) |sema| { - _ = try sema.resolveTypeFields(ty); + switch (strat) { + .sema => |sema| _ = try sema.resolveTypeFields(ty), + .eager => assert(union_obj.haveFieldTypes()), + .lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy, } - assert(union_obj.haveFieldTypes()); for (union_obj.fields.values()) |value| { - if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema)) + if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat)) return true; } else { return false; @@ -2501,16 +2505,17 @@ pub const Type = extern union { }, .union_safety_tagged, .union_tagged => { const union_obj = ty.cast(Payload.Union).?.data; - if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema)) { + if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat)) { return true; } - if (opt_sema) |sema| { - _ = try sema.resolveTypeFields(ty); + switch (strat) { + .sema => |sema| _ = try sema.resolveTypeFields(ty), + .eager => assert(union_obj.haveFieldTypes()), + .lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy, } - assert(union_obj.haveFieldTypes()); for (union_obj.fields.values()) |value| { - if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema)) + if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat)) return true; } else { return false; @@ -2518,9 +2523,9 @@ pub const Type = extern union { }, .array, .vector => return ty.arrayLen() != 0 and - try ty.elemType().hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema), + try ty.elemType().hasRuntimeBitsAdvanced(ignore_comptime_only, strat), .array_u8 => return ty.arrayLen() != 0, - .array_sentinel => return ty.childType().hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema), + .array_sentinel => return ty.childType().hasRuntimeBitsAdvanced(ignore_comptime_only, strat), .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data != 0, @@ -2529,7 +2534,7 @@ pub const Type = extern union { for (tuple.types) |field_ty, i| { const val = tuple.values[i]; if (val.tag() != .unreachable_value) continue; // comptime field - if (try field_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema)) return true; + if (try field_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat)) return true; } return false; }, @@ -2665,11 +2670,11 @@ pub const Type = extern union { } pub fn hasRuntimeBits(ty: Type) bool { - return hasRuntimeBitsAdvanced(ty, false, null) catch unreachable; + return hasRuntimeBitsAdvanced(ty, false, .eager) catch unreachable; } pub fn hasRuntimeBitsIgnoreComptime(ty: Type) bool { - return hasRuntimeBitsAdvanced(ty, true, null) catch unreachable; + return hasRuntimeBitsAdvanced(ty, true, .eager) catch unreachable; } pub fn isFnOrHasRuntimeBits(ty: Type) bool { @@ -2812,12 +2817,12 @@ pub const Type = extern union { } } - const AbiAlignmentAdvanced = union(enum) { + pub const AbiAlignmentAdvanced = union(enum) { scalar: u32, val: Value, }; - const AbiAlignmentAdvancedStrat = union(enum) { + pub const AbiAlignmentAdvancedStrat = union(enum) { eager, lazy: Allocator, sema: *Sema, @@ -2971,7 +2976,10 @@ pub const Type = extern union { switch (strat) { .eager, .sema => { - if (!(try child_type.hasRuntimeBitsAdvanced(false, opt_sema))) { + if (!(child_type.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) { + error.NeedLazy => return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(strat.lazy, ty) }, + else => |e| return e, + })) { return AbiAlignmentAdvanced{ .scalar = 1 }; } return child_type.abiAlignmentAdvanced(target, strat); @@ -2990,7 +2998,10 @@ pub const Type = extern union { const code_align = abiAlignment(Type.anyerror, target); switch (strat) { .eager, .sema => { - if (!(try data.payload.hasRuntimeBitsAdvanced(false, opt_sema))) { + if (!(data.payload.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) { + error.NeedLazy => return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(strat.lazy, ty) }, + else => |e| return e, + })) { return AbiAlignmentAdvanced{ .scalar = code_align }; } return AbiAlignmentAdvanced{ .scalar = @max( @@ -3044,7 +3055,10 @@ pub const Type = extern union { const fields = ty.structFields(); var big_align: u32 = 0; for (fields.values()) |field| { - if (!(try field.ty.hasRuntimeBitsAdvanced(false, opt_sema))) continue; + if (!(field.ty.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) { + error.NeedLazy => return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(strat.lazy, ty) }, + else => |e| return e, + })) continue; const field_align = if (field.abi_align != 0) field.abi_align @@ -3161,7 +3175,10 @@ pub const Type = extern union { var max_align: u32 = 0; if (have_tag) max_align = union_obj.tag_ty.abiAlignment(target); for (union_obj.fields.values()) |field| { - if (!(try field.ty.hasRuntimeBitsAdvanced(false, opt_sema))) continue; + if (!(field.ty.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) { + error.NeedLazy => return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(strat.lazy, ty) }, + else => |e| return e, + })) continue; const field_align = if (field.abi_align != 0) field.abi_align diff --git a/src/value.zig b/src/value.zig index 042a960b25..59cf9046f4 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1911,7 +1911,11 @@ pub const Value = extern union { .lazy_align => { const ty = lhs.castTag(.lazy_align).?.data; - if (try ty.hasRuntimeBitsAdvanced(false, opt_sema)) { + const strat: Type.AbiAlignmentAdvancedStrat = if (opt_sema) |sema| .{ .sema = sema } else .eager; + if (ty.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) { + error.NeedLazy => unreachable, + else => |e| return e, + }) { return .gt; } else { return .eq; @@ -1919,7 +1923,11 @@ pub const Value = extern union { }, .lazy_size => { const ty = lhs.castTag(.lazy_size).?.data; - if (try ty.hasRuntimeBitsAdvanced(false, opt_sema)) { + const strat: Type.AbiAlignmentAdvancedStrat = if (opt_sema) |sema| .{ .sema = sema } else .eager; + if (ty.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) { + error.NeedLazy => unreachable, + else => |e| return e, + }) { return .gt; } else { return .eq; From 0616d2966a623a005891a2037b49cc2b8ad5b9c4 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 16 Nov 2022 18:27:17 +0200 Subject: [PATCH 02/14] Sema: allow coercing typed undefined to int Closes #13556 --- src/Sema.zig | 20 ++++++++++++++++---- test/behavior/cast.zig | 8 ++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index b3f1dd1bf0..5151904f0b 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -24307,7 +24307,10 @@ fn coerceExtra( }, .Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) { .Float, .ComptimeFloat => float: { - const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse { + if (is_undef) { + return sema.addConstUndef(dest_ty); + } + const val = (try sema.resolveMaybeUndefVal(inst)) orelse { if (dest_ty.zigTypeTag() == .ComptimeInt) { if (!opts.report_err) return error.NotCoercible; return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_int' must be comptime-known"); @@ -24327,7 +24330,10 @@ fn coerceExtra( return try sema.addConstant(dest_ty, result_val); }, .Int, .ComptimeInt => { - if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { + if (is_undef) { + return sema.addConstUndef(dest_ty); + } + if (try sema.resolveMaybeUndefVal(inst)) |val| { // comptime-known integer to other number if (!(try sema.intFitsInType(val, dest_ty, null))) { if (!opts.report_err) return error.NotCoercible; @@ -24364,7 +24370,10 @@ fn coerceExtra( return try sema.addConstant(dest_ty, result_val); }, .Float => { - if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| { + if (is_undef) { + return sema.addConstUndef(dest_ty); + } + if (try sema.resolveMaybeUndefVal(inst)) |val| { const result_val = try val.floatCast(sema.arena, dest_ty, target); if (!val.eql(result_val, dest_ty, sema.mod)) { return sema.fail( @@ -24389,7 +24398,10 @@ fn coerceExtra( } }, .Int, .ComptimeInt => int: { - const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse { + if (is_undef) { + return sema.addConstUndef(dest_ty); + } + const val = (try sema.resolveMaybeUndefVal(inst)) orelse { if (dest_ty.zigTypeTag() == .ComptimeFloat) { if (!opts.report_err) return error.NotCoercible; return sema.failWithNeededComptime(block, inst_src, "value being casted to 'comptime_float' must be comptime-known"); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 0de4b6d53e..bf35fd069c 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1429,3 +1429,11 @@ test "peer type resolution of function pointer and function body" { try expect(@TypeOf(a, b) == *const fn () u32); try expect(@TypeOf(b, a) == *const fn () u32); } + +test "cast typed undefined to int" { + comptime { + const a: u16 = undefined; + const b: u8 = a; + _ = b; + } +} From 510b891d271bc69c64e4be8b3e008c8a51a235e7 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 16 Nov 2022 19:13:31 +0200 Subject: [PATCH 03/14] Sema: handle `opt_payload` in `beginComptimePtrLoad` --- src/Sema.zig | 4 ++++ test/behavior/type_info.zig | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index 5151904f0b..90de77a57e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -26555,6 +26555,10 @@ fn beginComptimePtrLoad( .null_value => { return sema.fail(block, src, "attempt to use null value", .{}); }, + .opt_payload => blk: { + const opt_payload = ptr_val.castTag(.opt_payload).?.data; + break :blk try sema.beginComptimePtrLoad(block, src, opt_payload, null); + }, .zero, .one, diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index 80a3c7210a..af1b740834 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -565,3 +565,10 @@ test "typeInfo resolves usingnamespace declarations" { try expect(@typeInfo(B).Struct.decls.len == 2); //a } + +test "value from struct @typeInfo default_value can be loaded at comptime" { + comptime { + const a = @typeInfo(@TypeOf(.{ .foo = @as(u8, 1) })).Struct.fields[0].default_value; + try expect(@ptrCast(*const u8, a).* == 1); + } +} From 7b131a7cd496da221434939c1f624629d02fd3d0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 16 Nov 2022 19:49:58 +0200 Subject: [PATCH 04/14] Module: fix `fieldSrcLoc` for generated types --- src/Module.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index af29a591cc..82dd802f53 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1044,7 +1044,8 @@ pub const Struct = struct { .root => return queryFieldSrc(tree.*, query, file, tree.containerDeclRoot()), - else => unreachable, + // This struct was generated using @Type + else => return s.srcLoc(mod), } } @@ -1270,7 +1271,8 @@ pub const Union = struct { .tagged_union_enum_tag, .tagged_union_enum_tag_trailing, => return queryFieldSrc(tree.*, query, file, tree.taggedUnionEnumTag(node)), - else => unreachable, + // This union was generated using @Type + else => return u.srcLoc(mod), } } From bc7687382784c94c24d9ff4ebe10b03916d81e7c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 17 Nov 2022 12:09:53 +0200 Subject: [PATCH 05/14] Sema: fix type check in `zirIntToPtr` Simple fix: don't assume a ptr type before it has been checked. Closes #13567 --- src/Sema.zig | 2 +- test/cases/compile_errors/inttoptr_non_ptr_type.zig | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/inttoptr_non_ptr_type.zig diff --git a/src/Sema.zig b/src/Sema.zig index 90de77a57e..c465d5f81d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -18778,8 +18778,8 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ptr_ty = try sema.resolveType(block, src, extra.lhs); - const elem_ty = ptr_ty.elemType2(); try sema.checkPtrType(block, type_src, ptr_ty); + const elem_ty = ptr_ty.elemType2(); const target = sema.mod.getTarget(); const ptr_align = try ptr_ty.ptrAlignmentAdvanced(target, sema); diff --git a/test/cases/compile_errors/inttoptr_non_ptr_type.zig b/test/cases/compile_errors/inttoptr_non_ptr_type.zig new file mode 100644 index 0000000000..6a8af0ab95 --- /dev/null +++ b/test/cases/compile_errors/inttoptr_non_ptr_type.zig @@ -0,0 +1,9 @@ +pub export fn entry() void { + _ = @intToPtr(i32, 10); +} + +// error +// backend=stage2 +// target=native +// +// :2:19: error: expected pointer type, found 'i32' From 9877a7d36c466a25fe722aa3ff301ee90b206d9c Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 17 Nov 2022 13:08:28 +0200 Subject: [PATCH 06/14] print_air: print ty_pl type --- src/print_air.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/print_air.zig b/src/print_air.zig index 0bbc1100f7..bed6f029b1 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -400,9 +400,13 @@ const Writer = struct { } fn writeTyPlBin(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; + const data = w.air.instructions.items(.data); + const ty_pl = data[inst].ty_pl; const extra = w.air.extraData(Air.Bin, ty_pl.payload).data; + const inst_ty = w.air.getRefType(data[inst].ty_pl.ty); + try w.writeType(s, inst_ty); + try s.writeAll(", "); try w.writeOperand(s, inst, 0, extra.lhs); try s.writeAll(", "); try w.writeOperand(s, inst, 1, extra.rhs); From 98b3734b67739ad0e7d53500308495d53250cab0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 17 Nov 2022 23:00:01 +0200 Subject: [PATCH 07/14] Sema: prioritize Value.variable over OPV when resolving const value Closes #12275 --- src/Sema.zig | 7 ++++++- test/behavior/basic.zig | 9 +++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index c465d5f81d..d1de0af362 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1897,10 +1897,15 @@ fn resolveMaybeUndefValAllowVariablesMaybeRuntime( } i -= Air.Inst.Ref.typed_value_map.len; + const air_tags = sema.air_instructions.items(.tag); if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| { + if (air_tags[i] == .constant) { + const ty_pl = sema.air_instructions.items(.data)[i].ty_pl; + const val = sema.air_values.items[ty_pl.payload]; + if (val.tag() == .variable) return val; + } return opv; } - const air_tags = sema.air_instructions.items(.tag); switch (air_tags[i]) { .constant => { const ty_pl = sema.air_instructions.items(.data)[i].ty_pl; diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index b0b25d3774..7351891959 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -1118,3 +1118,12 @@ test "ambiguous reference error ignores current declaration" { }; try expect(S.b.foo == 666); } + +test "pointer to zero sized global is mutable" { + const S = struct { + const Thing = struct {}; + + var thing: Thing = undefined; + }; + try expect(@TypeOf(&S.thing) == *S.Thing); +} From 034507ef7cc492e105ca90c42686aa52bc2097e3 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 17 Nov 2022 23:28:19 +0200 Subject: [PATCH 08/14] Module: fix compile error for non-comptime-known global initializer --- src/Module.zig | 2 +- .../global_variable_stored_in_global_const.zig | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/global_variable_stored_in_global_const.zig diff --git a/src/Module.zig b/src/Module.zig index 82dd802f53..d598993c3f 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4633,7 +4633,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { const address_space_src: LazySrcLoc = .{ .node_offset_var_decl_addrspace = 0 }; const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 }; const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 }; - const decl_tv = try sema.resolveInstValue(&block_scope, init_src, result_ref, undefined); + const decl_tv = try sema.resolveInstValue(&block_scope, init_src, result_ref, "global variable initializer must be comptime-known"); // Note this resolves the type of the Decl, not the value; if this Decl // is a struct, for example, this resolves `type` (which needs no resolution), diff --git a/test/cases/compile_errors/global_variable_stored_in_global_const.zig b/test/cases/compile_errors/global_variable_stored_in_global_const.zig new file mode 100644 index 0000000000..594498f1c1 --- /dev/null +++ b/test/cases/compile_errors/global_variable_stored_in_global_const.zig @@ -0,0 +1,12 @@ +var a: u32 = 2; +const b = a; +pub export fn entry() void { + _ = b; +} + +// error +// backend=stage2 +// target=native +// +// :2:11: error: unable to resolve comptime value +// :2:11: note: global variable initializer must be comptime-known From 835a1f7f0cfb6fdefdb7a6a3d3a6e04eafeb96d0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 18 Nov 2022 13:41:20 +0200 Subject: [PATCH 09/14] Sema: fix missing error on mismatched array init count Closes #13582 --- src/Sema.zig | 49 ++++++++++++------- .../array_init_invalid_elem_count.zig | 10 ++++ 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index d1de0af362..ed8a44a966 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4288,29 +4288,42 @@ fn zirValidateArrayInit( const array_ty = sema.typeOf(array_ptr).childType(); const array_len = array_ty.arrayLen(); - if (instrs.len != array_len and array_ty.isTuple()) { - const struct_obj = array_ty.castTag(.tuple).?.data; - var root_msg: ?*Module.ErrorMsg = null; - errdefer if (root_msg) |msg| msg.destroy(sema.gpa); + if (instrs.len != array_len) switch (array_ty.zigTypeTag()) { + .Struct => { + const struct_obj = array_ty.castTag(.tuple).?.data; + var root_msg: ?*Module.ErrorMsg = null; + errdefer if (root_msg) |msg| msg.destroy(sema.gpa); - for (struct_obj.values) |default_val, i| { - if (i < instrs.len) continue; + for (struct_obj.values) |default_val, i| { + if (i < instrs.len) continue; - if (default_val.tag() == .unreachable_value) { - const template = "missing tuple field with index {d}"; - if (root_msg) |msg| { - try sema.errNote(block, init_src, msg, template, .{i}); - } else { - root_msg = try sema.errMsg(block, init_src, template, .{i}); + if (default_val.tag() == .unreachable_value) { + const template = "missing tuple field with index {d}"; + if (root_msg) |msg| { + try sema.errNote(block, init_src, msg, template, .{i}); + } else { + root_msg = try sema.errMsg(block, init_src, template, .{i}); + } } } - } - if (root_msg) |msg| { - root_msg = null; - return sema.failWithOwnedErrorMsg(msg); - } - } + if (root_msg) |msg| { + root_msg = null; + return sema.failWithOwnedErrorMsg(msg); + } + }, + .Array => { + return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{ + array_len, instrs.len, + }); + }, + .Vector => { + return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{ + array_len, instrs.len, + }); + }, + else => unreachable, + }; if ((is_comptime or block.is_comptime) and (try sema.resolveDefinedValue(block, init_src, array_ptr)) != null) diff --git a/test/cases/compile_errors/array_init_invalid_elem_count.zig b/test/cases/compile_errors/array_init_invalid_elem_count.zig index d8df149d18..d61987479d 100644 --- a/test/cases/compile_errors/array_init_invalid_elem_count.zig +++ b/test/cases/compile_errors/array_init_invalid_elem_count.zig @@ -16,6 +16,14 @@ comptime { var a: A = A{}; _ = a; } +pub export fn entry1() void { + var bla: V = .{ 1, 2, 3, 4 }; + _ = bla; +} +pub export fn entry2() void { + var bla: A = .{ 1, 2, 3, 4 }; + _ = bla; +} // error // backend=stage2 @@ -25,3 +33,5 @@ comptime { // :8:17: error: expected 8 vector elements; found 0 // :12:17: error: expected 8 array elements; found 1 // :16:17: error: expected 8 array elements; found 0 +// :20:19: error: expected 8 vector elements; found 4 +// :24:19: error: expected 8 array elements; found 4 From 08a00f0d1ce477c2c57072b9b0e27eb36a73092a Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 20 Nov 2022 00:15:03 +0200 Subject: [PATCH 10/14] llvm: use alignment of ABI return type when it differs from raw return type This would previously cause miscompilations when the ABI type had bigger alignment than the raw type. --- src/codegen/llvm.zig | 2 +- test/c_abi/main.zig | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index afafda36a6..0da0ef18e2 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4876,7 +4876,7 @@ pub const FuncGen = struct { // In this case the function return type is honoring the calling convention by having // a different LLVM type than the usual one. We solve this here at the callsite // by bitcasting a pointer to our canonical type, then loading it if necessary. - const alignment = return_type.abiAlignment(target); + const alignment = self.dg.object.target_data.abiAlignmentOfType(abi_ret_ty); const rp = self.buildAlloca(llvm_ret_ty, alignment); const ptr_abi_ty = abi_ret_ty.pointerType(0); const casted_ptr = self.builder.buildBitCast(rp, ptr_abi_ty, ""); diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index 4f4e162aec..3b5d564633 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -917,8 +917,6 @@ test "CFF: C passes to Zig" { try expectOk(c_send_CFF()); } test "CFF: C returns to Zig" { - // segfault on aarch64 and mips - if (builtin.target.cpu.arch == .aarch64) return error.SkipZigTest; if (comptime builtin.cpu.arch.isMIPS()) return error.SkipZigTest; if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; if (comptime builtin.cpu.arch.isPPC64()) return error.SkipZigTest; From 9e7293619ffb26049af2248226a3d75ef274bfb0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 20 Nov 2022 13:23:08 +0200 Subject: [PATCH 11/14] llvm: aarch64 C ABI: pass byref params as mutable pointers Closes #13597 --- src/codegen/llvm.zig | 52 +++++++++++++++++++++++++++++++++++++++----- test/c_abi/cfuncs.c | 9 ++++++++ test/c_abi/main.zig | 13 +++++++++++ 3 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 0da0ef18e2..a629624c58 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -988,6 +988,25 @@ pub const Object = struct { args.appendAssumeCapacity(load_inst); } }, + .byref_mut => { + const param_ty = fn_info.param_types[it.zig_index - 1]; + const param_llvm_ty = try dg.lowerType(param_ty); + const param = llvm_func.getParam(llvm_arg_i); + const alignment = param_ty.abiAlignment(target); + + dg.addArgAttr(llvm_func, llvm_arg_i, "noundef"); + llvm_arg_i += 1; + + try args.ensureUnusedCapacity(1); + + if (isByRef(param_ty)) { + args.appendAssumeCapacity(param); + } else { + const load_inst = builder.buildLoad(param_llvm_ty, param, ""); + load_inst.setAlignment(alignment); + args.appendAssumeCapacity(load_inst); + } + }, .abi_sized_int => { assert(!it.byval_attr); const param_ty = fn_info.param_types[it.zig_index - 1]; @@ -2583,6 +2602,9 @@ pub const DeclGen = struct { const alignment = param_ty.abiAlignment(target); dg.addByRefParamAttrs(llvm_fn, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty); }, + .byref_mut => { + dg.addArgAttr(llvm_fn, it.llvm_index - 1, "noundef"); + }, // No attributes needed for these. .no_bits, .abi_sized_int, @@ -3101,7 +3123,7 @@ pub const DeclGen = struct { const param_ty = fn_info.param_types[it.zig_index - 1]; try llvm_params.append(try dg.lowerType(param_ty)); }, - .byref => { + .byref, .byref_mut => { const param_ty = fn_info.param_types[it.zig_index - 1]; const raw_llvm_ty = try dg.lowerType(param_ty); try llvm_params.append(raw_llvm_ty.pointerType(0)); @@ -4726,6 +4748,27 @@ pub const FuncGen = struct { try llvm_args.append(arg_ptr); } }, + .byref_mut => { + const arg = args[it.zig_index - 1]; + const param_ty = self.air.typeOf(arg); + const llvm_arg = try self.resolveInst(arg); + + const alignment = param_ty.abiAlignment(target); + const param_llvm_ty = try self.dg.lowerType(param_ty); + const arg_ptr = self.buildAlloca(param_llvm_ty, alignment); + if (isByRef(param_ty)) { + const load_inst = self.builder.buildLoad(param_llvm_ty, llvm_arg, ""); + load_inst.setAlignment(alignment); + + const store_inst = self.builder.buildStore(load_inst, arg_ptr); + store_inst.setAlignment(alignment); + try llvm_args.append(arg_ptr); + } else { + const store_inst = self.builder.buildStore(llvm_arg, arg_ptr); + store_inst.setAlignment(alignment); + try llvm_args.append(arg_ptr); + } + }, .abi_sized_int => { const arg = args[it.zig_index - 1]; const param_ty = self.air.typeOf(arg); @@ -10384,6 +10427,7 @@ const ParamTypeIterator = struct { no_bits, byval, byref, + byref_mut, abi_sized_int, multiple_llvm_types, slice, @@ -10547,7 +10591,7 @@ const ParamTypeIterator = struct { it.zig_index += 1; it.llvm_index += 1; switch (aarch64_c_abi.classifyType(ty, it.target)) { - .memory => return .byref, + .memory => return .byref_mut, .float_array => |len| return Lowering{ .float_array = len }, .byval => return .byval, .integer => { @@ -10578,9 +10622,7 @@ const ParamTypeIterator = struct { return .as_u16; } switch (riscv_c_abi.classifyType(ty, it.target)) { - .memory => { - return .byref; - }, + .memory => return .byref_mut, .byval => return .byval, .integer => return .abi_sized_int, .double_integer => return Lowering{ .i64_array = 2 }, diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c index 595c077489..795701a18a 100644 --- a/test/c_abi/cfuncs.c +++ b/test/c_abi/cfuncs.c @@ -833,3 +833,12 @@ struct PD zig_ret_PD(); int c_assert_ret_PD(){ return c_assert_PD(zig_ret_PD()); } + +struct ByRef { + int val; + int arr[15]; +}; +struct ByRef c_modify_by_ref_param(struct ByRef in) { + in.val = 42; + return in; +} diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index 3b5d564633..a5b8bda9c1 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -988,3 +988,16 @@ pub export fn zig_assert_PD(lv: PD) c_int { if (err != 0) std.debug.print("Received {}", .{lv}); return err; } + +const ByRef = extern struct { + val: c_int, + arr: [15]c_int, +}; +extern fn c_modify_by_ref_param(ByRef) ByRef; + +test "C function modifies by ref param" { + if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; + + const res = c_modify_by_ref_param(.{ .val = 1, .arr = undefined }); + try expect(res.val == 42); +} From 9e276d32f3f8d980e4bf4c0a52df0fa97717aacb Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 20 Nov 2022 13:58:37 +0200 Subject: [PATCH 12/14] Sema: fix memory management of missing field error Closes #13590 --- src/Sema.zig | 9 +++++---- ...g_struct_field_in_fn_called_at_comptime.zig | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 test/cases/compile_errors/missing_struct_field_in_fn_called_at_comptime.zig diff --git a/src/Sema.zig b/src/Sema.zig index ed8a44a966..a1c7fa7b91 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4111,6 +4111,7 @@ fn validateStructInit( .{fqn}, ); } + root_msg = null; return sema.failWithOwnedErrorMsg(msg); } @@ -4230,7 +4231,6 @@ fn validateStructInit( } if (root_msg) |msg| { - root_msg = null; if (struct_ty.castTag(.@"struct")) |struct_obj| { const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod); defer gpa.free(fqn); @@ -4241,6 +4241,7 @@ fn validateStructInit( .{fqn}, ); } + root_msg = null; return sema.failWithOwnedErrorMsg(msg); } @@ -17098,7 +17099,6 @@ fn finishStructInit( } if (root_msg) |msg| { - root_msg = null; if (struct_ty.castTag(.@"struct")) |struct_obj| { const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod); defer gpa.free(fqn); @@ -17109,6 +17109,7 @@ fn finishStructInit( .{fqn}, ); } + root_msg = null; return sema.failWithOwnedErrorMsg(msg); } @@ -27225,8 +27226,8 @@ fn coerceTupleToStruct( } if (root_msg) |msg| { - root_msg = null; try sema.addDeclaredHereNote(msg, struct_ty); + root_msg = null; return sema.failWithOwnedErrorMsg(msg); } @@ -27331,8 +27332,8 @@ fn coerceTupleToTuple( } if (root_msg) |msg| { - root_msg = null; try sema.addDeclaredHereNote(msg, tuple_ty); + root_msg = null; return sema.failWithOwnedErrorMsg(msg); } diff --git a/test/cases/compile_errors/missing_struct_field_in_fn_called_at_comptime.zig b/test/cases/compile_errors/missing_struct_field_in_fn_called_at_comptime.zig new file mode 100644 index 0000000000..1f3c4583cb --- /dev/null +++ b/test/cases/compile_errors/missing_struct_field_in_fn_called_at_comptime.zig @@ -0,0 +1,18 @@ +const S = struct { + a: u32, + b: comptime_int, + fn init() S { + return .{ .a = 1 }; + } +}; +comptime { + _ = S.init(); +} + +// error +// backend=stage2 +// target=native +// +// :5:17: error: missing struct field: b +// :1:11: note: struct 'tmp.S' declared here +// :9:15: note: called from here From d968d9d10329234afc1d7743bb9981245695ca2b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 20 Nov 2022 20:14:03 +0200 Subject: [PATCH 13/14] llvm: add attributes to the arguments of function pointer calls Closes #13605 --- src/codegen/llvm.zig | 67 +++++++++++++++++++++++++++++++++-- src/codegen/llvm/bindings.zig | 4 +-- src/zig_llvm.cpp | 30 ++++++++++++---- src/zig_llvm.h | 2 ++ test/c_abi/cfuncs.c | 29 +++++++++++++++ test/c_abi/main.zig | 31 ++++++++++++++++ 6 files changed, 152 insertions(+), 11 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a629624c58..8459920d61 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -4700,9 +4700,9 @@ pub const FuncGen = struct { break :blk ret_ptr; }; - if (fn_info.return_type.isError() and - self.dg.module.comp.bin_file.options.error_return_tracing) - { + const err_return_tracing = fn_info.return_type.isError() and + self.dg.module.comp.bin_file.options.error_return_tracing; + if (err_return_tracing) { try llvm_args.append(self.err_ret_trace.?); } @@ -4890,6 +4890,66 @@ pub const FuncGen = struct { "", ); + if (callee_ty.zigTypeTag() == .Pointer) { + // Add argument attributes for function pointer calls. + it = iterateParamTypes(self.dg, fn_info); + it.llvm_index += @boolToInt(sret); + it.llvm_index += @boolToInt(err_return_tracing); + while (it.next()) |lowering| switch (lowering) { + .byval => { + const param_index = it.zig_index - 1; + const param_ty = fn_info.param_types[param_index]; + if (!isByRef(param_ty)) { + self.dg.addByValParamAttrs(call, param_ty, param_index, fn_info, it.llvm_index - 1); + } + }, + .byref => { + const param_index = it.zig_index - 1; + const param_ty = fn_info.param_types[param_index]; + const param_llvm_ty = try self.dg.lowerType(param_ty); + const alignment = param_ty.abiAlignment(target); + self.dg.addByRefParamAttrs(call, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty); + }, + .byref_mut => { + self.dg.addArgAttr(call, it.llvm_index - 1, "noundef"); + }, + // No attributes needed for these. + .no_bits, + .abi_sized_int, + .multiple_llvm_types, + .as_u16, + .float_array, + .i32_array, + .i64_array, + => continue, + + .slice => { + assert(!it.byval_attr); + const param_ty = fn_info.param_types[it.zig_index - 1]; + const ptr_info = param_ty.ptrInfo().data; + const llvm_arg_i = it.llvm_index - 2; + + if (math.cast(u5, it.zig_index - 1)) |i| { + if (@truncate(u1, fn_info.noalias_bits >> i) != 0) { + self.dg.addArgAttr(call, llvm_arg_i, "noalias"); + } + } + if (param_ty.zigTypeTag() != .Optional) { + self.dg.addArgAttr(call, llvm_arg_i, "nonnull"); + } + if (!ptr_info.mutable) { + self.dg.addArgAttr(call, llvm_arg_i, "readonly"); + } + if (ptr_info.@"align" != 0) { + self.dg.addArgAttrInt(call, llvm_arg_i, "align", ptr_info.@"align"); + } else { + const elem_align = @max(ptr_info.pointee_type.abiAlignment(target), 1); + self.dg.addArgAttrInt(call, llvm_arg_i, "align", elem_align); + } + }, + }; + } + if (return_type.isNoReturn() and attr != .AlwaysTail) { _ = self.builder.buildUnreachable(); return null; @@ -10469,6 +10529,7 @@ const ParamTypeIterator = struct { it.llvm_index += 1; var buf: Type.Payload.ElemType = undefined; if (ty.isSlice() or (ty.zigTypeTag() == .Optional and ty.optionalChild(&buf).isSlice())) { + it.llvm_index += 1; return .slice; } else if (isByRef(ty)) { return .byref; diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 1b462312cd..90d0f51c7b 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -88,8 +88,8 @@ pub const Context = opaque { }; pub const Value = opaque { - pub const addAttributeAtIndex = LLVMAddAttributeAtIndex; - extern fn LLVMAddAttributeAtIndex(*Value, Idx: AttributeIndex, A: *Attribute) void; + pub const addAttributeAtIndex = ZigLLVMAddAttributeAtIndex; + extern fn ZigLLVMAddAttributeAtIndex(*Value, Idx: AttributeIndex, A: *Attribute) void; pub const removeEnumAttributeAtIndex = LLVMRemoveEnumAttributeAtIndex; extern fn LLVMRemoveEnumAttributeAtIndex(F: *Value, Idx: AttributeIndex, KindID: c_uint) void; diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 7134df6a9c..c38e311f67 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -444,6 +444,15 @@ LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, return wrap(call_inst); } +void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef A) { + if (isa(unwrap(Val))) { + unwrap(Val)->addAttributeAtIndex(Idx, unwrap(A)); + } else { + unwrap(Val)->addAttributeAtIndex(Idx, unwrap(A)); + } +} + + LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool isVolatile) { @@ -1065,12 +1074,21 @@ void ZigLLVMSetFastMath(LLVMBuilderRef builder_wrapped, bool on_state) { } } -void ZigLLVMAddByValAttr(LLVMValueRef fn_ref, unsigned ArgNo, LLVMTypeRef type_val) { - Function *func = unwrap(fn_ref); - AttrBuilder attr_builder(func->getContext()); - Type *llvm_type = unwrap(type_val); - attr_builder.addByValAttr(llvm_type); - func->addParamAttrs(ArgNo, attr_builder); +void ZigLLVMAddByValAttr(LLVMValueRef Val, unsigned ArgNo, LLVMTypeRef type_val) { + if (isa(unwrap(Val))) { + Function *func = unwrap(Val); + AttrBuilder attr_builder(func->getContext()); + Type *llvm_type = unwrap(type_val); + attr_builder.addByValAttr(llvm_type); + func->addParamAttrs(ArgNo, attr_builder); + } else { + CallInst *call = unwrap(Val); + AttrBuilder attr_builder(call->getContext()); + Type *llvm_type = unwrap(type_val); + attr_builder.addByValAttr(llvm_type); + // NOTE: +1 here since index 0 refers to the return value + call->addAttributeAtIndex(ArgNo + 1, attr_builder.getAttribute(Attribute::ByVal)); + } } void ZigLLVMAddSretAttr(LLVMValueRef fn_ref, LLVMTypeRef type_val) { diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 0e210f9545..2829801a46 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -129,6 +129,8 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMTypeRef functio LLVMValueRef Fn, LLVMValueRef *Args, unsigned NumArgs, enum ZigLLVM_CallingConv CC, enum ZigLLVM_CallAttr attr, const char *Name); +ZIG_EXTERN_C void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef A); + ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool isVolatile); diff --git a/test/c_abi/cfuncs.c b/test/c_abi/cfuncs.c index 795701a18a..20896669fe 100644 --- a/test/c_abi/cfuncs.c +++ b/test/c_abi/cfuncs.c @@ -842,3 +842,32 @@ struct ByRef c_modify_by_ref_param(struct ByRef in) { in.val = 42; return in; } + +struct ByVal { + struct { + unsigned long x; + unsigned long y; + unsigned long z; + } origin; + struct { + unsigned long width; + unsigned long height; + unsigned long depth; + } size; +}; + +void c_func_ptr_byval(void *a, void *b, struct ByVal in, unsigned long c, void *d, unsigned long e) { + assert_or_panic((intptr_t)a == 1); + assert_or_panic((intptr_t)b == 2); + + assert_or_panic(in.origin.x == 9); + assert_or_panic(in.origin.y == 10); + assert_or_panic(in.origin.z == 11); + assert_or_panic(in.size.width == 12); + assert_or_panic(in.size.height == 13); + assert_or_panic(in.size.depth == 14); + + assert_or_panic(c == 3); + assert_or_panic((intptr_t)d == 4); + assert_or_panic(e == 5); +} diff --git a/test/c_abi/main.zig b/test/c_abi/main.zig index a5b8bda9c1..47f0455744 100644 --- a/test/c_abi/main.zig +++ b/test/c_abi/main.zig @@ -1001,3 +1001,34 @@ test "C function modifies by ref param" { const res = c_modify_by_ref_param(.{ .val = 1, .arr = undefined }); try expect(res.val == 42); } + +const ByVal = extern struct { + origin: extern struct { + x: c_ulong, + y: c_ulong, + z: c_ulong, + }, + size: extern struct { + width: c_ulong, + height: c_ulong, + depth: c_ulong, + }, +}; + +extern fn c_func_ptr_byval(*anyopaque, *anyopaque, ByVal, c_ulong, *anyopaque, c_ulong) void; +test "C function that takes byval struct called via function pointer" { + if (comptime builtin.cpu.arch.isPPC()) return error.SkipZigTest; + + var fn_ptr = &c_func_ptr_byval; + fn_ptr( + @intToPtr(*anyopaque, 1), + @intToPtr(*anyopaque, 2), + ByVal{ + .origin = .{ .x = 9, .y = 10, .z = 11 }, + .size = .{ .width = 12, .height = 13, .depth = 14 }, + }, + @as(c_ulong, 3), + @intToPtr(*anyopaque, 4), + @as(c_ulong, 5), + ); +} From bbcd959c2b1721d81af5829b56e01cc472b58816 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 20 Nov 2022 17:34:30 +0200 Subject: [PATCH 14/14] cli: warn about using `--debug-compile-errors` on a release build --- src/main.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index 410414b3a3..24518d743d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1304,7 +1304,11 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "--no-gc-sections")) { linker_gc_sections = false; } else if (mem.eql(u8, arg, "--debug-compile-errors")) { - debug_compile_errors = true; + if (!crash_report.is_enabled) { + std.log.warn("Zig was compiled in a release mode. --debug-compile-errors has no effect.", .{}); + } else { + debug_compile_errors = true; + } } else if (mem.eql(u8, arg, "--verbose-link")) { verbose_link = true; } else if (mem.eql(u8, arg, "--verbose-cc")) {