Commit bc0a60c7a6

Andrew Kelley <superjoe30@gmail.com>
2017-09-10 04:46:08
more compile errors for non-const variables of things
closes #456
1 parent 5fdf3fa
Changed files (3)
src/analyze.cpp
@@ -2510,6 +2510,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
 
     IrInstruction *init_value = nullptr;
 
+    // TODO more validation for types that can't be used for export/extern variables
     TypeTableEntry *implicit_type = nullptr;
     if (explicit_type && explicit_type->id == TypeTableEntryIdInvalid) {
         implicit_type = explicit_type;
src/ir.cpp
@@ -9794,6 +9794,58 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
     zig_unreachable();
 }
 
+enum VarClassRequired {
+    VarClassRequiredAny,
+    VarClassRequiredConst,
+    VarClassRequiredIllegal,
+};
+
+static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) {
+    switch (type_entry->id) {
+        case TypeTableEntryIdInvalid:
+            zig_unreachable();
+        case TypeTableEntryIdUnreachable:
+        case TypeTableEntryIdVar:
+            return VarClassRequiredIllegal;
+        case TypeTableEntryIdBool:
+        case TypeTableEntryIdInt:
+        case TypeTableEntryIdFloat:
+        case TypeTableEntryIdVoid:
+        case TypeTableEntryIdPureError:
+        case TypeTableEntryIdFn:
+        case TypeTableEntryIdEnumTag:
+            return VarClassRequiredAny;
+        case TypeTableEntryIdNumLitFloat:
+        case TypeTableEntryIdNumLitInt:
+        case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdBlock:
+        case TypeTableEntryIdNullLit:
+        case TypeTableEntryIdOpaque:
+        case TypeTableEntryIdMetaType:
+        case TypeTableEntryIdNamespace:
+        case TypeTableEntryIdBoundFn:
+        case TypeTableEntryIdArgTuple:
+            return VarClassRequiredConst;
+
+        case TypeTableEntryIdPointer:
+            return get_var_class_required(type_entry->data.pointer.child_type);
+        case TypeTableEntryIdArray:
+            return get_var_class_required(type_entry->data.array.child_type);
+        case TypeTableEntryIdMaybe:
+            return get_var_class_required(type_entry->data.maybe.child_type);
+        case TypeTableEntryIdErrorUnion:
+            return get_var_class_required(type_entry->data.error.child_type);
+
+        case TypeTableEntryIdStruct:
+        case TypeTableEntryIdEnum:
+        case TypeTableEntryIdUnion:
+            // TODO check the fields of these things and make sure that they don't recursively
+            // contain any of the other variable classes
+            return VarClassRequiredAny;
+    }
+    zig_unreachable();
+}
+
 static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) {
     VariableTableEntry *var = decl_var_instruction->var;
 
@@ -9803,10 +9855,6 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
         return var->value->type;
     }
 
-    AstNodeVariableDeclaration *variable_declaration = &var->decl_node->data.variable_declaration;
-    bool is_export = (variable_declaration->visib_mod == VisibModExport);
-    bool is_extern = variable_declaration->is_extern;
-
     var->ref_count = 0;
 
     TypeTableEntry *explicit_type = nullptr;
@@ -9824,59 +9872,30 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
     AstNode *source_node = decl_var_instruction->base.source_node;
 
     IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, explicit_type);
+    bool is_comptime_var = ir_get_var_is_comptime(var);
+
     TypeTableEntry *result_type = casted_init_value->value.type;
     if (type_is_invalid(result_type)) {
         result_type = ira->codegen->builtin_types.entry_invalid;
-    }
-
-    bool is_comptime_var = ir_get_var_is_comptime(var);
-
-    switch (result_type->id) {
-        case TypeTableEntryIdInvalid:
-            break; // handled above
-        case TypeTableEntryIdNumLitFloat:
-        case TypeTableEntryIdNumLitInt:
-        case TypeTableEntryIdUndefLit:
-            if (is_export || is_extern || (!var->src_is_const && !is_comptime_var)) {
-                ir_add_error_node(ira, source_node, buf_sprintf("unable to infer variable type"));
-                result_type = ira->codegen->builtin_types.entry_invalid;
-            }
-            break;
-        case TypeTableEntryIdUnreachable:
-        case TypeTableEntryIdVar:
-        case TypeTableEntryIdBlock:
-        case TypeTableEntryIdNullLit:
-        case TypeTableEntryIdOpaque:
-            ir_add_error_node(ira, source_node,
-                buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name)));
-            result_type = ira->codegen->builtin_types.entry_invalid;
-            break;
-        case TypeTableEntryIdMetaType:
-        case TypeTableEntryIdNamespace:
-            if (casted_init_value->value.special == ConstValSpecialRuntime) {
+    } else {
+        switch (get_var_class_required(result_type)) {
+            case VarClassRequiredIllegal:
                 ir_add_error_node(ira, source_node,
-                    buf_sprintf("variable of type '%s' must be constant", buf_ptr(&result_type->name)));
+                    buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name)));
                 result_type = ira->codegen->builtin_types.entry_invalid;
