Skip to content

Commit b62e2fd

Browse files
committedDec 1, 2017
ability to specify tag type of enums
see #305
·
0.15.20.2.0
1 parent 5786df9 commit b62e2fd

File tree

11 files changed

+180
-6
lines changed

11 files changed

+180
-6
lines changed
 

‎doc/langref.html.in‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
<li><a href="#builtin-divTrunc">@divTrunc</a></li>
138138
<li><a href="#builtin-embedFile">@embedFile</a></li>
139139
<li><a href="#builtin-enumTagName">@enumTagName</a></li>
140+
<li><a href="#builtin-EnumTagType">@EnumTagType</a></li>
140141
<li><a href="#builtin-errorName">@errorName</a></li>
141142
<li><a href="#builtin-fence">@fence</a></li>
142143
<li><a href="#builtin-fieldParentPtr">@fieldParentPtr</a></li>
@@ -4256,6 +4257,11 @@ test.zig:6:2: error: found compile log statement
42564257
<p>
42574258
Converts an enum tag name to a slice of bytes.
42584259
</p>
4260+
<h3 id="builtin-EnumTagType">@EnumTagType</h3>
4261+
<pre><code class="zig">@EnumTagType(T: type) -&gt; type</code></pre>
4262+
<p>
4263+
Returns the integer type that is used to store the enumeration value.
4264+
</p>
42594265
<h3 id="builtin-errorName">@errorName</h3>
42604266
<pre><code class="zig">@errorName(err: error) -&gt; []u8</code></pre>
42614267
<p>
@@ -5837,7 +5843,7 @@ GroupedExpression = "(" Expression ")"
58375843

58385844
KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
58395845

5840-
ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" many(ContainerMember) "}"</code></pre>
5846+
ContainerDecl = option("extern" | "packed") ("struct" | "union" | ("enum" option(GroupedExpression))) "{" many(ContainerMember) "}"</code></pre>
58415847
<h2 id="zen">Zen</h2>
58425848
<ul>
58435849
<li>Communicate intent precisely.</li>

