authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-11-30 21:46:02-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-11-30 22:08:11-05:00
logb62e2fd8703129fcf0dc80675800f005e84ee724
treec70341d5db1c79901ce58d86c1aa24f5c972d57d
parent5786df933d37d52d57fef9c28acb9c2c23128d31

ability to specify tag type of enums

see #305

11 files changed, 180 insertions(+), 6 deletions(-)

doc/langref.html.in+7-1
......@@ -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
58385844KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
58395845
5840ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" many(ContainerMember) "}"</code></pre>
5846ContainerDecl = 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+8
......@@ -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
2700struct IrInstructionEnumTagType {
2701 IrInstruction base;
2702
2703 IrInstruction *target;
2704};
2705
26982706struct IrInstructionFieldParentPtr {
26992707 IrInstruction base;
27002708
src/analyze.cpp+20-1
......@@ -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+7-1
......@@ -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+3-1
......@@ -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+57
......@@ -551,6 +551,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTagName *) {
551551 return IrInstructionIdEnumTagName;
552552}
553553
554static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTagType *) {
555 return IrInstructionIdEnumTagType;
556}
557
554558static 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
2277static 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
22732288static 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
3084static 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
30693091static 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
15867static 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
1583415888static 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+9
......@@ -994,6 +994,12 @@ static void ir_print_arg_type(IrPrint *irp, IrInstructionArgType *instruction) {
994994 fprintf(irp->f, ")");
995995}
996996
997static 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
9981004static 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+6-1
......@@ -2377,7 +2377,7 @@ static AstNode *ast_parse_use(ParseContext *pc, size_t *token_index, VisibMod vi
23772377}
23782378
23792379/*
2380ContainerDecl = option("extern" | "packed") ("struct" | "enum" | "union") "{" many(ContainerMember) "}"
2380ContainerDecl = option("extern" | "packed") ("struct" | "union" | ("enum" option(GroupedExpression))) "{" many(ContainerMember) "}"
23812381ContainerMember = (ContainerField | FnDef | GlobalVarDecl)
23822382ContainerField = 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+11-1
......@@ -651,6 +651,14 @@ static bool c_is_unsigned_integer(Context *c, QualType qt) {
651651 }
652652}
653653
654static 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
654662static 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+24
......@@ -190,3 +190,27 @@ test "enum sizes" {
190190 assert(@sizeOf(ValueCount257) == 2);
191191 }
192192}
193
194const Small2 = enum (u2) {
195 One,
196 Two,
197};
198const Small = enum (u2) {
199 One,
200 Two,
201 Three,
202 Four,
203};
204
205test "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+28
......@@ -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}