Commit 651dc31247

Andrew Kelley <superjoe30@gmail.com>
2016-08-26 02:25:18
implement null as a literal type
1 parent 1f7ec74
src/all_types.hpp
@@ -394,6 +394,7 @@ enum CastOp {
     CastOpWidenOrShorten,
     CastOpToUnknownSizeArray,
     CastOpMaybeWrap,
+    CastOpNullToMaybe,
     CastOpErrorWrap,
     CastOpPureErrorWrap,
     CastOpPointerReinterpret,
@@ -692,19 +693,16 @@ struct AstNodeContainerInitExpr {
 
 struct AstNodeNullLiteral {
     // populated by semantic analyzer
-    StructValExprCodeGen resolved_struct_val_expr;
     Expr resolved_expr;
 };
 
 struct AstNodeUndefinedLiteral {
     // populated by semantic analyzer
-    StructValExprCodeGen resolved_struct_val_expr;
     Expr resolved_expr;
 };
 
 struct AstNodeZeroesLiteral {
     // populated by semantic analyzer
-    StructValExprCodeGen resolved_struct_val_expr;
     Expr resolved_expr;
 };
 
@@ -989,6 +987,7 @@ enum TypeTableEntryId {
     TypeTableEntryIdNumLitFloat,
     TypeTableEntryIdNumLitInt,
     TypeTableEntryIdUndefLit,
+    TypeTableEntryIdNullLit,
     TypeTableEntryIdMaybe,
     TypeTableEntryIdErrorUnion,
     TypeTableEntryIdPureError,
@@ -1228,6 +1227,7 @@ struct CodeGen {
         TypeTableEntry *entry_num_lit_int;
         TypeTableEntry *entry_num_lit_float;
         TypeTableEntry *entry_undef;
+        TypeTableEntry *entry_null;
         TypeTableEntry *entry_pure_error;
         TypeTableEntry *entry_os_enum;
         TypeTableEntry *entry_arch_enum;
src/analyze.cpp
@@ -237,6 +237,7 @@ static bool type_is_complete(TypeTableEntry *type_entry) {
         case TypeTableEntryIdNumLitFloat:
         case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNullLit:
         case TypeTableEntryIdMaybe:
         case TypeTableEntryIdErrorUnion:
         case TypeTableEntryIdPureError:
@@ -925,6 +926,7 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
         case TypeTableEntryIdNumLitFloat:
         case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNullLit:
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdGenericFn:
             fn_proto->skip = true;
@@ -963,6 +965,7 @@ static TypeTableEntry *analyze_fn_proto_type(CodeGen *g, ImportTableEntry *impor
             case TypeTableEntryIdNumLitFloat:
             case TypeTableEntryIdNumLitInt:
             case TypeTableEntryIdUndefLit:
+            case TypeTableEntryIdNullLit:
             case TypeTableEntryIdUnreachable:
             case TypeTableEntryIdNamespace:
             case TypeTableEntryIdGenericFn:
@@ -1912,6 +1915,7 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) {
         case TypeTableEntryIdNumLitFloat:
         case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNullLit:
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdGenericFn:
             return false;
@@ -2167,6 +2171,13 @@ static bool types_match_with_implicit_cast(CodeGen *g, TypeTableEntry *expected_
         return true;
     }
 
+    // implicit conversion from null literal to maybe type
+    if (expected_type->id == TypeTableEntryIdMaybe &&
+        actual_type->id == TypeTableEntryIdNullLit)
+    {
+        return true;
+    }
+
     // implicit conversion from error child type to error type
     if (expected_type->id == TypeTableEntryIdErrorUnion &&
         types_match_with_implicit_cast(g, expected_type->data.error.child_type, actual_type,
@@ -2971,13 +2982,6 @@ static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node,
     return g->builtin_types.entry_bool;
 }
 
-static TypeTableEntry *resolve_expr_const_val_as_null(CodeGen *g, AstNode *node, TypeTableEntry *type) {
-    Expr *expr = get_resolved_expr(node);
-    expr->const_val.ok = true;
-    expr->const_val.data.x_maybe = nullptr;
-    return type;
-}
-
 static TypeTableEntry *resolve_expr_const_val_as_non_null(CodeGen *g, AstNode *node,
         TypeTableEntry *type, ConstExprValue *other_val)
 {
@@ -3358,6 +3362,7 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
         case TypeTableEntryIdArray:
         case TypeTableEntryIdStruct:
         case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNullLit:
         case TypeTableEntryIdMaybe:
         case TypeTableEntryIdErrorUnion:
         case TypeTableEntryIdUnion:
@@ -3916,26 +3921,17 @@ static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *i
 {
     assert(node->type == NodeTypeNullLiteral);
 
-    if (!expected_type) {
-        add_node_error(g, node, buf_sprintf("unable to determine null type"));
-        return g->builtin_types.entry_invalid;
-    }
-
-    if (expected_type->id != TypeTableEntryIdMaybe) {
-        add_node_error(g, node,
-                buf_sprintf("expected maybe type, got '%s'", buf_ptr(&expected_type->name)));
-        return g->builtin_types.entry_invalid;
-    }
-
-    node->data.null_literal.resolved_struct_val_expr.type_entry = expected_type;
-    node->data.null_literal.resolved_struct_val_expr.source_node = node;
+    ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+    const_val->ok = true;
 
-    return resolve_expr_const_val_as_null(g, node, expected_type);
+    return g->builtin_types.entry_null;
 }
 
 static TypeTableEntry *analyze_undefined_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
+    assert(node->type == NodeTypeUndefinedLiteral);
+
     Expr *expr = get_resolved_expr(node);
     ConstExprValue *const_val = &expr->const_val;
 
@@ -4519,6 +4515,14 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
         }
     }
 
+    // explicit cast from null literal to maybe type
+    if (wanted_type->id == TypeTableEntryIdMaybe &&
+        actual_type->id == TypeTableEntryIdNullLit)
+    {
+        get_resolved_expr(node)->return_knowledge = ReturnKnowledgeKnownNull;
+        return resolve_cast(g, context, node, expr_node, wanted_type, CastOpNullToMaybe, true);
+    }
+
     // explicit cast from child type of error type to error type
     if (wanted_type->id == TypeTableEntryIdErrorUnion) {
         if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) {
@@ -5203,6 +5207,7 @@ static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry
                     case TypeTableEntryIdNumLitFloat:
                     case TypeTableEntryIdNumLitInt:
                     case TypeTableEntryIdUndefLit:
+                    case TypeTableEntryIdNullLit:
                     case TypeTableEntryIdNamespace:
                     case TypeTableEntryIdGenericFn:
                         add_node_error(g, expr_node,
@@ -6252,13 +6257,12 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import,
                 if (resolved_type->id == TypeTableEntryIdInvalid) {
                     return resolved_type;
                 } else if (resolved_type->id == TypeTableEntryIdErrorUnion) {
-                    TypeTableEntry *return_type = context->fn_entry->type_entry->data.fn.fn_type_id.return_type;
-                    if (return_type->id != TypeTableEntryIdErrorUnion &&
-                        return_type->id != TypeTableEntryIdPureError)
+                    if (expected_return_type->id != TypeTableEntryIdErrorUnion &&
+                        expected_return_type->id != TypeTableEntryIdPureError)
                     {
                         ErrorMsg *msg = add_node_error(g, node,
                             buf_sprintf("%%return statement in function with return type '%s'",
-                                buf_ptr(&return_type->name)));
+                                buf_ptr(&expected_return_type->name)));
                         AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type;
                         add_error_note(g, msg, return_type_node, buf_sprintf("function return type here"));
                     }
@@ -6283,11 +6287,10 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import,
                 if (resolved_type->id == TypeTableEntryIdInvalid) {
                     return resolved_type;
                 } else if (resolved_type->id == TypeTableEntryIdMaybe) {
-                    TypeTableEntry *return_type = context->fn_entry->type_entry->data.fn.fn_type_id.return_type;
-                    if (return_type->id != TypeTableEntryIdMaybe) {
+                    if (expected_return_type->id != TypeTableEntryIdMaybe) {
                         ErrorMsg *msg = add_node_error(g, node,
                             buf_sprintf("?return statement in function with return type '%s'",
-                                buf_ptr(&return_type->name)));
+                                buf_ptr(&expected_return_type->name)));
                         AstNode *return_type_node = context->fn_entry->fn_def_node->data.fn_def.fn_proto->data.fn_proto.return_type;
                         add_error_note(g, msg, return_type_node, buf_sprintf("function return type here"));
                     }
@@ -7227,6 +7230,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
         case TypeTableEntryIdNumLitFloat:
         case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNullLit:
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdGenericFn:
              zig_unreachable();
@@ -7335,6 +7339,8 @@ static uint32_t hash_const_val(TypeTableEntry *type, ConstExprValue *const_val)
             return hash_ptr(const_val->data.x_ptr.ptr);
         case TypeTableEntryIdUndefLit:
             return 162837799;
+        case TypeTableEntryIdNullLit:
+            return 844854567;
         case TypeTableEntryIdArray:
             // TODO better hashing algorithm
             return 1166190605;
@@ -7428,6 +7434,7 @@ static TypeTableEntry *type_of_first_thing_in_memory(TypeTableEntry *type_entry)
         case TypeTableEntryIdNumLitFloat:
         case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNullLit:
         case TypeTableEntryIdUnreachable:
         case TypeTableEntryIdMetaType:
         case TypeTableEntryIdVoid:
src/codegen.cpp
@@ -875,6 +875,9 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
 
                 return cast_expr->tmp_ptr;
             }
+        case CastOpNullToMaybe:
+            // handled by constant expression evaluator
+            zig_unreachable();
         case CastOpErrorWrap:
             {
                 assert(wanted_type->id == TypeTableEntryIdErrorUnion);
@@ -3806,6 +3809,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE
         case TypeTableEntryIdNumLitFloat:
         case TypeTableEntryIdNumLitInt:
         case TypeTableEntryIdUndefLit:
+        case TypeTableEntryIdNullLit:
         case TypeTableEntryIdVoid:
         case TypeTableEntryIdNamespace:
         case TypeTableEntryIdGenericFn:
@@ -4311,6 +4315,12 @@ static void define_builtin_types(CodeGen *g) {
         entry->deep_const = true;
         g->builtin_types.entry_undef = entry;
     }
+    {
+        TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNullLit);
+        buf_init_from_str(&entry->name, "(null)");
+        entry->deep_const = true;
+        g->builtin_types.entry_null = entry;
+    }
 
     for (int int_size_i = 0; int_size_i < array_length(int_sizes_in_bits); int_size_i += 1) {
         int size_in_bits = int_sizes_in_bits[int_size_i];
src/eval.cpp
@@ -45,6 +45,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *ty
             zig_panic("TODO");
         case TypeTableEntryIdUndefLit:
             zig_panic("TODO");
+        case TypeTableEntryIdNullLit:
+            zig_panic("TODO");
         case TypeTableEntryIdMaybe:
             zig_panic("TODO");
         case TypeTableEntryIdErrorUnion:
@@ -650,6 +652,10 @@ void eval_const_expr_implicit_cast(CastOp cast_op,
             const_val->data.x_maybe = other_val;
             const_val->ok = true;
             break;
+        case CastOpNullToMaybe:
+            const_val->data.x_maybe = nullptr;
+            const_val->ok = true;
+            break;
         case CastOpErrorWrap:
             const_val->data.x_err.err = nullptr;
             const_val->data.x_err.payload = other_val;
std/debug.zig
@@ -40,7 +40,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void {
 
                 %return out_stream.printInt(usize, return_address);
                 %return out_stream.printf("  -> ");
-                %return out_stream.printInt(u64, debug_info_offset);
+                %return out_stream.printInt(u64, compile_unit_offset);
                 %return out_stream.printf("\n");
                 maybe_fp = *(&const ?&const u8)(fp);
             }
@@ -82,7 +82,8 @@ fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 {
 }
 
 fn arangesOffset(st: &ElfStackTrace, target_address: usize) -> %?u64 {
-    const aranges = ?return st.aranges;
+    // TODO ability to implicitly cast null to %?T
+    const aranges = st.aranges ?? return (?u64)(null);
 
     %return st.elf.seekToSection(aranges);
 
@@ -129,7 +130,8 @@ fn arangesOffset(st: &ElfStackTrace, target_address: usize) -> %?u64 {
             if (address == 0 && length == 0) break;
 
             if (target_address >= address && target_address < address + length) {
-                return debug_info_offset;
+                // TODO ability to implicitly cast T to %?T
+                return (?u64)(debug_info_offset);
             }
         }
     }
test/cases/maybe_return.zig
@@ -0,0 +1,15 @@
+const assert = @import("std").debug.assert;
+
+#attribute("test")
+fn maybeReturn() {
+    assert(??foo(1235));
+    assert(if (const _ ?= foo(null)) false else true);
+    assert(!??foo(1234));
+}
+
+// TODO add another function with static_eval_enable(true)
+#static_eval_enable(false)
+fn foo(x: ?i32) -> ?bool {
+    const value = ?return x;
+    return value > 1234;
+}
test/run_tests.cpp
@@ -1211,7 +1211,7 @@ fn derp(){}
 
     add_compile_fail_case("assign null to non-nullable pointer", R"SOURCE(
 const a: &u8 = null;
-    )SOURCE", 1, ".tmp_source.zig:2:16: error: expected maybe type, got '&u8'");
+    )SOURCE", 1, ".tmp_source.zig:2:16: error: expected type '&u8', got '(null)'");
 
     add_compile_fail_case("indexing an array of size zero", R"SOURCE(
 const array = []u8{};
CMakeLists.txt
@@ -202,6 +202,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/builtin.zig" DESTINATION "${ZIG_STD_DEST}
 install(FILES "${CMAKE_SOURCE_DIR}/std/compiler_rt.zig" DESTINATION "${ZIG_STD_DEST}")
 install(FILES "${CMAKE_SOURCE_DIR}/std/cstr.zig" DESTINATION "${ZIG_STD_DEST}")
 install(FILES "${CMAKE_SOURCE_DIR}/std/debug.zig" DESTINATION "${ZIG_STD_DEST}")
+install(FILES "${CMAKE_SOURCE_DIR}/std/dwarf.zig" DESTINATION "${ZIG_STD_DEST}")
 install(FILES "${CMAKE_SOURCE_DIR}/std/elf.zig" DESTINATION "${ZIG_STD_DEST}")
 install(FILES "${CMAKE_SOURCE_DIR}/std/empty.zig" DESTINATION "${ZIG_STD_DEST}")
 install(FILES "${CMAKE_SOURCE_DIR}/std/endian.zig" DESTINATION "${ZIG_STD_DEST}")