From 6413c1bf213eb18165142e99f49d63c5f713d287 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 9 Oct 2026 20:18:17 +0200 Subject: [PATCH] Merge pull request 'AstGen: fix fuzzed bugs' (#35526) from gooncreeper/zig:astgen-fuzzing-2 into master Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35526 Reviewed-by: Andrew Kelley --- lib/std/zig/AstGen.zig | 167 +++++++++--------- test/cases/array_sentinel_cursor.zig | 14 ++ .../error_set_cannot_capture_by_reference.zig | 13 -- .../switch_capture_error_by_ref.zig | 25 --- .../switch_on_error_bad_captures_astgen.zig | 37 ++++ .../switch_on_error_bad_captures_sema.zig | 21 +++ ...tch_on_error_with_capture_by_reference.zig | 22 --- test/cases/decl_qualifier_cursor.zig | 45 +++++ test/cases/tuple_assign_cursor.zig | 13 ++ 9 files changed, 218 insertions(+), 139 deletions(-) create mode 100644 test/cases/array_sentinel_cursor.zig delete mode 100644 test/cases/compile_errors/error_set_cannot_capture_by_reference.zig delete mode 100644 test/cases/compile_errors/switch_capture_error_by_ref.zig create mode 100644 test/cases/compile_errors/switch_on_error_bad_captures_astgen.zig create mode 100644 test/cases/compile_errors/switch_on_error_bad_captures_sema.zig delete mode 100644 test/cases/compile_errors/switch_on_error_with_capture_by_reference.zig create mode 100644 test/cases/decl_qualifier_cursor.zig create mode 100644 test/cases/tuple_assign_cursor.zig diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index 0b98fcf2b92d6b3f80e187f54f8060343fd8e24e..bb3cda94038be396c53bde1984d8449a7544ee38 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -1460,6 +1460,7 @@ fn arrayInitExpr( mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(array_type.ast.elem_count)), "_")) { const len_inst = try gz.addInt(array_init.ast.elements.len); + const saved_cursor = astgen.saveSourceCursor(); const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type); if (array_type.ast.sentinel == .none) { const array_type_inst = try gz.addPlNode(.array_type, type_expr, Zir.Inst.Bin{ @@ -1468,6 +1469,7 @@ fn arrayInitExpr( }); break :inst .{ array_type_inst, elem_type }; } else { + astgen.restoreSourceCursor(saved_cursor); const sentinel_node = array_type.ast.sentinel.unwrap().?; const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, sentinel_node, .array_sentinel); const array_type_inst = try gz.addPlNode( @@ -1729,6 +1731,7 @@ fn structInitExpr( mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(array_type.ast.elem_count)), "_"); if (struct_init.ast.fields.len == 0) { if (is_inferred_array_len) { + const saved_cursor = astgen.saveSourceCursor(); const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type); const array_type_inst = if (array_type.ast.sentinel == .none) blk: { break :blk try gz.addPlNode(.array_type, type_expr, Zir.Inst.Bin{ @@ -1736,6 +1739,7 @@ fn structInitExpr( .rhs = elem_type, }); } else blk: { + astgen.restoreSourceCursor(saved_cursor); const sentinel_node = array_type.ast.sentinel.unwrap().?; const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, sentinel_node, .array_sentinel); break :blk try gz.addPlNode( @@ -3135,6 +3139,11 @@ fn varDecl( return astgen.failTok(tree.nodeMainToken(section_node), "cannot set section of local variable '{s}'", .{ident_name_raw}); } + const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node.unwrap()) |type_node| + try typeExpr(gz, scope, type_node) + else + .none; + const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node.unwrap()) |align_node| try comptimeExpr(gz, scope, coerced_align_ri, align_node, .@"align") else @@ -3155,8 +3164,8 @@ fn varDecl( if (align_inst == .none and !astgen.nodes_need_rl.contains(node)) { - const result_info: ResultInfo = if (var_decl.ast.type_node.unwrap()) |type_node| .{ - .rl = .{ .ty = try typeExpr(gz, scope, type_node) }, + const result_info: ResultInfo = if (type_inst != .none) .{ + .rl = .{ .ty = type_inst }, .ctx = .const_init, } else .{ .rl = .none, .ctx = .const_init }; const init_inst: Zir.Inst.Ref = try nameStratExpr(gz, scope, result_info, init_node, .dbg_var) orelse @@ -3185,8 +3194,7 @@ fn varDecl( const is_comptime = gz.is_comptime or tree.nodeTag(init_node) == .@"comptime"; - const init_rl: ResultInfo.Loc = if (var_decl.ast.type_node.unwrap()) |type_node| init_rl: { - const type_inst = try typeExpr(gz, scope, type_node); + const init_rl: ResultInfo.Loc = if (type_inst != .none) init_rl: { if (align_inst == .none) { break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } }; } else { @@ -3254,8 +3262,7 @@ fn varDecl( if (var_decl.comptime_token != null and gz.is_comptime) return astgen.failTok(var_decl.comptime_token.?, "'comptime var' is redundant in comptime scope", .{}); const is_comptime = var_decl.comptime_token != null or gz.is_comptime; - const alloc: Zir.Inst.Ref, const resolve_inferred: bool, const result_info: ResultInfo = if (var_decl.ast.type_node.unwrap()) |type_node| a: { - const type_inst = try typeExpr(gz, scope, type_node); + const alloc: Zir.Inst.Ref, const resolve_inferred: bool, const result_info: ResultInfo = if (type_inst != .none) a: { const alloc = alloc: { if (align_inst == .none) { const tag: Zir.Inst.Tag = if (is_comptime) @@ -3433,6 +3440,7 @@ fn assignDestructureMaybeDecls( const is_comptime = full.comptime_token != null or gz.is_comptime; const value_is_comptime = tree.nodeTag(full.ast.value_expr) == .@"comptime"; + const saved_cursor = astgen.saveSourceCursor(); // When declaring consts via a destructure, we always use a result pointer. // This avoids the need to create tuple types, and is also likely easier to // optimize, since it's a bit tricky for the optimizer to "split up" the @@ -3482,14 +3490,18 @@ fn assignDestructureMaybeDecls( // We also mark `const`s as comptime if the RHS is definitely comptime-known. const this_variable_comptime = is_comptime or (is_const and value_is_comptime); + const type_inst = if (full_var_decl.ast.type_node.unwrap()) |type_node| + try typeExpr(gz, scope, type_node) + else + .none; + const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node.unwrap()) |align_node| try comptimeExpr(gz, scope, coerced_align_ri, align_node, .@"align") else .none; - if (full_var_decl.ast.type_node.unwrap()) |type_node| { + if (type_inst != .none) { // Typed alloc - const type_inst = try typeExpr(gz, scope, type_node); const ptr = if (align_inst == .none) ptr: { const tag: Zir.Inst.Tag = if (this_variable_comptime) .alloc_comptime_mut @@ -3556,6 +3568,7 @@ fn assignDestructureMaybeDecls( if (any_lvalue_expr) { // At least one variable was an lvalue expr. Iterate again in order to // evaluate the lvalues from within the possible block_comptime. + astgen.restoreSourceCursor(saved_cursor); for (rl_components, full.ast.variables) |*variable_rl, variable_node| { if (variable_rl.* != .typed_ptr) continue; switch (tree.nodeTag(variable_node)) { @@ -3889,7 +3902,9 @@ fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node. return astgen.failNode(len_node, "unable to infer array size", .{}); } const len = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node, .array_length); + const saved_cursor = astgen.saveSourceCursor(); const elem_type = try typeExpr(gz, scope, extra.elem_type); + astgen.restoreSourceCursor(saved_cursor); const sentinel = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = elem_type } }, extra.sentinel, node, .array_sentinel); const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{ @@ -4061,43 +4076,41 @@ fn fnDecl( // We include a function *type*, not a value. const type_inst = try fnProtoExprInner(&type_gz, &type_gz.base, .{ .rl = .none }, decl_node, fn_proto, true); _ = try type_gz.addBreakWithSrcNode(.break_inline, decl_inst, type_inst, decl_node); + astgen.restoreSourceCursor(saved_cursor); } var align_gz = type_gz.makeSubBlock(scope); defer align_gz.unstack(); if (fn_proto.ast.align_expr.unwrap()) |align_expr| { - astgen.restoreSourceCursor(saved_cursor); const inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, align_expr); _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); } - var linksection_gz = align_gz.makeSubBlock(scope); - defer linksection_gz.unstack(); - - if (fn_proto.ast.section_expr.unwrap()) |section_expr| { - astgen.restoreSourceCursor(saved_cursor); - const inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_expr); - _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); - } - - var addrspace_gz = linksection_gz.makeSubBlock(scope); + var addrspace_gz = align_gz.makeSubBlock(scope); defer addrspace_gz.unstack(); if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| { - astgen.restoreSourceCursor(saved_cursor); const addrspace_ty = try addrspace_gz.addStdLangValue(addrspace_expr, .address_space); const inst = try expr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, addrspace_expr); _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); } - var value_gz = addrspace_gz.makeSubBlock(scope); + var linksection_gz = addrspace_gz.makeSubBlock(scope); + defer linksection_gz.unstack(); + + if (fn_proto.ast.section_expr.unwrap()) |section_expr| { + const inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_expr); + _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node); + } + + var value_gz = linksection_gz.makeSubBlock(scope); defer value_gz.unstack(); if (!is_extern) { // We include a function *value*, not a type. astgen.restoreSourceCursor(saved_cursor); - try astgen.fnDeclInner(&value_gz, &value_gz.base, saved_cursor, decl_inst, decl_node, body_node.unwrap().?, fn_proto); + try astgen.fnDeclInner(&value_gz, &value_gz.base, decl_inst, decl_node, body_node.unwrap().?, fn_proto); } // *Now* we can incorporate the full source code into the hasher. @@ -4119,8 +4132,8 @@ fn fnDecl( .type_gz = &type_gz, .align_gz = &align_gz, - .linksection_gz = &linksection_gz, .addrspace_gz = &addrspace_gz, + .linksection_gz = &linksection_gz, .value_gz = &value_gz, }); } @@ -4129,7 +4142,6 @@ fn fnDeclInner( astgen: *AstGen, decl_gz: *GenZir, scope: *Scope, - saved_cursor: SourceCursor, decl_inst: Zir.Inst.Index, decl_node: Ast.Node.Index, body_node: Ast.Node.Index, @@ -4266,6 +4278,31 @@ fn fnDeclInner( // instructions inside the expression blocks for cc and ret_ty to use the function // instruction as the body to break from. + var cc_gz = decl_gz.makeSubBlock(scope); + defer cc_gz.unstack(); + const cc_ref: Zir.Inst.Ref = blk: { + if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| { + const inst = try expr( + &cc_gz, + scope, + .{ .rl = .{ .coerced_ty = try cc_gz.addStdLangValue(callconv_expr, .calling_convention) } }, + callconv_expr, + ); + if (cc_gz.instructionsSlice().len == 0) { + // In this case we will send a len=0 body which can be encoded more efficiently. + break :blk inst; + } + _ = try cc_gz.addBreak(.break_inline, @fromBackingInt(@intCast(0)), inst); + break :blk inst; + } else if (has_inline_keyword) { + const inst = try cc_gz.addStdLangValue(decl_node, .calling_convention_inline); + _ = try cc_gz.addBreak(.break_inline, @fromBackingInt(@intCast(0)), inst); + break :blk inst; + } else { + break :blk .none; + } + }; + var ret_gz = decl_gz.makeSubBlock(params_scope); defer ret_gz.unstack(); any_param_used = false; // we will check this later @@ -4284,34 +4321,6 @@ fn fnDeclInner( const ret_body_param_refs = try astgen.fetchRemoveRefEntries(param_insts.items); const ret_ty_is_generic = any_param_used; - // We're jumping back in source, so restore the cursor. - astgen.restoreSourceCursor(saved_cursor); - - var cc_gz = decl_gz.makeSubBlock(scope); - defer cc_gz.unstack(); - const cc_ref: Zir.Inst.Ref = blk: { - if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| { - const inst = try expr( - &cc_gz, - scope, - .{ .rl = .{ .coerced_ty = try cc_gz.addStdLangValue(callconv_expr, .calling_convention) } }, - callconv_expr, - ); - if (cc_gz.instructionsSlice().len == 0) { - // In this case we will send a len=0 body which can be encoded more efficiently. - break :blk inst; - } - _ = try cc_gz.addBreak(.break_inline, @fromBackingInt(@intCast(0)), inst); - break :blk inst; - } else if (has_inline_keyword) { - const inst = try cc_gz.addStdLangValue(decl_node, .calling_convention_inline); - _ = try cc_gz.addBreak(.break_inline, @fromBackingInt(@intCast(0)), inst); - break :blk inst; - } else { - break :blk .none; - } - }; - var body_gz: GenZir = .{ .is_comptime = false, .src_baseline = fn_proto.ast.proto_node, @@ -4490,14 +4499,6 @@ fn globalVarDecl( _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node); } - var linksection_gz = type_gz.makeSubBlock(scope); - defer linksection_gz.unstack(); - - if (var_decl.ast.section_node.unwrap()) |section_node| { - const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_node); - _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node); - } - var addrspace_gz = type_gz.makeSubBlock(scope); defer addrspace_gz.unstack(); @@ -4507,6 +4508,14 @@ fn globalVarDecl( _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node); } + var linksection_gz = type_gz.makeSubBlock(scope); + defer linksection_gz.unstack(); + + if (var_decl.ast.section_node.unwrap()) |section_node| { + const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_node); + _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node); + } + var init_gz = type_gz.makeSubBlock(scope); defer init_gz.unstack(); @@ -4536,8 +4545,8 @@ fn globalVarDecl( .type_gz = &type_gz, .align_gz = &align_gz, - .linksection_gz = &linksection_gz, .addrspace_gz = &addrspace_gz, + .linksection_gz = &linksection_gz, .value_gz = &init_gz, }); } @@ -4599,8 +4608,8 @@ fn comptimeDecl( .linkage = .normal, .type_gz = &dummy_gz, .align_gz = &dummy_gz, - .linksection_gz = &dummy_gz, .addrspace_gz = &dummy_gz, + .linksection_gz = &dummy_gz, .value_gz = &comptime_gz, }); } @@ -7734,10 +7743,6 @@ fn switchExpr( }; if (capture != .none) assert(any_has_payload_capture); - if (is_err_switch) { - assert(!any_payload_is_ref); // should have failed by now - assert(!any_has_tag_capture); // should have failed by now - } prong_body: { scratch_scope.instructions_top = parent_gz.instructions.items.len; @@ -7844,6 +7849,10 @@ fn switchExpr( } } } + if (is_err_switch) { + assert(!any_payload_is_ref); // should have failed by now + assert(!any_has_tag_capture); // should have failed by now + } assert(scalar_case_index + multi_case_index + @intFromBool(has_else) == case_nodes.len); assert(multi_items_infos_start + multi_item_offset == bodies_start); @@ -11527,8 +11536,8 @@ const GenZir = struct { /// Must be called with the following stack set up: /// * gz (bottom) - /// * ret_gz /// * cc_gz + /// * ret_gz /// * body_gz (top) /// Unstacks all of those except for `gz`. fn addFunc( @@ -11575,16 +11584,16 @@ const GenZir = struct { stacked_gz = body_gz; break :body body; } else &.{}; - const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: { - const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz); - stacked_gz = cc_gz; - break :body cc_body; - } else &.{}; const ret_body: []const Zir.Inst.Index = if (args.ret_gz) |ret_gz| body: { const ret_body = ret_gz.instructionsSliceUptoOpt(stacked_gz); stacked_gz = ret_gz; break :body ret_body; } else &.{}; + const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: { + const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz); + stacked_gz = cc_gz; + break :body cc_body; + } else &.{}; break :bodies .{ body, cc_body, ret_body }; }; @@ -11725,8 +11734,8 @@ const GenZir = struct { // Order is important when unstacking. if (args.body_gz) |body_gz| body_gz.unstack(); - if (args.cc_gz) |cc_gz| cc_gz.unstack(); if (args.ret_gz) |ret_gz| ret_gz.unstack(); + if (args.cc_gz) |cc_gz| cc_gz.unstack(); astgen.instructions.appendAssumeCapacity(.{ .tag = tag, @@ -13502,10 +13511,10 @@ fn setDeclaration( /// Must be stacked on `type_gz`. align_gz: *GenZir, /// Must be stacked on `align_gz`. - linksection_gz: *GenZir, - /// Must be stacked on `linksection_gz`. addrspace_gz: *GenZir, - /// Must be stacked on `addrspace_gz` and have nothing stacked on top of it. + /// Must be stacked on `addrspace_gz`. + linksection_gz: *GenZir, + /// Must be stacked on `linksection_gz` and have nothing stacked on top of it. value_gz: *GenZir, }, ) !void { @@ -13513,9 +13522,9 @@ fn setDeclaration( const gpa = astgen.gpa; const type_body = args.type_gz.instructionsSliceUpto(args.align_gz); - const align_body = args.align_gz.instructionsSliceUpto(args.linksection_gz); - const linksection_body = args.linksection_gz.instructionsSliceUpto(args.addrspace_gz); - const addrspace_body = args.addrspace_gz.instructionsSliceUpto(args.value_gz); + const align_body = args.align_gz.instructionsSliceUpto(args.addrspace_gz); + const addrspace_body = args.addrspace_gz.instructionsSliceUpto(args.linksection_gz); + const linksection_body = args.linksection_gz.instructionsSliceUpto(args.value_gz); const value_body = args.value_gz.instructionsSlice(); const has_name = args.name != .empty; diff --git a/test/cases/array_sentinel_cursor.zig b/test/cases/array_sentinel_cursor.zig new file mode 100644 index 0000000000000000000000000000000000000000..7b20dd48a1e8029c740645a06a079016c842080b --- /dev/null +++ b/test/cases/array_sentinel_cursor.zig @@ -0,0 +1,14 @@ +fn s() usize { + return 64; +} + +fn T() type { + return u64; +} + +const Array = [64:s()]T(); +const empty = [_:s()]T(){}; +const items = [_:s()]T(){100}; + +// compile +// output_mode=Obj diff --git a/test/cases/compile_errors/error_set_cannot_capture_by_reference.zig b/test/cases/compile_errors/error_set_cannot_capture_by_reference.zig deleted file mode 100644 index d13f85d066983081dd6c2b02f48f1164c2162bde..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/error_set_cannot_capture_by_reference.zig +++ /dev/null @@ -1,13 +0,0 @@ -export fn entry() void { - const err: error{Foo} = error.Foo; - - switch (err) { - error.Foo => |*foo| { - foo catch {}; - }, - } -} - -// error -// -// :5:23: error: error set cannot be captured by reference diff --git a/test/cases/compile_errors/switch_capture_error_by_ref.zig b/test/cases/compile_errors/switch_capture_error_by_ref.zig deleted file mode 100644 index e432159d2c7fbf9f919c0539c6ee75fdd7f08cb5..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/switch_capture_error_by_ref.zig +++ /dev/null @@ -1,25 +0,0 @@ -export fn entry1() void { - switch (@as(anyerror, error.MyError)) { - error.MyError, error.MyOtherError => |*err| _ = err, - else => {}, - } -} - -export fn entry2() void { - switch (@as(anyerror, error.MyError)) { - inline error.MyError, error.MyOtherError => |*err| _ = err, - else => {}, - } -} - -export fn entry3() void { - switch (@as(anyerror, error.MyError)) { - else => |*err| _ = err, - } -} - -// error -// -// :3:47: error: error set cannot be captured by reference -// :10:54: error: error set cannot be captured by reference -// :17:18: error: error set cannot be captured by reference diff --git a/test/cases/compile_errors/switch_on_error_bad_captures_astgen.zig b/test/cases/compile_errors/switch_on_error_bad_captures_astgen.zig new file mode 100644 index 0000000000000000000000000000000000000000..97abfcd55fec648f62ccce83c932a461c78c97eb --- /dev/null +++ b/test/cases/compile_errors/switch_on_error_bad_captures_astgen.zig @@ -0,0 +1,37 @@ +const Error = error {OutOfMemory, ReadFailed}; +const result: Error!u64 = error.OutOfMemory; + +test { + _ = result catch |err| switch (err) { + error.ReadFailed => 2, + error.OutOfMemory => |*val| 1, + }; +} + +test { + _ = result catch |err| switch (err) { + error.ReadFailed => 2, + error.OutOfMemory => |_, tag| 1, + }; +} + +test { + _ = if (result) |n| n else |err| switch (err) { + error.ReadFailed => 2, + error.OutOfMemory => |*val| 1, + }; +} + +test { + _ = if (result) |n| n else |err| switch (err) { + error.ReadFailed => 2, + error.OutOfMemory => |_, tag| 1, + }; +} + +// error +// +// :7:32: error: error set cannot be captured by reference +// :14:34: error: cannot capture tag of error union +// :21:32: error: error set cannot be captured by reference +// :28:34: error: cannot capture tag of error union diff --git a/test/cases/compile_errors/switch_on_error_bad_captures_sema.zig b/test/cases/compile_errors/switch_on_error_bad_captures_sema.zig new file mode 100644 index 0000000000000000000000000000000000000000..6a5ff43818a2905d4ed50ac1b0e2eefdc4c8248d --- /dev/null +++ b/test/cases/compile_errors/switch_on_error_bad_captures_sema.zig @@ -0,0 +1,21 @@ +const Error = error{ OutOfMemory, ReadFailed }; +const err: Error = error.OutOfMemory; + +export fn a() void { + _ = switch (err) { + error.ReadFailed => 2, + error.OutOfMemory => |*val| @intFromError(val.*), + }; +} + +export fn b() void { + _ = switch (err) { + error.ReadFailed => 2, + error.OutOfMemory => |_, tag| @intFromEnum(tag), + }; +} + +// error +// +// :7:31: error: error set cannot be captured by reference +// :14:34: error: cannot capture tag of non-union type 'error{OutOfMemory,ReadFailed}' diff --git a/test/cases/compile_errors/switch_on_error_with_capture_by_reference.zig b/test/cases/compile_errors/switch_on_error_with_capture_by_reference.zig deleted file mode 100644 index d3206d02fe12f024513fcd5d19261251ae32c4f2..0000000000000000000000000000000000000000 --- a/test/cases/compile_errors/switch_on_error_with_capture_by_reference.zig +++ /dev/null @@ -1,22 +0,0 @@ -comptime { - const e: error{Foo}!u32 = error.Foo; - e catch |err| switch (err) { - error.Foo => |*foo| { - foo catch {}; - }, - }; -} - -comptime { - const e: error{Foo}!u32 = error.Foo; - if (e) {} else |err| switch (err) { - error.Foo => |*foo| { - foo catch {}; - }, - } -} - -// error -// -// :4:24: error: error set cannot be captured by reference -// :13:24: error: error set cannot be captured by reference diff --git a/test/cases/decl_qualifier_cursor.zig b/test/cases/decl_qualifier_cursor.zig new file mode 100644 index 0000000000000000000000000000000000000000..c4add0b6f11aad4253f89d49253bb9187542d3ca --- /dev/null +++ b/test/cases/decl_qualifier_cursor.zig @@ -0,0 +1,45 @@ +const lang = @import("std").lang; + +fn al() comptime_int { + return 16; +} + +fn as() lang.AddressSpace { + return .generic; +} + +fn ls() []const u8 { + return "text"; +} + +fn cc() lang.CallingConvention { + return .c; +} + +fn rv() type { + return void; +} + +extern var a: u64 align(al()) addrspace(as()) linksection(ls()); +extern fn b() align(al()) addrspace(as()) linksection(ls()) callconv(cc()) rv(); +// Intentionally not `export` so it is never analyzed since the qualifiers are +// not valid for all targets. The main point of this test is checking that Zir +// generation is done correctly for these, so the above is not an issue. +fn c() align(al()) addrspace(as()) linksection(ls()) callconv(cc()) rv() { + const v: struct { + x: u32, + y: u32, + } align(al()) = undefined; + const ta: u64, const tb: struct { + x: u32, + y: u32, + } align(al()) = .{ 0, .{ .x = 10, .y = 20 } }; + _ = v; + _ = a; + _ = b; + _ = ta; + _ = tb; +} + +// compile +// output_mode=Obj diff --git a/test/cases/tuple_assign_cursor.zig b/test/cases/tuple_assign_cursor.zig new file mode 100644 index 0000000000000000000000000000000000000000..a0318f263596dccceef036215f1c8653cc9057d5 --- /dev/null +++ b/test/cases/tuple_assign_cursor.zig @@ -0,0 +1,13 @@ +fn T() type { + return u64; +} + +test { + struct { + var a: u64 = 0; + }.a, const b: T() = .{ 1, 2 }; + _ = b; +} + +// compile +// output_mode=Obj -- 2.54.0