authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-17 16:13:25+03:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-21 12:21:30-07:00
logd851b24180fdf2b622b06e9a35e315541fb10aa1
tree3502677e69ee19a43104e8b5008e968bd0365a24
parent76e7959a90ae025a1934a4ad1c689663f523a4bc

Sema: validate function pointer alignment


7 files changed, 148 insertions(+), 19 deletions(-)

src/Module.zig+87-1
......@@ -2348,7 +2348,72 @@ pub const SrcLoc = struct {
23482348 }
23492349 } else unreachable;
23502350 },
2351
2351 .node_offset_fn_type_align => |node_off| {
2352 const tree = try src_loc.file_scope.getTree(gpa);
2353 const node_datas = tree.nodes.items(.data);
2354 const node_tags = tree.nodes.items(.tag);
2355 const node = src_loc.declRelativeToNodeIndex(node_off);
2356 var params: [1]Ast.Node.Index = undefined;
2357 const full = switch (node_tags[node]) {
2358 .fn_proto_simple => tree.fnProtoSimple(&params, node),
2359 .fn_proto_multi => tree.fnProtoMulti(node),
2360 .fn_proto_one => tree.fnProtoOne(&params, node),
2361 .fn_proto => tree.fnProto(node),
2362 .fn_decl => switch (node_tags[node_datas[node].lhs]) {
2363 .fn_proto_simple => tree.fnProtoSimple(&params, node_datas[node].lhs),
2364 .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs),
2365 .fn_proto_one => tree.fnProtoOne(&params, node_datas[node].lhs),
2366 .fn_proto => tree.fnProto(node_datas[node].lhs),
2367 else => unreachable,
2368 },
2369 else => unreachable,
2370 };
2371 return nodeToSpan(tree, full.ast.align_expr);
2372 },
2373 .node_offset_fn_type_addrspace => |node_off| {
2374 const tree = try src_loc.file_scope.getTree(gpa);
2375 const node_datas = tree.nodes.items(.data);
2376 const node_tags = tree.nodes.items(.tag);
2377 const node = src_loc.declRelativeToNodeIndex(node_off);
2378 var params: [1]Ast.Node.Index = undefined;
2379 const full = switch (node_tags[node]) {
2380 .fn_proto_simple => tree.fnProtoSimple(&params, node),
2381 .fn_proto_multi => tree.fnProtoMulti(node),
2382 .fn_proto_one => tree.fnProtoOne(&params, node),
2383 .fn_proto => tree.fnProto(node),
2384 .fn_decl => switch (node_tags[node_datas[node].lhs]) {
2385 .fn_proto_simple => tree.fnProtoSimple(&params, node_datas[node].lhs),
2386 .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs),
2387 .fn_proto_one => tree.fnProtoOne(&params, node_datas[node].lhs),
2388 .fn_proto => tree.fnProto(node_datas[node].lhs),
2389 else => unreachable,
2390 },
2391 else => unreachable,
2392 };
2393 return nodeToSpan(tree, full.ast.addrspace_expr);
2394 },
2395 .node_offset_fn_type_section => |node_off| {
2396 const tree = try src_loc.file_scope.getTree(gpa);
2397 const node_datas = tree.nodes.items(.data);
2398 const node_tags = tree.nodes.items(.tag);
2399 const node = src_loc.declRelativeToNodeIndex(node_off);
2400 var params: [1]Ast.Node.Index = undefined;
2401 const full = switch (node_tags[node]) {
2402 .fn_proto_simple => tree.fnProtoSimple(&params, node),
2403 .fn_proto_multi => tree.fnProtoMulti(node),
2404 .fn_proto_one => tree.fnProtoOne(&params, node),
2405 .fn_proto => tree.fnProto(node),
2406 .fn_decl => switch (node_tags[node_datas[node].lhs]) {
2407 .fn_proto_simple => tree.fnProtoSimple(&params, node_datas[node].lhs),
2408 .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs),
2409 .fn_proto_one => tree.fnProtoOne(&params, node_datas[node].lhs),
2410 .fn_proto => tree.fnProto(node_datas[node].lhs),
2411 else => unreachable,
2412 },
2413 else => unreachable,
2414 };
2415 return nodeToSpan(tree, full.ast.section_expr);
2416 },
23522417 .node_offset_fn_type_cc => |node_off| {
23532418 const tree = try src_loc.file_scope.getTree(gpa);
23542419 const node_datas = tree.nodes.items(.data);
......@@ -2778,6 +2843,24 @@ pub const LazySrcLoc = union(enum) {
27782843 /// range nodes. The error applies to all of them.
27792844 /// The Decl is determined contextually.
27802845 node_offset_switch_range: i32,
2846 /// The source location points to the align expr of a function type
2847 /// expression, found by taking this AST node index offset from the containing
2848 /// Decl AST node, which points to a function type AST node. Next, navigate to
2849 /// the calling convention node.
2850 /// The Decl is determined contextually.
2851 node_offset_fn_type_align: i32,
2852 /// The source location points to the addrspace expr of a function type
2853 /// expression, found by taking this AST node index offset from the containing
2854 /// Decl AST node, which points to a function type AST node. Next, navigate to
2855 /// the calling convention node.
2856 /// The Decl is determined contextually.
2857 node_offset_fn_type_addrspace: i32,
2858 /// The source location points to the linksection expr of a function type
2859 /// expression, found by taking this AST node index offset from the containing
2860 /// Decl AST node, which points to a function type AST node. Next, navigate to
2861 /// the calling convention node.
2862 /// The Decl is determined contextually.
2863 node_offset_fn_type_section: i32,
27812864 /// The source location points to the calling convention of a function type
27822865 /// expression, found by taking this AST node index offset from the containing
27832866 /// Decl AST node, which points to a function type AST node. Next, navigate to
......@@ -2897,6 +2980,9 @@ pub const LazySrcLoc = union(enum) {
28972980 .node_offset_switch_operand,
28982981 .node_offset_switch_special_prong,
28992982 .node_offset_switch_range,
2983 .node_offset_fn_type_align,
2984 .node_offset_fn_type_addrspace,
2985 .node_offset_fn_type_section,
29002986 .node_offset_fn_type_cc,
29012987 .node_offset_fn_type_ret_ty,
29022988 .node_offset_anyframe_type,
src/Sema.zig+27-8
......@@ -1884,13 +1884,22 @@ fn analyzeAsAlign(
18841884) !u32 {
18851885 const alignment_big = try sema.analyzeAsInt(block, src, air_ref, align_ty, "alignment must be comptime known");
18861886 const alignment = @intCast(u32, alignment_big); // We coerce to u16 in the prev line.
1887 try sema.validateAlign(block, src, alignment);
1888 return alignment;
1889}
1890
1891fn validateAlign(
1892 sema: *Sema,
1893 block: *Block,
1894 src: LazySrcLoc,
1895 alignment: u32,
1896) !void {
18871897 if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{});
18881898 if (!std.math.isPowerOfTwo(alignment)) {
18891899 return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{
18901900 alignment,
18911901 });
18921902 }
1893 return alignment;
18941903}
18951904
18961905pub fn resolveAlign(
......@@ -13902,8 +13911,9 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1390213911 break :blk 0;
1390313912 }
1390413913 }
13905 const abi_align = (try val.getUnsignedIntAdvanced(target, sema.kit(block, align_src))).?;
13906 break :blk @intCast(u32, abi_align);
13914 const abi_align = @intCast(u32, (try val.getUnsignedIntAdvanced(target, sema.kit(block, align_src))).?);
13915 try sema.validateAlign(block, align_src, abi_align);
13916 break :blk abi_align;
1390713917 } else 0;
1390813918
1390913919 const address_space = if (inst_data.flags.has_addrspace) blk: {
......@@ -13940,6 +13950,14 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1394013950
1394113951 if (elem_ty.zigTypeTag() == .NoReturn) {
1394213952 return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{});
13953 } else if (elem_ty.zigTypeTag() == .Fn) {
13954 if (inst_data.size != .One) {
13955 return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{});
13956 }
13957 const fn_align = elem_ty.abiAlignment(target);
13958 if (inst_data.flags.has_align and abi_align != 0 and abi_align != fn_align) {
13959 return sema.fail(block, align_src, "function pointer alignment disagrees with function alignment", .{});
13960 }
1394313961 } else if (inst_data.size == .Many and elem_ty.zigTypeTag() == .Opaque) {
1394413962 return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});
1394513963 } else if (inst_data.size == .C) {
......@@ -17709,15 +17727,14 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1770917727 defer tracy.end();
1771017728
1771117729 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
17712 const src = inst_data.src();
1771317730 const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
1771417731 const target = sema.mod.getTarget();
1771517732
17716 const align_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at align
17717 const addrspace_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at addrspace
17718 const section_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at section
17733 const align_src: LazySrcLoc = .{ .node_offset_fn_type_align = inst_data.src_node };
17734 const addrspace_src: LazySrcLoc = .{ .node_offset_fn_type_addrspace = inst_data.src_node };
17735 const section_src: LazySrcLoc = .{ .node_offset_fn_type_section = inst_data.src_node };
1771917736 const cc_src: LazySrcLoc = .{ .node_offset_fn_type_cc = inst_data.src_node };
17720 const ret_src: LazySrcLoc = src; // TODO add a LazySrcLoc that points at the return type
17737 const ret_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node };
1772117738
1772217739 var extra_index: usize = extra.end;
1772317740
......@@ -17742,6 +17759,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1774217759 break :blk null;
1774317760 }
1774417761 const alignment = @intCast(u32, val.toUnsignedInt(target));
17762 try sema.validateAlign(block, align_src, alignment);
1774517763 if (alignment == target_util.defaultFunctionAlignment(target)) {
1774617764 break :blk 0;
1774717765 } else {
......@@ -17757,6 +17775,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1775717775 else => |e| return e,
1775817776 };
1775917777 const alignment = @intCast(u32, align_tv.val.toUnsignedInt(target));
17778 try sema.validateAlign(block, align_src, alignment);
1776017779 if (alignment == target_util.defaultFunctionAlignment(target)) {
1776117780 break :blk 0;
1776217781 } else {
test/behavior/align.zig-1
......@@ -299,7 +299,6 @@ test "implicitly decreasing fn alignment" {
299299 try testImplicitlyDecreaseFnAlign(alignedBig, 5678);
300300}
301301
302// TODO make it a compile error to put align on the fn proto instead of on the ptr
303302fn testImplicitlyDecreaseFnAlign(ptr: *align(1) const fn () i32, answer: i32) !void {
304303 try expect(ptr() == answer);
305304}
test/cases/compile_errors/align_n_expr_function_pointers_is_a_compile_error.zig+1-1
......@@ -6,4 +6,4 @@ export fn foo() align(1) void {
66// backend=stage2
77// target=wasm32-freestanding-none
88//
9// :1:8: error: 'align' is not allowed on functions in wasm
9// :1:23: error: 'align' is not allowed on functions in wasm
\ No newline at end of file
test/cases/compile_errors/function_alignment_non_power_of_2.zig created+8
......@@ -0,0 +1,8 @@
1extern fn foo() align(3) void;
2export fn entry() void { return foo(); }
3
4// error
5// backend=stage2
6// target=native
7//
8// :1:23: error: alignment value '3' is not a power of two
test/cases/compile_errors/function_ptr_alignment.zig created+25
......@@ -0,0 +1,25 @@
1comptime {
2 var a: *align(2) @TypeOf(foo) = undefined;
3 _ = a;
4}
5fn foo() void {}
6
7comptime {
8 var a: *align(1) fn () void = undefined;
9 _ = a;
10}
11comptime {
12 var a: *align(2) fn () align(2) void = undefined;
13 _ = a;
14}
15comptime {
16 var a: *align(2) fn () void = undefined;
17 _ = a;
18}
19
20// error
21// backend=stage2
22// target=native
23//
24// :2:19: error: function pointer alignment disagrees with function alignment
25// :16:19: error: function pointer alignment disagrees with function alignment
test/cases/compile_errors/stage1/obj/function_alignment_non_power_of_2.zig deleted-8
......@@ -1,8 +0,0 @@
1extern fn foo() align(3) void;
2export fn entry() void { return foo(); }
3
4// error
5// backend=stage1
6// target=native
7//
8// tmp.zig:1:23: error: alignment value 3 is not a power of 2