From db0f372da8b25f4a911cd8e0b7f8e5cdfc64f940 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 17 Aug 2022 13:10:58 +0300 Subject: [PATCH 1/4] Sema: make optional noreturn behave correctly --- src/Sema.zig | 15 ++++++-- test/behavior/optional.zig | 36 +++++++++++++++++++ .../invalid_optional_payload_type.zig | 13 +++++++ 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 test/cases/compile_errors/invalid_optional_payload_type.zig diff --git a/src/Sema.zig b/src/Sema.zig index d7d6994bcd..ceeb8af23c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6684,8 +6684,13 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro defer tracy.end(); const inst_data = sema.code.instructions.items(.data)[inst].un_node; - const src = inst_data.src(); - const child_type = try sema.resolveType(block, src, inst_data.operand); + const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node }; + const child_type = try sema.resolveType(block, operand_src, inst_data.operand); + if (child_type.zigTypeTag() == .Opaque) { + return sema.fail(block, operand_src, "opaque type '{}' cannot be optional", .{child_type.fmt(sema.mod)}); + } else if (child_type.zigTypeTag() == .Null) { + return sema.fail(block, operand_src, "type '{}' cannot be optional", .{child_type.fmt(sema.mod)}); + } const opt_type = try Type.optional(sema.arena, child_type); return sema.addType(opt_type); @@ -25714,6 +25719,12 @@ fn analyzeIsNull( return Air.Inst.Ref.bool_false; } } + + const operand_ty = sema.typeOf(operand); + var buf: Type.Payload.ElemType = undefined; + if (operand_ty.zigTypeTag() == .Optional and operand_ty.optionalChild(&buf).zigTypeTag() == .NoReturn) { + return Air.Inst.Ref.bool_true; + } try sema.requireRuntimeBlock(block, src, null); const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; return block.addUnOp(air_tag, operand); diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 4e5eb5061c..c13a3b7e4f 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -369,3 +369,39 @@ test "optional pointer to zero bit error union payload" { some.foo(); } else |_| {} } + +const NoReturn = struct { + var a: u32 = undefined; + fn someData() bool { + a -= 1; + return a == 0; + } + fn loop() ?noreturn { + while (true) { + if (someData()) return null; + } + } + fn testOrelse() u32 { + loop() orelse return 123; + @compileError("bad"); + } +}; + +test "optional of noreturn used with if" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + + NoReturn.a = 64; + if (NoReturn.loop()) |_| { + @compileError("bad"); + } else { + try expect(true); + } +} + +test "optional of noreturn used with orelse" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + + NoReturn.a = 64; + const val = NoReturn.testOrelse(); + try expect(val == 123); +} diff --git a/test/cases/compile_errors/invalid_optional_payload_type.zig b/test/cases/compile_errors/invalid_optional_payload_type.zig new file mode 100644 index 0000000000..0058cd5e36 --- /dev/null +++ b/test/cases/compile_errors/invalid_optional_payload_type.zig @@ -0,0 +1,13 @@ +comptime { + _ = ?anyopaque; +} +comptime { + _ = ?@TypeOf(null); +} + +// error +// backend=stage2 +// target=native +// +// :2:10: error: opaque type 'anyopaque' cannot be optional +// :5:10: error: type '@TypeOf(null)' cannot be optional From b0a55e1b3be3a274546f9c18016e9609d546bdb0 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 17 Aug 2022 13:21:07 +0300 Subject: [PATCH 2/4] Sema: make noreturn error union behave correctly --- src/Sema.zig | 14 +++++ test/behavior/error.zig | 62 ++++++++++++++++++- .../invalid_error_union_payload_type.zig | 13 ++++ 3 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 test/cases/compile_errors/invalid_error_union_payload_type.zig diff --git a/src/Sema.zig b/src/Sema.zig index ceeb8af23c..772abf5a84 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6789,6 +6789,15 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr error_set.fmt(sema.mod), }); } + if (payload.zigTypeTag() == .Opaque) { + return sema.fail(block, rhs_src, "error union with payload of opaque type '{}' not allowed", .{ + payload.fmt(sema.mod), + }); + } else if (payload.zigTypeTag() == .ErrorSet) { + return sema.fail(block, rhs_src, "error union with payload of error set type '{}' not allowed", .{ + payload.fmt(sema.mod), + }); + } const err_union_ty = try Type.errorUnion(sema.arena, error_set, payload, sema.mod); return sema.addType(err_union_ty); } @@ -25763,6 +25772,11 @@ fn analyzeIsNonErrComptimeOnly( if (ot == .ErrorSet) return Air.Inst.Ref.bool_false; assert(ot == .ErrorUnion); + const payload_ty = operand_ty.errorUnionPayload(); + if (payload_ty.zigTypeTag() == .NoReturn) { + return Air.Inst.Ref.bool_false; + } + if (Air.refToIndex(operand)) |operand_inst| { switch (sema.air_instructions.items(.tag)[operand_inst]) { .wrap_errunion_payload => return Air.Inst.Ref.bool_true, diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 306dad5d9e..84b18a2738 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -725,7 +725,7 @@ test "simple else prong allowed even when all errors handled" { try expect(value == 255); } -test { +test "pointer to error union payload" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO @@ -736,3 +736,63 @@ test { const payload_ptr = &(err_union catch unreachable); try expect(payload_ptr.* == 15); } + +const NoReturn = struct { + var a: u32 = undefined; + fn someData() bool { + a -= 1; + return a == 0; + } + fn loop() !noreturn { + while (true) { + if (someData()) + return error.GenericFailure; + } + } + fn testTry() anyerror { + try loop(); + } + fn testCatch() anyerror { + loop() catch return error.OtherFailure; + @compileError("bad"); + } +}; + +test "error union of noreturn used with if" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + NoReturn.a = 64; + if (NoReturn.loop()) { + @compileError("bad"); + } else |err| { + try expect(err == error.GenericFailure); + } +} + +test "error union of noreturn used with try" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + NoReturn.a = 64; + const err = NoReturn.testTry(); + try expect(err == error.GenericFailure); +} + +test "error union of noreturn used with catch" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + NoReturn.a = 64; + const err = NoReturn.testCatch(); + try expect(err == error.OtherFailure); +} diff --git a/test/cases/compile_errors/invalid_error_union_payload_type.zig b/test/cases/compile_errors/invalid_error_union_payload_type.zig new file mode 100644 index 0000000000..f8646d9450 --- /dev/null +++ b/test/cases/compile_errors/invalid_error_union_payload_type.zig @@ -0,0 +1,13 @@ +comptime { + _ = anyerror!anyopaque; +} +comptime { + _ = anyerror!anyerror; +} + +// error +// backend=stage2 +// target=native +// +// :2:18: error: error union with payload of opaque type 'anyopaque' not allowed +// :5:18: error: error union with payload of error set type 'anyerror' not allowed From c3d5428cba4d02afbe1ec34bdc7bcb68f56d1b45 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 17 Aug 2022 15:07:20 +0300 Subject: [PATCH 3/4] Sema: properly handle noreturn fields in unions --- src/Sema.zig | 202 +++++++++++++++--- test/behavior/union.zig | 45 ++++ .../compile_errors/noreturn_struct_field.zig | 12 ++ ...ast_to_union_which_has_non-void_fields.zig | 2 - .../union_noreturn_field_initialized.zig | 43 ++++ 5 files changed, 270 insertions(+), 34 deletions(-) create mode 100644 test/cases/compile_errors/noreturn_struct_field.zig create mode 100644 test/cases/compile_errors/union_noreturn_field_initialized.zig diff --git a/src/Sema.zig b/src/Sema.zig index 772abf5a84..1aebe8e98e 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3772,6 +3772,7 @@ fn validateStructInit( } var root_msg: ?*Module.ErrorMsg = null; + errdefer if (root_msg) |msg| msg.destroy(sema.gpa); const struct_ptr = try sema.resolveInst(struct_ptr_zir_ref); if ((is_comptime or block.is_comptime) and @@ -3947,6 +3948,7 @@ fn validateStructInit( } if (root_msg) |msg| { + root_msg = null; if (struct_ty.castTag(.@"struct")) |struct_obj| { const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod); defer gpa.free(fqn); @@ -4005,6 +4007,8 @@ fn zirValidateArrayInit( if (instrs.len != array_len and array_ty.isTuple()) { const struct_obj = array_ty.castTag(.tuple).?.data; var root_msg: ?*Module.ErrorMsg = null; + errdefer if (root_msg) |msg| msg.destroy(sema.gpa); + for (struct_obj.values) |default_val, i| { if (i < instrs.len) continue; @@ -4019,6 +4023,7 @@ fn zirValidateArrayInit( } if (root_msg) |msg| { + root_msg = null; return sema.failWithOwnedErrorMsg(msg); } } @@ -8964,12 +8969,15 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError }, }; - const union_originally = blk: { + const maybe_union_ty = blk: { const zir_data = sema.code.instructions.items(.data); const cond_index = Zir.refToIndex(extra.data.operand).?; const raw_operand = sema.resolveInst(zir_data[cond_index].un_node.operand) catch unreachable; - break :blk sema.typeOf(raw_operand).zigTypeTag() == .Union; + break :blk sema.typeOf(raw_operand); }; + const union_originally = maybe_union_ty.zigTypeTag() == .Union; + var seen_union_fields: []?Module.SwitchProngSrc = &.{}; + defer gpa.free(seen_union_fields); const operand_ty = sema.typeOf(operand); @@ -9004,7 +9012,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError .Union => unreachable, // handled in zirSwitchCond .Enum => { var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount()); - defer gpa.free(seen_fields); + defer if (!union_originally) gpa.free(seen_fields); + if (union_originally) seen_union_fields = seen_fields; mem.set(?Module.SwitchProngSrc, seen_fields, null); // This is used for non-exhaustive enum values that do not correspond to any tags. @@ -9637,18 +9646,28 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const item = try sema.resolveInst(item_ref); // `item` is already guaranteed to be constant known. - _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) { - error.ComptimeBreak => { - const zir_datas = sema.code.instructions.items(.data); - const break_data = zir_datas[sema.comptime_break_inst].@"break"; - try sema.addRuntimeBreak(&case_block, .{ - .block_inst = break_data.block_inst, - .operand = break_data.operand, - .inst = sema.comptime_break_inst, - }); - }, - else => |e| return e, - }; + const analyze_body = if (union_originally) blk: { + const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; + const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod); + break :blk field_ty.zigTypeTag() != .NoReturn; + } else true; + + if (analyze_body) { + _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) { + error.ComptimeBreak => { + const zir_datas = sema.code.instructions.items(.data); + const break_data = zir_datas[sema.comptime_break_inst].@"break"; + try sema.addRuntimeBreak(&case_block, .{ + .block_inst = break_data.block_inst, + .operand = break_data.operand, + .inst = sema.comptime_break_inst, + }); + }, + else => |e| return e, + }; + } else { + _ = try case_block.addNoOp(.unreach); + } try wip_captures.finalize(); @@ -9689,20 +9708,34 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError if (ranges_len == 0) { cases_len += 1; + const analyze_body = if (union_originally) + for (items) |item_ref| { + const item = try sema.resolveInst(item_ref); + const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; + const field_ty = maybe_union_ty.unionFieldType(item_val, sema.mod); + if (field_ty.zigTypeTag() != .NoReturn) break true; + } else false + else + true; + const body = sema.code.extra[extra_index..][0..body_len]; extra_index += body_len; - _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) { - error.ComptimeBreak => { - const zir_datas = sema.code.instructions.items(.data); - const break_data = zir_datas[sema.comptime_break_inst].@"break"; - try sema.addRuntimeBreak(&case_block, .{ - .block_inst = break_data.block_inst, - .operand = break_data.operand, - .inst = sema.comptime_break_inst, - }); - }, - else => |e| return e, - }; + if (analyze_body) { + _ = sema.analyzeBodyInner(&case_block, body) catch |err| switch (err) { + error.ComptimeBreak => { + const zir_datas = sema.code.instructions.items(.data); + const break_data = zir_datas[sema.comptime_break_inst].@"break"; + try sema.addRuntimeBreak(&case_block, .{ + .block_inst = break_data.block_inst, + .operand = break_data.operand, + .inst = sema.comptime_break_inst, + }); + }, + else => |e| return e, + }; + } else { + _ = try case_block.addNoOp(.unreach); + } try cases_extra.ensureUnusedCapacity(gpa, 2 + items.len + case_block.instructions.items.len); @@ -9824,7 +9857,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = wip_captures.scope; - if (special.body.len != 0) { + const analyze_body = if (union_originally) + for (seen_union_fields) |seen_field, index| { + if (seen_field != null) continue; + const union_obj = maybe_union_ty.cast(Type.Payload.Union).?.data; + const field_ty = union_obj.fields.values()[index].ty; + if (field_ty.zigTypeTag() != .NoReturn) break true; + } else false + else + true; + + if (special.body.len != 0 and analyze_body) { _ = sema.analyzeBodyInner(&case_block, special.body) catch |err| switch (err) { error.ComptimeBreak => { const zir_datas = sema.code.instructions.items(.data); @@ -13225,6 +13268,14 @@ fn analyzeCmpUnionTag( const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src); const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src); + if (try sema.resolveMaybeUndefVal(block, tag_src, coerced_tag)) |enum_val| { + if (enum_val.isUndef()) return sema.addConstUndef(Type.bool); + const field_ty = union_ty.unionFieldType(enum_val, sema.mod); + if (field_ty.zigTypeTag() == .NoReturn) { + return Air.Inst.Ref.bool_false; + } + } + return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src); } @@ -15579,6 +15630,8 @@ fn finishStructInit( const gpa = sema.gpa; var root_msg: ?*Module.ErrorMsg = null; + errdefer if (root_msg) |msg| msg.destroy(sema.gpa); + if (struct_ty.isAnonStruct()) { const struct_obj = struct_ty.castTag(.anon_struct).?.data; for (struct_obj.values) |default_val, i| { @@ -15634,6 +15687,7 @@ fn finishStructInit( } if (root_msg) |msg| { + root_msg = null; if (struct_ty.castTag(.@"struct")) |struct_obj| { const fqn = try struct_obj.data.getFullyQualifiedName(sema.mod); defer gpa.free(fqn); @@ -21682,6 +21736,18 @@ fn unionFieldPtr( .@"addrspace" = union_ptr_ty.ptrAddressSpace(), }); + if (initializing and field.ty.zigTypeTag() == .NoReturn) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "cannot initialize 'noreturn' field of union", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' declared here", .{field_name}); + try sema.addDeclaredHereNote(msg, union_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: { switch (union_obj.layout) { .Auto => if (!initializing) { @@ -21734,6 +21800,10 @@ fn unionFieldPtr( const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag); try sema.addSafetyCheck(block, ok, .inactive_union_field); } + if (field.ty.zigTypeTag() == .NoReturn) { + _ = try block.addNoOp(.unreach); + return Air.Inst.Ref.unreachable_value; + } return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty); } @@ -21802,6 +21872,10 @@ fn unionFieldVal( const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag); try sema.addSafetyCheck(block, ok, .inactive_union_field); } + if (field.ty.zigTypeTag() == .NoReturn) { + _ = try block.addNoOp(.unreach); + return Air.Inst.Ref.unreachable_value; + } return block.addStructFieldVal(union_byval, field_index, field.ty); } @@ -25002,6 +25076,18 @@ fn coerceEnumToUnion( }; const field = union_obj.fields.values()[field_index]; const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty); + if (field_ty.zigTypeTag() == .NoReturn) { + const msg = msg: { + const msg = try sema.errMsg(block, inst_src, "cannot initialize 'noreturn' field of union", .{}); + errdefer msg.destroy(sema.gpa); + + const field_name = union_obj.fields.keys()[field_index]; + try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' declared here", .{field_name}); + try sema.addDeclaredHereNote(msg, union_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } const opv = (try sema.typeHasOnePossibleValue(block, inst_src, field_ty)) orelse { const msg = msg: { const field_name = union_obj.fields.keys()[field_index]; @@ -25037,13 +25123,37 @@ fn coerceEnumToUnion( return sema.failWithOwnedErrorMsg(msg); } + const union_obj = union_ty.cast(Type.Payload.Union).?.data; + { + var msg: ?*Module.ErrorMsg = null; + errdefer if (msg) |some| some.destroy(sema.gpa); + + for (union_obj.fields.values()) |field, i| { + if (field.ty.zigTypeTag() == .NoReturn) { + const err_msg = msg orelse try sema.errMsg( + block, + inst_src, + "runtime coercion from enum '{}' to union '{}' which has a 'noreturn' field", + .{ tag_ty.fmt(sema.mod), union_ty.fmt(sema.mod) }, + ); + msg = err_msg; + + try sema.addFieldErrNote(block, union_ty, i, err_msg, "'noreturn' field here", .{}); + } + } + if (msg) |some| { + msg = null; + try sema.addDeclaredHereNote(some, union_ty); + return sema.failWithOwnedErrorMsg(some); + } + } + // If the union has all fields 0 bits, the union value is just the enum value. if (union_ty.unionHasAllZeroBitFieldTypes()) { return block.addBitCast(union_ty, enum_tag); } const msg = msg: { - const union_obj = union_ty.cast(Type.Payload.Union).?.data; const msg = try sema.errMsg( block, inst_src, @@ -25054,11 +25164,11 @@ fn coerceEnumToUnion( var it = union_obj.fields.iterator(); var field_index: usize = 0; - while (it.next()) |field| { + while (it.next()) |field| : (field_index += 1) { const field_name = field.key_ptr.*; const field_ty = field.value_ptr.ty; + if (!field_ty.hasRuntimeBits()) continue; try sema.addFieldErrNote(block, union_ty, field_index, msg, "field '{s}' has type '{}'", .{ field_name, field_ty.fmt(sema.mod) }); - field_index += 1; } try sema.addDeclaredHereNote(msg, union_ty); break :msg msg; @@ -25361,6 +25471,7 @@ fn coerceTupleToStruct( // Populate default field values and report errors for missing fields. var root_msg: ?*Module.ErrorMsg = null; + errdefer if (root_msg) |msg| msg.destroy(sema.gpa); for (field_refs) |*field_ref, i| { if (field_ref.* != .none) continue; @@ -25386,6 +25497,7 @@ fn coerceTupleToStruct( } if (root_msg) |msg| { + root_msg = null; try sema.addDeclaredHereNote(msg, struct_ty); return sema.failWithOwnedErrorMsg(msg); } @@ -25455,6 +25567,7 @@ fn coerceTupleToTuple( // Populate default field values and report errors for missing fields. var root_msg: ?*Module.ErrorMsg = null; + errdefer if (root_msg) |msg| msg.destroy(sema.gpa); for (field_refs) |*field_ref, i| { if (field_ref.* != .none) continue; @@ -25490,6 +25603,7 @@ fn coerceTupleToTuple( } if (root_msg) |msg| { + root_msg = null; try sema.addDeclaredHereNote(msg, tuple_ty); return sema.failWithOwnedErrorMsg(msg); } @@ -27914,6 +28028,18 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void }; return sema.failWithOwnedErrorMsg(msg); } + if (field_ty.zigTypeTag() == .NoReturn) { + const msg = msg: { + const tree = try sema.getAstTree(&block_scope); + const field_src = enumFieldSrcLoc(decl, tree.*, 0, i); + const msg = try sema.errMsg(&block_scope, field_src, "struct fields cannot be 'noreturn'", .{}); + errdefer msg.destroy(sema.gpa); + + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } if (struct_obj.layout == .Extern and !sema.validateExternType(field.ty, .other)) { const msg = msg: { const tree = try sema.getAstTree(&block_scope); @@ -28725,6 +28851,16 @@ fn enumFieldSrcLoc( .container_decl_arg_trailing, => tree.containerDeclArg(enum_node), + .tagged_union, + .tagged_union_trailing, + => tree.taggedUnion(enum_node), + .tagged_union_two, + .tagged_union_two_trailing, + => tree.taggedUnionTwo(&buffer, enum_node), + .tagged_union_enum_tag, + .tagged_union_enum_tag_trailing, + => tree.taggedUnionEnumTag(enum_node), + // Container was constructed with `@Type`. else => return LazySrcLoc.nodeOffset(0), }; @@ -29375,7 +29511,9 @@ fn unionFieldAlignment( src: LazySrcLoc, field: Module.Union.Field, ) !u32 { - if (field.abi_align == 0) { + if (field.ty.zigTypeTag() == .NoReturn) { + return @as(u32, 0); + } else if (field.abi_align == 0) { return sema.typeAbiAlignment(block, src, field.ty); } else { return field.abi_align; diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 2f6fa78f0c..efca75af30 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1256,3 +1256,48 @@ test "return an extern union from C calling convention" { }); try expect(u.d == 4.0); } + +test "noreturn field in union" { + if (builtin.zig_backend == .stage1) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + + const U = union(enum) { + a: u32, + b: noreturn, + c: noreturn, + }; + var a = U{ .a = 1 }; + var count: u32 = 0; + if (a == .b) @compileError("bad"); + switch (a) { + .a => count += 1, + .b => |val| { + _ = val; + @compileError("bad"); + }, + .c => @compileError("bad"), + } + switch (a) { + .a => count += 1, + .b, .c => @compileError("bad"), + } + switch (a) { + .a, .b, .c => { + count += 1; + try expect(a == .a); + }, + } + switch (a) { + .a => count += 1, + else => @compileError("bad"), + } + switch (a) { + else => { + count += 1; + try expect(a == .a); + }, + } + try expect(count == 5); +} diff --git a/test/cases/compile_errors/noreturn_struct_field.zig b/test/cases/compile_errors/noreturn_struct_field.zig new file mode 100644 index 0000000000..90b243c31d --- /dev/null +++ b/test/cases/compile_errors/noreturn_struct_field.zig @@ -0,0 +1,12 @@ +const S = struct { + s: noreturn, +}; +comptime { + _ = @typeInfo(S); +} + +// error +// backend=stage2 +// target=native +// +// :2:5: error: struct fields cannot be 'noreturn' diff --git a/test/cases/compile_errors/runtime_cast_to_union_which_has_non-void_fields.zig b/test/cases/compile_errors/runtime_cast_to_union_which_has_non-void_fields.zig index c312d6db40..0142f422f4 100644 --- a/test/cases/compile_errors/runtime_cast_to_union_which_has_non-void_fields.zig +++ b/test/cases/compile_errors/runtime_cast_to_union_which_has_non-void_fields.zig @@ -18,6 +18,4 @@ fn foo(l: Letter) void { // // :11:20: error: runtime coercion from enum 'tmp.Letter' to union 'tmp.Value' which has non-void fields // :3:5: note: field 'A' has type 'i32' -// :4:5: note: field 'B' has type 'void' -// :5:5: note: field 'C' has type 'void' // :2:15: note: union declared here diff --git a/test/cases/compile_errors/union_noreturn_field_initialized.zig b/test/cases/compile_errors/union_noreturn_field_initialized.zig new file mode 100644 index 0000000000..66304d6a74 --- /dev/null +++ b/test/cases/compile_errors/union_noreturn_field_initialized.zig @@ -0,0 +1,43 @@ +pub export fn entry1() void { + const U = union(enum) { + a: u32, + b: noreturn, + fn foo(_: @This()) void {} + fn bar() noreturn { + unreachable; + } + }; + + var a = U{ .b = undefined }; + _ = a; +} +pub export fn entry2() void { + const U = union(enum) { + a: noreturn, + }; + var u: U = undefined; + u = .a; +} +pub export fn entry3() void { + const U = union(enum) { + a: noreturn, + b: void, + }; + var e = @typeInfo(U).Union.tag_type.?.a; + var u: U = undefined; + u = e; +} + +// error +// backend=stage2 +// target=native +// +// :11:21: error: cannot initialize 'noreturn' field of union +// :4:9: note: field 'b' declared here +// :2:15: note: union declared here +// :19:10: error: cannot initialize 'noreturn' field of union +// :16:9: note: field 'a' declared here +// :15:15: note: union declared here +// :28:9: error: runtime coercion from enum '@typeInfo(tmp.entry3.U).Union.tag_type.?' to union 'tmp.entry3.U' which has a 'noreturn' field +// :23:9: note: 'noreturn' field here +// :22:15: note: union declared here From 233049503a41ac4c6895f7126cda63349606b8f8 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 17 Aug 2022 16:10:46 +0300 Subject: [PATCH 4/4] Sema: allow empty enums and unions --- src/AstGen.zig | 9 - src/Sema.zig | 174 +++++++++--------- src/print_zir.zig | 15 +- src/type.zig | 5 +- test/behavior.zig | 1 + test/behavior/empty_union.zig | 54 ++++++ .../compile_errors/enum_with_0_fields.zig | 7 - ...e_for_exhaustive_enum_with_zero_fields.zig | 18 -- .../reify_type_for_union_with_zero_fields.zig | 17 -- .../union_fields_with_value_assignments.zig | 7 - .../compile_errors/union_with_0_fields.zig | 7 - ...types_in_function_call_raises_an_error.zig | 11 -- test/stage2/cbe.zig | 9 - 13 files changed, 156 insertions(+), 178 deletions(-) create mode 100644 test/behavior/empty_union.zig delete mode 100644 test/cases/compile_errors/enum_with_0_fields.zig delete mode 100644 test/cases/compile_errors/reify_type_for_exhaustive_enum_with_zero_fields.zig delete mode 100644 test/cases/compile_errors/reify_type_for_union_with_zero_fields.zig delete mode 100644 test/cases/compile_errors/union_fields_with_value_assignments.zig delete mode 100644 test/cases/compile_errors/union_with_0_fields.zig delete mode 100644 test/cases/compile_errors/using_invalid_types_in_function_call_raises_an_error.zig diff --git a/src/AstGen.zig b/src/AstGen.zig index 2fcd8cd994..ee1dbeffa4 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -4557,9 +4557,6 @@ fn unionDeclInner( wip_members.appendToField(@enumToInt(tag_value)); } } - if (field_count == 0) { - return astgen.failNode(node, "union declarations must have at least one tag", .{}); - } if (!block_scope.isEmpty()) { _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value); @@ -4715,12 +4712,6 @@ fn containerDecl( .nonexhaustive_node = nonexhaustive_node, }; }; - if (counts.total_fields == 0 and counts.nonexhaustive_node == 0) { - // One can construct an enum with no tags, and it functions the same as `noreturn`. But - // this is only useful for generic code; when explicitly using `enum {}` syntax, there - // must be at least one tag. - try astgen.appendErrorNode(node, "enum declarations must have at least one tag", .{}); - } if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) { try astgen.appendErrorNodeNotes( node, diff --git a/src/Sema.zig b/src/Sema.zig index 1aebe8e98e..1a0ded4a00 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -8979,6 +8979,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError var seen_union_fields: []?Module.SwitchProngSrc = &.{}; defer gpa.free(seen_union_fields); + var empty_enum = false; + const operand_ty = sema.typeOf(operand); var else_error_ty: ?Type = null; @@ -9012,6 +9014,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError .Union => unreachable, // handled in zirSwitchCond .Enum => { var seen_fields = try gpa.alloc(?Module.SwitchProngSrc, operand_ty.enumFieldCount()); + empty_enum = seen_fields.len == 0 and !operand_ty.isNonexhaustiveEnum(); defer if (!union_originally) gpa.free(seen_fields); if (union_originally) seen_union_fields = seen_fields; mem.set(?Module.SwitchProngSrc, seen_fields, null); @@ -9607,6 +9610,9 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } if (scalar_cases_len + multi_cases_len == 0) { + if (empty_enum) { + return Air.Inst.Ref.void_value; + } if (special_prong == .none) { return sema.fail(block, src, "switch must handle all possibilities", .{}); } @@ -16690,43 +16696,39 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in // Fields const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod)); - if (fields_len > 0) { - try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); - try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ + try enum_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); + try enum_obj.values.ensureTotalCapacityContext(new_decl_arena_allocator, fields_len, .{ + .ty = enum_obj.tag_ty, + .mod = mod, + }); + + var i: usize = 0; + while (i < fields_len) : (i += 1) { + const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i); + const field_struct_val = elem_val.castTag(.aggregate).?.data; + // TODO use reflection instead of magic numbers here + // name: []const u8 + const name_val = field_struct_val[0]; + // value: comptime_int + const value_val = field_struct_val[1]; + + const field_name = try name_val.toAllocatedBytes( + Type.initTag(.const_slice_u8), + new_decl_arena_allocator, + sema.mod, + ); + + const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name); + if (gop.found_existing) { + // TODO: better source location + return sema.fail(block, src, "duplicate enum tag {s}", .{field_name}); + } + + const copied_tag_val = try value_val.copy(new_decl_arena_allocator); + enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ .ty = enum_obj.tag_ty, .mod = mod, }); - - var i: usize = 0; - while (i < fields_len) : (i += 1) { - const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i); - const field_struct_val = elem_val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // name: []const u8 - const name_val = field_struct_val[0]; - // value: comptime_int - const value_val = field_struct_val[1]; - - const field_name = try name_val.toAllocatedBytes( - Type.initTag(.const_slice_u8), - new_decl_arena_allocator, - sema.mod, - ); - - const gop = enum_obj.fields.getOrPutAssumeCapacity(field_name); - if (gop.found_existing) { - // TODO: better source location - return sema.fail(block, src, "duplicate enum tag {s}", .{field_name}); - } - - const copied_tag_val = try value_val.copy(new_decl_arena_allocator); - enum_obj.values.putAssumeCapacityNoClobberContext(copied_tag_val, {}, .{ - .ty = enum_obj.tag_ty, - .mod = mod, - }); - } - } else { - return sema.fail(block, src, "enums must have at least one field", .{}); } try new_decl.finalizeNewArena(&new_decl_arena); @@ -16851,58 +16853,54 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in } // Fields - if (fields_len > 0) { - try union_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); + try union_obj.fields.ensureTotalCapacity(new_decl_arena_allocator, fields_len); - var i: usize = 0; - while (i < fields_len) : (i += 1) { - const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i); - const field_struct_val = elem_val.castTag(.aggregate).?.data; - // TODO use reflection instead of magic numbers here - // name: []const u8 - const name_val = field_struct_val[0]; - // field_type: type, - const field_type_val = field_struct_val[1]; - // alignment: comptime_int, - const alignment_val = field_struct_val[2]; + var i: usize = 0; + while (i < fields_len) : (i += 1) { + const elem_val = try fields_val.elemValue(sema.mod, sema.arena, i); + const field_struct_val = elem_val.castTag(.aggregate).?.data; + // TODO use reflection instead of magic numbers here + // name: []const u8 + const name_val = field_struct_val[0]; + // field_type: type, + const field_type_val = field_struct_val[1]; + // alignment: comptime_int, + const alignment_val = field_struct_val[2]; - const field_name = try name_val.toAllocatedBytes( - Type.initTag(.const_slice_u8), - new_decl_arena_allocator, - sema.mod, - ); + const field_name = try name_val.toAllocatedBytes( + Type.initTag(.const_slice_u8), + new_decl_arena_allocator, + sema.mod, + ); - if (enum_field_names) |set| { - set.putAssumeCapacity(field_name, {}); - } - - if (tag_ty_field_names) |*names| { - const enum_has_field = names.orderedRemove(field_name); - if (!enum_has_field) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(sema.mod) }); - errdefer msg.destroy(sema.gpa); - try sema.addDeclaredHereNote(msg, union_obj.tag_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(msg); - } - } - - const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); - if (gop.found_existing) { - // TODO: better source location - return sema.fail(block, src, "duplicate union field {s}", .{field_name}); - } - - var buffer: Value.ToTypeBuffer = undefined; - gop.value_ptr.* = .{ - .ty = try field_type_val.toType(&buffer).copy(new_decl_arena_allocator), - .abi_align = @intCast(u32, alignment_val.toUnsignedInt(target)), - }; + if (enum_field_names) |set| { + set.putAssumeCapacity(field_name, {}); } - } else { - return sema.fail(block, src, "unions must have at least one field", .{}); + + if (tag_ty_field_names) |*names| { + const enum_has_field = names.orderedRemove(field_name); + if (!enum_has_field) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(sema.mod) }); + errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, union_obj.tag_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + } + + const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); + if (gop.found_existing) { + // TODO: better source location + return sema.fail(block, src, "duplicate union field {s}", .{field_name}); + } + + var buffer: Value.ToTypeBuffer = undefined; + gop.value_ptr.* = .{ + .ty = try field_type_val.toType(&buffer).copy(new_decl_arena_allocator), + .abi_align = @intCast(u32, alignment_val.toUnsignedInt(target)), + }; } if (tag_ty_field_names) |names| { @@ -28146,10 +28144,6 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { extra_index = decls_it.extra_index; const body = zir.extra[extra_index..][0..body_len]; - if (fields_len == 0) { - assert(body.len == 0); - return; - } extra_index += body.len; const decl = mod.declPtr(decl_index); @@ -28237,6 +28231,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields; } + if (fields_len == 0) { + return; + } + const bits_per_field = 4; const fields_per_u32 = 32 / bits_per_field; const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; @@ -28772,7 +28770,9 @@ pub fn typeHasOnePossibleValue( const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; const tag_val = (try sema.typeHasOnePossibleValue(block, src, union_obj.tag_ty)) orelse return null; - const only_field = union_obj.fields.values()[0]; + const fields = union_obj.fields.values(); + if (fields.len == 0) return Value.initTag(.empty_struct_value); + const only_field = fields[0]; if (only_field.ty.eql(resolved_ty, sema.mod)) { const msg = try Module.ErrorMsg.create( sema.gpa, diff --git a/src/print_zir.zig b/src/print_zir.zig index 4bc96c4259..579a7970b7 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -1443,7 +1443,7 @@ const Writer = struct { try self.writeFlag(stream, "autoenum, ", small.auto_enum_tag); if (decls_len == 0) { - try stream.writeAll("{}, "); + try stream.writeAll("{}"); } else { const prev_parent_decl_node = self.parent_decl_node; if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); @@ -1454,16 +1454,21 @@ const Writer = struct { extra_index = try self.writeDecls(stream, decls_len, extra_index); self.indent -= 2; try stream.writeByteNTimes(' ', self.indent); - try stream.writeAll("}, "); + try stream.writeAll("}"); } - assert(fields_len != 0); - if (tag_type_ref != .none) { - try self.writeInstRef(stream, tag_type_ref); try stream.writeAll(", "); + try self.writeInstRef(stream, tag_type_ref); } + if (fields_len == 0) { + try stream.writeAll("})"); + try self.writeSrcNode(stream, src_node); + return; + } + try stream.writeAll(", "); + const body = self.code.extra[extra_index..][0..body_len]; extra_index += body.len; diff --git a/src/type.zig b/src/type.zig index 582ea230ef..55847b8add 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2488,7 +2488,7 @@ pub const Type = extern union { }, .union_safety_tagged, .union_tagged => { const union_obj = ty.cast(Payload.Union).?.data; - if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) { + if (union_obj.fields.count() > 0 and try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, sema_kit)) { return true; } if (sema_kit) |sk| { @@ -3113,6 +3113,9 @@ pub const Type = extern union { .sema_kit => unreachable, // handled above .lazy => |arena| return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(arena, ty) }, }; + if (union_obj.fields.count() == 0) { + return AbiAlignmentAdvanced{ .scalar = @boolToInt(union_obj.layout == .Extern) }; + } var max_align: u32 = 0; if (have_tag) max_align = union_obj.tag_ty.abiAlignment(target); diff --git a/test/behavior.zig b/test/behavior.zig index 40f8ca0fb3..ba8379cd72 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -167,6 +167,7 @@ test { if (builtin.zig_backend != .stage1) { _ = @import("behavior/decltest.zig"); _ = @import("behavior/packed_struct_explicit_backing_int.zig"); + _ = @import("behavior/empty_union.zig"); } if (builtin.os.tag != .wasi) { diff --git a/test/behavior/empty_union.zig b/test/behavior/empty_union.zig new file mode 100644 index 0000000000..051e464b72 --- /dev/null +++ b/test/behavior/empty_union.zig @@ -0,0 +1,54 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const expect = std.testing.expect; + +test "switch on empty enum" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const E = enum {}; + var e: E = undefined; + switch (e) {} +} + +test "switch on empty enum with a specified tag type" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const E = enum(u8) {}; + var e: E = undefined; + switch (e) {} +} + +test "switch on empty auto numbered tagged union" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const U = union(enum(u8)) {}; + var u: U = undefined; + switch (u) {} +} + +test "switch on empty tagged union" { + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const E = enum {}; + const U = union(E) {}; + var u: U = undefined; + switch (u) {} +} + +test "empty union" { + const U = union {}; + try expect(@sizeOf(U) == 0); + try expect(@alignOf(U) == 0); +} + +test "empty extern union" { + const U = extern union {}; + try expect(@sizeOf(U) == 0); + try expect(@alignOf(U) == 1); +} diff --git a/test/cases/compile_errors/enum_with_0_fields.zig b/test/cases/compile_errors/enum_with_0_fields.zig deleted file mode 100644 index f34065b69d..0000000000 --- a/test/cases/compile_errors/enum_with_0_fields.zig +++ /dev/null @@ -1,7 +0,0 @@ -const Foo = enum {}; - -// error -// backend=stage2 -// target=native -// -// :1:13: error: enum declarations must have at least one tag diff --git a/test/cases/compile_errors/reify_type_for_exhaustive_enum_with_zero_fields.zig b/test/cases/compile_errors/reify_type_for_exhaustive_enum_with_zero_fields.zig deleted file mode 100644 index 44876e938a..0000000000 --- a/test/cases/compile_errors/reify_type_for_exhaustive_enum_with_zero_fields.zig +++ /dev/null @@ -1,18 +0,0 @@ -const Tag = @Type(.{ - .Enum = .{ - .layout = .Auto, - .tag_type = u1, - .fields = &.{}, - .decls = &.{}, - .is_exhaustive = true, - }, -}); -export fn entry() void { - _ = @intToEnum(Tag, 0); -} - -// error -// backend=stage2 -// target=native -// -// :1:13: error: enums must have at least one field diff --git a/test/cases/compile_errors/reify_type_for_union_with_zero_fields.zig b/test/cases/compile_errors/reify_type_for_union_with_zero_fields.zig deleted file mode 100644 index 0b4f395c81..0000000000 --- a/test/cases/compile_errors/reify_type_for_union_with_zero_fields.zig +++ /dev/null @@ -1,17 +0,0 @@ -const Untagged = @Type(.{ - .Union = .{ - .layout = .Auto, - .tag_type = null, - .fields = &.{}, - .decls = &.{}, - }, -}); -export fn entry() void { - _ = Untagged{}; -} - -// error -// backend=stage2 -// target=native -// -// :1:18: error: unions must have at least one field diff --git a/test/cases/compile_errors/union_fields_with_value_assignments.zig b/test/cases/compile_errors/union_fields_with_value_assignments.zig deleted file mode 100644 index 2121568dd2..0000000000 --- a/test/cases/compile_errors/union_fields_with_value_assignments.zig +++ /dev/null @@ -1,7 +0,0 @@ -const Foo = union {}; - -// error -// backend=stage2 -// target=native -// -// :1:13: error: union declarations must have at least one tag diff --git a/test/cases/compile_errors/union_with_0_fields.zig b/test/cases/compile_errors/union_with_0_fields.zig deleted file mode 100644 index 2121568dd2..0000000000 --- a/test/cases/compile_errors/union_with_0_fields.zig +++ /dev/null @@ -1,7 +0,0 @@ -const Foo = union {}; - -// error -// backend=stage2 -// target=native -// -// :1:13: error: union declarations must have at least one tag diff --git a/test/cases/compile_errors/using_invalid_types_in_function_call_raises_an_error.zig b/test/cases/compile_errors/using_invalid_types_in_function_call_raises_an_error.zig deleted file mode 100644 index ee6d1b8b7c..0000000000 --- a/test/cases/compile_errors/using_invalid_types_in_function_call_raises_an_error.zig +++ /dev/null @@ -1,11 +0,0 @@ -const MenuEffect = enum {}; -fn func(effect: MenuEffect) void { _ = effect; } -export fn entry() void { - func(MenuEffect.ThisDoesNotExist); -} - -// error -// backend=stage2 -// target=native -// -// :1:20: error: enum declarations must have at least one tag diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 644cba74c1..321de1e3f6 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -704,15 +704,6 @@ pub fn addCases(ctx: *TestContext) !void { ":5:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value", }); - case.addError( - \\const E1 = enum {}; - \\export fn foo() void { - \\ _ = E1.a; - \\} - , &.{ - ":1:12: error: enum declarations must have at least one tag", - }); - case.addError( \\const E1 = enum { a, b, _ }; \\export fn foo() void {