‎src/all_types.hpp‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,6 +1286,7 @@ enum BuiltinFnId {
12861286
BuiltinFnIdIntToPtr,
12871287
BuiltinFnIdPtrToInt,
12881288
BuiltinFnIdEnumTagName,
1289+
BuiltinFnIdEnumTagType,
12891290
BuiltinFnIdFieldParentPtr,
12901291
BuiltinFnIdOffsetOf,
12911292
BuiltinFnIdInlineCall,
@@ -1911,6 +1912,7 @@ enum IrInstructionId {
19111912
IrInstructionIdDeclRef,
19121913
IrInstructionIdPanic,
19131914
IrInstructionIdEnumTagName,
1915+
IrInstructionIdEnumTagType,
19141916
IrInstructionIdFieldParentPtr,
19151917
IrInstructionIdOffsetOf,
19161918
IrInstructionIdTypeId,
@@ -2695,6 +2697,12 @@ struct IrInstructionEnumTagName {
26952697
IrInstruction *target;
26962698
};
26972699

2700+
struct IrInstructionEnumTagType {
2701+
IrInstruction base;
2702+
2703+
IrInstruction *target;
2704+
};
2705+
26982706
struct IrInstructionFieldParentPtr {
26992707
IrInstruction base;
27002708

‎src/analyze.cpp‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1267,7 +1267,7 @@ TypeTableEntry *create_enum_tag_type(CodeGen *g, TypeTableEntry *enum_type, Type
12671267
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdEnumTag);
12681268

12691269
buf_resize(&entry->name, 0);
1270-
buf_appendf(&entry->name, "@enumTagType(%s)", buf_ptr(&enum_type->name));
1270+
buf_appendf(&entry->name, "@EnumTagType(%s)", buf_ptr(&enum_type->name));
12711271

12721272
entry->is_copyable = true;
12731273
entry->data.enum_tag.enum_type = enum_type;
@@ -1391,6 +1391,25 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
13911391
}
13921392

13931393
TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
1394+
if (decl_node->data.container_decl.init_arg_expr != nullptr) {
1395+
TypeTableEntry *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
1396+
if (type_is_invalid(wanted_tag_int_type)) {
1397+
enum_type->data.enumeration.is_invalid = true;
1398+
} else if (wanted_tag_int_type->id != TypeTableEntryIdInt) {
1399+
enum_type->data.enumeration.is_invalid = true;
1400+
add_node_error(g, decl_node->data.container_decl.init_arg_expr,
1401+
buf_sprintf("expected integer, found '%s'", buf_ptr(&wanted_tag_int_type->name)));
1402+
} else if (wanted_tag_int_type->data.integral.bit_count < tag_int_type->data.integral.bit_count) {
1403+
enum_type->data.enumeration.is_invalid = true;
1404+
add_node_error(g, decl_node->data.container_decl.init_arg_expr,
1405+
buf_sprintf("'%s' too small to hold all bits; must be at least '%s'",
1406+
buf_ptr(&wanted_tag_int_type->name), buf_ptr(&tag_int_type->name)));
1407+
} else {
1408+
tag_int_type = wanted_tag_int_type;
1409+
}
1410+
}
1411+
1412+
13941413
TypeTableEntry *tag_type_entry = create_enum_tag_type(g, enum_type, tag_int_type);
13951414
enum_type->data.enumeration.tag_type = tag_type_entry;
13961415

‎src/ast_render.cpp‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
660660
{
661661
const char *layout_str = layout_string(node->data.container_decl.layout);
662662
const char *container_str = container_string(node->data.container_decl.kind);
663-
fprintf(ar->f, "%s%s {\n", layout_str, container_str);
663+
fprintf(ar->f, "%s%s", layout_str, container_str);
664+
if (node->data.container_decl.init_arg_expr != nullptr) {
665+
fprintf(ar->f, "(");
666+
render_node_grouped(ar, node->data.container_decl.init_arg_expr);
667+
fprintf(ar->f, ")");
668+
}
669+
fprintf(ar->f, " {\n");
664670
ar->indent += ar->indent_size;
665671
for (size_t field_i = 0; field_i < node->data.container_decl.fields.length; field_i += 1) {
666672
AstNode *field_node = node->data.container_decl.fields.at(field_i);

‎src/codegen.cpp‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3537,6 +3537,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
35373537
case IrInstructionIdOpaqueType:
35383538
case IrInstructionIdSetAlignStack:
35393539
case IrInstructionIdArgType:
3540+
case IrInstructionIdEnumTagType:
35403541
zig_unreachable();
35413542
case IrInstructionIdReturn:
35423543
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
@@ -5049,7 +5050,8 @@ static void define_builtin_fns(CodeGen *g) {
50495050
create_builtin_fn(g, BuiltinFnIdBitCast, "bitCast", 2);
50505051
create_builtin_fn(g, BuiltinFnIdIntToPtr, "intToPtr", 2);
50515052
create_builtin_fn(g, BuiltinFnIdPtrToInt, "ptrToInt", 1);
5052-
create_builtin_fn(g, BuiltinFnIdEnumTagName, "enumTagName", 1);
5053+
create_builtin_fn(g, BuiltinFnIdEnumTagName, "enumTagName", 1); // TODO rename to memberName
5054+
create_builtin_fn(g, BuiltinFnIdEnumTagType, "EnumTagType", 1);
50535055
create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3);
50545056
create_builtin_fn(g, BuiltinFnIdOffsetOf, "offsetOf", 2);
50555057
create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2);

‎src/ir.cpp‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTagName *) {
551551
return IrInstructionIdEnumTagName;
552552
}
553553

554+
static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTagType *) {
555+
return IrInstructionIdEnumTagType;
556+
}
557+
554558
static constexpr IrInstructionId ir_instruction_id(IrInstructionFieldParentPtr *) {
555559
return IrInstructionIdFieldParentPtr;
556560
}
@@ -2270,6 +2274,17 @@ static IrInstruction *ir_build_enum_tag_name(IrBuilder *irb, Scope *scope, AstNo
22702274
return &instruction->base;
22712275
}
22722276

2277+
static IrInstruction *ir_build_enum_tag_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
2278+
IrInstruction *target)
2279+
{
2280+
IrInstructionEnumTagType *instruction = ir_build_instruction<IrInstructionEnumTagType>(irb, scope, source_node);
2281+
instruction->target = target;
2282+
2283+
ir_ref_instruction(target, irb->current_basic_block);
2284+
2285+
return &instruction->base;
2286+
}
2287+
22732288
static IrInstruction *ir_build_field_parent_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
22742289
IrInstruction *type_value, IrInstruction *field_name, IrInstruction *field_ptr, TypeStructField *field)
22752290
{
@@ -3066,6 +3081,13 @@ static IrInstruction *ir_instruction_enumtagname_get_dep(IrInstructionEnumTagNam
30663081
}
30673082
}
30683083

