diff --git a/CMakeLists.txt b/CMakeLists.txt index b78b2cb49..3dce9a90f 100644 --- a/CMakeLists.txt +++ b/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}") diff --git a/src/all_types.hpp b/src/all_types.hpp index de47409d6..8cda62d28 100644 --- a/src/all_types.hpp +++ b/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; diff --git a/src/analyze.cpp b/src/analyze.cpp index 639444c6c..597c07f8e 100644 --- a/src/analyze.cpp +++ b/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; - } + ConstExprValue *const_val = &get_resolved_expr(node)->const_val; + const_val->ok = true; - 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; - - 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: diff --git a/src/codegen.cpp b/src/codegen.cpp index 12ba5378a..82f708415 100644 --- a/src/codegen.cpp +++ b/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]; diff --git a/src/eval.cpp b/src/eval.cpp index 1196e52d5..31683a487 100644 --- a/src/eval.cpp +++ b/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; diff --git a/std/debug.zig b/std/debug.zig index 8781c87b2..541c9436a 100644 --- a/std/debug.zig +++ b/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); } } } diff --git a/test/cases/maybe_return.zig b/test/cases/maybe_return.zig new file mode 100644 index 000000000..e779c1c50 --- /dev/null +++ b/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; +} diff --git a/test/run_tests.cpp b/test/run_tests.cpp index e95c0aa73..c2fdb0904 100644 --- a/test/run_tests.cpp +++ b/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{};