Commit d0551db5cd

Andrew Kelley <andrew@ziglang.org>
2019-03-24 05:44:18
introduce the enum literal type
see #683
1 parent 64dddd7
src/all_types.hpp
@@ -317,6 +317,7 @@ struct ConstExprValue {
         ConstArrayValue x_array;
         ConstPtrValue x_ptr;
         ConstArgTuple x_arg_tuple;
+        Buf *x_enum_literal;
 
         // populated if special == ConstValSpecialRuntime
         RuntimeHintErrorUnion rh_error_union;
@@ -468,6 +469,7 @@ enum NodeType {
     NodeTypeAwaitExpr,
     NodeTypeSuspend,
     NodeTypePromiseType,
+    NodeTypeEnumLiteral,
 };
 
 enum CallingConvention {
@@ -929,6 +931,11 @@ struct AstNodePromiseType {
     AstNode *payload_type; // can be NULL
 };
 
+struct AstNodeEnumLiteral {
+    Token *period;
+    Token *identifier;
+};
+
 struct AstNode {
     enum NodeType type;
     size_t line;
@@ -989,6 +996,7 @@ struct AstNode {
         AstNodeAwaitExpr await_expr;
         AstNodeSuspend suspend;
         AstNodePromiseType promise_type;
+        AstNodeEnumLiteral enum_literal;
     } data;
 };
 
@@ -1252,6 +1260,7 @@ enum ZigTypeId {
     ZigTypeIdOpaque,
     ZigTypeIdPromise,
     ZigTypeIdVector,
+    ZigTypeIdEnumLiteral,
 };
 
 enum OnePossibleValue {
@@ -1741,6 +1750,7 @@ struct CodeGen {
         ZigType *entry_global_error_set;
         ZigType *entry_arg_tuple;
         ZigType *entry_promise;
+        ZigType *entry_enum_literal;
     } builtin_types;
     ZigType *align_amt_type;
     ZigType *stack_trace_type;
src/analyze.cpp
@@ -242,6 +242,7 @@ AstNode *type_decl_node(ZigType *type_entry) {
         case ZigTypeIdArray:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdOptional:
@@ -303,6 +304,7 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) {
         case ZigTypeIdArray:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdOptional:
@@ -1463,6 +1465,7 @@ static Error emit_error_unless_type_allowed_in_packed_struct(CodeGen *g, ZigType
         case ZigTypeIdUnreachable:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdErrorUnion:
@@ -1550,6 +1553,7 @@ bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) {
         case ZigTypeIdMetaType:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdErrorUnion:
@@ -1712,6 +1716,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
                 return g->builtin_types.entry_invalid;
             case ZigTypeIdComptimeFloat:
             case ZigTypeIdComptimeInt:
+            case ZigTypeIdEnumLiteral:
             case ZigTypeIdBoundFn:
             case ZigTypeIdMetaType:
             case ZigTypeIdVoid:
@@ -1806,6 +1811,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc
 
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdBoundFn:
         case ZigTypeIdMetaType:
         case ZigTypeIdUnreachable:
@@ -3621,6 +3627,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
         case NodeTypeAwaitExpr:
         case NodeTypeSuspend:
         case NodeTypePromiseType:
+        case NodeTypeEnumLiteral:
             zig_unreachable();
     }
 }
@@ -3658,6 +3665,7 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry
             return g->builtin_types.entry_invalid;
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdMetaType:
         case ZigTypeIdVoid:
         case ZigTypeIdBool:
@@ -3807,7 +3815,8 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
             implicit_type = g->builtin_types.entry_invalid;
         } else if ((!is_const || linkage == VarLinkageExternal) &&
                 (implicit_type->id == ZigTypeIdComptimeFloat ||
-                implicit_type->id == ZigTypeIdComptimeInt))
+                implicit_type->id == ZigTypeIdComptimeInt ||
+                implicit_type->id == ZigTypeIdEnumLiteral))
         {
             add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
             implicit_type = g->builtin_types.entry_invalid;
@@ -4051,6 +4060,7 @@ static bool is_container(ZigType *type_entry) {
         case ZigTypeIdArray:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdOptional:
@@ -4109,6 +4119,7 @@ void resolve_container_type(CodeGen *g, ZigType *type_entry) {
         case ZigTypeIdArray:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdOptional:
@@ -4647,6 +4658,7 @@ bool handle_is_ptr(ZigType *type_entry) {
         case ZigTypeIdMetaType:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdBoundFn:
@@ -4827,6 +4839,8 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
                 }
                 return result;
             }
+        case ZigTypeIdEnumLiteral:
+            return buf_hash(const_val->data.x_enum_literal) * 2691276464;
         case ZigTypeIdEnum:
             {
                 uint32_t result = 31643936;
@@ -4974,6 +4988,7 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) {
         case ZigTypeIdFloat:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdBoundFn:
@@ -5043,6 +5058,7 @@ static bool return_type_is_cacheable(ZigType *return_type) {
         case ZigTypeIdFloat:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdBoundFn:
@@ -5173,6 +5189,7 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) {
         case ZigTypeIdOpaque:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdMetaType:
         case ZigTypeIdBoundFn:
         case ZigTypeIdArgTuple:
@@ -5236,6 +5253,7 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) {
             zig_unreachable();
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdMetaType:
@@ -5794,6 +5812,8 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) {
         case ZigTypeIdInt:
         case ZigTypeIdComptimeInt:
             return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ;
+        case ZigTypeIdEnumLiteral:
+            return buf_eql_buf(a->data.x_enum_literal, b->data.x_enum_literal);
         case ZigTypeIdPointer:
         case ZigTypeIdFn:
             return const_values_equal_ptr(a, b);
@@ -6044,6 +6064,9 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
         case ZigTypeIdInt:
             bigint_append_buf(buf, &const_val->data.x_bigint, 10);
             return;
+        case ZigTypeIdEnumLiteral:
+            buf_append_buf(buf, const_val->data.x_enum_literal);
+            return;
         case ZigTypeIdMetaType:
             buf_appendf(buf, "%s", buf_ptr(&const_val->data.x_type->name));
             return;
@@ -6213,6 +6236,7 @@ uint32_t type_id_hash(TypeId x) {
         case ZigTypeIdStruct:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdOptional:
@@ -6260,6 +6284,7 @@ bool type_id_eql(TypeId a, TypeId b) {
         case ZigTypeIdStruct:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdOptional:
@@ -6439,6 +6464,7 @@ static const ZigTypeId all_type_ids[] = {
     ZigTypeIdOpaque,
     ZigTypeIdPromise,
     ZigTypeIdVector,
+    ZigTypeIdEnumLiteral,
 };
 
 ZigTypeId type_id_at_index(size_t index) {
@@ -6504,6 +6530,8 @@ size_t type_id_index(ZigType *entry) {
             return 22;
         case ZigTypeIdVector:
             return 23;
+        case ZigTypeIdEnumLiteral:
+            return 24;
     }
     zig_unreachable();
 }
@@ -6534,6 +6562,8 @@ const char *type_id_name(ZigTypeId id) {
             return "ComptimeFloat";
         case ZigTypeIdComptimeInt:
             return "ComptimeInt";
+        case ZigTypeIdEnumLiteral:
+            return "EnumLiteral";
         case ZigTypeIdUndefined:
             return "Undefined";
         case ZigTypeIdNull:
src/ast_render.cpp
@@ -259,6 +259,8 @@ static const char *node_type_str(NodeType node_type) {
             return "PromiseType";
         case NodeTypePointerType:
             return "PointerType";
+        case NodeTypeEnumLiteral:
+            return "EnumLiteral";
     }
     zig_unreachable();
 }
@@ -1154,6 +1156,11 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
                 }
                 break;
             }
+        case NodeTypeEnumLiteral:
+            {
+                fprintf(ar->f, ".%s", buf_ptr(&node->data.enum_literal.identifier->data.str_lit.str));
+                break;
+            }
         case NodeTypeParamDecl:
         case NodeTypeTestDecl:
         case NodeTypeStructField:
src/codegen.cpp
@@ -5818,6 +5818,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
         case ZigTypeIdUnreachable:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdErrorUnion:
@@ -6419,6 +6420,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
         case ZigTypeIdUnreachable:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdBoundFn:
@@ -7005,6 +7007,12 @@ static void define_builtin_types(CodeGen *g) {
         g->builtin_types.entry_num_lit_int = entry;
         g->primitive_type_table.put(&entry->name, entry);
     }
+    {
+        ZigType *entry = new_type_table_entry(ZigTypeIdEnumLiteral);
+        buf_init_from_str(&entry->name, "(enum literal)");
+        entry->zero_bits = true;
+        g->builtin_types.entry_enum_literal = entry;
+    }
     {
         ZigType *entry = new_type_table_entry(ZigTypeIdUndefined);
         buf_init_from_str(&entry->name, "(undefined)");
@@ -7175,7 +7183,6 @@ static void define_builtin_types(CodeGen *g) {
         ZigType *entry = get_promise_type(g, nullptr);
         g->primitive_type_table.put(&entry->name, entry);
     }
-
 }
 
 
@@ -7527,6 +7534,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) {
             "    Opaque: void,\n"
             "    Promise: Promise,\n"
             "    Vector: Vector,\n"
+            "    EnumLiteral: void,\n"
             "\n\n"
             "    pub const Int = struct {\n"
             "        is_signed: bool,\n"
@@ -8626,6 +8634,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e
         case ZigTypeIdMetaType:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdBoundFn:
@@ -8812,6 +8821,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu
         case ZigTypeIdBoundFn:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdArgTuple:
@@ -8965,6 +8975,7 @@ static void gen_h_file(CodeGen *g) {
             case ZigTypeIdPointer:
             case ZigTypeIdComptimeFloat:
             case ZigTypeIdComptimeInt:
+            case ZigTypeIdEnumLiteral:
             case ZigTypeIdArray:
             case ZigTypeIdUndefined:
             case ZigTypeIdNull:
src/ir.cpp
@@ -257,6 +257,7 @@ static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) {
         case ZigTypeIdBool:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdPointer:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
@@ -1144,6 +1145,14 @@ static IrInstruction *ir_build_const_bool(IrBuilder *irb, Scope *scope, AstNode
     return &const_instruction->base;
 }
 
+static IrInstruction *ir_build_const_enum_literal(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *name) {
+    IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
+    const_instruction->base.value.type = irb->codegen->builtin_types.entry_enum_literal;
+    const_instruction->base.value.special = ConstValSpecialStatic;
+    const_instruction->base.value.data.x_enum_literal = name;
+    return &const_instruction->base;
+}
+
 static IrInstruction *ir_build_const_bound_fn(IrBuilder *irb, Scope *scope, AstNode *source_node,
     ZigFn *fn_entry, IrInstruction *first_arg)
 {
@@ -5794,6 +5803,12 @@ static IrInstruction *ir_gen_bool_literal(IrBuilder *irb, Scope *scope, AstNode
     return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value);
 }
 
+static IrInstruction *ir_gen_enum_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeEnumLiteral);
+    Buf *name = &node->data.enum_literal.identifier->data.str_lit.str;
+    return ir_build_const_enum_literal(irb, scope, node, name);
+}
+
 static IrInstruction *ir_gen_string_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
     assert(node->type == NodeTypeStringLiteral);
 
@@ -7564,6 +7579,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
             return ir_lval_wrap(irb, scope, ir_gen_await_expr(irb, scope, node), lval);
         case NodeTypeSuspend:
             return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval);
+        case NodeTypeEnumLiteral:
+            return ir_lval_wrap(irb, scope, ir_gen_enum_literal(irb, scope, node), lval);
     }
     zig_unreachable();
 }