-            }
-            break;
-        case TypeTableEntryIdVoid:
-        case TypeTableEntryIdBool:
-        case TypeTableEntryIdInt:
-        case TypeTableEntryIdFloat:
-        case TypeTableEntryIdPointer:
-        case TypeTableEntryIdArray:
-        case TypeTableEntryIdStruct:
-        case TypeTableEntryIdMaybe:
-        case TypeTableEntryIdErrorUnion:
-        case TypeTableEntryIdPureError:
-        case TypeTableEntryIdEnum:
-        case TypeTableEntryIdUnion:
-        case TypeTableEntryIdFn:
-        case TypeTableEntryIdBoundFn:
-        case TypeTableEntryIdEnumTag:
-        case TypeTableEntryIdArgTuple:
-            // OK
-            break;
+                break;
+            case VarClassRequiredConst:
+                if (!var->src_is_const && !is_comptime_var) {
+                    ir_add_error_node(ira, source_node,
+                        buf_sprintf("variable of type '%s' must be const or comptime",
+                            buf_ptr(&result_type->name)));
+                    result_type = ira->codegen->builtin_types.entry_invalid;
+                }
+                break;
+            case VarClassRequiredAny:
+                // OK
+                break;
+        }
     }
 
     var->value->type = result_type;
test/compile_errors.zig
@@ -1435,20 +1435,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
         \\fn bar() -> %i32 { 0 }
     , ".tmp_source.zig:2:14: error: expression value is ignored");
 
-    cases.add("integer literal on a non-comptime var",
-        \\export fn foo() {
-        \\    var i = 0;
-        \\    while (i < 10) : (i += 1) { }
-        \\}
-    , ".tmp_source.zig:2:5: error: unable to infer variable type");
-
-    cases.add("undefined literal on a non-comptime var",
-        \\export fn foo() {
-        \\    var i = undefined;
-        \\    i = i32(1);
-        \\}
-    , ".tmp_source.zig:2:5: error: unable to infer variable type");
-
     cases.add("dereference an array",
         \\var s_buffer: [10]u8 = undefined;
         \\pub fn pass(in: []u8) -> []u8 {
@@ -2090,4 +2076,40 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
     ,
         ".tmp_source.zig:5:9: error: expected type '&Derp', found '&c_void'");
 
+    cases.add("non-const variables of things that require const variables",
+        \\const Opaque = @OpaqueType();
+        \\
+        \\export fn entry(opaque: &Opaque) {
+        \\   var m2 = &2;
+        \\   const y: u32 = *m2;
+        \\
+        \\   var a = undefined;
+        \\   var b = 1;
+        \\   var c = 1.0;
+        \\   var d = this;
+        \\   var e = null;
+        \\   var f = *opaque;
+        \\   var g = i32;
+        \\   var h = @import("std");
+        \\   var i = (Foo {}).bar;
+        \\
+        \\   var z: noreturn = return;
+        \\}
+        \\
+        \\const Foo = struct {
+        \\    fn bar(self: &const Foo) {}
+        \\};
+    ,
+        ".tmp_source.zig:4:4: error: variable of type '&const (integer literal)' must be const or comptime",
+        ".tmp_source.zig:7:4: error: variable of type '(undefined)' must be const or comptime",
+        ".tmp_source.zig:8:4: error: variable of type '(integer literal)' must be const or comptime",
+        ".tmp_source.zig:9:4: error: variable of type '(float literal)' must be const or comptime",
+        ".tmp_source.zig:10:4: error: variable of type '(block)' must be const or comptime",
+        ".tmp_source.zig:11:4: error: variable of type '(null)' must be const or comptime",
+        ".tmp_source.zig:12:4: error: variable of type 'Opaque' must be const or comptime",
+        ".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime",
+        ".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime",
+        ".tmp_source.zig:15:4: error: variable of type '(bound fn(&const Foo))' must be const or comptime",
+        ".tmp_source.zig:17:4: error: unreachable code");
+
 }