Commit d10c52c194

Jacob Young <jacobly0@users.noreply.github.com>
2024-03-16 16:46:45
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.
1 parent f88a971
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#}
       <p>
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 {
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
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
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 {
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
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
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
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 {
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 {
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,
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.
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".
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) {
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);
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,
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);
         };
+        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,
+            };
 
-        const target = sema.mod.getTarget();
+            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_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);
         };
-        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);
     }
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) {
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,
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 {
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,
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;
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" {
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'
test/cases/compile_errors/inferring_error_set_of_function_pointer.zig
@@ -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
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
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'
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,
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,
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,