3084+
static IrInstruction *ir_instruction_enumtagtype_get_dep(IrInstructionEnumTagType *instruction, size_t index) {
3085+
switch (index) {
3086+
case 0: return instruction->target;
3087+
default: return nullptr;
3088+
}
3089+
}
3090+
30693091
static IrInstruction *ir_instruction_fieldparentptr_get_dep(IrInstructionFieldParentPtr *instruction, size_t index) {
30703092
switch (index) {
30713093
case 0: return instruction->type_value;
@@ -3326,6 +3348,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
33263348
return ir_instruction_panic_get_dep((IrInstructionPanic *) instruction, index);
33273349
case IrInstructionIdEnumTagName:
33283350
return ir_instruction_enumtagname_get_dep((IrInstructionEnumTagName *) instruction, index);
3351+
case IrInstructionIdEnumTagType:
3352+
return ir_instruction_enumtagtype_get_dep((IrInstructionEnumTagType *) instruction, index);
33293353
case IrInstructionIdFieldParentPtr:
33303354
return ir_instruction_fieldparentptr_get_dep((IrInstructionFieldParentPtr *) instruction, index);
33313355
case IrInstructionIdOffsetOf:
@@ -4681,6 +4705,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
46814705
IrInstruction *actual_tag = ir_build_enum_tag(irb, scope, node, arg0_value);
46824706
return ir_build_enum_tag_name(irb, scope, node, actual_tag);
46834707
}
4708+
case BuiltinFnIdEnumTagType:
4709+
{
4710+
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4711+
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4712+
if (arg0_value == irb->codegen->invalid_instruction)
4713+
return arg0_value;
4714+
4715+
return ir_build_enum_tag_type(irb, scope, node, arg0_value);
4716+
}
46844717
case BuiltinFnIdFieldParentPtr:
46854718
{
46864719
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
@@ -15831,6 +15864,27 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc
1583115864
return ira->codegen->builtin_types.entry_type;
1583215865
}
1583315866

15867+
static TypeTableEntry *ir_analyze_instruction_enum_tag_type(IrAnalyze *ira, IrInstructionEnumTagType *instruction) {
15868+
IrInstruction *target_inst = instruction->target->other;
15869+
TypeTableEntry *enum_type = ir_resolve_type(ira, target_inst);
15870+
if (type_is_invalid(enum_type))
15871+
return ira->codegen->builtin_types.entry_invalid;
15872+
if (enum_type->id != TypeTableEntryIdEnum) {
15873+
ir_add_error(ira, target_inst, buf_sprintf("expected enum, found '%s'", buf_ptr(&enum_type->name)));
15874+
return ira->codegen->builtin_types.entry_invalid;
15875+
}
15876+
ensure_complete_type(ira->codegen, enum_type);
15877+
if (type_is_invalid(enum_type))
15878+
return ira->codegen->builtin_types.entry_invalid;
15879+
15880+
TypeTableEntry *non_int_tag_type = enum_type->data.enumeration.tag_type;
15881+
assert(non_int_tag_type->id == TypeTableEntryIdEnumTag);
15882+
15883+
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
15884+
out_val->data.x_type = non_int_tag_type->data.enum_tag.int_type;
15885+
return ira->codegen->builtin_types.entry_type;
15886+
}
15887+
1583415888
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
1583515889
switch (instruction->id) {
1583615890
case IrInstructionIdInvalid:
@@ -16029,6 +16083,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
1602916083
return ir_analyze_instruction_set_align_stack(ira, (IrInstructionSetAlignStack *)instruction);
1603016084
case IrInstructionIdArgType:
1603116085
return ir_analyze_instruction_arg_type(ira, (IrInstructionArgType *)instruction);
16086+
case IrInstructionIdEnumTagType:
16087+
return ir_analyze_instruction_enum_tag_type(ira, (IrInstructionEnumTagType *)instruction);
1603216088
}
1603316089
zig_unreachable();
1603416090
}
@@ -16214,6 +16270,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
1621416270
case IrInstructionIdAlignCast:
1621516271
case IrInstructionIdOpaqueType:
1621616272
case IrInstructionIdArgType:
16273+
case IrInstructionIdEnumTagType:
1621716274
return false;
1621816275
case IrInstructionIdAsm:
1621916276
{

‎src/ir_print.cpp‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,12 @@ static void ir_print_arg_type(IrPrint *irp, IrInstructionArgType *instruction) {
994994
fprintf(irp->f, ")");
995995
}
996996

997+
static void ir_print_enum_tag_type(IrPrint *irp, IrInstructionEnumTagType *instruction) {
998+
fprintf(irp->f, "@EnumTagType(");
999+
ir_print_other_instruction(irp, instruction->target);
1000+
fprintf(irp->f, ")");
1001+
}
1002+
9971003

9981004
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
9991005
ir_print_prefix(irp, instruction);
@@ -1312,6 +1318,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
13121318
case IrInstructionIdArgType:
13131319
ir_print_arg_type(irp, (IrInstructionArgType *)instruction);
13141320
break;
1321+
case IrInstructionIdEnumTagType:
1322+
ir_print_enum_tag_type(irp, (IrInstructionEnumTagType *)instruction);
1323+
break;
13151324
}
13161325
fprintf(irp->f, "\n");
13171326
}

