From 86e6acb37b242a60917f5552999d918c24fdf791 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Dec 2022 15:39:40 +0200 Subject: [PATCH 1/8] AstGen: improve error message for missing parameter name Closes #13393 --- src/AstGen.zig | 23 +++++++++++++++++++ .../compile_errors/missing_parameter_name.zig | 19 +++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 test/cases/compile_errors/missing_parameter_name.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 009d05e5ed..b18d550347 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -3696,6 +3696,29 @@ fn fnDecl( if (param.anytype_ellipsis3) |tok| { return astgen.failTok(tok, "missing parameter name", .{}); } else { + ambiguous: { + if (tree.nodes.items(.tag)[param.type_expr] != .identifier) break :ambiguous; + const main_token = tree.nodes.items(.main_token)[param.type_expr]; + const identifier_str = tree.tokenSlice(main_token); + if (isPrimitive(identifier_str)) break :ambiguous; + return astgen.failNodeNotes( + param.type_expr, + "missing parameter name or type", + .{}, + &[_]u32{ + try astgen.errNoteNode( + param.type_expr, + "if this is a name, annotate its type '{s}: T'", + .{identifier_str}, + ), + try astgen.errNoteNode( + param.type_expr, + "if this is a type, give it a name ': {s}'", + .{identifier_str}, + ), + }, + ); + } return astgen.failNode(param.type_expr, "missing parameter name", .{}); } } else 0; diff --git a/test/cases/compile_errors/missing_parameter_name.zig b/test/cases/compile_errors/missing_parameter_name.zig new file mode 100644 index 0000000000..3e576a1a6d --- /dev/null +++ b/test/cases/compile_errors/missing_parameter_name.zig @@ -0,0 +1,19 @@ +fn f2(u64) u64 { + return x; +} +fn f3(*x) u64 { + return x; +} +fn f1(x) u64 { + return x; +} + +// error +// backend=stage2 +// target=native +// +// :1:7: error: missing parameter name +// :4:7: error: missing parameter name +// :7:7: error: missing parameter name or type +// :7:7: note: if this is a name, annotate its type 'x: T' +// :7:7: note: if this is a type, give it a name ': x' From 59dad43de26a89ca72a97224a171d724dcc6ee41 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Dec 2022 18:46:59 +0200 Subject: [PATCH 2/8] Sema: add error for failed assumption about struct having runtime bits --- src/Module.zig | 2 ++ src/Sema.zig | 20 ++++++++++++ src/type.zig | 31 +++++++++++++++---- src/value.zig | 2 +- .../invalid_dependency_on_struct_size.zig | 19 ++++++++++++ 5 files changed, 67 insertions(+), 7 deletions(-) create mode 100644 test/cases/compile_errors/invalid_dependency_on_struct_size.zig diff --git a/src/Module.zig b/src/Module.zig index a18297c8ff..68d0ac8af5 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -940,6 +940,7 @@ pub const Struct = struct { requires_comptime: PropertyBoolean = .unknown, have_field_inits: bool = false, is_tuple: bool, + assumed_runtime_bits: bool = false, pub const Fields = std.StringArrayHashMapUnmanaged(Field); @@ -1205,6 +1206,7 @@ pub const Union = struct { fully_resolved, }, requires_comptime: PropertyBoolean = .unknown, + assumed_runtime_bits: bool = false, pub const Field = struct { /// undefined until `status` is `have_field_types` or `have_layout`. diff --git a/src/Sema.zig b/src/Sema.zig index c384605e1b..73ebf3d0aa 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -29237,6 +29237,16 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void { struct_obj.status = .have_layout; _ = try sema.resolveTypeRequiresComptime(resolved_ty); + + if (struct_obj.assumed_runtime_bits and !resolved_ty.hasRuntimeBits()) { + const msg = try Module.ErrorMsg.create( + sema.gpa, + struct_obj.srcLoc(sema.mod), + "struct layout depends on it having runtime bits", + .{}, + ); + return sema.failWithOwnedErrorMsg(msg); + } } // otherwise it's a tuple; no need to resolve anything } @@ -29401,6 +29411,16 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void { } union_obj.status = .have_layout; _ = try sema.resolveTypeRequiresComptime(resolved_ty); + + if (union_obj.assumed_runtime_bits and !resolved_ty.hasRuntimeBits()) { + const msg = try Module.ErrorMsg.create( + sema.gpa, + union_obj.srcLoc(sema.mod), + "union layout depends on it having runtime bits", + .{}, + ); + return sema.failWithOwnedErrorMsg(msg); + } } // In case of querying the ABI alignment of this struct, we will ask diff --git a/src/type.zig b/src/type.zig index 21cfdf9d73..5fcd0f6a26 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2459,6 +2459,7 @@ pub const Type = extern union { if (struct_obj.status == .field_types_wip) { // In this case, we guess that hasRuntimeBits() for this type is true, // and then later if our guess was incorrect, we emit a compile error. + struct_obj.assumed_runtime_bits = true; return true; } switch (strat) { @@ -2491,6 +2492,12 @@ pub const Type = extern union { .@"union" => { const union_obj = ty.castTag(.@"union").?.data; + if (union_obj.status == .field_types_wip) { + // In this case, we guess that hasRuntimeBits() for this type is true, + // and then later if our guess was incorrect, we emit a compile error. + union_obj.assumed_runtime_bits = true; + return true; + } switch (strat) { .sema => |sema| _ = try sema.resolveTypeFields(ty), .eager => assert(union_obj.haveFieldTypes()), @@ -3027,8 +3034,9 @@ pub const Type = extern union { const struct_obj = ty.castTag(.@"struct").?.data; if (opt_sema) |sema| { if (struct_obj.status == .field_types_wip) { - // We'll guess "pointer-aligned" and if we guess wrong, emit - // a compile error later. + // We'll guess "pointer-aligned", if the struct has an + // underaligned pointer field then some allocations + // might require explicit alignment. return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; } _ = try sema.resolveTypeFields(ty); @@ -3153,8 +3161,9 @@ pub const Type = extern union { }; if (opt_sema) |sema| { if (union_obj.status == .field_types_wip) { - // We'll guess "pointer-aligned" and if we guess wrong, emit - // a compile error later. + // We'll guess "pointer-aligned", if the union has an + // underaligned pointer field then some allocations + // might require explicit alignment. return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }; } _ = try sema.resolveTypeFields(ty); @@ -5234,7 +5243,12 @@ pub const Type = extern union { .@"struct" => { const struct_obj = ty.castTag(.@"struct").?.data; switch (struct_obj.requires_comptime) { - .wip, .unknown => unreachable, // This function asserts types already resolved. + .wip, .unknown => { + // Return false to avoid incorrect dependency loops. + // This will be handled correctly once merged with + // `Sema.typeRequiresComptime`. + return false; + }, .no => return false, .yes => return true, } @@ -5243,7 +5257,12 @@ pub const Type = extern union { .@"union", .union_safety_tagged, .union_tagged => { const union_obj = ty.cast(Type.Payload.Union).?.data; switch (union_obj.requires_comptime) { - .wip, .unknown => unreachable, // This function asserts types already resolved. + .wip, .unknown => { + // Return false to avoid incorrect dependency loops. + // This will be handled correctly once merged with + // `Sema.typeRequiresComptime`. + return false; + }, .no => return false, .yes => return true, } diff --git a/src/value.zig b/src/value.zig index be643f65a3..adff60983f 100644 --- a/src/value.zig +++ b/src/value.zig @@ -187,7 +187,7 @@ pub const Value = extern union { bound_fn, /// The ABI alignment of the payload type. lazy_align, - /// The ABI alignment of the payload type. + /// The ABI size of the payload type. lazy_size, pub const last_no_payload_tag = Tag.empty_array; diff --git a/test/cases/compile_errors/invalid_dependency_on_struct_size.zig b/test/cases/compile_errors/invalid_dependency_on_struct_size.zig new file mode 100644 index 0000000000..02ea7e2710 --- /dev/null +++ b/test/cases/compile_errors/invalid_dependency_on_struct_size.zig @@ -0,0 +1,19 @@ +comptime { + const S = struct { + const Foo = struct { + y: Bar, + }; + const Bar = struct { + y: if (@sizeOf(Foo) == 0) u64 else void, + }; + }; + + _ = @sizeOf(S.Foo) + 1; +} + +// error +// backend=stage2 +// target=native +// +// :6:21: error: struct layout depends on it having runtime bits +// :4:13: note: while checking this field From 7f9e841f746bb3eaf6ac205092a30bc7ed12a068 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Dec 2022 18:51:26 +0200 Subject: [PATCH 3/8] Sema: do not forcibly canonicalize unresolved pointer element type Closes #13308 --- src/Sema.zig | 12 ++---------- src/type.zig | 12 ++++++++++-- test/behavior/struct.zig | 12 ++++++++++++ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 73ebf3d0aa..cc6e5e95d2 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -16825,7 +16825,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const bitoffset_src: LazySrcLoc = .{ .node_offset_ptr_bitoffset = extra.data.src_node }; const hostsize_src: LazySrcLoc = .{ .node_offset_ptr_hostsize = extra.data.src_node }; - const unresolved_elem_ty = blk: { + const elem_ty = blk: { const air_inst = try sema.resolveInst(extra.data.elem_type); const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| { if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer()) { @@ -16854,7 +16854,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air // Check if this happens to be the lazy alignment of our element type, in // which case we can make this 0 without resolving it. if (val.castTag(.lazy_align)) |payload| { - if (payload.data.eql(unresolved_elem_ty, sema.mod)) { + if (payload.data.eql(elem_ty, sema.mod)) { break :blk 0; } } @@ -16887,14 +16887,6 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air return sema.fail(block, bitoffset_src, "bit offset starts after end of host integer", .{}); } - const elem_ty = if (abi_align == 0) - unresolved_elem_ty - else t: { - const elem_ty = try sema.resolveTypeFields(unresolved_elem_ty); - try sema.resolveTypeLayout(elem_ty); - break :t elem_ty; - }; - if (elem_ty.zigTypeTag() == .NoReturn) { return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}); } else if (elem_ty.zigTypeTag() == .Fn) { diff --git a/src/type.zig b/src/type.zig index 5fcd0f6a26..46b33a34fa 100644 --- a/src/type.zig +++ b/src/type.zig @@ -6491,8 +6491,16 @@ pub const Type = extern union { // type, we change it to 0 here. If this causes an assertion trip because the // pointee type needs to be resolved more, that needs to be done before calling // this ptr() function. - if (d.@"align" != 0 and d.@"align" == d.pointee_type.abiAlignment(target)) { - d.@"align" = 0; + if (d.@"align" != 0) canonicalize: { + if (d.pointee_type.castTag(.@"struct")) |struct_ty| { + if (!struct_ty.data.haveLayout()) break :canonicalize; + } + if (d.pointee_type.cast(Payload.Union)) |union_ty| { + if (!union_ty.data.haveLayout()) break :canonicalize; + } + if (d.@"align" == d.pointee_type.abiAlignment(target)) { + d.@"align" = 0; + } } // Canonicalize host_size. If it matches the bit size of the pointee type, diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index a74b9fdd53..1807b7a447 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1406,3 +1406,15 @@ test "address of zero-bit field is equal to address of only field" { try std.testing.expectEqual(&a, a_ptr); } } + +test "struct field has a pointer to an aligned version of itself" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const E = struct { + next: *align(1) @This(), + }; + var e: E = undefined; + e = .{ .next = &e }; + + try expect(&e == e.next); +} From 0e38cc16d51178525e89774ce9151651b6a0e99a Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Dec 2022 19:56:43 +0200 Subject: [PATCH 4/8] Sema: fix comparisons between lazy and runtime values Closes #12498 --- lib/std/fs.zig | 2 -- src/Sema.zig | 6 ++++-- src/arch/aarch64/CodeGen.zig | 2 +- src/arch/arm/CodeGen.zig | 2 +- src/arch/sparc64/CodeGen.zig | 2 +- src/arch/wasm/CodeGen.zig | 10 +++++----- src/arch/x86_64/CodeGen.zig | 2 +- src/codegen.zig | 14 +++++++------- src/codegen/llvm.zig | 2 +- src/codegen/spirv.zig | 4 ++-- src/link/Dwarf.zig | 2 +- src/value.zig | 21 +++++++++++++++------ test/behavior.zig | 1 + test/behavior/bugs/12498.zig | 8 ++++++++ 14 files changed, 48 insertions(+), 30 deletions(-) create mode 100644 test/behavior/bugs/12498.zig diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 3ef8e5319c..8ae21259ae 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -809,8 +809,6 @@ pub const IterableDir = struct { // and we avoid the code complexity here. const w = os.wasi; start_over: while (true) { - // TODO https://github.com/ziglang/zig/issues/12498 - _ = @sizeOf(w.dirent_t) + 1; // According to the WASI spec, the last entry might be truncated, // so we need to check if the left buffer contains the whole dirent. if (self.end_index - self.index < @sizeOf(w.dirent_t)) { diff --git a/src/Sema.zig b/src/Sema.zig index cc6e5e95d2..ad89408c26 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -20262,7 +20262,7 @@ fn analyzeShuffle( var buf: Value.ElemValueBuffer = undefined; const elem = mask.elemValueBuffer(sema.mod, i, &buf); if (elem.isUndef()) continue; - const int = elem.toSignedInt(); + const int = elem.toSignedInt(sema.mod.getTarget()); var unsigned: u32 = undefined; var chosen: u32 = undefined; if (int >= 0) { @@ -20304,7 +20304,7 @@ fn analyzeShuffle( values[i] = Value.undef; continue; } - const int = mask_elem_val.toSignedInt(); + const int = mask_elem_val.toSignedInt(sema.mod.getTarget()); const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int); if (int >= 0) { values[i] = try a_val.elemValue(sema.mod, sema.arena, unsigned); @@ -28299,6 +28299,7 @@ fn cmpNumeric( var lhs_bits: usize = undefined; if (try sema.resolveMaybeUndefVal(lhs)) |lhs_val| { + try sema.resolveLazyValue(lhs_val); if (lhs_val.isUndef()) return sema.addConstUndef(Type.bool); if (lhs_val.isNan()) switch (op) { @@ -28357,6 +28358,7 @@ fn cmpNumeric( var rhs_bits: usize = undefined; if (try sema.resolveMaybeUndefVal(rhs)) |rhs_val| { + try sema.resolveLazyValue(rhs_val); if (rhs_val.isUndef()) return sema.addConstUndef(Type.bool); if (rhs_val.isNan()) switch (op) { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 52f9a544b1..979472c5a5 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -6247,7 +6247,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { if (info.bits <= 64) { const unsigned = switch (info.signedness) { .signed => blk: { - const signed = typed_value.val.toSignedInt(); + const signed = typed_value.val.toSignedInt(target); break :blk @bitCast(u64, signed); }, .unsigned => typed_value.val.toUnsignedInt(target), diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 970b9376e5..4afadb2c58 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -6121,7 +6121,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { if (info.bits <= ptr_bits) { const unsigned = switch (info.signedness) { .signed => blk: { - const signed = @intCast(i32, typed_value.val.toSignedInt()); + const signed = @intCast(i32, typed_value.val.toSignedInt(target)); break :blk @bitCast(u32, signed); }, .unsigned => @intCast(u32, typed_value.val.toUnsignedInt(target)), diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index f22849c652..72c6b8021b 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -3786,7 +3786,7 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { if (info.bits <= 64) { const unsigned = switch (info.signedness) { .signed => blk: { - const signed = typed_value.val.toSignedInt(); + const signed = typed_value.val.toSignedInt(target); break :blk @bitCast(u64, signed); }, .unsigned => typed_value.val.toUnsignedInt(target), diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index ebfff25b2f..b11335f4c3 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2702,11 +2702,11 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { switch (int_info.signedness) { .signed => switch (int_info.bits) { 0...32 => return WValue{ .imm32 = @intCast(u32, toTwosComplement( - val.toSignedInt(), + val.toSignedInt(target), @intCast(u6, int_info.bits), )) }, 33...64 => return WValue{ .imm64 = toTwosComplement( - val.toSignedInt(), + val.toSignedInt(target), @intCast(u7, int_info.bits), ) }, else => unreachable, @@ -2873,15 +2873,15 @@ fn valueAsI32(func: *const CodeGen, val: Value, ty: Type) i32 { } }, .Int => switch (ty.intInfo(func.target).signedness) { - .signed => return @truncate(i32, val.toSignedInt()), + .signed => return @truncate(i32, val.toSignedInt(target)), .unsigned => return @bitCast(i32, @truncate(u32, val.toUnsignedInt(target))), }, .ErrorSet => { const kv = func.bin_file.base.options.module.?.getErrorValue(val.getError().?) catch unreachable; // passed invalid `Value` to function return @bitCast(i32, kv.value); }, - .Bool => return @intCast(i32, val.toSignedInt()), - .Pointer => return @intCast(i32, val.toSignedInt()), + .Bool => return @intCast(i32, val.toSignedInt(target)), + .Pointer => return @intCast(i32, val.toSignedInt(target)), else => unreachable, // Programmer called this function for an illegal type } } diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 9efd50aec4..9b11eed1f2 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -7007,7 +7007,7 @@ fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { .Int => { const info = typed_value.ty.intInfo(self.target.*); if (info.bits <= ptr_bits and info.signedness == .signed) { - return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt()) }; + return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt(target)) }; } if (!(info.bits > ptr_bits or info.signedness == .signed)) { return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; diff --git a/src/codegen.zig b/src/codegen.zig index 5a28ce1860..0a20153396 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -459,7 +459,7 @@ pub fn generateSymbol( if (info.bits <= 8) { const x: u8 = switch (info.signedness) { .unsigned => @intCast(u8, typed_value.val.toUnsignedInt(target)), - .signed => @bitCast(u8, @intCast(i8, typed_value.val.toSignedInt())), + .signed => @bitCast(u8, @intCast(i8, typed_value.val.toSignedInt(target))), }; try code.append(x); return Result{ .appended = {} }; @@ -488,13 +488,13 @@ pub fn generateSymbol( }, .signed => { if (info.bits <= 16) { - const x = @intCast(i16, typed_value.val.toSignedInt()); + const x = @intCast(i16, typed_value.val.toSignedInt(target)); mem.writeInt(i16, try code.addManyAsArray(2), x, endian); } else if (info.bits <= 32) { - const x = @intCast(i32, typed_value.val.toSignedInt()); + const x = @intCast(i32, typed_value.val.toSignedInt(target)); mem.writeInt(i32, try code.addManyAsArray(4), x, endian); } else { - const x = typed_value.val.toSignedInt(); + const x = typed_value.val.toSignedInt(target); mem.writeInt(i64, try code.addManyAsArray(8), x, endian); } }, @@ -536,13 +536,13 @@ pub fn generateSymbol( }, .signed => { if (info.bits <= 16) { - const x = @intCast(i16, int_val.toSignedInt()); + const x = @intCast(i16, int_val.toSignedInt(target)); mem.writeInt(i16, try code.addManyAsArray(2), x, endian); } else if (info.bits <= 32) { - const x = @intCast(i32, int_val.toSignedInt()); + const x = @intCast(i32, int_val.toSignedInt(target)); mem.writeInt(i32, try code.addManyAsArray(4), x, endian); } else { - const x = int_val.toSignedInt(); + const x = int_val.toSignedInt(target); mem.writeInt(i64, try code.addManyAsArray(8), x, endian); } }, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6215a11e6f..3fd1effc21 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -8932,7 +8932,7 @@ pub const FuncGen = struct { if (elem.isUndef()) { val.* = llvm_i32.getUndef(); } else { - const int = elem.toSignedInt(); + const int = elem.toSignedInt(self.dg.module.getTarget()); const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int + a_len); val.* = llvm_i32.constInt(unsigned, .False); } diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index ada3918baf..bd0c8bc53c 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -360,7 +360,7 @@ pub const DeclGen = struct { // Note, value is required to be sign-extended, so we don't need to mask off the upper bits. // See https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#Literal - var int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt()) else val.toUnsignedInt(target); + var int_bits = if (ty.isSignedInt()) @bitCast(u64, val.toSignedInt(target)) else val.toUnsignedInt(target); const value: spec.LiteralContextDependentNumber = switch (backing_bits) { 1...32 => .{ .uint32 = @truncate(u32, int_bits) }, @@ -763,7 +763,7 @@ pub const DeclGen = struct { if (elem.isUndef()) { self.func.body.writeOperand(spec.LiteralInteger, 0xFFFF_FFFF); } else { - const int = elem.toSignedInt(); + const int = elem.toSignedInt(self.getTarget()); const unsigned = if (int >= 0) @intCast(u32, int) else @intCast(u32, ~int + a_len); self.func.body.writeOperand(spec.LiteralInteger, unsigned); } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 843d14edd7..9e14695344 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -409,7 +409,7 @@ pub const DeclState = struct { // See https://github.com/ziglang/zig/issues/645 var int_buffer: Value.Payload.U64 = undefined; const field_int_val = value.enumToInt(ty, &int_buffer); - break :value @bitCast(u64, field_int_val.toSignedInt()); + break :value @bitCast(u64, field_int_val.toSignedInt(target)); } else @intCast(u64, field_i); mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), value, target_endian); } diff --git a/src/value.zig b/src/value.zig index adff60983f..d3035946f9 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1201,8 +1201,8 @@ pub const Value = extern union { } /// Asserts the value is an integer and it fits in a i64 - pub fn toSignedInt(self: Value) i64 { - switch (self.tag()) { + pub fn toSignedInt(val: Value, target: Target) i64 { + switch (val.tag()) { .zero, .bool_false, .the_only_possible_value, // i0, u0 @@ -1212,10 +1212,19 @@ pub const Value = extern union { .bool_true, => return 1, - .int_u64 => return @intCast(i64, self.castTag(.int_u64).?.data), - .int_i64 => return self.castTag(.int_i64).?.data, - .int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().to(i64) catch unreachable, - .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().to(i64) catch unreachable, + .int_u64 => return @intCast(i64, val.castTag(.int_u64).?.data), + .int_i64 => return val.castTag(.int_i64).?.data, + .int_big_positive => return val.castTag(.int_big_positive).?.asBigInt().to(i64) catch unreachable, + .int_big_negative => return val.castTag(.int_big_negative).?.asBigInt().to(i64) catch unreachable, + + .lazy_align => { + const ty = val.castTag(.lazy_align).?.data; + return @intCast(i64, ty.abiAlignment(target)); + }, + .lazy_size => { + const ty = val.castTag(.lazy_size).?.data; + return @intCast(i64, ty.abiSize(target)); + }, .undef => unreachable, else => unreachable, diff --git a/test/behavior.zig b/test/behavior.zig index 4e959fcf7d..3b6eb9c6ef 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -90,6 +90,7 @@ test { _ = @import("behavior/bugs/12430.zig"); _ = @import("behavior/bugs/12486.zig"); _ = @import("behavior/bugs/12488.zig"); + _ = @import("behavior/bugs/12498.zig"); _ = @import("behavior/bugs/12551.zig"); _ = @import("behavior/bugs/12644.zig"); _ = @import("behavior/bugs/12680.zig"); diff --git a/test/behavior/bugs/12498.zig b/test/behavior/bugs/12498.zig new file mode 100644 index 0000000000..3e4bafc2db --- /dev/null +++ b/test/behavior/bugs/12498.zig @@ -0,0 +1,8 @@ +const std = @import("std"); +const expect = std.testing.expect; + +const S = struct { a: usize }; +test "lazy abi size used in comparison" { + var rhs: i32 = 100; + try expect(@sizeOf(S) < rhs); +} From e2509ddbe69a56bb1f4a56b46946b2a706d5aabe Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Dec 2022 20:16:47 +0200 Subject: [PATCH 5/8] AstGen: add error for invalid string comparisons These operations are allowed because the string literals are just pointers but they produce unexpected results. These errors prevent beginners from shooting themselves in the foot while still allowing advanced users to circumvent them if they desire to do so. Closes #8290 --- src/AstGen.zig | 13 +++++++++ .../compile_errors/invalid_compare_string.zig | 29 +++++++++++++++++++ test/cases/compile_errors/switch_on_slice.zig | 2 +- 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/invalid_compare_string.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index b18d550347..1ec0cfa78f 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -5628,6 +5628,14 @@ fn simpleBinOp( const tree = astgen.tree; const node_datas = tree.nodes.items(.data); + if (op_inst_tag == .cmp_neq or op_inst_tag == .cmp_eq) { + const node_tags = tree.nodes.items(.tag); + const str = if (op_inst_tag == .cmp_eq) "==" else "!="; + if (node_tags[node_datas[node].lhs] == .string_literal or + node_tags[node_datas[node].rhs] == .string_literal) + return astgen.failNode(node, "cannot compare strings with {s}", .{str}); + } + const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node); var line: u32 = undefined; var column: u32 = undefined; @@ -6625,6 +6633,11 @@ fn switchExpr( continue; } + for (case.ast.values) |val| { + if (node_tags[val] == .string_literal) + return astgen.failNode(val, "cannot switch on strings", .{}); + } + if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) { scalar_cases_len += 1; } else { diff --git a/test/cases/compile_errors/invalid_compare_string.zig b/test/cases/compile_errors/invalid_compare_string.zig new file mode 100644 index 0000000000..a5c7f041a5 --- /dev/null +++ b/test/cases/compile_errors/invalid_compare_string.zig @@ -0,0 +1,29 @@ +comptime { + var a = "foo"; + if (a == "foo") unreachable; +} +comptime { + var a = "foo"; + if (a == ("foo")) unreachable; // intentionally allow +} +comptime { + var a = "foo"; + switch (a) { + "foo" => unreachable, + else => {}, + } +} +comptime { + var a = "foo"; + switch (a) { + ("foo") => unreachable, // intentionally allow + else => {}, + } +} + +// error +// backend=stage2 +// target=native +// +// :3:11: error: cannot compare strings with == +// :12:9: error: cannot switch on strings diff --git a/test/cases/compile_errors/switch_on_slice.zig b/test/cases/compile_errors/switch_on_slice.zig index b4644b132c..c2b28f67ab 100644 --- a/test/cases/compile_errors/switch_on_slice.zig +++ b/test/cases/compile_errors/switch_on_slice.zig @@ -1,7 +1,7 @@ pub export fn entry() void { var a: [:0]const u8 = "foo"; switch (a) { - "--version", "version" => unreachable, + ("--version"), ("version") => unreachable, else => {}, } } From f20e449fd6ad63c58e76670f230783d0dd399b93 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Dec 2022 20:56:40 +0200 Subject: [PATCH 6/8] Sema: improve error for mismatched type in implicit return Closes #2653 --- src/AstGen.zig | 12 +++---- src/Sema.zig | 32 ++++++++++++++++--- src/Zir.zig | 8 ++--- src/print_zir.zig | 2 +- src/type.zig | 11 +++++++ .../hello_world_with_updates.1.zig | 4 +-- ...ntrol_reaches_end_of_non-void_function.zig | 9 ------ .../type_error_in_implicit_return.zig | 17 ++++++++++ .../hello_world_with_updates.1.zig | 6 ++-- .../hello_world_with_updates.1.zig | 4 +-- .../hello_world_with_updates.1.zig | 4 +-- 11 files changed, 75 insertions(+), 34 deletions(-) delete mode 100644 test/cases/compile_errors/control_reaches_end_of_non-void_function.zig create mode 100644 test/cases/compile_errors/type_error_in_implicit_return.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 1ec0cfa78f..4e571ffda9 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2632,7 +2632,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As .compile_error, .ret_node, .ret_load, - .ret_tok, + .ret_implicit, .ret_err_value, .@"unreachable", .repeat, @@ -3914,9 +3914,8 @@ fn fnDecl( // As our last action before the return, "pop" the error trace if needed _ = try gz.addRestoreErrRetIndex(.ret, .always); - // Since we are adding the return instruction here, we must handle the coercion. - // We do this by using the `ret_tok` instruction. - _ = try fn_gz.addUnTok(.ret_tok, .void_value, tree.lastToken(body_node)); + // Add implicit return at end of function. + _ = try fn_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node)); } break :func try decl_gz.addFunc(.{ @@ -4334,9 +4333,8 @@ fn testDecl( // As our last action before the return, "pop" the error trace if needed _ = try gz.addRestoreErrRetIndex(.ret, .always); - // Since we are adding the return instruction here, we must handle the coercion. - // We do this by using the `ret_tok` instruction. - _ = try fn_block.addUnTok(.ret_tok, .void_value, tree.lastToken(body_node)); + // Add implicit return at end of function. + _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node)); } const func_inst = try decl_block.addFunc(.{ diff --git a/src/Sema.zig b/src/Sema.zig index ad89408c26..bd7bdb48cb 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1098,7 +1098,7 @@ fn analyzeBodyInner( // These functions match the return type of analyzeBody so that we can // tail call them here. .compile_error => break sema.zirCompileError(block, inst), - .ret_tok => break sema.zirRetTok(block, inst), + .ret_implicit => break sema.zirRetImplicit(block, inst), .ret_node => break sema.zirRetNode(block, inst), .ret_load => break sema.zirRetLoad(block, inst), .ret_err_value => break sema.zirRetErrValue(block, inst), @@ -16546,7 +16546,7 @@ fn zirRetErrValue( return sema.analyzeRet(block, result_inst, src); } -fn zirRetTok( +fn zirRetImplicit( sema: *Sema, block: *Block, inst: Zir.Inst.Index, @@ -16556,9 +16556,33 @@ fn zirRetTok( const inst_data = sema.code.instructions.items(.data)[inst].un_tok; const operand = try sema.resolveInst(inst_data.operand); - const src = inst_data.src(); - return sema.analyzeRet(block, operand, src); + const r_brace_src = inst_data.src(); + const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 }; + const base_tag = sema.fn_ret_ty.baseZigTypeTag(); + if (base_tag == .NoReturn) { + const msg = msg: { + const msg = try sema.errMsg(block, ret_ty_src, "function declared '{}' implicitly returns", .{ + sema.fn_ret_ty.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } else if (base_tag != .Void) { + const msg = msg: { + const msg = try sema.errMsg(block, ret_ty_src, "function with non-void return type '{}' implicitly returns", .{ + sema.fn_ret_ty.fmt(sema.mod), + }); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, r_brace_src, msg, "control flow reaches end of body here", .{}); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + + return sema.analyzeRet(block, operand, .unneeded); } fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { diff --git a/src/Zir.zig b/src/Zir.zig index c7e1d22d22..ed425ea73e 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -519,7 +519,7 @@ pub const Inst = struct { /// Includes an operand as the return value. /// Includes a token source location. /// Uses the `un_tok` union field. - ret_tok, + ret_implicit, /// Sends control flow back to the function's callee. /// The return operand is `error.foo` where `foo` is given by the string. /// If the current function has an inferred error set, the error given by the @@ -1256,7 +1256,7 @@ pub const Inst = struct { .compile_error, .ret_node, .ret_load, - .ret_tok, + .ret_implicit, .ret_err_value, .@"unreachable", .repeat, @@ -1530,7 +1530,7 @@ pub const Inst = struct { .compile_error, .ret_node, .ret_load, - .ret_tok, + .ret_implicit, .ret_err_value, .ret_ptr, .ret_type, @@ -1659,7 +1659,7 @@ pub const Inst = struct { .ref = .un_tok, .ret_node = .un_node, .ret_load = .un_node, - .ret_tok = .un_tok, + .ret_implicit = .un_tok, .ret_err_value = .str_tok, .ret_err_value_code = .str_tok, .ret_ptr = .node, diff --git a/src/print_zir.zig b/src/print_zir.zig index 9135c22010..542f0e977d 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -235,7 +235,7 @@ const Writer = struct { => try self.writeUnNode(stream, inst), .ref, - .ret_tok, + .ret_implicit, .closure_capture, .switch_capture_tag, => try self.writeUnTok(stream, inst), diff --git a/src/type.zig b/src/type.zig index 46b33a34fa..d36bd285f8 100644 --- a/src/type.zig +++ b/src/type.zig @@ -160,6 +160,17 @@ pub const Type = extern union { } } + pub fn baseZigTypeTag(self: Type) std.builtin.TypeId { + return switch (self.zigTypeTag()) { + .ErrorUnion => self.errorUnionPayload().baseZigTypeTag(), + .Optional => { + var buf: Payload.ElemType = undefined; + return self.optionalChild(&buf).baseZigTypeTag(); + }, + else => |t| t, + }; + } + pub fn isSelfComparable(ty: Type, is_equality_cmp: bool) bool { return switch (ty.zigTypeTag()) { .Int, diff --git a/test/cases/aarch64-macos/hello_world_with_updates.1.zig b/test/cases/aarch64-macos/hello_world_with_updates.1.zig index e18a4c6a1e..dcf18bbf87 100644 --- a/test/cases/aarch64-macos/hello_world_with_updates.1.zig +++ b/test/cases/aarch64-macos/hello_world_with_updates.1.zig @@ -2,5 +2,5 @@ pub export fn main() noreturn {} // error // -// :1:32: error: function declared 'noreturn' returns -// :1:22: note: 'noreturn' declared here +// :1:22: error: function declared 'noreturn' implicitly returns +// :1:32: note: control flow reaches end of body here diff --git a/test/cases/compile_errors/control_reaches_end_of_non-void_function.zig b/test/cases/compile_errors/control_reaches_end_of_non-void_function.zig deleted file mode 100644 index c92b6b0927..0000000000 --- a/test/cases/compile_errors/control_reaches_end_of_non-void_function.zig +++ /dev/null @@ -1,9 +0,0 @@ -fn a() i32 {} -export fn entry() void { _ = a(); } - -// error -// backend=stage2 -// target=native -// -// :1:13: error: expected type 'i32', found 'void' -// :1:8: note: function return type declared here diff --git a/test/cases/compile_errors/type_error_in_implicit_return.zig b/test/cases/compile_errors/type_error_in_implicit_return.zig new file mode 100644 index 0000000000..8c8d498c97 --- /dev/null +++ b/test/cases/compile_errors/type_error_in_implicit_return.zig @@ -0,0 +1,17 @@ +fn f1(x: bool) u32 { + if (x) return 1; +} +fn f2() noreturn {} +pub export fn entry() void { + _ = f1(true); + _ = f2(); +} + +// error +// backend=stage2 +// target=native +// +// :1:16: error: function with non-void return type 'u32' implicitly returns +// :3:1: note: control flow reaches end of body here +// :4:9: error: function declared 'noreturn' implicitly returns +// :4:19: note: control flow reaches end of body here diff --git a/test/cases/x86_64-linux/hello_world_with_updates.1.zig b/test/cases/x86_64-linux/hello_world_with_updates.1.zig index 1f1a6a9682..dcf18bbf87 100644 --- a/test/cases/x86_64-linux/hello_world_with_updates.1.zig +++ b/test/cases/x86_64-linux/hello_world_with_updates.1.zig @@ -1,6 +1,6 @@ -pub export fn _start() noreturn {} +pub export fn main() noreturn {} // error // -// :1:34: error: function declared 'noreturn' returns -// :1:24: note: 'noreturn' declared here +// :1:22: error: function declared 'noreturn' implicitly returns +// :1:32: note: control flow reaches end of body here diff --git a/test/cases/x86_64-macos/hello_world_with_updates.1.zig b/test/cases/x86_64-macos/hello_world_with_updates.1.zig index e18a4c6a1e..dcf18bbf87 100644 --- a/test/cases/x86_64-macos/hello_world_with_updates.1.zig +++ b/test/cases/x86_64-macos/hello_world_with_updates.1.zig @@ -2,5 +2,5 @@ pub export fn main() noreturn {} // error // -// :1:32: error: function declared 'noreturn' returns -// :1:22: note: 'noreturn' declared here +// :1:22: error: function declared 'noreturn' implicitly returns +// :1:32: note: control flow reaches end of body here diff --git a/test/cases/x86_64-windows/hello_world_with_updates.1.zig b/test/cases/x86_64-windows/hello_world_with_updates.1.zig index e18a4c6a1e..dcf18bbf87 100644 --- a/test/cases/x86_64-windows/hello_world_with_updates.1.zig +++ b/test/cases/x86_64-windows/hello_world_with_updates.1.zig @@ -2,5 +2,5 @@ pub export fn main() noreturn {} // error // -// :1:32: error: function declared 'noreturn' returns -// :1:22: note: 'noreturn' declared here +// :1:22: error: function declared 'noreturn' implicitly returns +// :1:32: note: control flow reaches end of body here From 74285a4ed70848452f34623454d665daae7f9522 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Dec 2022 21:01:07 +0200 Subject: [PATCH 7/8] add test for error message improved by self-hosted Closes #5099 --- .../struct_type_mismatch_in_arg.zig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/cases/compile_errors/struct_type_mismatch_in_arg.zig diff --git a/test/cases/compile_errors/struct_type_mismatch_in_arg.zig b/test/cases/compile_errors/struct_type_mismatch_in_arg.zig new file mode 100644 index 0000000000..3e53189f2f --- /dev/null +++ b/test/cases/compile_errors/struct_type_mismatch_in_arg.zig @@ -0,0 +1,17 @@ +const Foo = struct { i: i32 }; +const Bar = struct { j: i32 }; + +pub fn helper(_: Foo, _: Bar) void { } + +comptime { + helper(Bar { .j = 10 }, Bar { .j = 10 }); + helper(Bar { .i = 10 }, Bar { .j = 10 }); +} + +// error +// backend=stage2 +// target=native +// +// :7:16: error: expected type 'tmp.Foo', found 'tmp.Bar' +// :1:13: note: struct declared here +// :2:13: note: struct declared here From b500e0eb179218f5eb03408c09b5e5a928f0c46e Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 2 Dec 2022 21:36:41 +0200 Subject: [PATCH 8/8] Sema: add "parameter type declared here" note to type coercion --- src/Sema.zig | 63 +++++++++++++++---- ...ction_passing_array_instead_of_pointer.zig | 1 + ..._bit_offset_pointer_to_regular_pointer.zig | 1 + ...et_in_param_ty_instantiate_incorrectly.zig | 1 + ...ted_pointer_to_null-terminated_pointer.zig | 1 + .../double_pointer_to_anyopaque_pointer.zig | 1 + ...mplicitly_increasing_pointer_alignment.zig | 1 + .../pass_const_ptr_to_mutable_ptr_fn.zig | 1 + .../struct_init_passed_to_type_param.zig | 1 + .../struct_type_mismatch_in_arg.zig | 1 + ...ointer_coerced_to_pointer_to_opaque_{}.zig | 1 + 11 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index bd7bdb48cb..09fd184320 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -291,8 +291,8 @@ pub const Block = struct { try sema.errNote(ci.block, ci.src, parent, prefix ++ "it is inside a @cImport", .{}); }, .comptime_ret_ty => |rt| { - const src_loc = if (try sema.funcDeclSrc(rt.func)) |capture| blk: { - var src_loc = capture; + const src_loc = if (try sema.funcDeclSrc(rt.func)) |fn_decl| blk: { + var src_loc = fn_decl.srcLoc(); src_loc.lazy = .{ .node_offset_fn_type_ret_ty = 0 }; break :blk src_loc; } else blk: { @@ -5843,7 +5843,7 @@ fn lookupInNamespace( return null; } -fn funcDeclSrc(sema: *Sema, func_inst: Air.Inst.Ref) !?Module.SrcLoc { +fn funcDeclSrc(sema: *Sema, func_inst: Air.Inst.Ref) !?*Decl { const func_val = (try sema.resolveMaybeUndefVal(func_inst)) orelse return null; if (func_val.isUndef()) return null; const owner_decl_index = switch (func_val.tag()) { @@ -5852,8 +5852,7 @@ fn funcDeclSrc(sema: *Sema, func_inst: Air.Inst.Ref) !?Module.SrcLoc { .decl_ref => sema.mod.declPtr(func_val.castTag(.decl_ref).?.data).val.castTag(.function).?.data.owner_decl, else => return null, }; - const owner_decl = sema.mod.declPtr(owner_decl_index); - return owner_decl.srcLoc(); + return sema.mod.declPtr(owner_decl_index); } pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref { @@ -6031,7 +6030,7 @@ fn zirCall( break :check_args; } - const decl_src = try sema.funcDeclSrc(func); + const maybe_decl = try sema.funcDeclSrc(func); const member_str = if (bound_arg_src != null) "member function " else ""; const variadic_str = if (func_ty_info.is_var_args) "at least " else ""; const msg = msg: { @@ -6048,7 +6047,7 @@ fn zirCall( ); errdefer msg.destroy(sema.gpa); - if (decl_src) |some| try sema.mod.errNoteNonLazy(some, msg, "function declared here", .{}); + if (maybe_decl) |fn_decl| try sema.mod.errNoteNonLazy(fn_decl.srcLoc(), msg, "function declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -6242,7 +6241,7 @@ fn analyzeCall( const func_ty_info = func_ty.fnInfo(); const cc = func_ty_info.cc; if (cc == .Naked) { - const decl_src = try sema.funcDeclSrc(func); + const maybe_decl = try sema.funcDeclSrc(func); const msg = msg: { const msg = try sema.errMsg( block, @@ -6252,7 +6251,7 @@ fn analyzeCall( ); errdefer msg.destroy(sema.gpa); - if (decl_src) |some| try sema.mod.errNoteNonLazy(some, msg, "function declared here", .{}); + if (maybe_decl) |fn_decl| try sema.mod.errNoteNonLazy(fn_decl.srcLoc(), msg, "function declared here", .{}); break :msg msg; }; return sema.failWithOwnedErrorMsg(msg); @@ -6488,6 +6487,7 @@ fn analyzeCall( &should_memoize, memoized_call_key, func_ty_info.param_types, + func, ) catch |err| switch (err) { error.NeededSourceLocation => { _ = sema.inst_map.remove(inst); @@ -6504,6 +6504,7 @@ fn analyzeCall( &should_memoize, memoized_call_key, func_ty_info.param_types, + func, ); return error.AnalysisFail; }, @@ -6646,12 +6647,17 @@ fn analyzeCall( const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); for (uncasted_args) |uncasted_arg, i| { if (i < fn_params_len) { + const opts: CoerceOpts = .{ .param_src = .{ + .func_inst = func, + .param_i = @intCast(u32, i), + } }; const param_ty = func_ty.fnParamType(i); args[i] = sema.analyzeCallArg( block, .unneeded, param_ty, uncasted_arg, + opts, ) catch |err| switch (err) { error.NeededSourceLocation => { const decl = sema.mod.declPtr(block.src_decl); @@ -6660,6 +6666,7 @@ fn analyzeCall( Module.argSrc(call_src.node_offset.x, sema.gpa, decl, i, bound_arg_src), param_ty, uncasted_arg, + opts, ); return error.AnalysisFail; }, @@ -6741,6 +6748,7 @@ fn analyzeInlineCallArg( should_memoize: *bool, memoized_call_key: Module.MemoizedCall.Key, raw_param_types: []const Type, + func_inst: Air.Inst.Ref, ) !void { const zir_tags = sema.code.instructions.items(.tag); switch (zir_tags[inst]) { @@ -6765,7 +6773,13 @@ fn analyzeInlineCallArg( return err; }; } - const casted_arg = try sema.coerce(arg_block, param_ty, uncasted_arg, arg_src); + const casted_arg = sema.coerceExtra(arg_block, param_ty, uncasted_arg, arg_src, .{ .param_src = .{ + .func_inst = func_inst, + .param_i = @intCast(u32, arg_i.*), + } }) catch |err| switch (err) { + error.NotCoercible => unreachable, + else => |e| return e, + }; if (is_comptime_call) { sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg); @@ -6855,9 +6869,13 @@ fn analyzeCallArg( arg_src: LazySrcLoc, param_ty: Type, uncasted_arg: Air.Inst.Ref, + opts: CoerceOpts, ) !Air.Inst.Ref { try sema.resolveTypeFully(param_ty); - return sema.coerce(block, param_ty, uncasted_arg, arg_src); + return sema.coerceExtra(block, param_ty, uncasted_arg, arg_src, opts) catch |err| switch (err) { + error.NotCoercible => unreachable, + else => |e| return e, + }; } fn analyzeGenericCallArg( @@ -24056,6 +24074,25 @@ const CoerceOpts = struct { is_ret: bool = false, /// Should coercion to comptime_int ermit an error message. no_cast_to_comptime_int: bool = false, + + param_src: struct { + func_inst: Air.Inst.Ref = .none, + param_i: u32 = undefined, + + fn get(info: @This(), sema: *Sema) !?Module.SrcLoc { + if (info.func_inst == .none) return null; + const fn_decl = (try sema.funcDeclSrc(info.func_inst)) orelse return null; + const param_src = Module.paramSrc(0, sema.gpa, fn_decl, info.param_i); + if (param_src == .node_offset_param) { + return Module.SrcLoc{ + .file_scope = fn_decl.getFileScope(), + .parent_decl_node = fn_decl.src_node, + .lazy = LazySrcLoc.nodeOffset(param_src.node_offset_param), + }; + } + return param_src.toSrcLoc(fn_decl); + } + } = .{}, }; fn coerceExtra( @@ -24715,6 +24752,10 @@ fn coerceExtra( } } + if (try opts.param_src.get(sema)) |param_src| { + try sema.mod.errNoteNonLazy(param_src, msg, "parameter type declared here", .{}); + } + // TODO maybe add "cannot store an error in type '{}'" note break :msg msg; diff --git a/test/cases/compile_errors/calling_var_args_extern_function_passing_array_instead_of_pointer.zig b/test/cases/compile_errors/calling_var_args_extern_function_passing_array_instead_of_pointer.zig index 846a5ff81a..c9e2e2e5eb 100644 --- a/test/cases/compile_errors/calling_var_args_extern_function_passing_array_instead_of_pointer.zig +++ b/test/cases/compile_errors/calling_var_args_extern_function_passing_array_instead_of_pointer.zig @@ -8,3 +8,4 @@ pub extern fn foo(format: *const u8, ...) void; // target=native // // :2:16: error: expected type '*const u8', found '[5:0]u8' +// :4:27: note: parameter type declared here diff --git a/test/cases/compile_errors/casting_bit_offset_pointer_to_regular_pointer.zig b/test/cases/compile_errors/casting_bit_offset_pointer_to_regular_pointer.zig index 132627557e..7fd38280c6 100644 --- a/test/cases/compile_errors/casting_bit_offset_pointer_to_regular_pointer.zig +++ b/test/cases/compile_errors/casting_bit_offset_pointer_to_regular_pointer.zig @@ -21,3 +21,4 @@ export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } // :8:16: error: expected type '*const u3', found '*align(0:3:1) const u3' // :8:16: note: pointer host size '1' cannot cast into pointer host size '0' // :8:16: note: pointer bit offset '3' cannot cast into pointer bit offset '0' +// :11:11: note: parameter type declared here diff --git a/test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig b/test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig index dc533442fb..8855755ec1 100644 --- a/test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig +++ b/test/cases/compile_errors/closure_get_in_param_ty_instantiate_incorrectly.zig @@ -22,3 +22,4 @@ pub export fn entry() void { // target=native // // :17:25: error: expected type 'u32', found 'type' +// :3:21: note: parameter type declared here diff --git a/test/cases/compile_errors/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig b/test/cases/compile_errors/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig index 0074c26de7..3670def4ee 100644 --- a/test/cases/compile_errors/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig +++ b/test/cases/compile_errors/disallow_coercion_from_non-null-terminated_pointer_to_null-terminated_pointer.zig @@ -11,3 +11,4 @@ pub export fn entry() void { // // :5:14: error: expected type '[*:0]const u8', found '[*]const u8' // :5:14: note: destination pointer requires '0' sentinel +// :1:20: note: parameter type declared here diff --git a/test/cases/compile_errors/double_pointer_to_anyopaque_pointer.zig b/test/cases/compile_errors/double_pointer_to_anyopaque_pointer.zig index 6aa9618dbd..c7e54738d1 100644 --- a/test/cases/compile_errors/double_pointer_to_anyopaque_pointer.zig +++ b/test/cases/compile_errors/double_pointer_to_anyopaque_pointer.zig @@ -24,5 +24,6 @@ pub export fn entry3() void { // :4:35: note: cannot implicitly cast double pointer '*const *const usize' to anyopaque pointer '*const anyopaque' // :9:10: error: expected type '?*anyopaque', found '*[*:0]u8' // :9:10: note: cannot implicitly cast double pointer '*[*:0]u8' to anyopaque pointer '?*anyopaque' +// :11:12: note: parameter type declared here // :15:35: error: expected type '*const anyopaque', found '*?*usize' // :15:35: note: cannot implicitly cast double pointer '*?*usize' to anyopaque pointer '*const anyopaque' diff --git a/test/cases/compile_errors/implicitly_increasing_pointer_alignment.zig b/test/cases/compile_errors/implicitly_increasing_pointer_alignment.zig index b9a9fb2faf..13adba1b91 100644 --- a/test/cases/compile_errors/implicitly_increasing_pointer_alignment.zig +++ b/test/cases/compile_errors/implicitly_increasing_pointer_alignment.zig @@ -18,3 +18,4 @@ fn bar(x: *u32) void { // // :8:9: error: expected type '*u32', found '*align(1) u32' // :8:9: note: pointer alignment '1' cannot cast into pointer alignment '4' +// :11:11: note: parameter type declared here diff --git a/test/cases/compile_errors/pass_const_ptr_to_mutable_ptr_fn.zig b/test/cases/compile_errors/pass_const_ptr_to_mutable_ptr_fn.zig index 39c55870f1..fd24b58f55 100644 --- a/test/cases/compile_errors/pass_const_ptr_to_mutable_ptr_fn.zig +++ b/test/cases/compile_errors/pass_const_ptr_to_mutable_ptr_fn.zig @@ -16,3 +16,4 @@ export fn entry() usize { return @sizeOf(@TypeOf(&foo)); } // // :4:19: error: expected type '*[]const u8', found '*const []const u8' // :4:19: note: cast discards const qualifier +// :6:14: note: parameter type declared here diff --git a/test/cases/compile_errors/struct_init_passed_to_type_param.zig b/test/cases/compile_errors/struct_init_passed_to_type_param.zig index b00c27986f..01906ac613 100644 --- a/test/cases/compile_errors/struct_init_passed_to_type_param.zig +++ b/test/cases/compile_errors/struct_init_passed_to_type_param.zig @@ -12,3 +12,4 @@ export const value = hi(MyStruct{ .x = 12 }); // // :7:33: error: expected type 'type', found 'tmp.MyStruct' // :1:18: note: struct declared here +// :3:19: note: parameter type declared here diff --git a/test/cases/compile_errors/struct_type_mismatch_in_arg.zig b/test/cases/compile_errors/struct_type_mismatch_in_arg.zig index 3e53189f2f..a52bdfab6c 100644 --- a/test/cases/compile_errors/struct_type_mismatch_in_arg.zig +++ b/test/cases/compile_errors/struct_type_mismatch_in_arg.zig @@ -15,3 +15,4 @@ comptime { // :7:16: error: expected type 'tmp.Foo', found 'tmp.Bar' // :1:13: note: struct declared here // :2:13: note: struct declared here +// :4:18: note: parameter type declared here diff --git a/test/cases/compile_errors/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig b/test/cases/compile_errors/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig index e22c285cb0..a050eb6a4c 100644 --- a/test/cases/compile_errors/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig +++ b/test/cases/compile_errors/wrong_pointer_coerced_to_pointer_to_opaque_{}.zig @@ -12,3 +12,4 @@ export fn foo() void { // :5:9: error: expected type '*tmp.Derp', found '*anyopaque' // :5:9: note: pointer type child 'anyopaque' cannot cast into pointer type child 'tmp.Derp' // :1:14: note: opaque declared here +// :2:18: note: parameter type declared here