Commit 4e37fb2fa2

Andrew Kelley <superjoe30@gmail.com>
2016-04-20 02:15:36
implement constant initialization of enum values
see #5
1 parent 9658c05
Changed files (3)
src/analyze.cpp
@@ -2110,7 +2110,8 @@ static TypeEnumField *get_enum_field(TypeTableEntry *enum_type, Buf *name) {
 }
 
 static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        AstNode *field_access_node, AstNode *value_node, TypeTableEntry *enum_type, Buf *field_name)
+        AstNode *field_access_node, AstNode *value_node, TypeTableEntry *enum_type, Buf *field_name,
+        AstNode *out_node)
 {
     assert(field_access_node->type == NodeTypeFieldAccessExpr);
 
@@ -2119,15 +2120,32 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp
 
     if (type_enum_field) {
         if (value_node) {
-            analyze_expression(g, import, context, type_enum_field->type_entry, value_node);
+            AstNode **value_node_ptr = value_node->parent_field;
+            TypeTableEntry *value_type = analyze_expression(g, import, context,
+                    type_enum_field->type_entry, value_node);
+
+            if (value_type->id == TypeTableEntryIdInvalid) {
+                return g->builtin_types.entry_invalid;
+            }
 
             StructValExprCodeGen *codegen = &field_access_node->data.field_access_expr.resolved_struct_val_expr;
             codegen->type_entry = enum_type;
             codegen->source_node = field_access_node;
-            context->fn_entry->struct_val_expr_alloca_list.append(codegen);
 
-            Expr *expr = get_resolved_expr(field_access_node);
-            expr->const_val.ok = false;
+            ConstExprValue *value_const_val = &get_resolved_expr(*value_node_ptr)->const_val;
+            if (value_const_val->ok) {
+                ConstExprValue *const_val = &get_resolved_expr(out_node)->const_val;
+                const_val->ok = true;
+                const_val->data.x_enum.tag = type_enum_field->value;
+                const_val->data.x_enum.payload = value_const_val;
+            } else {
+                if (context->fn_entry) {
+                    context->fn_entry->struct_val_expr_alloca_list.append(codegen);
+                } else {
+                    add_node_error(g, *value_node_ptr, buf_sprintf("unable to evaluate constant expression"));
+                    return g->builtin_types.entry_invalid;
+                }
+            }
         } else if (type_enum_field->type_entry->id != TypeTableEntryIdVoid) {
             add_node_error(g, field_access_node,
                 buf_sprintf("enum value '%s.%s' requires parameter of type '%s'",
@@ -2135,7 +2153,7 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp
                     buf_ptr(field_name),
                     buf_ptr(&type_enum_field->type_entry->name)));
         } else {
-            Expr *expr = get_resolved_expr(field_access_node);
+            Expr *expr = get_resolved_expr(out_node);
             expr->const_val.ok = true;
             expr->const_val.data.x_enum.tag = type_enum_field->value;
             expr->const_val.data.x_enum.payload = nullptr;
@@ -2396,7 +2414,7 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i
         } else if (wrapped_in_fn_call) {
             return resolve_expr_const_val_as_type(g, node, child_type);
         } else if (child_type->id == TypeTableEntryIdEnum) {
-            return analyze_enum_value_expr(g, import, context, node, nullptr, child_type, field_name);
+            return analyze_enum_value_expr(g, import, context, node, nullptr, child_type, field_name, node);
         } else if (child_type->id == TypeTableEntryIdStruct) {
             BlockContext *container_block_context = get_container_block_context(child_type);
             auto entry = container_block_context->decl_table.maybe_get(field_name);
@@ -4725,7 +4743,7 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import
                         node->data.fn_call_expr.enum_type = child_type;
 
                         return analyze_enum_value_expr(g, import, context, fn_ref_expr, value_node,
-                                child_type, field_name);
+                                child_type, field_name, node);
                     }
                 } else if (child_type->id == TypeTableEntryIdStruct) {
                     Buf *field_name = &fn_ref_expr->data.field_access_expr.field_name;
src/codegen.cpp
@@ -2937,7 +2937,23 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE
                     assert(enum_field->value == const_val->data.x_enum.tag);
                     LLVMValueRef union_value;
                     if (type_has_bits(enum_field->type_entry)) {
-                        union_value = gen_const_val(g, union_type, const_val->data.x_enum.payload);
+                        uint64_t union_type_bytes = LLVMStoreSizeOfType(g->target_data_ref,
+                                union_type->type_ref);
+                        uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref,
+                                enum_field->type_entry->type_ref);
+                        uint64_t pad_bytes = union_type_bytes - field_type_bytes;
+
+                        LLVMValueRef correctly_typed_value = gen_const_val(g, enum_field->type_entry,
+                                const_val->data.x_enum.payload);
+                        if (pad_bytes == 0) {
+                            union_value = correctly_typed_value;
+                        } else {
+                            LLVMValueRef fields[] = {
+                                correctly_typed_value,
+                                LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_bytes)),
+                            };
+                            union_value = LLVMConstStruct(fields, 2, false);
+                        }
                     } else {
                         union_value = LLVMGetUndef(union_type->type_ref);
                     }
test/self_hosted.zig
@@ -1204,3 +1204,43 @@ fn const_expression_eval_handling_of_variables() {
         x = false;
     }
 }
+
+
+
+#attribute("test")
+fn constant_enum_initialization_with_differing_sizes() {
+    test3_1(test3_foo);
+    test3_2(test3_bar);
+}
+enum Test3Foo {
+    One,
+    Two: f32,
+    Three: Test3Point,
+}
+struct Test3Point {
+    x: i32,
+    y: i32,
+}
+const test3_foo = Test3Foo.Three(Test3Point {.x = 3, .y = 4});
+const test3_bar = Test3Foo.Two(13);
+#static_eval_enable(false)
+fn test3_1(f: Test3Foo) {
+    switch (f) {
+        Three => |pt| {
+            assert(pt.x == 3);
+            assert(pt.y == 4);
+        },
+        else => unreachable{},
+    }
+}
+#static_eval_enable(false)
+fn test3_2(f: Test3Foo) {
+    switch (f) {
+        Two => |x| {
+            assert(x == 13);
+        },
+        else => unreachable{},
+    }
+}
+
+