Commit 28c5a8f2ca

Andrew Kelley <superjoe30@gmail.com>
2015-12-16 02:21:59
analyze: clean up type checking
1 parent 431d8f9
doc/langref.md
@@ -157,6 +157,7 @@ KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False)
 
 ```
 x() x[] x.y
+&x
 !x -x ~x
 as
 * / %
src/analyze.cpp
@@ -551,8 +551,92 @@ static TypeTableEntry *get_return_type(BlockContext *context) {
     return return_type_node->codegen_node->data.type_node.entry;
 }
 
-static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *node,
+static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, TypeTableEntry *other_type) {
+    NumLit num_lit = literal_type->data.num_lit.kind;
+    uint64_t lit_size_in_bits = num_lit_bit_count(num_lit);
+
+    switch (other_type->id) {
+        case TypeTableEntryIdInvalid:
+        case TypeTableEntryIdNumberLiteral:
+            zig_unreachable();
+        case TypeTableEntryIdVoid:
+        case TypeTableEntryIdBool:
+        case TypeTableEntryIdUnreachable:
+        case TypeTableEntryIdPointer:
+        case TypeTableEntryIdArray:
+        case TypeTableEntryIdStruct:
+            return false;
+        case TypeTableEntryIdInt:
+            if (is_num_lit_unsigned(num_lit)) {
+                return lit_size_in_bits <= other_type->size_in_bits;
+            } else {
+                return false;
+            }
+        case TypeTableEntryIdFloat:
+            if (is_num_lit_float(num_lit)) {
+                return lit_size_in_bits <= other_type->size_in_bits;
+            } else {
+                return false;
+            }
+    }
+    zig_unreachable();
+}
+
+static TypeTableEntry * resolve_rhs_number_literal(CodeGen *g, AstNode *non_literal_node,
+        TypeTableEntry *non_literal_type, AstNode *literal_node, TypeTableEntry *literal_type)
+{
+    assert(literal_node->codegen_node);
+    NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node;
+
+    if (num_lit_fits_in_other_type(g, literal_type, non_literal_type)) {
+        assert(!codegen_num_lit->resolved_type);
+        codegen_num_lit->resolved_type = non_literal_type;
+        return non_literal_type;
+    } else {
+        return nullptr;
+    }
+}
+
+static TypeTableEntry * resolve_number_literals(CodeGen *g, AstNode *node1, AstNode *node2,
         TypeTableEntry *type1, TypeTableEntry *type2)
+{
+    if (type1->id == TypeTableEntryIdNumberLiteral &&
+        type2->id == TypeTableEntryIdNumberLiteral)
+    {
+        assert(node1->codegen_node);
+        assert(node2->codegen_node);
+
+        NumberLiteralNode *codegen_num_lit_1 = &node1->codegen_node->data.num_lit_node;
+        NumberLiteralNode *codegen_num_lit_2 = &node2->codegen_node->data.num_lit_node;
+
+        assert(!codegen_num_lit_1->resolved_type);
+        assert(!codegen_num_lit_2->resolved_type);
+
+        if (is_num_lit_float(type1->data.num_lit.kind) &&
+            is_num_lit_float(type2->data.num_lit.kind))
+        {
+            codegen_num_lit_1->resolved_type = g->builtin_types.entry_f64;
+            codegen_num_lit_2->resolved_type = g->builtin_types.entry_f64;
+            return g->builtin_types.entry_f64;
+        } else if (is_num_lit_unsigned(type1->data.num_lit.kind) &&
+                   is_num_lit_unsigned(type2->data.num_lit.kind))
+        {
+            codegen_num_lit_1->resolved_type = g->builtin_types.entry_u64;
+            codegen_num_lit_2->resolved_type = g->builtin_types.entry_u64;
+            return g->builtin_types.entry_u64;
+        } else {
+            return nullptr;
+        }
+    } else if (type1->id == TypeTableEntryIdNumberLiteral) {
+        return resolve_rhs_number_literal(g, node2, type2, node1, type1);
+    } else {
+        assert(type2->id == TypeTableEntryIdNumberLiteral);
+        return resolve_rhs_number_literal(g, node1, type1, node2, type2);
+    }
+}
+
+static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *node,
+        TypeTableEntry *type1, TypeTableEntry *type2, AstNode *node1, AstNode *node2)
 {
     if (type1->id == TypeTableEntryIdInvalid ||
         type2->id == TypeTableEntryIdInvalid)
@@ -576,48 +660,23 @@ static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *no
                type1 == type2)
     {
         return type1;
+    } else if (type1->id == TypeTableEntryIdNumberLiteral ||
+               type2->id == TypeTableEntryIdNumberLiteral)
+    {
+        TypeTableEntry *resolved_type = resolve_number_literals(g, node1, node2, type1, type2);
+        if (resolved_type)
+            return resolved_type;
     } else if (type1 == type2) {
         return type1;
     }
 
     add_node_error(g, node,
-        buf_sprintf("ambiguous expression type: '%s' vs '%s'",
+        buf_sprintf("incompatible types: '%s' and '%s'",
             buf_ptr(&type1->name), buf_ptr(&type2->name)));
 
     return g->builtin_types.entry_invalid;
 }
 
-static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, TypeTableEntry *other_type) {
-    NumLit num_lit = literal_type->data.num_lit.kind;
-    uint64_t lit_size_in_bits = num_lit_bit_count(num_lit);
-
-    switch (other_type->id) {
-        case TypeTableEntryIdInvalid:
-        case TypeTableEntryIdNumberLiteral:
-            zig_unreachable();
-        case TypeTableEntryIdVoid:
-        case TypeTableEntryIdBool:
-        case TypeTableEntryIdUnreachable:
-        case TypeTableEntryIdPointer:
-        case TypeTableEntryIdArray:
-        case TypeTableEntryIdStruct:
-            return false;
-        case TypeTableEntryIdInt:
-            if (is_num_lit_unsigned(num_lit)) {
-                return lit_size_in_bits <= other_type->size_in_bits;
-            } else {
-                return false;
-            }
-        case TypeTableEntryIdFloat:
-            if (is_num_lit_float(num_lit)) {
-                return lit_size_in_bits <= other_type->size_in_bits;
-            } else {
-                return false;
-            }
-    }
-    zig_unreachable();
-}
-
 static TypeTableEntry *resolve_type_compatibility(CodeGen *g, BlockContext *context, AstNode *node,
         TypeTableEntry *expected_type, TypeTableEntry *actual_type)
 {
@@ -676,7 +735,7 @@ static TypeTableEntry *resolve_peer_type_compatibility(CodeGen *g, BlockContext
     assert(type1);
     assert(type2);
 
-    TypeTableEntry *parent_type = determine_peer_type_compatibility(g, parent_node, type1, type2);
+    TypeTableEntry *parent_type = determine_peer_type_compatibility(g, parent_node, type1, type2, child1, child2);
 
     if (parent_type->id == TypeTableEntryIdInvalid) {
         return parent_type;
@@ -928,60 +987,6 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
     }
 }
 
-static TypeTableEntry * resolve_rhs_number_literal(CodeGen *g, AstNode *non_literal_node,
-        TypeTableEntry *non_literal_type, AstNode *literal_node, TypeTableEntry *literal_type)
-{
-    assert(literal_node->codegen_node);
-    NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node;
-
-    if (num_lit_fits_in_other_type(g, literal_type, non_literal_type)) {
-        assert(!codegen_num_lit->resolved_type);
-        codegen_num_lit->resolved_type = non_literal_type;
-        return non_literal_type;
-    } else {
-        return nullptr;
-    }
-}
-
-static TypeTableEntry * resolve_number_literals(CodeGen *g, AstNode *node1, AstNode *node2) {
-    TypeTableEntry *type1 = node1->codegen_node->expr_node.type_entry;
-    TypeTableEntry *type2 = node2->codegen_node->expr_node.type_entry;
-
-    if (type1->id == TypeTableEntryIdNumberLiteral &&
-        type2->id == TypeTableEntryIdNumberLiteral)
-    {
-        assert(node1->codegen_node);
-        assert(node2->codegen_node);
-
-        NumberLiteralNode *codegen_num_lit_1 = &node1->codegen_node->data.num_lit_node;
-        NumberLiteralNode *codegen_num_lit_2 = &node2->codegen_node->data.num_lit_node;
-
-        assert(!codegen_num_lit_1->resolved_type);
-        assert(!codegen_num_lit_2->resolved_type);
-
-        if (is_num_lit_float(type1->data.num_lit.kind) &&
-            is_num_lit_float(type2->data.num_lit.kind))
-        {
-            codegen_num_lit_1->resolved_type = g->builtin_types.entry_f64;
-            codegen_num_lit_2->resolved_type = g->builtin_types.entry_f64;
-            return g->builtin_types.entry_f64;
-        } else if (is_num_lit_unsigned(type1->data.num_lit.kind) &&
-                   is_num_lit_unsigned(type2->data.num_lit.kind))
-        {
-            codegen_num_lit_1->resolved_type = g->builtin_types.entry_u64;
-            codegen_num_lit_2->resolved_type = g->builtin_types.entry_u64;
-            return g->builtin_types.entry_u64;
-        } else {
-            return nullptr;
-        }
-    } else if (type1->id == TypeTableEntryIdNumberLiteral) {
-        return resolve_rhs_number_literal(g, node2, type2, node1, type1);
-    } else {
-        assert(type2->id == TypeTableEntryIdNumberLiteral);
-        return resolve_rhs_number_literal(g, node1, type1, node2, type2);
-    }
-}
-
 static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
@@ -1052,31 +1057,9 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
                 AstNode *op2 = node->data.bin_op_expr.op2;
                 TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1);
                 TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2);
-                bool cmp_ok = false;
-                if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) {
-                    cmp_ok = true;
-                } else if (lhs_type->id == TypeTableEntryIdNumberLiteral ||
-                           rhs_type->id == TypeTableEntryIdNumberLiteral)
-                {
-                    cmp_ok = resolve_number_literals(g, op1, op2);
-                } else if (lhs_type->id == TypeTableEntryIdInt) {
-                    if (rhs_type->id == TypeTableEntryIdInt &&
-                        lhs_type->data.integral.is_signed == rhs_type->data.integral.is_signed &&
-                        lhs_type->size_in_bits == rhs_type->size_in_bits)
-                    {
-                        cmp_ok = true;
-                    }
-                } else if (lhs_type->id == TypeTableEntryIdFloat) {
-                    if (rhs_type->id == TypeTableEntryIdFloat &&
-                        lhs_type->size_in_bits == rhs_type->size_in_bits)
-                    {
-                        cmp_ok = true;
-                    }
-                }
-                if (!cmp_ok) {
-                    add_node_error(g, node, buf_sprintf("unable to compare '%s' with '%s'",
-                            buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
-                }
+
+                resolve_peer_type_compatibility(g, context, node, op1, op2, lhs_type, rhs_type);
+
                 return g->builtin_types.entry_bool;
             }
         case BinOpTypeBinOr:
@@ -1104,34 +1087,7 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
                 TypeTableEntry *lhs_type = analyze_expression(g, import, context, expected_type, op1);
                 TypeTableEntry *rhs_type = analyze_expression(g, import, context, expected_type, op2);
 
-                TypeTableEntry *return_type = nullptr;
-
-                if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) {
-                    return_type = g->builtin_types.entry_invalid;
-                } else if (lhs_type->id == TypeTableEntryIdNumberLiteral ||
-                           rhs_type->id == TypeTableEntryIdNumberLiteral)
-                {
-                    return_type = resolve_number_literals(g, op1, op2);
-                } else if (lhs_type->id == TypeTableEntryIdInt &&
-                           lhs_type == rhs_type)
-                {
-                    return_type = lhs_type;
-                } else if (lhs_type->id == TypeTableEntryIdFloat &&
-                           lhs_type == rhs_type)
-                {
-                    return_type = lhs_type;
-                }
-                if (!return_type) {
-                    if (node->data.bin_op_expr.bin_op == BinOpTypeAdd) {
-                        add_node_error(g, node, buf_sprintf("unable to add '%s' and '%s'",
-                                buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
-                    } else {
-                        add_node_error(g, node, buf_sprintf("unable to subtract '%s' and '%s'",
-                                buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
-                    }
-                    return g->builtin_types.entry_invalid;
-                }
-                return return_type;
+                return resolve_peer_type_compatibility(g, context, node, op1, op2, lhs_type, rhs_type);
             }
         case BinOpTypeMult:
         case BinOpTypeDiv:
src/analyze.hpp
@@ -145,6 +145,7 @@ struct CodeGen {
     struct {
         TypeTableEntry *entry_bool;
         TypeTableEntry *entry_u8;
+        TypeTableEntry *entry_u32;
         TypeTableEntry *entry_u64;
         TypeTableEntry *entry_i8;
         TypeTableEntry *entry_i32;
src/codegen.cpp
@@ -130,7 +130,8 @@ static LLVMValueRef find_or_create_string(CodeGen *g, Buf *str, bool c) {
 }
 
 static TypeTableEntry *get_expr_type(AstNode *node) {
-    return node->codegen_node->expr_node.type_entry;
+    TypeTableEntry *cast_type = node->codegen_node->expr_node.implicit_cast.type;
+    return cast_type ? cast_type : node->codegen_node->expr_node.type_entry;
 }
 
 static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) {
@@ -288,8 +289,10 @@ static LLVMValueRef gen_bare_cast(CodeGen *g, AstNode *node, LLVMValueRef expr_v
             } else if (actual_type->size_in_bits < wanted_type->size_in_bits) {
                 if (actual_type->data.integral.is_signed && wanted_type->data.integral.is_signed) {
                     return LLVMBuildSExt(g->builder, expr_val, wanted_type->type_ref, "");
+                } else if (!actual_type->data.integral.is_signed && !wanted_type->data.integral.is_signed) {
+                    return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, "");
                 } else {
-                    zig_panic("TODO gen_cast_expr widen unsigned");
+                    zig_panic("TODO gen_cast_expr mixing of signness");
                 }
             } else {
                 assert(actual_type->size_in_bits > wanted_type->size_in_bits);
@@ -1328,6 +1331,19 @@ static void define_builtin_types(CodeGen *g) {
         g->type_table.put(&entry->name, entry);
         g->builtin_types.entry_u8 = entry;
     }
+    {
+        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
+        entry->type_ref = LLVMInt32Type();
+        buf_init_from_str(&entry->name, "u32");
+        entry->size_in_bits = 32;
+        entry->align_in_bits = 32;
+        entry->data.integral.is_signed = false;
+        entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
+                entry->size_in_bits, entry->align_in_bits,
+                LLVMZigEncoding_DW_ATE_unsigned());
+        g->type_table.put(&entry->name, entry);
+        g->builtin_types.entry_u32 = entry;
+    }
     {
         TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
         entry->type_ref = LLVMInt64Type();
test/run_tests.cpp
@@ -796,7 +796,7 @@ fn f() {
     const y = if true { 1 as i32 };
 }
     )SOURCE", 2, ".tmp_source.zig:3:21: error: expected type 'i32', got 'void'",
-                 ".tmp_source.zig:4:15: error: ambiguous expression type: 'i32' vs 'void'");
+                 ".tmp_source.zig:4:15: error: incompatible types: 'i32' and 'void'");
 }
 
 static void print_compiler_invocation(TestCase *test_case) {