@@ -12264,6 +12281,7 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *
         case ZigTypeIdArgTuple:
         case ZigTypeIdPromise:
         case ZigTypeIdEnum:
+        case ZigTypeIdEnumLiteral:
             operator_allowed = is_equality_cmp;
             break;
 
@@ -13596,6 +13614,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
                 case ZigTypeIdUnreachable:
                 case ZigTypeIdComptimeFloat:
                 case ZigTypeIdComptimeInt:
+                case ZigTypeIdEnumLiteral:
                 case ZigTypeIdUndefined:
                 case ZigTypeIdNull:
                 case ZigTypeIdOptional:
@@ -13629,6 +13648,7 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio
         case ZigTypeIdArgTuple:
         case ZigTypeIdOpaque:
         case ZigTypeIdPromise:
+        case ZigTypeIdEnumLiteral:
             ir_add_error(ira, target,
                     buf_sprintf("invalid export target type '%s'", buf_ptr(&target->value.type->name)));
             break;
@@ -14805,6 +14825,7 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_
         case ZigTypeIdStruct:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdOptional:
@@ -16448,6 +16469,7 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
         case ZigTypeIdStruct:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdOptional:
         case ZigTypeIdErrorUnion:
         case ZigTypeIdErrorSet:
@@ -16560,6 +16582,7 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira,
         case ZigTypeIdStruct:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdOptional:
         case ZigTypeIdErrorUnion:
         case ZigTypeIdErrorSet:
@@ -16613,6 +16636,7 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira,
         case ZigTypeIdNull:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdBoundFn:
         case ZigTypeIdMetaType:
         case ZigTypeIdArgTuple:
@@ -17019,6 +17043,7 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira,
         case ZigTypeIdFloat:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdPointer:
         case ZigTypeIdPromise:
         case ZigTypeIdFn:
@@ -18237,6 +18262,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr
         case ZigTypeIdUnreachable:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdArgTuple:
@@ -20443,6 +20469,7 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct
         case ZigTypeIdUnreachable:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdBoundFn:
@@ -21293,6 +21320,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
         case ZigTypeIdUnreachable:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdPromise:
@@ -21452,6 +21480,7 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou
         case ZigTypeIdUnreachable:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdPromise:
@@ -21609,6 +21638,7 @@ static bool type_can_bit_cast(ZigType *t) {
         case ZigTypeIdUnreachable:
         case ZigTypeIdComptimeFloat:
         case ZigTypeIdComptimeInt:
+        case ZigTypeIdEnumLiteral:
         case ZigTypeIdUndefined:
         case ZigTypeIdNull:
         case ZigTypeIdPointer:
src/parser.cpp
@@ -81,6 +81,7 @@ static AstNode *ast_parse_for_type_expr(ParseContext *pc);
 static AstNode *ast_parse_while_type_expr(ParseContext *pc);
 static AstNode *ast_parse_switch_expr(ParseContext *pc);
 static AstNode *ast_parse_asm_expr(ParseContext *pc);
+static AstNode *ast_parse_enum_lit(ParseContext *pc);
 static AstNode *ast_parse_asm_output(ParseContext *pc);
 static AsmOutput *ast_parse_asm_output_item(ParseContext *pc);
 static AstNode *ast_parse_asm_input(ParseContext *pc);
@@ -1161,6 +1162,10 @@ static AstNode *ast_parse_prefix_expr(ParseContext *pc) {
 //      / Block
 //      / CurlySuffixExpr
 static AstNode *ast_parse_primary_expr(ParseContext *pc) {
+    AstNode *enum_lit = ast_parse_enum_lit(pc);
+    if (enum_lit != nullptr)
+        return enum_lit;
+
     AstNode *asm_expr = ast_parse_asm_expr(pc);
     if (asm_expr != nullptr)
         return asm_expr;
@@ -1831,6 +1836,18 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc) {
     return res;
 }
 
+static AstNode *ast_parse_enum_lit(ParseContext *pc) {
+    Token *period = eat_token_if(pc, TokenIdDot);
+    if (period == nullptr)
+        return nullptr;
+
+    Token *identifier = expect_token(pc, TokenIdSymbol);
+    AstNode *res = ast_create_node(pc, NodeTypeEnumLiteral, period);
+    res->data.enum_literal.period = period;
+    res->data.enum_literal.identifier = identifier;
+    return res;
+}
+
 // AsmOutput <- COLON AsmOutputList AsmInput?
 static AstNode *ast_parse_asm_output(ParseContext *pc) {
     if (eat_token_if(pc, TokenIdColon) == nullptr)
@@ -3000,5 +3017,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
         case NodeTypeSuspend:
             visit_field(&node->data.suspend.block, visit, context);
             break;
+        case NodeTypeEnumLiteral:
+            break;
     }
 }
src-self-hosted/ir.zig
@@ -1186,6 +1186,7 @@ pub const Builder = struct {
             ast.Node.Id.AsyncAttribute => return error.Unimplemented,
             ast.Node.Id.ParamDecl => return error.Unimplemented,
             ast.Node.Id.FieldInitializer => return error.Unimplemented,
+            ast.Node.Id.EnumLiteral => return error.Unimplemented,
         }
     }
 
