Skip to content

Improve extern enum #3909

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src-self-hosted/translate_c.zig
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void {
.PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}),
.Auto => unreachable, // Not legal on functions
.Register => unreachable, // Not legal on functions
else => unreachable,
},
};
const proto_node = switch (ZigClangType_getTypeClass(fn_type)) {
Expand Down Expand Up @@ -702,6 +703,7 @@ fn transBinaryOperator(
.XorAssign,
.OrAssign,
=> unreachable,
else => unreachable,
}
}

Expand Down Expand Up @@ -995,6 +997,7 @@ fn transStringLiteral(
"TODO: support string literal kind {}",
.{kind},
),
else => unreachable,
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/analyze.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2682,7 +2682,7 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) {

// Make sure the value is unique
auto entry = occupied_tag_values.put_unique(type_enum_field->value, field_node);
if (entry != nullptr) {
if (entry != nullptr && enum_type->data.enumeration.layout != ContainerLayoutExtern) {
enum_type->data.enumeration.resolve_status = ResolveStatusInvalid;

Buf *val_buf = buf_alloc();
Expand Down
2 changes: 1 addition & 1 deletion src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3271,7 +3271,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable,
LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
instruction->target->value->type, tag_int_type, target_val);

if (ir_want_runtime_safety(g, &instruction->base)) {
if (ir_want_runtime_safety(g, &instruction->base) && wanted_type->data.enumeration.layout != ContainerLayoutExtern) {
LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue");
LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue");
size_t field_count = wanted_type->data.enumeration.src_field_count;
Expand Down
6 changes: 5 additions & 1 deletion src/ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12728,7 +12728,7 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour
return ira->codegen->invalid_instruction;

TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint);
if (field == nullptr) {
if (field == nullptr && wanted_type->data.enumeration.layout != ContainerLayoutExtern) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &val->data.x_bigint, 10);
ErrorMsg *msg = ir_add_error(ira, source_instr,
Expand Down Expand Up @@ -25791,6 +25791,10 @@ static IrInstruction *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
}
}
if (!instruction->have_else_prong) {
if (switch_type->data.enumeration.layout == ContainerLayoutExtern) {
ir_add_error(ira, &instruction->base,
buf_sprintf("switch on an extern enum must have an else prong"));
}
for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) {
TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i];

Expand Down
19 changes: 19 additions & 0 deletions test/compile_errors.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@ const tests = @import("tests.zig");
const builtin = @import("builtin");

pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.add("switch on extern enum missing else prong",
\\const i = extern enum {
\\ n = 0,
\\ o = 2,
\\ p = 4,
\\ q = 4,
\\};
\\pub fn main() void {
\\ var x = @intToEnum(i, 52);
\\ switch (x) {
\\ .n,
\\ .o,
\\ .p => unreachable,
\\ }
\\}
, &[_][]const u8{
"tmp.zig:9:5: error: switch on an extern enum must have an else prong",
});

cases.add("invalid float literal",
\\const std = @import("std");
\\
Expand Down
1 change: 1 addition & 0 deletions test/stage1/behavior/cast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,7 @@ test "peer resolution of string literals" {
.b => "two",
.c => "three",
.d => "four",
else => unreachable,
};
expect(mem.eql(u8, cmd, "two"));
}
Expand Down
27 changes: 27 additions & 0 deletions test/stage1/behavior/enum.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
const expect = @import("std").testing.expect;
const mem = @import("std").mem;

test "extern enum" {
const S = struct {
const i = extern enum {
n = 0,
o = 2,
p = 4,
q = 4,
};
fn doTheTest(y: c_int) void {
var x = i.o;
expect(@enumToInt(x) == 2);
x = @intToEnum(i, 12);
expect(@enumToInt(x) == 12);
x = @intToEnum(i, y);
expect(@enumToInt(x) == 52);
switch (x) {
.n,
.o,
.p => unreachable,
else => {},
}
}
};
S.doTheTest(52);
comptime S.doTheTest(52);
}

test "enum type" {
const foo1 = Foo{ .One = 13 };
const foo2 = Foo{
Expand Down