‎src/parser.cpp‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2377,7 +2377,7 @@ static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, VisibMod vi
23772377
}
23782378

23792379
/*
2380-
ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" many(ContainerMember) "}"
2380+
ContainerDecl = option("extern" | "packed") ("struct" | "union" | ("enum" option(GroupedExpression))) "{" many(ContainerMember) "}"
23812381
ContainerMember = (ContainerField | FnDef | GlobalVarDecl)
23822382
ContainerField = Symbol option(":" Expression) ","
23832383
*/
@@ -2415,6 +2415,10 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,
24152415
node->data.container_decl.layout = layout;
24162416
node->data.container_decl.kind = kind;
24172417

2418+
if (kind == ContainerKindEnum || kind == ContainerKindStruct) {
2419+
node->data.container_decl.init_arg_expr = ast_parse_grouped_expr(pc, token_index, false);
2420+
}
2421+
24182422
ast_eat_token(pc, token_index, TokenIdLBrace);
24192423

24202424
for (;;) {
@@ -2804,6 +2808,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
28042808
case NodeTypeContainerDecl:
28052809
visit_node_list(&node->data.container_decl.fields, visit, context);
28062810
visit_node_list(&node->data.container_decl.decls, visit, context);
2811+
visit_field(&node->data.container_decl.init_arg_expr, visit, context);
28072812
break;
28082813
case NodeTypeStructField:
28092814
visit_field(&node->data.struct_field.type, visit, context);

‎src/translate_c.cpp‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,14 @@ static bool c_is_unsigned_integer(Context *c, QualType qt) {
651651
}
652652
}
653653

654+
static bool c_is_builtin_type(Context *c, QualType qt, BuiltinType::Kind kind) {
655+
const Type *c_type = qual_type_canon(qt);
656+
if (c_type->getTypeClass() != Type::Builtin)
657+
return false;
658+
const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(c_type);
659+
return builtin_ty->getKind() == kind;
660+
}
661+
654662
static bool c_is_float(Context *c, QualType qt) {
655663
const Type *c_type = qt.getTypePtr();
656664
if (c_type->getTypeClass() != Type::Builtin)
@@ -3426,7 +3434,9 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
34263434
AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl);
34273435
enum_node->data.container_decl.kind = ContainerKindEnum;
34283436
enum_node->data.container_decl.layout = ContainerLayoutExtern;
3429-
enum_node->data.container_decl.init_arg_expr = tag_int_type;
3437+
if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt)) {
3438+
enum_node->data.container_decl.init_arg_expr = tag_int_type;
3439+
}
34303440

34313441
enum_node->data.container_decl.fields.resize(field_count);
34323442
uint32_t i = 0;

‎test/cases/enum.zig‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,27 @@ test "enum sizes" {
190190
assert(@sizeOf(ValueCount257) == 2);
191191
}
192192
}
193+
194+
const Small2 = enum (u2) {
195+
One,
196+
Two,
197+
};
198+
const Small = enum (u2) {
199+
One,
200+
Two,
201+
Three,
202+
Four,
203+
};
204+
205+
test "set enum tag type" {
206+
{
207+
var x = Small.One;
208+
x = Small.Two;
209+
comptime assert(@EnumTagType(Small) == u2);
210+
}
211+
{
212+
var x = Small2.One;
213+
x = Small2.Two;
214+
comptime assert(@EnumTagType(Small2) == u2);
215+
}
216+
}

‎test/compile_errors.zig‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2362,4 +2362,32 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
23622362
".tmp_source.zig:4:25: error: aoeu",
23632363
".tmp_source.zig:1:36: note: called from here",
23642364
".tmp_source.zig:12:20: note: referenced here");
2365+
2366+
cases.add("specify enum tag type that is too small",
2367+
\\const Small = enum (u2) {
2368+
\\ One,
2369+
\\ Two,
2370+
\\ Three,
2371+
\\ Four,
2372+
\\ Five,
2373+
\\};
2374+
\\
2375+
\\export fn entry() {
2376+
\\ var x = Small.One;
2377+
\\}
2378+
,
2379+
".tmp_source.zig:1:20: error: 'u2' too small to hold all bits; must be at least 'u3'");
2380+
2381+
cases.add("specify non-integer enum tag type",
2382+
\\const Small = enum (f32) {
2383+
\\ One,
2384+
\\ Two,
2385+
\\ Three,
2386+
\\};
2387+
\\
2388+
\\export fn entry() {
2389+
\\ var x = Small.One;
2390+
\\}
2391+
,
2392+
".tmp_source.zig:1:20: error: expected integer, found 'f32'");
23652393
}

0 commit comments

Comments
 (0)
Please sign in to comment.