src-self-hosted/type.zig
@@ -32,6 +32,7 @@ pub const Type = struct {
             Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp),
             Id.ComptimeFloat => @fieldParentPtr(ComptimeFloat, "base", base).destroy(comp),
             Id.ComptimeInt => @fieldParentPtr(ComptimeInt, "base", base).destroy(comp),
+            Id.EnumLiteral => @fieldParentPtr(EnumLiteral, "base", base).destroy(comp),
             Id.Undefined => @fieldParentPtr(Undefined, "base", base).destroy(comp),
             Id.Null => @fieldParentPtr(Null, "base", base).destroy(comp),
             Id.Optional => @fieldParentPtr(Optional, "base", base).destroy(comp),
@@ -65,6 +66,7 @@ pub const Type = struct {
             Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context),
             Id.ComptimeFloat => unreachable,
             Id.ComptimeInt => unreachable,
+            Id.EnumLiteral => unreachable,
             Id.Undefined => unreachable,
             Id.Null => unreachable,
             Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context),
@@ -85,6 +87,7 @@ pub const Type = struct {
             Id.Type,
             Id.ComptimeFloat,
             Id.ComptimeInt,
+            Id.EnumLiteral,
             Id.Undefined,
             Id.Null,
             Id.BoundFn,
@@ -118,6 +121,7 @@ pub const Type = struct {
             Id.Type,
             Id.ComptimeFloat,
             Id.ComptimeInt,
+            Id.EnumLiteral,
             Id.Undefined,
             Id.Null,
             Id.BoundFn,
@@ -940,6 +944,20 @@ pub const Type = struct {
         }
     };
 
+    pub const EnumLiteral = struct {
+        base: Type,
+
+        /// Adds 1 reference to the resulting type
+        pub fn get(comp: *Compilation) *EnumLiteral {
+            comp.comptime_int_type.base.base.ref();
+            return comp.comptime_int_type;
+        }
+
+        pub fn destroy(self: *EnumLiteral, comp: *Compilation) void {
+            comp.gpa().destroy(self);
+        }
+    };
+
     pub const Undefined = struct {
         base: Type,
 
std/zig/ast.zig
@@ -296,6 +296,7 @@ pub const Node = struct {
         // Primary expressions
         IntegerLiteral,
         FloatLiteral,
+        EnumLiteral,
         StringLiteral,
         MultilineStringLiteral,
         CharLiteral,
@@ -1849,6 +1850,24 @@ pub const Node = struct {
         }
     };
 
+    pub const EnumLiteral = struct {
+        base: Node,
+        dot: TokenIndex,
+        name: TokenIndex,
+
+        pub fn iterate(self: *EnumLiteral, index: usize) ?*Node {
+            return null;
+        }
+
+        pub fn firstToken(self: *const EnumLiteral) TokenIndex {
+            return self.dot;
+        }
+
+        pub fn lastToken(self: *const EnumLiteral) TokenIndex {
+            return self.name;
+        }
+    };
+
     pub const FloatLiteral = struct {
         base: Node,
         token: TokenIndex,
std/zig/parse.zig
@@ -2543,6 +2543,27 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
                         _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.IntegerLiteral, token.index);
                         continue;
                     },
+                    Token.Id.Period => {
+                        const name_token = nextToken(&tok_it, &tree);
+                        if (name_token.ptr.id != Token.Id.Identifier) {
+                            ((try tree.errors.addOne())).* = Error{
+                                .ExpectedToken = Error.ExpectedToken{
+                                    .token = name_token.index,
+                                    .expected_id = Token.Id.Identifier,
+                                },
+                            };
+                            return tree;
+                        }
+
+                        const node = try arena.create(ast.Node.EnumLiteral);
+                        node.* = ast.Node.EnumLiteral{
+                            .base = ast.Node{ .id = ast.Node.Id.EnumLiteral },
+                            .dot = token.index,
+                            .name = name_token.index,
+                        };
+                        opt_ctx.store(&node.base);
+                        continue;
+                    },
                     Token.Id.FloatLiteral => {
                         _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token.index);
                         continue;
std/zig/parser_test.zig
@@ -1,3 +1,10 @@
+test "zig fmt: enum literal" {
+    try testCanonical(
+        \\const x = .hi;
+        \\
+    );
+}
+
 test "zig fmt: character literal larger than u8" {
     try testCanonical(
         \\const x = '\U01f4a9';
std/zig/render.zig
@@ -1667,6 +1667,13 @@ fn renderExpression(
             return renderToken(tree, stream, asm_output.lastToken(), indent, start_col, space); // )
         },
 
+        ast.Node.Id.EnumLiteral => {
+            const enum_literal = @fieldParentPtr(ast.Node.EnumLiteral, "base", base);
+
+            try renderToken(tree, stream, enum_literal.dot, indent, start_col, Space.None); // .
+            return renderToken(tree, stream, enum_literal.name, indent, start_col, space); // name
+        },
+
         ast.Node.Id.StructField,
         ast.Node.Id.UnionTag,
         ast.Node.Id.EnumTag,
std/hash_map.zig
@@ -490,6 +490,7 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type
         builtin.TypeId.ComptimeFloat,
         builtin.TypeId.ComptimeInt,
         builtin.TypeId.Type,
+        builtin.TypeId.EnumLiteral,
         => return 0,
 
         builtin.TypeId.Pointer => |info| switch (info.size) {
@@ -531,6 +532,7 @@ pub fn autoEql(a: var, b: @typeOf(a)) bool {
         builtin.TypeId.Float,
         builtin.TypeId.ComptimeFloat,
         builtin.TypeId.ComptimeInt,
+        builtin.TypeId.EnumLiteral,
         builtin.TypeId.Promise,
         builtin.TypeId.Enum,
         builtin.TypeId.BoundFn,
std/testing.zig
@@ -42,6 +42,7 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
         TypeId.Float,
         TypeId.ComptimeFloat,
         TypeId.ComptimeInt,
+        TypeId.EnumLiteral,
         TypeId.Enum,
         TypeId.Fn,
         TypeId.Promise,
test/stage1/behavior/enum.zig
@@ -892,3 +892,12 @@ test "tag name with assigned enum values" {
     var b = LocalFoo.B;
     expect(mem.eql(u8, @tagName(b), "B"));
 }
+
+test "enum literal equality" {
+    const x = .hi;
+    const y = .ok;
+    const z = .hi;
+
+    expect(x != y);
+    expect(x == z);
+}
test/stage1/behavior/type_info.zig
@@ -190,7 +190,7 @@ fn testUnion() void {
     expect(TypeId(typeinfo_info) == TypeId.Union);
     expect(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto);
     expect(typeinfo_info.Union.tag_type.? == TypeId);
-    expect(typeinfo_info.Union.fields.len == 24);
+    expect(typeinfo_info.Union.fields.len == 25);
     expect(typeinfo_info.Union.fields[4].enum_field != null);
     expect(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
     expect(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int));