From d10c52c194a093f58df40bc6122f24380f0cc097 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 16 Mar 2024 16:46:45 +0100 Subject: [PATCH] AstGen: disallow alignment on function types A pointer type already has an alignment, so this information does not need to be duplicated on the function type. This already has precedence with addrspace which is already disallowed on function types for this reason. Also fixes `@TypeOf(&func)` to have the correct addrspace and alignment. --- doc/langref.html.in | 10 +- lib/std/builtin.zig | 1 - lib/std/c/darwin.zig | 10 +- lib/std/c/dragonfly.zig | 8 +- lib/std/c/freebsd.zig | 8 +- lib/std/c/haiku.zig | 8 +- lib/std/c/netbsd.zig | 8 +- lib/std/c/openbsd.zig | 12 +- lib/std/c/solaris.zig | 10 +- lib/std/meta.zig | 9 +- lib/std/os/emscripten.zig | 8 +- lib/std/os/linux.zig | 41 ++-- lib/std/zig/AstGen.zig | 14 +- src/InternPool.zig | 28 +-- src/Module.zig | 180 +++++++----------- src/Sema.zig | 43 ++--- src/codegen/c.zig | 4 +- src/codegen/llvm.zig | 4 +- src/type.zig | 10 +- test/behavior/align.zig | 36 ++-- test/behavior/type.zig | 2 - test/behavior/type_info.zig | 42 +++- test/behavior/typename.zig | 6 +- .../compile_errors/function_ptr_alignment.zig | 28 +-- ...nferring_error_set_of_function_pointer.zig | 9 - .../compile_errors/invalid_function_types.zig | 25 +++ ...sing_an_under-aligned_function_pointer.zig | 4 +- .../reify_type.Fn_with_is_generic_true.zig | 1 - ...th_is_var_args_true_and_non-C_callconv.zig | 1 - .../reify_type.Fn_with_return_type_null.zig | 1 - 30 files changed, 259 insertions(+), 312 deletions(-) delete mode 100644 test/cases/compile_errors/inferring_error_set_of_function_pointer.zig create mode 100644 test/cases/compile_errors/invalid_function_types.zig diff --git a/doc/langref.html.in b/doc/langref.html.in index 9d48238843..7ffbcc0110 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2780,10 +2780,16 @@ fn noop4() align(4) void {} test "function alignment" { try expect(derp() == 1234); - try expect(@TypeOf(noop1) == fn () align(1) void); - try expect(@TypeOf(noop4) == fn () align(4) void); + try expect(@TypeOf(derp) == fn () i32); + try expect(@TypeOf(&derp) == *align(@sizeOf(usize) * 2) const fn () i32); + noop1(); + try expect(@TypeOf(noop1) == fn () void); + try expect(@TypeOf(&noop1) == *align(1) const fn () void); + noop4(); + try expect(@TypeOf(noop4) == fn () void); + try expect(@TypeOf(&noop4) == *align(4) const fn () void); } {#code_end#}

diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index d9e72f2019..0238d35c7d 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -420,7 +420,6 @@ pub const Type = union(enum) { /// therefore must be kept in sync with the compiler implementation. pub const Fn = struct { calling_convention: CallingConvention, - alignment: comptime_int, is_generic: bool, is_var_args: bool, /// TODO change the language spec to make this not optional. diff --git a/lib/std/c/darwin.zig b/lib/std/c/darwin.zig index 8dd122c3ba..8442ac9fe0 100644 --- a/lib/std/c/darwin.zig +++ b/lib/std/c/darwin.zig @@ -1053,10 +1053,10 @@ pub const sigset_t = u32; pub const empty_sigset: sigset_t = 0; pub const SIG = struct { - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); - pub const HOLD = @as(?Sigaction.handler_fn, @ptrFromInt(5)); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const HOLD: ?Sigaction.handler_fn = @ptrFromInt(5); /// block specified signal set pub const BLOCK = 1; @@ -1150,7 +1150,7 @@ pub const siginfo_t = extern struct { /// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; handler: extern union { diff --git a/lib/std/c/dragonfly.zig b/lib/std/c/dragonfly.zig index 8cbdd22c40..183a81bba2 100644 --- a/lib/std/c/dragonfly.zig +++ b/lib/std/c/dragonfly.zig @@ -616,9 +616,9 @@ pub const S = struct { pub const BADSIG = SIG.ERR; pub const SIG = struct { - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); pub const BLOCK = 1; pub const UNBLOCK = 2; @@ -690,7 +690,7 @@ pub const empty_sigset = sigset_t{ .__bits = [_]c_uint{0} ** _SIG_WORDS }; pub const sig_atomic_t = c_int; pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; /// signal handler diff --git a/lib/std/c/freebsd.zig b/lib/std/c/freebsd.zig index 94854cf090..a89ca30968 100644 --- a/lib/std/c/freebsd.zig +++ b/lib/std/c/freebsd.zig @@ -695,9 +695,9 @@ pub const SIG = struct { pub const UNBLOCK = 2; pub const SETMASK = 3; - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); pub const WORDS = 4; pub const MAXSIG = 128; @@ -1171,7 +1171,7 @@ const NSIG = 32; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; /// signal handler diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig index 723d953d2d..12b5201acd 100644 --- a/lib/std/c/haiku.zig +++ b/lib/std/c/haiku.zig @@ -441,9 +441,9 @@ pub const SA = struct { }; pub const SIG = struct { - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); pub const HUP = 1; pub const INT = 2; @@ -690,7 +690,7 @@ const NSIG = 32; /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (i32) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (i32) callconv(.C) void; /// signal handler __sigaction_u: extern union { diff --git a/lib/std/c/netbsd.zig b/lib/std/c/netbsd.zig index 475fd55e22..c06857787a 100644 --- a/lib/std/c/netbsd.zig +++ b/lib/std/c/netbsd.zig @@ -800,9 +800,9 @@ pub const winsize = extern struct { const NSIG = 32; pub const SIG = struct { - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); pub const WORDS = 4; pub const MAXSIG = 128; @@ -864,7 +864,7 @@ pub const SIG = struct { /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; /// signal handler diff --git a/lib/std/c/openbsd.zig b/lib/std/c/openbsd.zig index 75a4d6e0e8..4fd450cd5c 100644 --- a/lib/std/c/openbsd.zig +++ b/lib/std/c/openbsd.zig @@ -795,11 +795,11 @@ pub const winsize = extern struct { const NSIG = 33; pub const SIG = struct { - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const CATCH = @as(?Sigaction.handler_fn, @ptrFromInt(2)); - pub const HOLD = @as(?Sigaction.handler_fn, @ptrFromInt(3)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const CATCH: ?Sigaction.handler_fn = @ptrFromInt(2); + pub const HOLD: ?Sigaction.handler_fn = @ptrFromInt(3); pub const HUP = 1; pub const INT = 2; @@ -842,7 +842,7 @@ pub const SIG = struct { /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; /// signal handler diff --git a/lib/std/c/solaris.zig b/lib/std/c/solaris.zig index ef64acd43b..838b6985cc 100644 --- a/lib/std/c/solaris.zig +++ b/lib/std/c/solaris.zig @@ -798,10 +798,10 @@ pub const winsize = extern struct { const NSIG = 75; pub const SIG = struct { - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); - pub const HOLD = @as(?Sigaction.handler_fn, @ptrFromInt(2)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const HOLD: ?Sigaction.handler_fn = @ptrFromInt(2); pub const WORDS = 4; pub const MAXSIG = 75; @@ -874,7 +874,7 @@ pub const SIG = struct { /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; /// signal options diff --git a/lib/std/meta.zig b/lib/std/meta.zig index f7b418d71d..da0f629748 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -57,10 +57,9 @@ test stringToEnum { } /// Returns the alignment of type T. -/// Note that if T is a pointer or function type the result is different than -/// the one returned by @alignOf(T). +/// Note that if T is a pointer type the result is different than the one +/// returned by @alignOf(T). /// If T is a pointer type the alignment of the type it points to is returned. -/// If T is a function type the alignment a target-dependent value is returned. pub fn alignment(comptime T: type) comptime_int { return switch (@typeInfo(T)) { .Optional => |info| switch (@typeInfo(info.child)) { @@ -68,7 +67,6 @@ pub fn alignment(comptime T: type) comptime_int { else => @alignOf(T), }, .Pointer => |info| info.alignment, - .Fn => |info| info.alignment, else => @alignOf(T), }; } @@ -80,7 +78,8 @@ test alignment { try testing.expect(alignment([]align(1) u8) == 1); try testing.expect(alignment([]align(2) u8) == 2); try testing.expect(alignment(fn () void) > 0); - try testing.expect(alignment(fn () align(128) void) == 128); + try testing.expect(alignment(*const fn () void) > 0); + try testing.expect(alignment(*align(128) const fn () void) == 128); } /// Given a parameterized type (array, vector, pointer, optional), returns the "child type". diff --git a/lib/std/os/emscripten.zig b/lib/std/os/emscripten.zig index 04c3996f15..924b2dd0b2 100644 --- a/lib/std/os/emscripten.zig +++ b/lib/std/os/emscripten.zig @@ -689,13 +689,13 @@ pub const SIG = struct { pub const SYS = 31; pub const UNUSED = SIG.SYS; - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(std.math.maxInt(usize))); - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(std.math.maxInt(usize)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); }; pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; handler: extern union { diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 3892e4326a..a5cdab18ce 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1327,16 +1327,14 @@ pub fn flock(fd: fd_t, operation: i32) usize { return syscall2(.flock, @as(usize, @bitCast(@as(isize, fd))), @as(usize, @bitCast(@as(isize, operation)))); } -var vdso_clock_gettime = @as(?*const anyopaque, @ptrCast(&init_vdso_clock_gettime)); - // We must follow the C calling convention when we call into the VDSO -const vdso_clock_gettime_ty = *align(1) const fn (i32, *timespec) callconv(.C) usize; +const VdsoClockGettime = *align(1) const fn (i32, *timespec) callconv(.C) usize; +var vdso_clock_gettime: ?VdsoClockGettime = &init_vdso_clock_gettime; pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { if (@hasDecl(VDSO, "CGT_SYM")) { - const ptr = @atomicLoad(?*const anyopaque, &vdso_clock_gettime, .unordered); - if (ptr) |fn_ptr| { - const f = @as(vdso_clock_gettime_ty, @ptrCast(fn_ptr)); + const ptr = @atomicLoad(?VdsoClockGettime, &vdso_clock_gettime, .unordered); + if (ptr) |f| { const rc = f(clk_id, tp); switch (rc) { 0, @as(usize, @bitCast(-@as(isize, @intFromEnum(E.INVAL)))) => return rc, @@ -1348,15 +1346,12 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { } fn init_vdso_clock_gettime(clk: i32, ts: *timespec) callconv(.C) usize { - const ptr = @as(?*const anyopaque, @ptrFromInt(vdso.lookup(VDSO.CGT_VER, VDSO.CGT_SYM))); + const ptr: ?VdsoClockGettime = @ptrFromInt(vdso.lookup(VDSO.CGT_VER, VDSO.CGT_SYM)); // Note that we may not have a VDSO at all, update the stub address anyway // so that clock_gettime will fall back on the good old (and slow) syscall - @atomicStore(?*const anyopaque, &vdso_clock_gettime, ptr, .monotonic); + @atomicStore(?VdsoClockGettime, &vdso_clock_gettime, ptr, .monotonic); // Call into the VDSO if available - if (ptr) |fn_ptr| { - const f = @as(vdso_clock_gettime_ty, @ptrCast(fn_ptr)); - return f(clk, ts); - } + if (ptr) |f| return f(clk, ts); return @as(usize, @bitCast(-@as(isize, @intFromEnum(E.NOSYS)))); } @@ -2516,9 +2511,9 @@ pub const SIG = if (is_mips) struct { pub const SYS = 31; pub const UNUSED = SIG.SYS; - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); } else if (is_sparc) struct { pub const BLOCK = 1; pub const UNBLOCK = 2; @@ -2560,9 +2555,9 @@ pub const SIG = if (is_mips) struct { pub const PWR = LOST; pub const IO = SIG.POLL; - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); } else struct { pub const BLOCK = 0; pub const UNBLOCK = 1; @@ -2603,9 +2598,9 @@ pub const SIG = if (is_mips) struct { pub const SYS = 31; pub const UNUSED = SIG.SYS; - pub const ERR = @as(?Sigaction.handler_fn, @ptrFromInt(maxInt(usize))); - pub const DFL = @as(?Sigaction.handler_fn, @ptrFromInt(0)); - pub const IGN = @as(?Sigaction.handler_fn, @ptrFromInt(1)); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); }; pub const kernel_rwf = u32; @@ -3709,7 +3704,7 @@ pub const all_mask: sigset_t = [_]u32{0xffffffff} ** @typeInfo(sigset_t).Array.l pub const app_mask: sigset_t = [2]u32{ 0xfffffffc, 0x7fffffff } ++ [_]u32{0xffffffff} ** 30; const k_sigaction_funcs = struct { - const handler = ?*const fn (c_int) align(1) callconv(.C) void; + const handler = ?*align(1) const fn (c_int) callconv(.C) void; const restorer = *const fn () callconv(.C) void; }; @@ -3736,7 +3731,7 @@ pub const k_sigaction = switch (native_arch) { /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. pub const Sigaction = extern struct { - pub const handler_fn = *const fn (c_int) align(1) callconv(.C) void; + pub const handler_fn = *align(1) const fn (c_int) callconv(.C) void; pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void; handler: extern union { diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 364e49ae8f..54efe22b8d 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -1369,16 +1369,16 @@ fn fnProtoExpr( break :is_var_args false; }; - const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { - break :inst try expr(&block_scope, scope, coerced_align_ri, fn_proto.ast.align_expr); - }; + if (fn_proto.ast.align_expr != 0) { + return astgen.failNode(fn_proto.ast.align_expr, "function type cannot have an alignment", .{}); + } if (fn_proto.ast.addrspace_expr != 0) { - return astgen.failNode(fn_proto.ast.addrspace_expr, "addrspace not allowed on function prototypes", .{}); + return astgen.failNode(fn_proto.ast.addrspace_expr, "function type cannot have an addrspace", .{}); } if (fn_proto.ast.section_expr != 0) { - return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{}); + return astgen.failNode(fn_proto.ast.section_expr, "function type cannot have a linksection", .{}); } const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0) @@ -1394,7 +1394,7 @@ fn fnProtoExpr( const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1; const is_inferred_error = token_tags[maybe_bang] == .bang; if (is_inferred_error) { - return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{}); + return astgen.failTok(maybe_bang, "function type cannot have an inferred error set", .{}); } const ret_ty = try expr(&block_scope, scope, coerced_type_ri, fn_proto.ast.return_type); @@ -1403,7 +1403,7 @@ fn fnProtoExpr( .cc_ref = cc, .cc_gz = null, - .align_ref = align_ref, + .align_ref = .none, .align_gz = null, .ret_ref = ret_ty, .ret_gz = null, diff --git a/src/InternPool.zig b/src/InternPool.zig index 832927071e..8611a67564 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -765,16 +765,10 @@ pub const Key = union(enum) { /// Tells whether a parameter is noalias. See `paramIsNoalias` helper /// method for accessing this. noalias_bits: u32, - /// `none` indicates the function has the default alignment for - /// function code on the target. In this case, this field *must* be set - /// to `none`, otherwise the `InternPool` equality and hashing - /// functions will return incorrect results. - alignment: Alignment, cc: std.builtin.CallingConvention, is_var_args: bool, is_generic: bool, is_noinline: bool, - align_is_generic: bool, cc_is_generic: bool, section_is_generic: bool, addrspace_is_generic: bool, @@ -794,7 +788,6 @@ pub const Key = union(enum) { a.return_type == b.return_type and a.comptime_bits == b.comptime_bits and a.noalias_bits == b.noalias_bits and - a.alignment == b.alignment and a.cc == b.cc and a.is_var_args == b.is_var_args and a.is_generic == b.is_generic and @@ -808,7 +801,6 @@ pub const Key = union(enum) { std.hash.autoHash(hasher, self.return_type); std.hash.autoHash(hasher, self.comptime_bits); std.hash.autoHash(hasher, self.noalias_bits); - std.hash.autoHash(hasher, self.alignment); std.hash.autoHash(hasher, self.cc); std.hash.autoHash(hasher, self.is_var_args); std.hash.autoHash(hasher, self.is_generic); @@ -3587,18 +3579,16 @@ pub const Tag = enum(u8) { flags: Flags, pub const Flags = packed struct(u32) { - alignment: Alignment, cc: std.builtin.CallingConvention, is_var_args: bool, is_generic: bool, has_comptime_bits: bool, has_noalias_bits: bool, is_noinline: bool, - align_is_generic: bool, cc_is_generic: bool, section_is_generic: bool, addrspace_is_generic: bool, - _: u9 = 0, + _: u16 = 0, }; }; @@ -4918,11 +4908,9 @@ fn extraFuncType(ip: *const InternPool, extra_index: u32) Key.FuncType { .return_type = type_function.data.return_type, .comptime_bits = comptime_bits, .noalias_bits = noalias_bits, - .alignment = type_function.data.flags.alignment, .cc = type_function.data.flags.cc, .is_var_args = type_function.data.flags.is_var_args, .is_noinline = type_function.data.flags.is_noinline, - .align_is_generic = type_function.data.flags.align_is_generic, .cc_is_generic = type_function.data.flags.cc_is_generic, .section_is_generic = type_function.data.flags.section_is_generic, .addrspace_is_generic = type_function.data.flags.addrspace_is_generic, @@ -6211,8 +6199,6 @@ pub const GetFuncTypeKey = struct { comptime_bits: u32 = 0, noalias_bits: u32 = 0, /// `null` means generic. - alignment: ?Alignment = .none, - /// `null` means generic. cc: ?std.builtin.CallingConvention = .Unspecified, is_var_args: bool = false, is_generic: bool = false, @@ -6242,14 +6228,12 @@ pub fn getFuncType(ip: *InternPool, gpa: Allocator, key: GetFuncTypeKey) Allocat .params_len = params_len, .return_type = key.return_type, .flags = .{ - .alignment = key.alignment orelse .none, .cc = key.cc orelse .Unspecified, .is_var_args = key.is_var_args, .has_comptime_bits = key.comptime_bits != 0, .has_noalias_bits = key.noalias_bits != 0, .is_generic = key.is_generic, .is_noinline = key.is_noinline, - .align_is_generic = key.alignment == null, .cc_is_generic = key.cc == null, .section_is_generic = key.section_is_generic, .addrspace_is_generic = key.addrspace_is_generic, @@ -6433,14 +6417,12 @@ pub fn getFuncDeclIes(ip: *InternPool, gpa: Allocator, key: GetFuncDeclIesKey) A .params_len = params_len, .return_type = @enumFromInt(ip.items.len - 2), .flags = .{ - .alignment = key.alignment orelse .none, .cc = key.cc orelse .Unspecified, .is_var_args = key.is_var_args, .has_comptime_bits = key.comptime_bits != 0, .has_noalias_bits = key.noalias_bits != 0, .is_generic = key.is_generic, .is_noinline = key.is_noinline, - .align_is_generic = key.alignment == null, .cc_is_generic = key.cc == null, .section_is_generic = key.section_is_generic, .addrspace_is_generic = key.addrspace_is_generic, @@ -6553,7 +6535,6 @@ pub fn getFuncInstance(ip: *InternPool, gpa: Allocator, arg: GetFuncInstanceKey) .param_types = arg.param_types, .return_type = arg.bare_return_type, .noalias_bits = arg.noalias_bits, - .alignment = arg.alignment, .cc = arg.cc, .is_noinline = arg.is_noinline, }); @@ -6610,6 +6591,7 @@ pub fn getFuncInstance(ip: *InternPool, gpa: Allocator, arg: GetFuncInstanceKey) func_index, func_extra_index, func_ty, + arg.alignment, arg.section, ); } @@ -6673,14 +6655,12 @@ pub fn getFuncInstanceIes( .params_len = params_len, .return_type = error_union_type, .flags = .{ - .alignment = arg.alignment, .cc = arg.cc, .is_var_args = false, .has_comptime_bits = false, .has_noalias_bits = arg.noalias_bits != 0, .is_generic = false, .is_noinline = arg.is_noinline, - .align_is_generic = false, .cc_is_generic = false, .section_is_generic = false, .addrspace_is_generic = false, @@ -6741,6 +6721,7 @@ pub fn getFuncInstanceIes( func_index, func_extra_index, func_ty, + arg.alignment, arg.section, ); } @@ -6752,6 +6733,7 @@ fn finishFuncInstance( func_index: Index, func_extra_index: u32, func_ty: Index, + alignment: Alignment, section: OptionalNullTerminatedString, ) Allocator.Error!Index { const fn_owner_decl = ip.declPtr(ip.funcDeclOwner(generic_owner)); @@ -6764,7 +6746,7 @@ fn finishFuncInstance( .owns_tv = true, .ty = @import("type.zig").Type.fromInterned(func_ty), .val = @import("Value.zig").fromInterned(func_index), - .alignment = .none, + .alignment = alignment, .@"linksection" = section, .@"addrspace" = fn_owner_decl.@"addrspace", .analysis = .complete, diff --git a/src/Module.zig b/src/Module.zig index 8f6def21ae..bfc5a35e10 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3596,6 +3596,18 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { log.debug("semaDecl '{d}'", .{@intFromEnum(decl_index)}); + const old_has_tv = decl.has_tv; + // The following values are ignored if `!old_has_tv` + const old_ty = decl.ty; + const old_val = decl.val; + const old_align = decl.alignment; + const old_linksection = decl.@"linksection"; + const old_addrspace = decl.@"addrspace"; + const old_is_inline = if (decl.getOwnedFunction(mod)) |prev_func| + prev_func.analysis(ip).state == .inline_only + else + false; + const decl_inst = decl.zir_decl_index.unwrap().?.resolve(ip); const gpa = mod.gpa; @@ -3733,141 +3745,96 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { }; } - switch (ip.indexToKey(decl_tv.val.toIntern())) { - .func => |func| { - const owns_tv = func.owner_decl == decl_index; - if (owns_tv) { - var prev_type_has_bits = false; - var prev_is_inline = false; - var type_changed = true; - - if (decl.has_tv) { - prev_type_has_bits = decl.ty.isFnOrHasRuntimeBits(mod); - type_changed = !decl.ty.eql(decl_tv.ty, mod); - if (decl.getOwnedFunction(mod)) |prev_func| { - prev_is_inline = prev_func.analysis(ip).state == .inline_only; - } - } - - decl.ty = decl_tv.ty; - decl.val = Value.fromInterned((try decl_tv.val.intern(decl_tv.ty, mod))); - // linksection, align, and addrspace were already set by Sema - decl.has_tv = true; - decl.owns_tv = owns_tv; - decl.analysis = .complete; - - const is_inline = decl.ty.fnCallingConvention(mod) == .Inline; - if (decl.is_exported) { - const export_src: LazySrcLoc = .{ .token_offset = @intFromBool(decl.is_pub) }; - if (is_inline) { - return sema.fail(&block_scope, export_src, "export of inline function", .{}); - } - // The scope needs to have the decl in it. - try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index); - } - // TODO: align, linksection, addrspace? - const changed = type_changed or is_inline != prev_is_inline; - return .{ - .invalidate_decl_val = changed, - .invalidate_decl_ref = changed, - }; - } - }, - else => {}, - } - - decl.owns_tv = false; - var queue_linker_work = false; - var is_extern = false; + var queue_linker_work = true; + var is_func = false; + var is_inline = false; switch (decl_tv.val.toIntern()) { .generic_poison => unreachable, .unreachable_value => unreachable, else => switch (ip.indexToKey(decl_tv.val.toIntern())) { - .variable => |variable| if (variable.decl == decl_index) { - decl.owns_tv = true; - queue_linker_work = true; + .variable => |variable| { + decl.owns_tv = variable.decl == decl_index; + queue_linker_work = decl.owns_tv; }, - .extern_func => |extern_fn| if (extern_fn.decl == decl_index) { - decl.owns_tv = true; - queue_linker_work = true; - is_extern = true; + .extern_func => |extern_func| { + decl.owns_tv = extern_func.decl == decl_index; + queue_linker_work = decl.owns_tv; + is_func = decl.owns_tv; }, - .func => {}, - - else => { - queue_linker_work = true; + .func => |func| { + decl.owns_tv = func.owner_decl == decl_index; + queue_linker_work = false; + is_inline = decl.owns_tv and decl_tv.ty.fnCallingConvention(mod) == .Inline; + is_func = decl.owns_tv; }, + + else => {}, }, } - const old_has_tv = decl.has_tv; - // The following values are ignored if `!old_has_tv` - const old_ty = decl.ty; - const old_val = decl.val; - const old_align = decl.alignment; - const old_linksection = decl.@"linksection"; - const old_addrspace = decl.@"addrspace"; - decl.ty = decl_tv.ty; decl.val = Value.fromInterned((try decl_tv.val.intern(decl_tv.ty, mod))); - decl.alignment = blk: { - const align_body = decl_bodies.align_body orelse break :blk .none; - const align_ref = try sema.resolveInlineBody(&block_scope, align_body, decl_inst); - break :blk try sema.analyzeAsAlign(&block_scope, align_src, align_ref); - }; - decl.@"linksection" = blk: { - const linksection_body = decl_bodies.linksection_body orelse break :blk .none; - const linksection_ref = try sema.resolveInlineBody(&block_scope, linksection_body, decl_inst); - const bytes = try sema.toConstString(&block_scope, section_src, linksection_ref, .{ - .needed_comptime_reason = "linksection must be comptime-known", - }); - if (mem.indexOfScalar(u8, bytes, 0) != null) { - return sema.fail(&block_scope, section_src, "linksection cannot contain null bytes", .{}); - } else if (bytes.len == 0) { - return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{}); - } - const section = try ip.getOrPutString(gpa, bytes); - break :blk section.toOptional(); - }; - decl.@"addrspace" = blk: { - const addrspace_ctx: Sema.AddressSpaceContext = switch (ip.indexToKey(decl_tv.val.toIntern())) { - .variable => .variable, - .extern_func, .func => .function, - else => .constant, + // Function linksection, align, and addrspace were already set by Sema + if (!is_func) { + decl.alignment = blk: { + const align_body = decl_bodies.align_body orelse break :blk .none; + const align_ref = try sema.resolveInlineBody(&block_scope, align_body, decl_inst); + break :blk try sema.analyzeAsAlign(&block_scope, align_src, align_ref); }; - - const target = sema.mod.getTarget(); - - const addrspace_body = decl_bodies.addrspace_body orelse break :blk switch (addrspace_ctx) { - .function => target_util.defaultAddressSpace(target, .function), - .variable => target_util.defaultAddressSpace(target, .global_mutable), - .constant => target_util.defaultAddressSpace(target, .global_constant), - else => unreachable, + decl.@"linksection" = blk: { + const linksection_body = decl_bodies.linksection_body orelse break :blk .none; + const linksection_ref = try sema.resolveInlineBody(&block_scope, linksection_body, decl_inst); + const bytes = try sema.toConstString(&block_scope, section_src, linksection_ref, .{ + .needed_comptime_reason = "linksection must be comptime-known", + }); + if (mem.indexOfScalar(u8, bytes, 0) != null) { + return sema.fail(&block_scope, section_src, "linksection cannot contain null bytes", .{}); + } else if (bytes.len == 0) { + return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{}); + } + const section = try ip.getOrPutString(gpa, bytes); + break :blk section.toOptional(); }; - const addrspace_ref = try sema.resolveInlineBody(&block_scope, addrspace_body, decl_inst); - break :blk try sema.analyzeAsAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx); - }; + decl.@"addrspace" = blk: { + const addrspace_ctx: Sema.AddressSpaceContext = switch (ip.indexToKey(decl_tv.val.toIntern())) { + .variable => .variable, + .extern_func, .func => .function, + else => .constant, + }; + + const target = sema.mod.getTarget(); + + const addrspace_body = decl_bodies.addrspace_body orelse break :blk switch (addrspace_ctx) { + .function => target_util.defaultAddressSpace(target, .function), + .variable => target_util.defaultAddressSpace(target, .global_mutable), + .constant => target_util.defaultAddressSpace(target, .global_constant), + else => unreachable, + }; + const addrspace_ref = try sema.resolveInlineBody(&block_scope, addrspace_body, decl_inst); + break :blk try sema.analyzeAsAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx); + }; + } decl.has_tv = true; decl.analysis = .complete; const result: SemaDeclResult = if (old_has_tv) .{ - .invalidate_decl_val = !decl.ty.eql(old_ty, mod) or !decl.val.eql(old_val, decl.ty, mod), + .invalidate_decl_val = !decl.ty.eql(old_ty, mod) or + !decl.val.eql(old_val, decl.ty, mod) or + is_inline != old_is_inline, .invalidate_decl_ref = !decl.ty.eql(old_ty, mod) or decl.alignment != old_align or decl.@"linksection" != old_linksection or - decl.@"addrspace" != old_addrspace, + decl.@"addrspace" != old_addrspace or + is_inline != old_is_inline, } else .{ .invalidate_decl_val = true, .invalidate_decl_ref = true, }; - const has_runtime_bits = is_extern or - (queue_linker_work and try sema.typeHasRuntimeBits(decl.ty)); - + const has_runtime_bits = queue_linker_work and (is_func or try sema.typeHasRuntimeBits(decl.ty)); if (has_runtime_bits) { - // Needed for codegen_decl which will call updateDecl and then the // codegen backend wants full access to the Decl Type. try sema.resolveTypeFully(decl.ty); @@ -3881,6 +3848,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { if (decl.is_exported) { const export_src: LazySrcLoc = .{ .token_offset = @intFromBool(decl.is_pub) }; + if (is_inline) return sema.fail(&block_scope, export_src, "export of inline function", .{}); // The scope needs to have the decl in it. try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index); } diff --git a/src/Sema.zig b/src/Sema.zig index 54d5e6df7d..cf103e7230 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7605,7 +7605,6 @@ fn analyzeCall( .param_types = new_param_types, .return_type = owner_info.return_type, .noalias_bits = owner_info.noalias_bits, - .alignment = if (owner_info.align_is_generic) null else owner_info.alignment, .cc = if (owner_info.cc_is_generic) null else owner_info.cc, .is_var_args = owner_info.is_var_args, .is_noinline = owner_info.is_noinline, @@ -9629,7 +9628,6 @@ fn funcCommon( .comptime_bits = comptime_bits, .return_type = bare_return_type.toIntern(), .cc = cc, - .alignment = alignment, .section_is_generic = section == .generic, .addrspace_is_generic = address_space == null, .is_var_args = var_args, @@ -9640,6 +9638,7 @@ fn funcCommon( if (is_extern) { assert(comptime_bits == 0); assert(cc != null); + assert(alignment != null); assert(section != .generic); assert(address_space != null); assert(!is_generic); @@ -17623,8 +17622,6 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const field_values = .{ // calling_convention: CallingConvention, (try mod.enumValueFieldIndex(callconv_ty, @intFromEnum(func_ty_info.cc))).toIntern(), - // alignment: comptime_int, - (try mod.intValue(Type.comptime_int, ty.abiAlignment(mod).toByteUnits(0))).toIntern(), // is_generic: bool, Value.makeBool(func_ty_info.is_generic).toIntern(), // is_var_args: bool, @@ -19701,12 +19698,6 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air if (inst_data.size != .One) { return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{}); } - const fn_align = mod.typeToFunc(elem_ty).?.alignment; - if (inst_data.flags.has_align and abi_align != .none and fn_align != .none and - abi_align != fn_align) - { - return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{}); - } } else if (inst_data.size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) { return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{}); } else if (inst_data.size == .C) { @@ -21030,7 +21021,6 @@ fn zirReify( .needed_comptime_reason = "operand to @Type must be comptime-known", }); const union_val = ip.indexToKey(val.toIntern()).un; - const target = mod.getTarget(); if (try Value.fromInterned(union_val.val).anyUndef(mod)) return sema.failWithUseOfUndef(block, src); const tag_index = type_info_ty.unionTagFieldIndex(Value.fromInterned(union_val.tag), mod).?; switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) { @@ -21171,12 +21161,6 @@ fn zirReify( if (ptr_size != .One) { return sema.fail(block, src, "function pointers must be single pointers", .{}); } - const fn_align = mod.typeToFunc(elem_ty).?.alignment; - if (abi_align != .none and fn_align != .none and - abi_align != fn_align) - { - return sema.fail(block, src, "function pointer alignment disagrees with function alignment", .{}); - } } else if (ptr_size == .Many and elem_ty.zigTypeTag(mod) == .Opaque) { return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{}); } else if (ptr_size == .C) { @@ -21429,10 +21413,6 @@ fn zirReify( ip, try ip.getOrPutString(gpa, "calling_convention"), ).?); - const alignment_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( - ip, - try ip.getOrPutString(gpa, "alignment"), - ).?); const is_generic_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, try ip.getOrPutString(gpa, "is_generic"), @@ -21461,11 +21441,6 @@ fn zirReify( try sema.checkCallConvSupportsVarArgs(block, src, cc); } - const alignment = alignment: { - const alignment = try sema.validateAlignAllowZero(block, src, try alignment_val.toUnsignedIntAdvanced(sema)); - const default = target_util.defaultFunctionAlignment(target); - break :alignment if (alignment == default) .none else alignment; - }; const return_type = return_type_val.optionalValue(mod) orelse return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{}); @@ -21510,7 +21485,6 @@ fn zirReify( .param_types = param_types, .noalias_bits = noalias_bits, .return_type = return_type.toIntern(), - .alignment = alignment, .cc = cc, .is_var_args = is_var_args, }); @@ -32536,16 +32510,21 @@ fn analyzeDeclRefInner(sema: *Sema, decl_index: InternPool.DeclIndex, analyze_fn const mod = sema.mod; try sema.ensureDeclAnalyzed(decl_index); - const decl = mod.declPtr(decl_index); - const decl_tv = try decl.typedValue(); + const decl_tv = try mod.declPtr(decl_index).typedValue(); + const owner_decl = mod.declPtr(switch (mod.intern_pool.indexToKey(decl_tv.val.toIntern())) { + .variable => |variable| variable.decl, + .extern_func => |extern_func| extern_func.decl, + .func => |func| func.owner_decl, + else => decl_index, + }); // TODO: if this is a `decl_ref` of a non-variable decl, only depend on decl type try sema.declareDependency(.{ .decl_val = decl_index }); const ptr_ty = try sema.ptrType(.{ .child = decl_tv.ty.toIntern(), .flags = .{ - .alignment = decl.alignment, - .is_const = if (decl.val.getVariable(mod)) |variable| variable.is_const else true, - .address_space = decl.@"addrspace", + .alignment = owner_decl.alignment, + .is_const = if (decl_tv.val.getVariable(mod)) |variable| variable.is_const else true, + .address_space = owner_decl.@"addrspace", }, }); if (analyze_fn_body) { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 8d630480e2..928cc995dc 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1635,7 +1635,7 @@ pub const DeclGen = struct { switch (kind) { .forward => {}, - .complete => if (fn_info.alignment.toByteUnitsOptional()) |a| { + .complete => if (fn_decl.alignment.toByteUnitsOptional()) |a| { try w.print("{}zig_align_fn({})", .{ trailing, a }); trailing = .maybe_space; }, @@ -1666,7 +1666,7 @@ pub const DeclGen = struct { switch (kind) { .forward => { - if (fn_info.alignment.toByteUnitsOptional()) |a| { + if (fn_decl.alignment.toByteUnitsOptional()) |a| { try w.print(" zig_align_fn({})", .{a}); } switch (name) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2a5a187e0d..909f5b056f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2949,8 +2949,8 @@ pub const Object = struct { else => function_index.setCallConv(toLlvmCallConv(fn_info.cc, target), &o.builder), } - if (fn_info.alignment != .none) - function_index.setAlignment(fn_info.alignment.toLlvm(), &o.builder); + if (decl.alignment != .none) + function_index.setAlignment(decl.alignment.toLlvm(), &o.builder); // Function attributes that are independent of analysis results of the function body. try o.addCommonFnAttributes(&attributes, owner_mod); diff --git a/src/type.zig b/src/type.zig index 664498e353..9ea16d6224 100644 --- a/src/type.zig +++ b/src/type.zig @@ -396,9 +396,6 @@ pub const Type = struct { try writer.writeAll("..."); } try writer.writeAll(") "); - if (fn_info.alignment.toByteUnitsOptional()) |a| { - try writer.print("align({d}) ", .{a}); - } if (fn_info.cc != .Unspecified) { try writer.writeAll("callconv(."); try writer.writeAll(@tagName(fn_info.cc)); @@ -949,12 +946,7 @@ pub const Type = struct { }, // represents machine code; not a pointer - .func_type => |func_type| return .{ - .scalar = if (func_type.alignment != .none) - func_type.alignment - else - target_util.defaultFunctionAlignment(target), - }, + .func_type => return .{ .scalar = target_util.defaultFunctionAlignment(target) }, .simple_type => |t| switch (t) { .bool, diff --git a/test/behavior/align.zig b/test/behavior/align.zig index e00fbe6744..671e46501a 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -311,12 +311,6 @@ test "page aligned array on stack" { try expect(number2 == 43); } -fn derp() align(@sizeOf(usize) * 2) i32 { - return 1234; -} -fn noop1() align(1) void {} -fn noop4() align(4) void {} - test "function alignment" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; @@ -325,11 +319,25 @@ test "function alignment" { // function alignment is a compile error on wasm32/wasm64 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; - try expect(derp() == 1234); - try expect(@TypeOf(noop1) == fn () align(1) void); - try expect(@TypeOf(noop4) == fn () align(4) void); - noop1(); - noop4(); + const S = struct { + fn alignExpr() align(@sizeOf(usize) * 2) i32 { + return 1234; + } + fn align1() align(1) void {} + fn align4() align(4) void {} + }; + + try expect(S.alignExpr() == 1234); + try expect(@TypeOf(S.alignExpr) == fn () i32); + try expect(@TypeOf(&S.alignExpr) == *align(@sizeOf(usize) * 2) const fn () i32); + + S.align1(); + try expect(@TypeOf(S.align1) == fn () void); + try expect(@TypeOf(&S.align1) == *align(1) const fn () void); + + S.align4(); + try expect(@TypeOf(S.align4) == fn () void); + try expect(@TypeOf(&S.align4) == *align(4) const fn () void); } test "implicitly decreasing fn alignment" { @@ -345,7 +353,7 @@ test "implicitly decreasing fn alignment" { try testImplicitlyDecreaseFnAlign(alignedBig, 5678); } -fn testImplicitlyDecreaseFnAlign(ptr: *const fn () align(1) i32, answer: i32) !void { +fn testImplicitlyDecreaseFnAlign(ptr: *align(1) const fn () i32, answer: i32) !void { try expect(ptr() == answer); } @@ -368,10 +376,10 @@ test "@alignCast functions" { try expect(fnExpectsOnly1(simple4) == 0x19); } -fn fnExpectsOnly1(ptr: *const fn () align(1) i32) i32 { +fn fnExpectsOnly1(ptr: *align(1) const fn () i32) i32 { return fnExpects4(@alignCast(ptr)); } -fn fnExpects4(ptr: *const fn () align(4) i32) i32 { +fn fnExpects4(ptr: *align(4) const fn () i32) i32 { return ptr(); } fn simple4() align(4) i32 { diff --git a/test/behavior/type.zig b/test/behavior/type.zig index da42672be6..c9290ecc31 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -527,7 +527,6 @@ test "Type.Fn" { { const fn_info = std.builtin.Type{ .Fn = .{ .calling_convention = .C, - .alignment = 0, .is_generic = false, .is_var_args = false, .return_type = void, @@ -643,7 +642,6 @@ test "reified function type params initialized with field pointer" { const Bar = @Type(.{ .Fn = .{ .calling_convention = .Unspecified, - .alignment = 0, .is_generic = false, .is_var_args = false, .return_type = void, diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index d4e3eb3b83..0d54b14c39 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -356,16 +356,38 @@ test "type info: function type info" { } fn testFunction() !void { - const fn_info = @typeInfo(@TypeOf(typeInfoFoo)); - try expect(fn_info == .Fn); - try expect(fn_info.Fn.alignment > 0); - try expect(fn_info.Fn.calling_convention == .C); - try expect(!fn_info.Fn.is_generic); - try expect(fn_info.Fn.params.len == 2); - try expect(fn_info.Fn.is_var_args); - try expect(fn_info.Fn.return_type.? == usize); - const fn_aligned_info = @typeInfo(@TypeOf(typeInfoFooAligned)); - try expect(fn_aligned_info.Fn.alignment == 4); + const foo_fn_type = @TypeOf(typeInfoFoo); + const foo_fn_info = @typeInfo(foo_fn_type); + try expect(foo_fn_info.Fn.calling_convention == .C); + try expect(!foo_fn_info.Fn.is_generic); + try expect(foo_fn_info.Fn.params.len == 2); + try expect(foo_fn_info.Fn.is_var_args); + try expect(foo_fn_info.Fn.return_type.? == usize); + const foo_ptr_fn_info = @typeInfo(@TypeOf(&typeInfoFoo)); + try expect(foo_ptr_fn_info.Pointer.size == .One); + try expect(foo_ptr_fn_info.Pointer.is_const); + try expect(!foo_ptr_fn_info.Pointer.is_volatile); + try expect(foo_ptr_fn_info.Pointer.address_space == .generic); + try expect(foo_ptr_fn_info.Pointer.child == foo_fn_type); + try expect(!foo_ptr_fn_info.Pointer.is_allowzero); + try expect(foo_ptr_fn_info.Pointer.sentinel == null); + + const aligned_foo_fn_type = @TypeOf(typeInfoFooAligned); + const aligned_foo_fn_info = @typeInfo(aligned_foo_fn_type); + try expect(aligned_foo_fn_info.Fn.calling_convention == .C); + try expect(!aligned_foo_fn_info.Fn.is_generic); + try expect(aligned_foo_fn_info.Fn.params.len == 2); + try expect(aligned_foo_fn_info.Fn.is_var_args); + try expect(aligned_foo_fn_info.Fn.return_type.? == usize); + const aligned_foo_ptr_fn_info = @typeInfo(@TypeOf(&typeInfoFooAligned)); + try expect(aligned_foo_ptr_fn_info.Pointer.size == .One); + try expect(aligned_foo_ptr_fn_info.Pointer.is_const); + try expect(!aligned_foo_ptr_fn_info.Pointer.is_volatile); + try expect(aligned_foo_ptr_fn_info.Pointer.alignment == 4); + try expect(aligned_foo_ptr_fn_info.Pointer.address_space == .generic); + try expect(aligned_foo_ptr_fn_info.Pointer.child == aligned_foo_fn_type); + try expect(!aligned_foo_ptr_fn_info.Pointer.is_allowzero); + try expect(aligned_foo_ptr_fn_info.Pointer.sentinel == null); } extern fn typeInfoFoo(a: usize, b: bool, ...) callconv(.C) usize; diff --git a/test/behavior/typename.zig b/test/behavior/typename.zig index 80c9a0619e..c3eefc8de7 100644 --- a/test/behavior/typename.zig +++ b/test/behavior/typename.zig @@ -78,11 +78,9 @@ test "basic" { try expectEqualStrings("fn (comptime u32) void", @typeName(fn (comptime u32) void)); try expectEqualStrings("fn (noalias []u8) void", @typeName(fn (noalias []u8) void)); - try expectEqualStrings("fn () align(32) void", @typeName(fn () align(32) void)); try expectEqualStrings("fn () callconv(.C) void", @typeName(fn () callconv(.C) void)); - try expectEqualStrings("fn () align(32) callconv(.C) void", @typeName(fn () align(32) callconv(.C) void)); - try expectEqualStrings("fn (...) align(32) callconv(.C) void", @typeName(fn (...) align(32) callconv(.C) void)); - try expectEqualStrings("fn (u32, ...) align(32) callconv(.C) void", @typeName(fn (u32, ...) align(32) callconv(.C) void)); + try expectEqualStrings("fn (...) callconv(.C) void", @typeName(fn (...) callconv(.C) void)); + try expectEqualStrings("fn (u32, ...) callconv(.C) void", @typeName(fn (u32, ...) callconv(.C) void)); } test "top level decl" { diff --git a/test/cases/compile_errors/function_ptr_alignment.zig b/test/cases/compile_errors/function_ptr_alignment.zig index 995ef8d9b1..cf97e61f40 100644 --- a/test/cases/compile_errors/function_ptr_alignment.zig +++ b/test/cases/compile_errors/function_ptr_alignment.zig @@ -1,28 +1,16 @@ -comptime { - var a: *align(2) @TypeOf(foo) = undefined; - _ = &a; -} -fn foo() void {} +fn align1() align(1) void {} +fn align2() align(2) void {} comptime { - var a: *align(1) fn () void = undefined; - _ = &a; -} -comptime { - var a: *align(2) fn () align(2) void = undefined; - _ = &a; -} -comptime { - var a: *align(2) fn () void = undefined; - _ = &a; -} -comptime { - var a: *align(1) fn () align(2) void = undefined; - _ = &a; + _ = @as(*align(1) const fn () void, &align2); + _ = @as(*align(1) const fn () void, &align1); + _ = @as(*align(2) const fn () void, &align2); + _ = @as(*align(2) const fn () void, &align1); } // error // backend=stage2 // target=native // -// :20:19: error: function pointer alignment disagrees with function alignment +// :8:41: error: expected type '*align(2) const fn () void', found '*const fn () void' +// :8:41: note: pointer alignment '1' cannot cast into pointer alignment '2' diff --git a/test/cases/compile_errors/inferring_error_set_of_function_pointer.zig b/test/cases/compile_errors/inferring_error_set_of_function_pointer.zig deleted file mode 100644 index ce1b276360..0000000000 --- a/test/cases/compile_errors/inferring_error_set_of_function_pointer.zig +++ /dev/null @@ -1,9 +0,0 @@ -comptime { - const z: ?fn () !void = null; -} - -// error -// backend=stage2 -// target=native -// -// :2:21: error: function prototype may not have inferred error set diff --git a/test/cases/compile_errors/invalid_function_types.zig b/test/cases/compile_errors/invalid_function_types.zig new file mode 100644 index 0000000000..e4553d22f1 --- /dev/null +++ b/test/cases/compile_errors/invalid_function_types.zig @@ -0,0 +1,25 @@ +comptime { + _ = fn name() void; +} +comptime { + _ = fn () align(128) void; +} +comptime { + _ = fn () addrspace(.generic) void; +} +comptime { + _ = fn () linksection("section") void; +} +comptime { + _ = fn () !void; +} + +// error +// backend=stage2 +// target=native +// +// :2:12: error: function type cannot have a name +// :5:21: error: function type cannot have an alignment +// :8:26: error: function type cannot have an addrspace +// :11:27: error: function type cannot have a linksection +// :14:15: error: function type cannot have an inferred error set diff --git a/test/cases/compile_errors/passing_an_under-aligned_function_pointer.zig b/test/cases/compile_errors/passing_an_under-aligned_function_pointer.zig index c6b06f7329..e37441563b 100644 --- a/test/cases/compile_errors/passing_an_under-aligned_function_pointer.zig +++ b/test/cases/compile_errors/passing_an_under-aligned_function_pointer.zig @@ -1,7 +1,7 @@ export fn entry() void { testImplicitlyDecreaseFnAlign(alignedSmall, 1234); } -fn testImplicitlyDecreaseFnAlign(ptr: *const fn () align(8) i32, answer: i32) void { +fn testImplicitlyDecreaseFnAlign(ptr: *align(8) const fn () i32, answer: i32) void { if (ptr() != answer) unreachable; } fn alignedSmall() align(4) i32 { @@ -12,5 +12,5 @@ fn alignedSmall() align(4) i32 { // backend=stage2 // target=x86_64-linux // -// :2:35: error: expected type '*const fn () align(8) i32', found '*const fn () align(4) i32' +// :2:35: error: expected type '*align(8) const fn () i32', found '*align(4) const fn () i32' // :2:35: note: pointer alignment '4' cannot cast into pointer alignment '8' diff --git a/test/cases/compile_errors/reify_type.Fn_with_is_generic_true.zig b/test/cases/compile_errors/reify_type.Fn_with_is_generic_true.zig index bb2c4bbe62..4ac1312440 100644 --- a/test/cases/compile_errors/reify_type.Fn_with_is_generic_true.zig +++ b/test/cases/compile_errors/reify_type.Fn_with_is_generic_true.zig @@ -1,7 +1,6 @@ const Foo = @Type(.{ .Fn = .{ .calling_convention = .Unspecified, - .alignment = 0, .is_generic = true, .is_var_args = false, .return_type = u0, diff --git a/test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig b/test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig index 678fbe3ed7..23ea4f2095 100644 --- a/test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig +++ b/test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig @@ -1,7 +1,6 @@ const Foo = @Type(.{ .Fn = .{ .calling_convention = .Unspecified, - .alignment = 0, .is_generic = false, .is_var_args = true, .return_type = u0, diff --git a/test/cases/compile_errors/reify_type.Fn_with_return_type_null.zig b/test/cases/compile_errors/reify_type.Fn_with_return_type_null.zig index d348a0c908..073cf0f4cc 100644 --- a/test/cases/compile_errors/reify_type.Fn_with_return_type_null.zig +++ b/test/cases/compile_errors/reify_type.Fn_with_return_type_null.zig @@ -1,7 +1,6 @@ const Foo = @Type(.{ .Fn = .{ .calling_convention = .Unspecified, - .alignment = 0, .is_generic = false, .is_var_args = false, .return_type = null,