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 009d05e5edbaae7849f045b5666f964e224613cd..b18d5503471e9a8a58be5b94712c5212baf6249e 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 0000000000000000000000000000000000000000..3e576a1a6d253da0778c1c82f31c57e943715197 --- /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' -- 2.54.0 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 a18297c8ffea845c533c9d712d3029910b6ab6f5..68d0ac8af51f3bca81a8834057d71fd6220ba2e2 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 c384605e1b522cebb596e21c70f545fb4af7ba75..73ebf3d0aa548f50abaca971919823a267a5c714 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 21cfdf9d733b173df0596d373bb0794488cadd1c..5fcd0f6a26b51bc01c78f61208a4bc518ceaa334 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 be643f65a37ea4c1bf95838235e94773025013ec..adff60983fb440de8217366b1f13036fc400f2d9 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 0000000000000000000000000000000000000000..02ea7e27104a54e73383361e4162460e8cd83fc5 --- /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 -- 2.54.0 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 73ebf3d0aa548f50abaca971919823a267a5c714..cc6e5e95d2d81ee1020dfe52c1882ea4e00355e3 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 5fcd0f6a26b51bc01c78f61208a4bc518ceaa334..46b33a34fae7db30928fb9da2e46970f45c17b29 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 a74b9fdd531ba3880497fe7e6618f53f11b85466..1807b7a44778615c0f31f45cae66dbb11f0437be 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); +} -- 2.54.0 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 3ef8e5319cf94ef651334cd824fc526ab515e0dc..8ae21259ae1d0eeaf147184dc951238b0f4b37cf 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 cc6e5e95d2d81ee1020dfe52c1882ea4e00355e3..ad89408c2625bf94bb081143be8fc473cbd73909 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 52f9a544b1ad8069ee6fcbc7b8b25df7ff1eace7..979472c5a57e528b33b60b7009c39ff2591e4810 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 970b9376e59801ecc80531f11bce9cf9868a01c0..4afadb2c58862d3b232ee2a5af0957527250e556 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 f22849c6529d7b247914fc849d3533ef6330f130..72c6b8021baf75cb4bd2fb7caeb88318aafa98fb 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 ebfff25b2f1aa2580f2d6f180672f4246bb406ca..b11335f4c3b17a5e154946e6f16df1d451f5282a 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 9efd50aec40870ad87a6238f3ab64f6b2a927b00..9b11eed1f21b8defc3c128467e649063a981c158 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 5a28ce18602102f770bddc1def375711598f0926..0a201533967f8f85837dbb813d9d174df5308ada 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 6215a11e6f7106ebc7c9e949e1fdbb80bcebf207..3fd1effc21d058f809f98997170bf72a8661a9f8 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 ada3918bafba2d4692ae87d3427e818c51bdf4ee..bd0c8bc53c1525879b71c7f57805ef8365732aaf 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 843d14edd74d891b594a9b8408e6757b6a26c429..9e146953447f97f4b7f8c1a6f856a3661554ae51 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 adff60983fb440de8217366b1f13036fc400f2d9..d3035946f9800c4cbc8e61c690730450e52f50e6 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 4e959fcf7da9b0833ef256c8b5e6946114d9808d..3b6eb9c6ef0ec884a59f5639c68e986d965a1887 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 0000000000000000000000000000000000000000..3e4bafc2db1ea994be232987a8de38719e9be283 --- /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); +} -- 2.54.0 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 b18d5503471e9a8a58be5b94712c5212baf6249e..1ec0cfa78f0ec3385be94b3bf22d45f8c54ddc24 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 0000000000000000000000000000000000000000..a5c7f041a5649d9608ce6d65ec2b1f43c0864799 --- /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 b4644b132c7dbb1ad2fc2eec62623f3091c4e70c..c2b28f67abb931404b9bb5fa41cf606a1f622820 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 => {}, } } -- 2.54.0 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 1ec0cfa78f0ec3385be94b3bf22d45f8c54ddc24..4e571ffda9cf5d5f80e55ee08b2ac57296a5d00a 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 ad89408c2625bf94bb081143be8fc473cbd73909..bd7bdb48cb356dbd974ef79d8d166715df58be52 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 c7e1d22d22a01aeaf23095465a046d64050410c0..ed425ea73e1e691f238b7f801fd6f0b9db8f5776 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 9135c220107eb9aa52d527e96e9bc7b7d8e6b79f..542f0e977d6ec904ef797c4d434659ba45dc92cf 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 46b33a34fae7db30928fb9da2e46970f45c17b29..d36bd285f8d46329f8ddcb9b6718f29adcb917de 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 e18a4c6a1e13146fa0cef6489284630329a49faf..dcf18bbf87713e6dcaa556378643fc88664023da 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 c92b6b092715d435426a4763a657de647cdbbcf6..0000000000000000000000000000000000000000 --- 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 0000000000000000000000000000000000000000..8c8d498c97cc924283c3922c1599b85d9382a93d --- /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 1f1a6a9682d063f07086a2abae97589d2f799d48..dcf18bbf87713e6dcaa556378643fc88664023da 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 e18a4c6a1e13146fa0cef6489284630329a49faf..dcf18bbf87713e6dcaa556378643fc88664023da 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 e18a4c6a1e13146fa0cef6489284630329a49faf..dcf18bbf87713e6dcaa556378643fc88664023da 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 -- 2.54.0 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 0000000000000000000000000000000000000000..3e53189f2f8b6d9cdae8b7a9be6ce7abbe064c09 --- /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 -- 2.54.0 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 bd7bdb48cb356dbd974ef79d8d166715df58be52..09fd184320004259529dc59fab391b4e277eb219 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 846a5ff81a3686fb7370c5e570315c4cab6533d6..c9e2e2e5eb820250efcb4c319346656a880ad0f2 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 132627557e0493ac39a3b381d0d0f660507fd386..7fd38280c6dc10afe23e3be708b9acdb47d66ba3 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 dc533442fb5d206b99d330ddb56514764d36c31f..8855755ec1b7cc4d2c456e5e94b540f7b0ca8af7 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 0074c26de7344806ce4310c1ea7c35519df854ba..3670def4eec86c8cdcfbbe65c2ac4cb876a1c10b 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 6aa9618dbd2e389a2c3fc232f11694fc2ba542dd..c7e54738d15614173edbe5c2a2f3a9c7d5ace3a1 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 b9a9fb2faf3fd15334b0bae5c73b42cf368843b4..13adba1b9140bd21d762e8b1a9114afd4efa1b2a 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 39c55870f16d4b4ee4a19c4e2af76555f0230087..fd24b58f55637d91f58f5894ba7a0bc681f4fdfc 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 b00c27986fd3fe59a1957a60596c368b7cf8ec0e..01906ac6137c554ae20f4bf6d135e88bb67c8136 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 3e53189f2f8b6d9cdae8b7a9be6ce7abbe064c09..a52bdfab6c59ce12313c261da4a3750e0868a859 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 e22c285cb0d0f1895e05fbdca59cdd9b49190616..a050eb6a4c1ec83b61966fc5b5ef90b25fd40bdf 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 -- 2.54.0