From b2f02a820f1ed46721ed55243cead52efed055d7 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 21 Aug 2022 17:07:22 +0300 Subject: [PATCH 1/7] Sema: check for astgen failures in `semaStructFields` The struct might be a top level struct in which case it might not have Zir. Closes #12548 --- src/Sema.zig | 4 +++- test/compile_errors.zig | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index f3ddf21206..582299bc9e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -27909,7 +27909,9 @@ fn resolveInferredErrorSetTy( fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void { const gpa = mod.gpa; const decl_index = struct_obj.owner_decl; - const zir = struct_obj.namespace.file_scope.zir; + const file_scope = struct_obj.namespace.file_scope; + if (file_scope.status != .success_zir) return error.AnalysisFail; + const zir = file_scope.zir; const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; assert(extended.opcode == .struct_decl); const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f8ede21a9d..60de07d1e3 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -184,7 +184,7 @@ pub fn addCases(ctx: *TestContext) !void { } { - const case = ctx.obj("argument causes error ", .{}); + const case = ctx.obj("argument causes error", .{}); case.backend = .stage2; case.addSourceFile("b.zig", @@ -208,6 +208,23 @@ pub fn addCases(ctx: *TestContext) !void { }); } + { + const case = ctx.obj("astgen failure in file struct", .{}); + case.backend = .stage2; + + case.addSourceFile("b.zig", + \\bad + ); + + case.addError( + \\pub export fn entry() void { + \\ _ = (@sizeOf(@import("b.zig"))); + \\} + , &[_][]const u8{ + ":1:1: error: struct field missing type", + }); + } + // TODO test this in stage2, but we won't even try in stage1 //ctx.objErrStage1("inline fn calls itself indirectly", // \\export fn foo() void { From b55a5007faad1de054e86e00bfdc9a58e5fc4ff8 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Sun, 21 Aug 2022 18:04:46 +0300 Subject: [PATCH 2/7] Sema: fix parameter of type 'T' must be comptime error Closes #12519 Closes #12505 --- doc/langref.html.in | 4 ++-- lib/std/fs/path.zig | 2 +- lib/std/math.zig | 2 +- lib/std/math/float.zig | 2 +- lib/std/zig/c_translation.zig | 2 +- lib/std/zig/parse.zig | 2 +- src/Module.zig | 8 +++---- src/Sema.zig | 18 ++++++++++----- src/type.zig | 2 +- test/behavior/cast.zig | 2 +- test/behavior/error.zig | 2 +- test/behavior/fn.zig | 2 +- test/behavior/struct.zig | 2 +- ...omptime_parameter_not_declared_as_such.zig | 23 +++++++++++++++++++ 14 files changed, 51 insertions(+), 22 deletions(-) create mode 100644 test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig diff --git a/doc/langref.html.in b/doc/langref.html.in index 9247d3bc43..04d736609a 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5023,8 +5023,8 @@ fn shiftLeftOne(a: u32) callconv(.Inline) u32 { // Another file can use @import and call sub2 pub fn sub2(a: i8, b: i8) i8 { return a - b; } -// Functions can be used as values and are equivalent to pointers. -const call2_op = fn (a: i8, b: i8) i8; +// Function pointers are prefixed with `*const `. +const call2_op = *const fn (a: i8, b: i8) i8; fn do_op(fn_call: call2_op, op1: i8, op2: i8) i8 { return fn_call(op1, op2); } diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index d5583dcc80..9dc3367688 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -42,7 +42,7 @@ pub fn isSep(byte: u8) bool { /// This is different from mem.join in that the separator will not be repeated if /// it is found at the end or beginning of a pair of consecutive paths. -fn joinSepMaybeZ(allocator: Allocator, separator: u8, sepPredicate: fn (u8) bool, paths: []const []const u8, zero: bool) ![]u8 { +fn joinSepMaybeZ(allocator: Allocator, separator: u8, comptime sepPredicate: fn (u8) bool, paths: []const []const u8, zero: bool) ![]u8 { if (paths.len == 0) return if (zero) try allocator.dupe(u8, &[1]u8{0}) else &[0]u8{}; // Find first non-empty path index. diff --git a/lib/std/math.zig b/lib/std/math.zig index 40b5eb9204..1ed9604612 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -1548,7 +1548,7 @@ test "boolMask" { } /// Return the mod of `num` with the smallest integer type -pub fn comptimeMod(num: anytype, denom: comptime_int) IntFittingRange(0, denom - 1) { +pub fn comptimeMod(num: anytype, comptime denom: comptime_int) IntFittingRange(0, denom - 1) { return @intCast(IntFittingRange(0, denom - 1), @mod(num, denom)); } diff --git a/lib/std/math/float.zig b/lib/std/math/float.zig index 30e456fcbd..768cc03285 100644 --- a/lib/std/math/float.zig +++ b/lib/std/math/float.zig @@ -8,7 +8,7 @@ inline fn mantissaOne(comptime T: type) comptime_int { } /// Creates floating point type T from an unbiased exponent and raw mantissa. -inline fn reconstructFloat(comptime T: type, exponent: comptime_int, mantissa: comptime_int) T { +inline fn reconstructFloat(comptime T: type, comptime exponent: comptime_int, comptime mantissa: comptime_int) T { const TBits = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } }); const biased_exponent = @as(TBits, exponent + floatExponentMax(T)); return @bitCast(T, (biased_exponent << floatMantissaBits(T)) | @as(TBits, mantissa)); diff --git a/lib/std/zig/c_translation.zig b/lib/std/zig/c_translation.zig index 8a2086e9ad..348e3a7133 100644 --- a/lib/std/zig/c_translation.zig +++ b/lib/std/zig/c_translation.zig @@ -349,7 +349,7 @@ test "shuffleVectorIndex" { /// Constructs a [*c] pointer with the const and volatile annotations /// from SelfType for pointing to a C flexible array of ElementType. -pub fn FlexibleArrayType(comptime SelfType: type, ElementType: type) type { +pub fn FlexibleArrayType(comptime SelfType: type, comptime ElementType: type) type { switch (@typeInfo(SelfType)) { .Pointer => |ptr| { return @Type(.{ .Pointer = .{ diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index a03764a91c..fda6ad98b9 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -3670,7 +3670,7 @@ const Parser = struct { } /// KEYWORD_if LPAREN Expr RPAREN PtrPayload? Body (KEYWORD_else Payload? Body)? - fn parseIf(p: *Parser, bodyParseFn: fn (p: *Parser) Error!Node.Index) !Node.Index { + fn parseIf(p: *Parser, comptime bodyParseFn: fn (p: *Parser) Error!Node.Index) !Node.Index { const if_token = p.eatToken(.keyword_if) orelse return null_node; _ = try p.expectToken(.l_paren); const condition = try p.expectExpr(); diff --git a/src/Module.zig b/src/Module.zig index 3577115ded..45e0779c54 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -6072,17 +6072,17 @@ pub fn paramSrc( else => unreachable, }; var it = full.iterate(tree); - while (true) { - if (it.param_i == param_i) { - const param = it.next().?; + var i: usize = 0; + while (it.next()) |param| : (i += 1) { + if (i == param_i) { if (param.anytype_ellipsis3) |some| { const main_token = tree.nodes.items(.main_token)[decl.src_node]; return .{ .token_offset_param = @bitCast(i32, some) - @bitCast(i32, main_token) }; } return .{ .node_offset_param = decl.nodeIndexToRelative(param.type_expr) }; } - _ = it.next(); } + unreachable; } pub fn argSrc( diff --git a/src/Sema.zig b/src/Sema.zig index 582299bc9e..a3207b8539 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -76,6 +76,8 @@ types_to_resolve: std.ArrayListUnmanaged(Air.Inst.Ref) = .{}, post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .{}, /// Populated with the last compile error created. err: ?*Module.ErrorMsg = null, +/// True when analyzing a generic instantiation. Used to suppress some errors. +is_generic_instantiation: bool = false, const std = @import("std"); const math = std.math; @@ -6495,6 +6497,7 @@ fn instantiateGenericCall( .comptime_args = try new_decl_arena_allocator.alloc(TypedValue, uncasted_args.len), .comptime_args_fn_inst = module_fn.zir_body_inst, .preallocated_new_func = new_module_func, + .is_generic_instantiation = true, }; defer child_sema.deinit(); @@ -7789,6 +7792,7 @@ fn funcCommon( &is_generic, is_extern, cc_workaround, + has_body, ) catch |err| switch (err) { error.NeededSourceLocation => { const decl = sema.mod.declPtr(block.src_decl); @@ -7802,6 +7806,7 @@ fn funcCommon( &is_generic, is_extern, cc_workaround, + has_body, ); return error.AnalysisFail; }, @@ -8005,6 +8010,7 @@ fn analyzeParameter( is_generic: *bool, is_extern: bool, cc: std.builtin.CallingConvention, + has_body: bool, ) !void { const requires_comptime = try sema.typeRequiresComptime(block, param_src, param.ty); comptime_params[i] = param.is_comptime or requires_comptime; @@ -8053,9 +8059,9 @@ fn analyzeParameter( }; return sema.failWithOwnedErrorMsg(msg); } - if (requires_comptime and !param.is_comptime) { + if (!sema.is_generic_instantiation and requires_comptime and !param.is_comptime and has_body) { const msg = msg: { - const msg = try sema.errMsg(block, param_src, "parametter of type '{}' must be declared comptime", .{ + const msg = try sema.errMsg(block, param_src, "parameter of type '{}' must be declared comptime", .{ param.ty.fmt(sema.mod), }); errdefer msg.destroy(sema.gpa); @@ -8153,7 +8159,7 @@ fn zirParam( try block.params.append(sema.gpa, .{ .ty = param_ty, - .is_comptime = is_comptime, + .is_comptime = comptime_syntax, .name = param_name, }); const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison)); @@ -16318,7 +16324,7 @@ fn zirUnaryMath( block: *Block, inst: Zir.Inst.Index, air_tag: Air.Inst.Tag, - eval: fn (Value, Type, Allocator, std.Target) Allocator.Error!Value, + comptime eval: fn (Value, Type, Allocator, std.Target) Allocator.Error!Value, ) CompileError!Air.Inst.Ref { const tracy = trace(@src()); defer tracy.end(); @@ -17777,7 +17783,7 @@ fn zirBitCount( block: *Block, inst: Zir.Inst.Index, air_tag: Air.Inst.Tag, - comptimeOp: fn (val: Value, ty: Type, target: std.Target) u64, + comptime comptimeOp: fn (val: Value, ty: Type, target: std.Target) u64, ) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); @@ -29491,7 +29497,7 @@ pub fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ => { const child_ty = ty.childType(); if (child_ty.zigTypeTag() == .Fn) { - return false; + return child_ty.fnInfo().is_generic; } else { return sema.typeRequiresComptime(block, src, child_ty); } diff --git a/src/type.zig b/src/type.zig index d2885f537f..1592dcf469 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2394,7 +2394,7 @@ pub const Type = extern union { if (ignore_comptime_only) { return true; } else if (ty.childType().zigTypeTag() == .Fn) { - return true; + return !ty.childType().fnInfo().is_generic; } else if (sema_kit) |sk| { return !(try sk.sema.typeRequiresComptime(sk.block, sk.src, ty)); } else { diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index ac3a4daeab..fa0877258c 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1281,7 +1281,7 @@ test "*const [N]null u8 to ?[]const u8" { test "cast between [*c]T and ?[*:0]T on fn parameter" { const S = struct { const Handler = ?fn ([*c]const u8) callconv(.C) void; - fn addCallback(handler: Handler) void { + fn addCallback(comptime handler: Handler) void { _ = handler; } diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 84b18a2738..b355c85819 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -168,7 +168,7 @@ fn entryPtr() void { fooPtr(ptr); } -fn foo2(f: fn () anyerror!void) void { +fn foo2(comptime f: fn () anyerror!void) void { const x = f(); x catch { @panic("fail"); diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 044e4ff049..d68cd89210 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -137,7 +137,7 @@ test "implicit cast function unreachable return" { wantsFnWithVoid(fnWithUnreachable); } -fn wantsFnWithVoid(f: fn () void) void { +fn wantsFnWithVoid(comptime f: fn () void) void { _ = f; } diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index eb29c9038d..12c874f8ba 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -147,7 +147,7 @@ test "fn call of struct field" { return 13; } - fn callStructField(foo: Foo) i32 { + fn callStructField(comptime foo: Foo) i32 { return foo.ptr(); } }; diff --git a/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig b/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig new file mode 100644 index 0000000000..e4d9eed079 --- /dev/null +++ b/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig @@ -0,0 +1,23 @@ +fn f(_: anytype) void {} +fn g(h: *const fn (anytype) void) void { + h({}); +} +pub export fn entry() void { + g(f); +} + +pub fn comptimeMod(num: anytype, denom: comptime_int) void { + _ = num; + _ = denom; +} + +pub export fn entry1() void { + _ = comptimeMod(1, 2); +} + +// error +// backend=stage2 +// target=native +// +// :2:6: error: parameter of type '*const fn(anytype) void' must be declared comptime +// :9:34: error: parameter of type 'comptime_int' must be declared comptime From c1afe57d70606a20af42cb761b4919765295dcd4 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 22 Aug 2022 00:28:08 +0300 Subject: [PATCH 3/7] Sema: resolve lazy values in `resolveMaybeUndefValIntable` Closes #12512 Closes #12513 --- src/Sema.zig | 5 ++++- test/behavior/eval.zig | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index a3207b8539..24bbdb0b0c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1698,7 +1698,10 @@ fn resolveMaybeUndefValIntable( .elem_ptr => check = check.castTag(.elem_ptr).?.data.array_ptr, .eu_payload_ptr, .opt_payload_ptr => check = check.cast(Value.Payload.PayloadPtr).?.data.container_ptr, .generic_poison => return error.GenericPoison, - else => return val, + else => { + try sema.resolveLazyValue(block, src, val); + return val; + }, }; } diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 49183227b5..bc1c3628d7 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -1325,3 +1325,25 @@ test "value in if block is comptime known" { }; comptime try expect(std.mem.eql(u8, first, second)); } + +test "lazy sizeof is resolved in division" { + const A = struct { + a: u32, + }; + const a = 2; + try expect(@sizeOf(A) / a == 2); + try expect(@sizeOf(A) - a == 2); +} + +test "lazy value is resolved as slice operand" { + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + + const A = struct { a: u32 }; + var a: [512]u64 = undefined; + + const ptr1 = a[0..@sizeOf(A)]; + const ptr2 = @ptrCast([*]u8, &a)[0..@sizeOf(A)]; + try expect(@ptrToInt(ptr1) == @ptrToInt(ptr2)); + try expect(ptr1.len == ptr2.len); +} From 74c7782c6083d398a4f0f126a4597c605d5223cd Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 22 Aug 2022 00:33:57 +0300 Subject: [PATCH 4/7] Sema: make orelse with C pointers behave like stage1 for now Closes #12537 --- src/Sema.zig | 2 ++ test/behavior/optional.zig | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/Sema.zig b/src/Sema.zig index 24bbdb0b0c..2355e8374d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7261,6 +7261,8 @@ fn zirOptionalPayload( if (operand_ty.ptrSize() != .C) { return sema.failWithExpectedOptionalType(block, src, operand_ty); } + // TODO https://github.com/ziglang/zig/issues/6597 + if (true) break :t operand_ty; const ptr_info = operand_ty.ptrInfo().data; break :t try Type.ptr(sema.arena, sema.mod, .{ .pointee_type = try ptr_info.pointee_type.copy(sema.arena), diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index c13a3b7e4f..28261daf1f 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -405,3 +405,10 @@ test "optional of noreturn used with orelse" { const val = NoReturn.testOrelse(); try expect(val == 123); } + +test "orelse on C pointer" { + // TODO https://github.com/ziglang/zig/issues/6597 + const foo: [*c]const u8 = "hey"; + const d = foo orelse @compileError("bad"); + try expectEqual([*c]const u8, @TypeOf(d)); +} From b0bcd4add29b6fbbe2bbc22719703d3c81ed594b Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 22 Aug 2022 10:17:58 +0300 Subject: [PATCH 5/7] Sema: allow optional pointers in packed structs Closes #12572 --- src/Sema.zig | 2 +- src/codegen/llvm.zig | 9 ++++++--- test/behavior/packed-struct.zig | 12 ++++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 2355e8374d..9538073e5c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -20565,8 +20565,8 @@ fn validatePackedType(ty: Type) bool { .AnyFrame, .Fn, .Array, - .Optional, => return false, + .Optional => return ty.isPtrLikeOptional(), .Void, .Bool, .Float, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 5c537cd5bc..98458854d1 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3417,7 +3417,10 @@ pub const DeclGen = struct { }); const ty_bit_size = @intCast(u16, field.ty.bitSize(target)); const small_int_ty = dg.context.intType(ty_bit_size); - const small_int_val = non_int_val.constBitCast(small_int_ty); + const small_int_val = if (field.ty.isPtrAtRuntime()) + non_int_val.constPtrToInt(small_int_ty) + else + non_int_val.constBitCast(small_int_ty); const shift_rhs = int_llvm_ty.constInt(running_bits, .False); // If the field is as large as the entire packed struct, this // zext would go from, e.g. i16 to i16. This is legal with @@ -5343,7 +5346,7 @@ pub const FuncGen = struct { const same_size_int = self.context.intType(elem_bits); const truncated_int = self.builder.buildTrunc(shifted_value, same_size_int, ""); return self.builder.buildBitCast(truncated_int, elem_llvm_ty, ""); - } else if (field_ty.zigTypeTag() == .Pointer) { + } else if (field_ty.isPtrAtRuntime()) { const elem_bits = @intCast(c_uint, field_ty.bitSize(target)); const same_size_int = self.context.intType(elem_bits); const truncated_int = self.builder.buildTrunc(shifted_value, same_size_int, ""); @@ -8408,7 +8411,7 @@ pub const FuncGen = struct { const non_int_val = try self.resolveInst(elem); const ty_bit_size = @intCast(u16, field.ty.bitSize(target)); const small_int_ty = self.dg.context.intType(ty_bit_size); - const small_int_val = if (field.ty.zigTypeTag() == .Pointer) + const small_int_val = if (field.ty.isPtrAtRuntime()) self.builder.buildPtrToInt(non_int_val, small_int_ty, "") else self.builder.buildBitCast(non_int_val, small_int_ty, ""); diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 8c34f5741b..540914bd25 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -434,3 +434,15 @@ test "@ptrToInt on a packed struct field" { }; try expect(@ptrToInt(&S.p0.z) - @ptrToInt(&S.p0.x) == 2); } + +test "optional pointer in packed struct" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + + const T = packed struct { ptr: ?*const u8 }; + var n: u8 = 0; + const x = T{ .ptr = &n }; + try expect(x.ptr.? == &n); +} From 560baf67ce77de41da3d03183300cdf1b6d90567 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 22 Aug 2022 10:54:07 +0300 Subject: [PATCH 6/7] Sema: fix implicit cast from extern fn to fn ptr Closes #12570 --- src/Sema.zig | 2 +- test/behavior/fn.zig | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 9538073e5c..42d5e96d3d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22564,7 +22564,7 @@ fn coerceExtra( // Function body to function pointer. if (inst_ty.zigTypeTag() == .Fn) { const fn_val = try sema.resolveConstValue(block, .unneeded, inst, undefined); - const fn_decl = fn_val.castTag(.function).?.data.owner_decl; + const fn_decl = fn_val.pointerDecl().?; const inst_as_ptr = try sema.analyzeDeclRef(fn_decl); return sema.coerce(block, dest_ty, inst_as_ptr, inst_src); } diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index d68cd89210..47ba63c429 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -422,3 +422,24 @@ test "import passed byref to function in return type" { var list = S.get(); try expect(list.items.len == 0); } + +test "implicit cast function to function ptr" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const S1 = struct { + export fn someFunctionThatReturnsAValue() c_int { + return 123; + } + }; + var fnPtr1: *const fn () callconv(.C) c_int = S1.someFunctionThatReturnsAValue; + try expect(fnPtr1() == 123); + const S2 = struct { + extern fn someFunctionThatReturnsAValue() c_int; + }; + var fnPtr2: *const fn () callconv(.C) c_int = S2.someFunctionThatReturnsAValue; + try expect(fnPtr2() == 123); +} From 5404dcdfd844e4b9f47dc49a1f43f0e1075a563f Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 22 Aug 2022 11:13:01 +0300 Subject: [PATCH 7/7] Sema: fix fieldCallBind on tuples and anon structs Closes #12573 --- src/Sema.zig | 30 ++++++++++++++----- .../bogus_method_call_on_slice.zig | 8 +++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 42d5e96d3d..4f558ccae4 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -21394,14 +21394,30 @@ fn fieldCallBind( switch (concrete_ty.zigTypeTag()) { .Struct => { const struct_ty = try sema.resolveTypeFields(block, src, concrete_ty); - const struct_obj = struct_ty.castTag(.@"struct").?.data; + if (struct_ty.castTag(.@"struct")) |struct_obj| { + const field_index_usize = struct_obj.data.fields.getIndex(field_name) orelse + break :find_field; + const field_index = @intCast(u32, field_index_usize); + const field = struct_obj.data.fields.values()[field_index]; - const field_index_usize = struct_obj.fields.getIndex(field_name) orelse - break :find_field; - const field_index = @intCast(u32, field_index_usize); - const field = struct_obj.fields.values()[field_index]; - - return finishFieldCallBind(sema, block, src, ptr_ty, field.ty, field_index, object_ptr); + return finishFieldCallBind(sema, block, src, ptr_ty, field.ty, field_index, object_ptr); + } else if (struct_ty.isTuple()) { + if (mem.eql(u8, field_name, "len")) { + return sema.addIntUnsigned(Type.usize, struct_ty.structFieldCount()); + } + if (std.fmt.parseUnsigned(u32, field_name, 10)) |field_index| { + if (field_index >= struct_ty.structFieldCount()) break :find_field; + return finishFieldCallBind(sema, block, src, ptr_ty, struct_ty.structFieldType(field_index), field_index, object_ptr); + } else |_| {} + } else { + const max = struct_ty.structFieldCount(); + var i: u32 = 0; + while (i < max) : (i += 1) { + if (mem.eql(u8, struct_ty.structFieldName(i), field_name)) { + return finishFieldCallBind(sema, block, src, ptr_ty, struct_ty.structFieldType(i), i, object_ptr); + } + } + } }, .Union => { const union_ty = try sema.resolveTypeFields(block, src, concrete_ty); diff --git a/test/cases/compile_errors/bogus_method_call_on_slice.zig b/test/cases/compile_errors/bogus_method_call_on_slice.zig index b5cb5e472a..ed18f43f48 100644 --- a/test/cases/compile_errors/bogus_method_call_on_slice.zig +++ b/test/cases/compile_errors/bogus_method_call_on_slice.zig @@ -3,9 +3,17 @@ fn f(m: []const u8) void { m.copy(u8, self[0..], m); } export fn entry() usize { return @sizeOf(@TypeOf(&f)); } +pub export fn entry1() void { + .{}.bar(); +} +pub export fn entry2() void { + .{ .foo = 1 }.bar(); +} // error // backend=stage2 // target=native // +// :7:8: error: no field or member function named 'bar' in '@TypeOf(.{})' +// :10:18: error: no field or member function named 'bar' in 'struct{comptime foo: comptime_int = 1}' // :3:6: error: no field or member function named 'copy' in '[]const u8'