From 5161d70620342749b1995fdaabb39220654cc941 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 31 Jan 2018 01:51:15 -0500 Subject: *WIP* error sets --- src/analyze.cpp | 212 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 124 insertions(+), 88 deletions(-) (limited to 'src/analyze.cpp') diff --git a/src/analyze.cpp b/src/analyze.cpp index 4fa8dad7ce..3b98999081 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -224,7 +224,7 @@ bool type_is_complete(TypeTableEntry *type_entry) { case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -260,7 +260,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) { case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -514,29 +514,39 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { } } -TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { - if (child_type->error_parent) - return child_type->error_parent; +TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, TypeTableEntry *payload_type) { + assert(err_set_type->id == TypeTableEntryIdErrorSet); + + TypeId type_id = {}; + type_id.id = TypeTableEntryIdErrorUnion; + type_id.data.error_union.err_set_type = err_set_type; + type_id.data.error_union.payload_type = payload_type; + + auto existing_entry = g->type_table.maybe_get(type_id); + if (existing_entry) { + return existing_entry->value; + } TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion); entry->is_copyable = true; - assert(child_type->type_ref); - assert(child_type->di_type); - ensure_complete_type(g, child_type); + assert(payload_type->type_ref); + assert(payload_type->di_type); + ensure_complete_type(g, payload_type); buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "%%%s", buf_ptr(&child_type->name)); + buf_appendf(&entry->name, "%s!%s", buf_ptr(&err_set_type->name), buf_ptr(&payload_type->name)); - entry->data.error.child_type = child_type; + entry->data.error_union.err_set_type = err_set_type; + entry->data.error_union.payload_type = payload_type; - if (!type_has_bits(child_type)) { - entry->type_ref = g->err_tag_type->type_ref; - entry->di_type = g->err_tag_type->di_type; + if (!type_has_bits(payload_type)) { + entry->type_ref = err_set_type->type_ref; + entry->di_type = err_set_type->di_type; } else { LLVMTypeRef elem_types[] = { - g->err_tag_type->type_ref, - child_type->type_ref, + err_set_type->type_ref, + payload_type->type_ref, }; entry->type_ref = LLVMStructType(elem_types, 2, false); @@ -547,12 +557,12 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name), compile_unit_scope, di_file, line); - uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, g->err_tag_type->type_ref); - uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, g->err_tag_type->type_ref); + uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, err_set_type->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, err_set_type->type_ref); uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, err_union_err_index); - uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_type->type_ref); - uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_type->type_ref); + uint64_t value_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, payload_type->type_ref); + uint64_t value_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, payload_type->type_ref); uint64_t value_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, entry->type_ref, err_union_payload_index); @@ -565,13 +575,13 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { tag_debug_size_in_bits, tag_debug_align_in_bits, tag_offset_in_bits, - 0, child_type->di_type), + 0, err_set_type->di_type), ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(entry->di_type), "value", di_file, line, value_debug_size_in_bits, value_debug_align_in_bits, value_offset_in_bits, - 0, child_type->di_type), + 0, payload_type->di_type), }; ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, @@ -587,7 +597,7 @@ TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { entry->di_type = replacement_di_type; } - child_type->error_parent = entry; + g->type_table.put(type_id, entry); return entry; } @@ -937,7 +947,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { handle_is_ptr(fn_type_id->return_type); bool prefix_arg_error_return_trace = g->have_err_ret_tracing && (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion || - fn_type_id->return_type->id == TypeTableEntryIdPureError); + fn_type_id->return_type->id == TypeTableEntryIdErrorSet); // +1 for maybe making the first argument the return value // +1 for maybe last argument the error return trace LLVMTypeRef *gen_param_types = allocate(2 + fn_type_id->param_count); @@ -1177,7 +1187,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -1218,7 +1228,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -1263,7 +1273,23 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { zig_unreachable(); } -static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) { +static TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry) { + TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); + buf_resize(&err_set_type->name, 0); + buf_appendf(&err_set_type->name, "%s.errors", buf_ptr(&fn_entry->symbol_name)); + err_set_type->is_copyable = true; + err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref; + err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type; + err_set_type->data.error_set.err_count = 0; + err_set_type->data.error_set.errors = nullptr; + err_set_type->data.error_set.infer_fn = fn_entry; + + g->error_di_types.append(&err_set_type->di_type); + + return err_set_type; +} + +static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, FnTableEntry *fn_entry) { assert(proto_node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; @@ -1359,7 +1385,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: @@ -1382,8 +1408,13 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c } } - fn_type_id.return_type = (fn_proto->return_type == nullptr) ? - g->builtin_types.entry_void : analyze_type_expr(g, child_scope, fn_proto->return_type); + TypeTableEntry *specified_return_type = analyze_type_expr(g, child_scope, fn_proto->return_type); + if (fn_proto->auto_err_set) { + TypeTableEntry *inferred_err_set_type = get_auto_err_set_type(g, fn_entry); + fn_type_id.return_type = get_error_union_type(g, inferred_err_set_type, specified_return_type); + } else { + fn_type_id.return_type = specified_return_type; + } if (type_is_invalid(fn_type_id.return_type)) { return g->builtin_types.entry_invalid; @@ -1434,7 +1465,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: @@ -2756,7 +2787,8 @@ TypeTableEntry *get_test_fn_type(CodeGen *g) { return g->test_fn_type; FnTypeId fn_type_id = {0}; - fn_type_id.return_type = get_error_type(g, g->builtin_types.entry_void); + fn_type_id.return_type = get_error_union_type(g, g->builtin_types.entry_global_error_set, + g->builtin_types.entry_void); g->test_fn_type = get_fn_type(g, &fn_type_id); return g->test_fn_type; } @@ -2824,7 +2856,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { Scope *child_scope = fn_table_entry->fndef_scope ? &fn_table_entry->fndef_scope->base : tld_fn->base.parent_scope; - fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope); + fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope, fn_table_entry); if (fn_proto->section_expr != nullptr) { if (fn_table_entry->body_node == nullptr) { @@ -2949,29 +2981,6 @@ static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope g->resolve_queue.append(&tld_fn->base); } -static void preview_error_value_decl(CodeGen *g, AstNode *node) { - assert(node->type == NodeTypeErrorValueDecl); - - ErrorTableEntry *err = allocate(1); - - err->decl_node = node; - buf_init_from_buf(&err->name, node->data.error_value_decl.name); - - auto existing_entry = g->error_table.maybe_get(&err->name); - if (existing_entry) { - // duplicate error definitions allowed and they get the same value - err->value = existing_entry->value->value; - } else { - size_t error_value_count = g->error_decls.length; - assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)g->err_tag_type->data.integral.bit_count)); - err->value = (uint32_t)error_value_count; - g->error_decls.append(node); - g->error_table.put(&err->name, err); - } - - node->data.error_value_decl.err = err; -} - static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope) { assert(node->type == NodeTypeCompTime); @@ -3045,10 +3054,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { import->use_decls.append(node); break; } - case NodeTypeErrorValueDecl: - // error value declarations do not depend on other top level decls - preview_error_value_decl(g, node); - break; case NodeTypeTestDecl: preview_test_decl(g, node, decls_scope); break; @@ -3097,6 +3102,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeVarLiteral: case NodeTypeIfErrorExpr: case NodeTypeTestExpr: + case NodeTypeErrorSetDecl: zig_unreachable(); } } @@ -3147,7 +3153,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: @@ -3403,13 +3409,16 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * actual_type->data.maybe.child_type); } - // error + // error union if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) { return types_match_const_cast_only( - expected_type->data.error.child_type, - actual_type->data.error.child_type); + expected_type->data.error_union.payload_type, + actual_type->data.error_union.payload_type) && + types_match_const_cast_only( + expected_type->data.error_union.err_set_type, + actual_type->data.error_union.err_set_type); } // fn @@ -3625,7 +3634,7 @@ static bool is_container(TypeTableEntry *type_entry) { case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -3673,7 +3682,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -3774,14 +3783,36 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ &fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node); fn_table_entry->implicit_return_type = block_return_type; - if (block_return_type->id == TypeTableEntryIdInvalid || - fn_table_entry->analyzed_executable.invalid) - { + if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) { assert(g->errors.length > 0); fn_table_entry->anal_state = FnAnalStateInvalid; return; } + if (fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) { + TypeTableEntry *return_err_set_type = fn_type_id->return_type->data.error_union.err_set_type; + if (return_err_set_type->data.error_set.infer_fn != nullptr) { + TypeTableEntry *inferred_err_set_type; + if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorSet) { + inferred_err_set_type = fn_table_entry->implicit_return_type; + } else if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorUnion) { + inferred_err_set_type = fn_table_entry->implicit_return_type->data.error_union.err_set_type; + } else { + add_node_error(g, return_type_node, + buf_sprintf("function with inferred error set must return at least one possible error")); + fn_table_entry->anal_state = FnAnalStateInvalid; + return; + } + + return_err_set_type->data.error_set.infer_fn = nullptr; + return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count; + return_err_set_type->data.error_set.errors = allocate(inferred_err_set_type->data.error_set.err_count); + for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) { + return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i]; + } + } + } + if (g->verbose_ir) { fprintf(stderr, "{ // (analyzed)\n"); ir_print(g, stderr, &fn_table_entry->analyzed_executable, 4); @@ -3791,7 +3822,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ fn_table_entry->anal_state = FnAnalStateComplete; } -static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { +void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { assert(fn_table_entry->anal_state != FnAnalStateProbing); if (fn_table_entry->anal_state != FnAnalStateReady) return; @@ -4022,7 +4053,8 @@ void semantic_analyze(CodeGen *g) { for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) { Tld *tld = g->resolve_queue.at(g->resolve_queue_index); bool pointer_only = false; - resolve_top_level_decl(g, tld, pointer_only, nullptr); + AstNode *source_node = nullptr; + resolve_top_level_decl(g, tld, pointer_only, source_node); } for (; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) { @@ -4114,7 +4146,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdPointer: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdEnum: return false; @@ -4122,7 +4154,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { case TypeTableEntryIdStruct: return type_has_bits(type_entry); case TypeTableEntryIdErrorUnion: - return type_has_bits(type_entry->data.error.child_type); + return type_has_bits(type_entry->data.error_union.payload_type); case TypeTableEntryIdMaybe: return type_has_bits(type_entry->data.maybe.child_type) && type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer && @@ -4386,9 +4418,9 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case TypeTableEntryIdErrorUnion: // TODO better hashing algorithm return 3415065496; - case TypeTableEntryIdPureError: - // TODO better hashing algorithm - return 2630160122; + case TypeTableEntryIdErrorSet: + assert(const_val->data.x_err_set != nullptr); + return const_val->data.x_err_set->value ^ 2630160122; case TypeTableEntryIdFn: return 4133894920 ^ hash_ptr(const_val->data.x_fn.fn_entry); case TypeTableEntryIdNamespace: @@ -4515,7 +4547,7 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: case TypeTableEntryIdEnum: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdBool: case TypeTableEntryIdInt: @@ -4894,8 +4926,8 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { return a->data.x_type == b->data.x_type; case TypeTableEntryIdVoid: return true; - case TypeTableEntryIdPureError: - return a->data.x_pure_err == b->data.x_pure_err; + case TypeTableEntryIdErrorSet: + return a->data.x_err_set->value == b->data.x_err_set->value; case TypeTableEntryIdFn: return a->data.x_fn.fn_entry == b->data.x_fn.fn_entry; case TypeTableEntryIdBool: @@ -5256,9 +5288,9 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { buf_appendf(buf, "(union %s constant)", buf_ptr(&type_entry->name)); return; } - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: { - buf_appendf(buf, "(pure error constant)"); + buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->name), buf_ptr(&const_val->data.x_err_set->name)); return; } case TypeTableEntryIdArgTuple: @@ -5319,8 +5351,7 @@ uint32_t type_id_hash(TypeId x) { case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: - case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: @@ -5329,6 +5360,8 @@ uint32_t type_id_hash(TypeId x) { case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: zig_unreachable(); + case TypeTableEntryIdErrorUnion: + return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type); case TypeTableEntryIdPointer: return hash_ptr(x.data.pointer.child_type) + (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) + @@ -5363,8 +5396,7 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: - case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: @@ -5374,6 +5406,10 @@ bool type_id_eql(TypeId a, TypeId b) { case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: zig_unreachable(); + case TypeTableEntryIdErrorUnion: + return a.data.error_union.err_set_type == b.data.error_union.err_set_type && + a.data.error_union.payload_type == b.data.error_union.payload_type; + case TypeTableEntryIdPointer: return a.data.pointer.child_type == b.data.pointer.child_type && a.data.pointer.is_const == b.data.pointer.is_const && @@ -5478,7 +5514,7 @@ static const TypeTableEntryId all_type_ids[] = { TypeTableEntryIdNullLit, TypeTableEntryIdMaybe, TypeTableEntryIdErrorUnion, - TypeTableEntryIdPureError, + TypeTableEntryIdErrorSet, TypeTableEntryIdEnum, TypeTableEntryIdUnion, TypeTableEntryIdFn, @@ -5533,7 +5569,7 @@ size_t type_id_index(TypeTableEntryId id) { return 13; case TypeTableEntryIdErrorUnion: return 14; - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: return 15; case TypeTableEntryIdEnum: return 16; @@ -5590,8 +5626,8 @@ const char *type_id_name(TypeTableEntryId id) { return "Nullable"; case TypeTableEntryIdErrorUnion: return "ErrorUnion"; - case TypeTableEntryIdPureError: - return "Error"; + case TypeTableEntryIdErrorSet: + return "ErrorSet"; case TypeTableEntryIdEnum: return "Enum"; case TypeTableEntryIdUnion: -- cgit v1.2.3 From 5f518dbeb952186b7c11777b2454256c8c4fb9ac Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 31 Jan 2018 22:48:40 -0500 Subject: *WIP* error sets converting std lib --- TODO | 5 ++ build.zig | 4 +- doc/docgen.zig | 18 ++--- doc/langref.html.in | 6 +- example/cat/main.zig | 8 +-- example/guess_number/main.zig | 2 +- example/hello_world/hello.zig | 2 +- example/mix_o_files/build.zig | 2 +- example/shared_library/build.zig | 2 +- src-self-hosted/main.zig | 14 ++-- src-self-hosted/module.zig | 8 +-- src-self-hosted/parser.zig | 36 +++++----- src/analyze.cpp | 10 +-- src/parser.cpp | 30 +++++++-- std/array_list.zig | 10 +-- std/base64.zig | 23 +++---- std/buf_map.zig | 4 +- std/buf_set.zig | 4 +- std/buffer.zig | 18 ++--- std/build.zig | 49 ++++++-------- std/crypto/throughput_test.zig | 2 +- std/cstr.zig | 4 +- std/debug/failing_allocator.zig | 4 +- std/elf.zig | 10 ++- std/fmt/index.zig | 65 +++++++++--------- std/hash_map.zig | 4 +- std/heap.zig | 12 ++-- std/io.zig | 108 ++++++++++++------------------ std/linked_list.zig | 4 +- std/math/index.zig | 44 ++++-------- std/mem.zig | 22 +++--- std/net.zig | 34 +++------- std/os/child_process.zig | 50 +++++++------- std/os/get_user_id.zig | 7 +- std/os/index.zig | 2 - std/os/linux.zig | 2 +- std/os/path.zig | 29 +++----- std/os/windows/util.zig | 21 ++---- std/special/build_file_template.zig | 2 +- std/special/build_runner.zig | 8 +-- std/unicode.zig | 20 ++---- test/cases/cast.zig | 14 ++-- test/cases/defer.zig | 4 +- test/cases/enum_with_members.zig | 2 +- test/cases/error.zig | 21 ++---- test/cases/ir_block_deps.zig | 4 +- test/cases/misc.zig | 4 +- test/cases/switch.zig | 2 +- test/cases/switch_prong_err_enum.zig | 6 +- test/cases/switch_prong_implicit_cast.zig | 4 +- test/cases/try.zig | 7 +- test/cases/while.zig | 6 +- test/compare_output.zig | 32 ++++----- test/compile_errors.zig | 6 +- test/runtime_safety.zig | 42 ++++++------ test/standalone/brace_expansion/build.zig | 2 +- test/standalone/brace_expansion/main.zig | 13 ++-- test/standalone/issue_339/build.zig | 2 +- test/standalone/pkg_import/build.zig | 2 +- test/standalone/pkg_import/test.zig | 2 +- test/standalone/use_alias/build.zig | 2 +- test/tests.zig | 13 ++-- 62 files changed, 389 insertions(+), 510 deletions(-) create mode 100644 TODO (limited to 'src/analyze.cpp') diff --git a/TODO b/TODO new file mode 100644 index 0000000000..47272138fe --- /dev/null +++ b/TODO @@ -0,0 +1,5 @@ +sed -i 's/\(\bfn .*) \)%\(.*{\)$/\1!\2/g' $(find .. -name "*.zig") + +comptime assert(error{} ! i32 == i32); + + diff --git a/build.zig b/build.zig index 0a898776f8..5b27a96c31 100644 --- a/build.zig +++ b/build.zig @@ -10,7 +10,7 @@ const ArrayList = std.ArrayList; const Buffer = std.Buffer; const io = std.io; -pub fn build(b: &Builder) %void { +pub fn build(b: &Builder) !void { const mode = b.standardReleaseOptions(); var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); @@ -149,7 +149,7 @@ const LibraryDep = struct { includes: ArrayList([]const u8), }; -fn findLLVM(b: &Builder, llvm_config_exe: []const u8) %LibraryDep { +fn findLLVM(b: &Builder, llvm_config_exe: []const u8) !LibraryDep { const libs_output = try b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"}); const includes_output = try b.exec([][]const u8{llvm_config_exe, "--includedir"}); const libdir_output = try b.exec([][]const u8{llvm_config_exe, "--libdir"}); diff --git a/doc/docgen.zig b/doc/docgen.zig index a7b4084e7e..c8bae7e084 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -12,7 +12,7 @@ const exe_ext = std.build.Target(std.build.Target.Native).exeFileExt(); const obj_ext = std.build.Target(std.build.Target.Native).oFileExt(); const tmp_dir_name = "docgen_tmp"; -pub fn main() %void { +pub fn main() !void { // TODO use a more general purpose allocator here var inc_allocator = try std.heap.IncrementingAllocator.init(max_doc_file_size); defer inc_allocator.deinit(); @@ -243,13 +243,13 @@ fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const return error.ParseError; } -fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) %void { +fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) !void { if (token.id != id) { return parseError(tokenizer, token, "expected {}, found {}", @tagName(id), @tagName(token.id)); } } -fn eatToken(tokenizer: &Tokenizer, id: Token.Id) %Token { +fn eatToken(tokenizer: &Tokenizer, id: Token.Id) !Token { const token = tokenizer.next(); try assertToken(tokenizer, token, id); return token; @@ -316,7 +316,7 @@ const Action = enum { Close, }; -fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) %Toc { +fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) !Toc { var urls = std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator); errdefer urls.deinit(); @@ -540,7 +540,7 @@ fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) %Toc { }; } -fn urlize(allocator: &mem.Allocator, input: []const u8) %[]u8 { +fn urlize(allocator: &mem.Allocator, input: []const u8) ![]u8 { var buf = try std.Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -560,7 +560,7 @@ fn urlize(allocator: &mem.Allocator, input: []const u8) %[]u8 { return buf.toOwnedSlice(); } -fn escapeHtml(allocator: &mem.Allocator, input: []const u8) %[]u8 { +fn escapeHtml(allocator: &mem.Allocator, input: []const u8) ![]u8 { var buf = try std.Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -604,7 +604,7 @@ test "term color" { assert(mem.eql(u8, result, "AgreenB")); } -fn termColor(allocator: &mem.Allocator, input: []const u8) %[]u8 { +fn termColor(allocator: &mem.Allocator, input: []const u8) ![]u8 { var buf = try std.Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -686,7 +686,7 @@ fn termColor(allocator: &mem.Allocator, input: []const u8) %[]u8 { error ExampleFailedToCompile; -fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io.OutStream, zig_exe: []const u8) %void { +fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io.OutStream, zig_exe: []const u8) !void { var code_progress_index: usize = 0; for (toc.nodes) |node| { switch (node) { @@ -977,7 +977,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: &io error ChildCrashed; error ChildExitError; -fn exec(allocator: &mem.Allocator, args: []const []const u8) %os.ChildProcess.ExecResult { +fn exec(allocator: &mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult { const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size); switch (result.term) { os.ChildProcess.Term.Exited => |exit_code| { diff --git a/doc/langref.html.in b/doc/langref.html.in index c070686c56..72d1f946af 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5598,7 +5598,9 @@ Block = option(Symbol ":") "{" many(Statement) "}" Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" -TypeExpr = PrefixOpExpression | "var" +TypeExpr = ErrorSetExpr | "var" + +ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression BlockOrExpression = Block | Expression @@ -5680,7 +5682,7 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression = TypeExpr option(ContainerInitExpression) -MultiplyOperator = "!" | "*" | "/" | "%" | "**" | "*%" +MultiplyOperator = "*" | "/" | "%" | "**" | "*%" PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression diff --git a/example/cat/main.zig b/example/cat/main.zig index f519ca3f18..acd9495a83 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -5,7 +5,7 @@ const os = std.os; const warn = std.debug.warn; const allocator = std.debug.global_allocator; -pub fn main() %void { +pub fn main() !void { var args_it = os.args(); const exe = try unwrapArg(??args_it.next(allocator)); var catted_anything = false; @@ -36,12 +36,12 @@ pub fn main() %void { } } -fn usage(exe: []const u8) %void { +fn usage(exe: []const u8) !void { warn("Usage: {} [FILE]...\n", exe); return error.Invalid; } -fn cat_file(stdout: &io.File, file: &io.File) %void { +fn cat_file(stdout: &io.File, file: &io.File) !void { var buf: [1024 * 4]u8 = undefined; while (true) { @@ -61,7 +61,7 @@ fn cat_file(stdout: &io.File, file: &io.File) %void { } } -fn unwrapArg(arg: %[]u8) %[]u8 { +fn unwrapArg(arg: %[]u8) ![]u8 { return arg catch |err| { warn("Unable to parse command line: {}\n", err); return err; diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig index 482fb2f61e..d6a7b94b6c 100644 --- a/example/guess_number/main.zig +++ b/example/guess_number/main.zig @@ -5,7 +5,7 @@ const fmt = std.fmt; const Rand = std.rand.Rand; const os = std.os; -pub fn main() %void { +pub fn main() !void { var stdout_file = try io.getStdOut(); var stdout_file_stream = io.FileOutStream.init(&stdout_file); const stdout = &stdout_file_stream.stream; diff --git a/example/hello_world/hello.zig b/example/hello_world/hello.zig index 9fd050d701..8e65e06a96 100644 --- a/example/hello_world/hello.zig +++ b/example/hello_world/hello.zig @@ -1,6 +1,6 @@ const std = @import("std"); -pub fn main() %void { +pub fn main() !void { // If this program is run without stdout attached, exit with an error. var stdout_file = try std.io.getStdOut(); // If this program encounters pipe failure when printing to stdout, exit diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig index 25d0fdd2ff..59993012fb 100644 --- a/example/mix_o_files/build.zig +++ b/example/mix_o_files/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) %void { +pub fn build(b: &Builder) !void { const obj = b.addObject("base64", "base64.zig"); const exe = b.addCExecutable("test"); diff --git a/example/shared_library/build.zig b/example/shared_library/build.zig index 52d60da819..9a42d3e312 100644 --- a/example/shared_library/build.zig +++ b/example/shared_library/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) %void { +pub fn build(b: &Builder) !void { const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); const exe = b.addCExecutable("test"); diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 7f9e5073a5..3e0c00853e 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -20,7 +20,7 @@ error ZigInstallationNotFound; const default_zig_cache_name = "zig-cache"; -pub fn main() %void { +pub fn main() !void { main2() catch |err| { if (err != error.InvalidCommandLineArguments) { warn("{}\n", @errorName(err)); @@ -48,7 +48,7 @@ fn badArgs(comptime format: []const u8, args: ...) error { return error.InvalidCommandLineArguments; } -pub fn main2() %void { +pub fn main2() !void { const allocator = std.heap.c_allocator; const args = try os.argsAlloc(allocator); @@ -472,7 +472,7 @@ pub fn main2() %void { } } -fn printUsage(stream: &io.OutStream) %void { +fn printUsage(stream: &io.OutStream) !void { try stream.write( \\Usage: zig [command] [options] \\ @@ -548,7 +548,7 @@ fn printUsage(stream: &io.OutStream) %void { ); } -fn printZen() %void { +fn printZen() !void { var stdout_file = try io.getStdErr(); try stdout_file.write( \\ @@ -569,7 +569,7 @@ fn printZen() %void { } /// Caller must free result -fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) %[]u8 { +fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) ![]u8 { if (zig_install_prefix_arg) |zig_install_prefix| { return testZigInstallPrefix(allocator, zig_install_prefix) catch |err| { warn("No Zig installation found at prefix {}: {}\n", zig_install_prefix_arg, @errorName(err)); @@ -585,7 +585,7 @@ fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const } /// Caller must free result -fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) %[]u8 { +fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 { const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig"); errdefer allocator.free(test_zig_dir); @@ -599,7 +599,7 @@ fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) %[]u8 } /// Caller must free result -fn findZigLibDir(allocator: &mem.Allocator) %[]u8 { +fn findZigLibDir(allocator: &mem.Allocator) ![]u8 { const self_exe_path = try os.selfExeDirPath(allocator); defer allocator.free(self_exe_path); diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index f7bf6e0de7..8aec3b06e0 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -198,7 +198,7 @@ pub const Module = struct { self.allocator.destroy(self); } - pub fn build(self: &Module) %void { + pub fn build(self: &Module) !void { if (self.llvm_argv.len != 0) { var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.allocator, [][]const []const u8 { [][]const u8{"zig (LLVM option parsing)"}, self.llvm_argv, }); @@ -263,11 +263,11 @@ pub const Module = struct { } - pub fn link(self: &Module, out_file: ?[]const u8) %void { + pub fn link(self: &Module, out_file: ?[]const u8) !void { warn("TODO link"); } - pub fn addLinkLib(self: &Module, name: []const u8, provided_explicitly: bool) %&LinkLib { + pub fn addLinkLib(self: &Module, name: []const u8, provided_explicitly: bool) !&LinkLib { const is_libc = mem.eql(u8, name, "c"); if (is_libc) { @@ -297,7 +297,7 @@ pub const Module = struct { } }; -fn printError(comptime format: []const u8, args: ...) %void { +fn printError(comptime format: []const u8, args: ...) !void { var stderr_file = try std.io.getStdErr(); var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file); const out_stream = &stderr_file_out_stream.stream; diff --git a/src-self-hosted/parser.zig b/src-self-hosted/parser.zig index 550d29b2c4..c18c746cf4 100644 --- a/src-self-hosted/parser.zig +++ b/src-self-hosted/parser.zig @@ -63,7 +63,7 @@ pub const Parser = struct { NullableField: &?&ast.Node, List: &ArrayList(&ast.Node), - pub fn store(self: &const DestPtr, value: &ast.Node) %void { + pub fn store(self: &const DestPtr, value: &ast.Node) !void { switch (*self) { DestPtr.Field => |ptr| *ptr = value, DestPtr.NullableField => |ptr| *ptr = value, @@ -99,7 +99,7 @@ pub const Parser = struct { /// Returns an AST tree, allocated with the parser's allocator. /// Result should be freed with `freeAst` when done. - pub fn parse(self: &Parser) %Tree { + pub fn parse(self: &Parser) !Tree { var stack = self.initUtilityArrayList(State); defer self.deinitUtilityArrayList(stack); @@ -544,7 +544,7 @@ pub const Parser = struct { } } - fn createRoot(self: &Parser) %&ast.NodeRoot { + fn createRoot(self: &Parser) !&ast.NodeRoot { const node = try self.allocator.create(ast.NodeRoot); *node = ast.NodeRoot { @@ -599,7 +599,7 @@ pub const Parser = struct { return node; } - fn createParamDecl(self: &Parser) %&ast.NodeParamDecl { + fn createParamDecl(self: &Parser) !&ast.NodeParamDecl { const node = try self.allocator.create(ast.NodeParamDecl); *node = ast.NodeParamDecl { @@ -613,7 +613,7 @@ pub const Parser = struct { return node; } - fn createBlock(self: &Parser, begin_token: &const Token) %&ast.NodeBlock { + fn createBlock(self: &Parser, begin_token: &const Token) !&ast.NodeBlock { const node = try self.allocator.create(ast.NodeBlock); *node = ast.NodeBlock { @@ -625,7 +625,7 @@ pub const Parser = struct { return node; } - fn createInfixOp(self: &Parser, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) %&ast.NodeInfixOp { + fn createInfixOp(self: &Parser, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) !&ast.NodeInfixOp { const node = try self.allocator.create(ast.NodeInfixOp); *node = ast.NodeInfixOp { @@ -638,7 +638,7 @@ pub const Parser = struct { return node; } - fn createPrefixOp(self: &Parser, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) %&ast.NodePrefixOp { + fn createPrefixOp(self: &Parser, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) !&ast.NodePrefixOp { const node = try self.allocator.create(ast.NodePrefixOp); *node = ast.NodePrefixOp { @@ -650,7 +650,7 @@ pub const Parser = struct { return node; } - fn createIdentifier(self: &Parser, name_token: &const Token) %&ast.NodeIdentifier { + fn createIdentifier(self: &Parser, name_token: &const Token) !&ast.NodeIdentifier { const node = try self.allocator.create(ast.NodeIdentifier); *node = ast.NodeIdentifier { @@ -660,7 +660,7 @@ pub const Parser = struct { return node; } - fn createIntegerLiteral(self: &Parser, token: &const Token) %&ast.NodeIntegerLiteral { + fn createIntegerLiteral(self: &Parser, token: &const Token) !&ast.NodeIntegerLiteral { const node = try self.allocator.create(ast.NodeIntegerLiteral); *node = ast.NodeIntegerLiteral { @@ -670,7 +670,7 @@ pub const Parser = struct { return node; } - fn createFloatLiteral(self: &Parser, token: &const Token) %&ast.NodeFloatLiteral { + fn createFloatLiteral(self: &Parser, token: &const Token) !&ast.NodeFloatLiteral { const node = try self.allocator.create(ast.NodeFloatLiteral); *node = ast.NodeFloatLiteral { @@ -680,13 +680,13 @@ pub const Parser = struct { return node; } - fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) %&ast.NodeIdentifier { + fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) !&ast.NodeIdentifier { const node = try self.createIdentifier(name_token); try dest_ptr.store(&node.base); return node; } - fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) %&ast.NodeParamDecl { + fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) !&ast.NodeParamDecl { const node = try self.createParamDecl(); try list.append(&node.base); return node; @@ -730,13 +730,13 @@ pub const Parser = struct { return error.ParseError; } - fn expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) %void { + fn expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) !void { if (token.id != id) { return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id)); } } - fn eatToken(self: &Parser, id: @TagType(Token.Id)) %Token { + fn eatToken(self: &Parser, id: @TagType(Token.Id)) !Token { const token = self.getNextToken(); try self.expectToken(token, id); return token; @@ -763,7 +763,7 @@ pub const Parser = struct { indent: usize, }; - pub fn renderAst(self: &Parser, stream: &std.io.OutStream, root_node: &ast.NodeRoot) %void { + pub fn renderAst(self: &Parser, stream: &std.io.OutStream, root_node: &ast.NodeRoot) !void { var stack = self.initUtilityArrayList(RenderAstFrame); defer self.deinitUtilityArrayList(stack); @@ -802,7 +802,7 @@ pub const Parser = struct { Indent: usize, }; - pub fn renderSource(self: &Parser, stream: &std.io.OutStream, root_node: &ast.NodeRoot) %void { + pub fn renderSource(self: &Parser, stream: &std.io.OutStream, root_node: &ast.NodeRoot) !void { var stack = self.initUtilityArrayList(RenderState); defer self.deinitUtilityArrayList(stack); @@ -1038,7 +1038,7 @@ pub const Parser = struct { var fixed_buffer_mem: [100 * 1024]u8 = undefined; -fn testParse(source: []const u8, allocator: &mem.Allocator) %[]u8 { +fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { var padded_source: [0x100]u8 = undefined; std.mem.copy(u8, padded_source[0..source.len], source); padded_source[source.len + 0] = '\n'; @@ -1064,7 +1064,7 @@ error MemoryLeakDetected; // TODO test for memory leaks // TODO test for valid frees -fn testCanonical(source: []const u8) %void { +fn testCanonical(source: []const u8) !void { const needed_alloc_count = x: { // Try it once with unlimited memory, make sure it works var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]); diff --git a/src/analyze.cpp b/src/analyze.cpp index 3b98999081..f574892ebc 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -516,6 +516,7 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, TypeTableEntry *payload_type) { assert(err_set_type->id == TypeTableEntryIdErrorSet); + assert(!type_is_invalid(payload_type)); TypeId type_id = {}; type_id.id = TypeTableEntryIdErrorUnion; @@ -1409,6 +1410,11 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c } TypeTableEntry *specified_return_type = analyze_type_expr(g, child_scope, fn_proto->return_type); + if (type_is_invalid(specified_return_type)) { + fn_type_id.return_type = g->builtin_types.entry_invalid; + return g->builtin_types.entry_invalid; + } + if (fn_proto->auto_err_set) { TypeTableEntry *inferred_err_set_type = get_auto_err_set_type(g, fn_entry); fn_type_id.return_type = get_error_union_type(g, inferred_err_set_type, specified_return_type); @@ -1416,10 +1422,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c fn_type_id.return_type = specified_return_type; } - if (type_is_invalid(fn_type_id.return_type)) { - return g->builtin_types.entry_invalid; - } - if (fn_type_id.cc != CallingConventionUnspecified && !type_allowed_in_extern(g, fn_type_id.return_type)) { add_node_error(g, fn_proto->return_type, buf_sprintf("return type '%s' not allowed in function with calling convention '%s'", diff --git a/src/parser.cpp b/src/parser.cpp index b5b35a9f1a..5034a95567 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -241,7 +241,28 @@ static Token *ast_eat_token(ParseContext *pc, size_t *token_index, TokenId token } /* -TypeExpr = PrefixOpExpression | "var" +ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression +*/ +static AstNode *ast_parse_error_set_expr(ParseContext *pc, size_t *token_index, bool mandatory) { + AstNode *prefix_op_expr = ast_parse_prefix_op_expr(pc, token_index, mandatory); + if (!prefix_op_expr) { + return nullptr; + } + Token *token = &pc->tokens->at(*token_index); + if (token->id == TokenIdBang) { + *token_index += 1; + AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); + node->data.bin_op_expr.op1 = prefix_op_expr; + node->data.bin_op_expr.bin_op = BinOpTypeErrorUnion; + node->data.bin_op_expr.op2 = ast_parse_prefix_op_expr(pc, token_index, true); + return node; + } else { + return prefix_op_expr; + } +} + +/* +TypeExpr = ErrorSetExpr | "var" */ static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -250,7 +271,7 @@ static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool *token_index += 1; return node; } else { - return ast_parse_prefix_op_expr(pc, token_index, mandatory); + return ast_parse_error_set_expr(pc, token_index, mandatory); } } @@ -2346,10 +2367,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token); return node; } - - return node; - } - if (next_token->id == TokenIdBang) { + } else if (next_token->id == TokenIdBang) { *token_index += 1; node->data.fn_proto.auto_err_set = true; next_token = &pc->tokens->at(*token_index); diff --git a/std/array_list.zig b/std/array_list.zig index bc4d3c1d81..b4ac8cc45c 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -59,18 +59,18 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ return result; } - pub fn append(l: &Self, item: &const T) %void { + pub fn append(l: &Self, item: &const T) !void { const new_item_ptr = try l.addOne(); *new_item_ptr = *item; } - pub fn appendSlice(l: &Self, items: []align(A) const T) %void { + pub fn appendSlice(l: &Self, items: []align(A) const T) !void { try l.ensureCapacity(l.len + items.len); mem.copy(T, l.items[l.len..], items); l.len += items.len; } - pub fn resize(l: &Self, new_len: usize) %void { + pub fn resize(l: &Self, new_len: usize) !void { try l.ensureCapacity(new_len); l.len = new_len; } @@ -80,7 +80,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ l.len = new_len; } - pub fn ensureCapacity(l: &Self, new_capacity: usize) %void { + pub fn ensureCapacity(l: &Self, new_capacity: usize) !void { var better_capacity = l.items.len; if (better_capacity >= new_capacity) return; while (true) { @@ -90,7 +90,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ l.items = try l.allocator.alignedRealloc(T, A, l.items, better_capacity); } - pub fn addOne(l: &Self) %&T { + pub fn addOne(l: &Self) !&T { const new_length = l.len + 1; try l.ensureCapacity(new_length); const result = &l.items[l.len]; diff --git a/std/base64.zig b/std/base64.zig index 8cd89b67b5..f91c1e56d6 100644 --- a/std/base64.zig +++ b/std/base64.zig @@ -79,8 +79,6 @@ pub const Base64Encoder = struct { }; pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char); -error InvalidPadding; -error InvalidCharacter; pub const Base64Decoder = struct { /// e.g. 'A' => 0. @@ -111,7 +109,7 @@ pub const Base64Decoder = struct { } /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding. - pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) %usize { + pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) !usize { if (source.len % 4 != 0) return error.InvalidPadding; return calcDecodedSizeExactUnsafe(source, decoder.pad_char); } @@ -119,7 +117,7 @@ pub const Base64Decoder = struct { /// dest.len must be what you get from ::calcSize. /// invalid characters result in error.InvalidCharacter. /// invalid padding results in error.InvalidPadding. - pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) %void { + pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) !void { assert(dest.len == (decoder.calcSize(source) catch unreachable)); assert(source.len % 4 == 0); @@ -163,8 +161,6 @@ pub const Base64Decoder = struct { } }; -error OutputTooSmall; - pub const Base64DecoderWithIgnore = struct { decoder: Base64Decoder, char_is_ignored: [256]bool, @@ -185,7 +181,7 @@ pub const Base64DecoderWithIgnore = struct { } /// If no characters end up being ignored or padding, this will be the exact decoded size. - pub fn calcSizeUpperBound(encoded_len: usize) %usize { + pub fn calcSizeUpperBound(encoded_len: usize) !usize { return @divTrunc(encoded_len, 4) * 3; } @@ -193,7 +189,7 @@ pub const Base64DecoderWithIgnore = struct { /// Invalid padding results in error.InvalidPadding. /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound. /// Returns the number of bytes writen to dest. - pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) %usize { + pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) !usize { const decoder = &decoder_with_ignore.decoder; var src_cursor: usize = 0; @@ -378,7 +374,7 @@ test "base64" { comptime (testBase64() catch unreachable); } -fn testBase64() %void { +fn testBase64() !void { try testAllApis("", ""); try testAllApis("f", "Zg=="); try testAllApis("fo", "Zm8="); @@ -412,7 +408,7 @@ fn testBase64() %void { try testOutputTooSmallError("AAAAAA=="); } -fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void { +fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void { // Base64Encoder { var buffer: [0x100]u8 = undefined; @@ -449,7 +445,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) %void } } -fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) %void { +fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !void { const standard_decoder_ignore_space = Base64DecoderWithIgnore.init( standard_alphabet_chars, standard_pad_char, " "); var buffer: [0x100]u8 = undefined; @@ -458,8 +454,7 @@ fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) %voi assert(mem.eql(u8, decoded[0..written], expected_decoded)); } -error ExpectedError; -fn testError(encoded: []const u8, expected_err: error) %void { +fn testError(encoded: []const u8, expected_err: error) !void { const standard_decoder_ignore_space = Base64DecoderWithIgnore.init( standard_alphabet_chars, standard_pad_char, " "); var buffer: [0x100]u8 = undefined; @@ -475,7 +470,7 @@ fn testError(encoded: []const u8, expected_err: error) %void { } else |err| if (err != expected_err) return err; } -fn testOutputTooSmallError(encoded: []const u8) %void { +fn testOutputTooSmallError(encoded: []const u8) !void { const standard_decoder_ignore_space = Base64DecoderWithIgnore.init( standard_alphabet_chars, standard_pad_char, " "); var buffer: [0x100]u8 = undefined; diff --git a/std/buf_map.zig b/std/buf_map.zig index 15ffe785e6..d7f81cf2cc 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -27,7 +27,7 @@ pub const BufMap = struct { self.hash_map.deinit(); } - pub fn set(self: &BufMap, key: []const u8, value: []const u8) %void { + pub fn set(self: &BufMap, key: []const u8, value: []const u8) !void { if (self.hash_map.get(key)) |entry| { const value_copy = try self.copy(value); errdefer self.free(value_copy); @@ -67,7 +67,7 @@ pub const BufMap = struct { self.hash_map.allocator.free(mut_value); } - fn copy(self: &BufMap, value: []const u8) %[]const u8 { + fn copy(self: &BufMap, value: []const u8) ![]const u8 { const result = try self.hash_map.allocator.alloc(u8, value.len); mem.copy(u8, result, value); return result; diff --git a/std/buf_set.zig b/std/buf_set.zig index 2349c17433..4fa16762b6 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -24,7 +24,7 @@ pub const BufSet = struct { self.hash_map.deinit(); } - pub fn put(self: &BufSet, key: []const u8) %void { + pub fn put(self: &BufSet, key: []const u8) !void { if (self.hash_map.get(key) == null) { const key_copy = try self.copy(key); errdefer self.free(key_copy); @@ -55,7 +55,7 @@ pub const BufSet = struct { self.hash_map.allocator.free(mut_value); } - fn copy(self: &BufSet, value: []const u8) %[]const u8 { + fn copy(self: &BufSet, value: []const u8) ![]const u8 { const result = try self.hash_map.allocator.alloc(u8, value.len); mem.copy(u8, result, value); return result; diff --git a/std/buffer.zig b/std/buffer.zig index 34428aa8e4..e0892d5933 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -12,14 +12,14 @@ pub const Buffer = struct { list: ArrayList(u8), /// Must deinitialize with deinit. - pub fn init(allocator: &Allocator, m: []const u8) %Buffer { + pub fn init(allocator: &Allocator, m: []const u8) !Buffer { var self = try initSize(allocator, m.len); mem.copy(u8, self.list.items, m); return self; } /// Must deinitialize with deinit. - pub fn initSize(allocator: &Allocator, size: usize) %Buffer { + pub fn initSize(allocator: &Allocator, size: usize) !Buffer { var self = initNull(allocator); try self.resize(size); return self; @@ -37,7 +37,7 @@ pub const Buffer = struct { } /// Must deinitialize with deinit. - pub fn initFromBuffer(buffer: &const Buffer) %Buffer { + pub fn initFromBuffer(buffer: &const Buffer) !Buffer { return Buffer.init(buffer.list.allocator, buffer.toSliceConst()); } @@ -80,7 +80,7 @@ pub const Buffer = struct { self.list.items[self.len()] = 0; } - pub fn resize(self: &Buffer, new_len: usize) %void { + pub fn resize(self: &Buffer, new_len: usize) !void { try self.list.resize(new_len + 1); self.list.items[self.len()] = 0; } @@ -93,24 +93,24 @@ pub const Buffer = struct { return self.list.len - 1; } - pub fn append(self: &Buffer, m: []const u8) %void { + pub fn append(self: &Buffer, m: []const u8) !void { const old_len = self.len(); try self.resize(old_len + m.len); mem.copy(u8, self.list.toSlice()[old_len..], m); } // TODO: remove, use OutStream for this - pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) %void { + pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) !void { return fmt.format(self, append, format, args); } // TODO: remove, use OutStream for this - pub fn appendByte(self: &Buffer, byte: u8) %void { + pub fn appendByte(self: &Buffer, byte: u8) !void { return self.appendByteNTimes(byte, 1); } // TODO: remove, use OutStream for this - pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) %void { + pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) !void { var prev_size: usize = self.len(); const new_size = prev_size + count; try self.resize(new_size); @@ -137,7 +137,7 @@ pub const Buffer = struct { return mem.eql(u8, self.list.items[start..l], m); } - pub fn replaceContents(self: &const Buffer, m: []const u8) %void { + pub fn replaceContents(self: &const Buffer, m: []const u8) !void { try self.resize(m.len); mem.copy(u8, self.list.toSlice(), m); } diff --git a/std/build.zig b/std/build.zig index 6c56988896..1a5c48e684 100644 --- a/std/build.zig +++ b/std/build.zig @@ -15,13 +15,6 @@ const BufSet = std.BufSet; const BufMap = std.BufMap; const fmt_lib = std.fmt; -error ExtraArg; -error UncleanExit; -error InvalidStepName; -error DependencyLoopDetected; -error NoCompilerFound; -error NeedAnObject; - pub const Builder = struct { uninstall_tls: TopLevelStep, install_tls: TopLevelStep, @@ -242,7 +235,7 @@ pub const Builder = struct { self.lib_paths.append(path) catch unreachable; } - pub fn make(self: &Builder, step_names: []const []const u8) %void { + pub fn make(self: &Builder, step_names: []const []const u8) !void { var wanted_steps = ArrayList(&Step).init(self.allocator); defer wanted_steps.deinit(); @@ -278,7 +271,7 @@ pub const Builder = struct { return &self.uninstall_tls.step; } - fn makeUninstall(uninstall_step: &Step) %void { + fn makeUninstall(uninstall_step: &Step) !void { const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls); @@ -292,7 +285,7 @@ pub const Builder = struct { // TODO remove empty directories } - fn makeOneStep(self: &Builder, s: &Step) %void { + fn makeOneStep(self: &Builder, s: &Step) !void { if (s.loop_flag) { warn("Dependency loop detected:\n {}\n", s.name); return error.DependencyLoopDetected; @@ -313,7 +306,7 @@ pub const Builder = struct { try s.make(); } - fn getTopLevelStepByName(self: &Builder, name: []const u8) %&Step { + fn getTopLevelStepByName(self: &Builder, name: []const u8) !&Step { for (self.top_level_steps.toSliceConst()) |top_level_step| { if (mem.eql(u8, top_level_step.step.name, name)) { return &top_level_step.step; @@ -548,7 +541,7 @@ pub const Builder = struct { return self.invalid_user_input; } - fn spawnChild(self: &Builder, argv: []const []const u8) %void { + fn spawnChild(self: &Builder, argv: []const []const u8) !void { return self.spawnChildEnvMap(null, &self.env_map, argv); } @@ -595,7 +588,7 @@ pub const Builder = struct { } } - pub fn makePath(self: &Builder, path: []const u8) %void { + pub fn makePath(self: &Builder, path: []const u8) !void { os.makePath(self.allocator, self.pathFromRoot(path)) catch |err| { warn("Unable to create path {}: {}\n", path, @errorName(err)); return err; @@ -630,11 +623,11 @@ pub const Builder = struct { self.installed_files.append(full_path) catch unreachable; } - fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) %void { + fn copyFile(self: &Builder, source_path: []const u8, dest_path: []const u8) !void { return self.copyFileMode(source_path, dest_path, 0o666); } - fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: usize) %void { + fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: usize) !void { if (self.verbose) { warn("cp {} {}\n", source_path, dest_path); } @@ -672,7 +665,7 @@ pub const Builder = struct { } } - pub fn findProgram(self: &Builder, names: []const []const u8, paths: []const []const u8) %[]const u8 { + pub fn findProgram(self: &Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { // TODO report error for ambiguous situations const exe_extension = (Target { .Native = {}}).exeFileExt(); for (self.search_prefixes.toSliceConst()) |search_prefix| { @@ -721,7 +714,7 @@ pub const Builder = struct { return error.FileNotFound; } - pub fn exec(self: &Builder, argv: []const []const u8) %[]u8 { + pub fn exec(self: &Builder, argv: []const []const u8) ![]u8 { const max_output_size = 100 * 1024; const result = try os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size); switch (result.term) { @@ -1180,12 +1173,12 @@ pub const LibExeObjStep = struct { self.disable_libc = disable; } - fn make(step: &Step) %void { + fn make(step: &Step) !void { const self = @fieldParentPtr(LibExeObjStep, "step", step); return if (self.is_zig) self.makeZig() else self.makeC(); } - fn makeZig(self: &LibExeObjStep) %void { + fn makeZig(self: &LibExeObjStep) !void { const builder = self.builder; assert(self.is_zig); @@ -1396,7 +1389,7 @@ pub const LibExeObjStep = struct { } } - fn makeC(self: &LibExeObjStep) %void { + fn makeC(self: &LibExeObjStep) !void { const builder = self.builder; const cc = builder.getCCExe(); @@ -1687,7 +1680,7 @@ pub const TestStep = struct { self.exec_cmd_args = args; } - fn make(step: &Step) %void { + fn make(step: &Step) !void { const self = @fieldParentPtr(TestStep, "step", step); const builder = self.builder; @@ -1796,7 +1789,7 @@ pub const CommandStep = struct { return self; } - fn make(step: &Step) %void { + fn make(step: &Step) !void { const self = @fieldParentPtr(CommandStep, "step", step); const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; @@ -1836,7 +1829,7 @@ const InstallArtifactStep = struct { return self; } - fn make(step: &Step) %void { + fn make(step: &Step) !void { const self = @fieldParentPtr(Self, "step", step); const builder = self.builder; @@ -1868,7 +1861,7 @@ pub const InstallFileStep = struct { }; } - fn make(step: &Step) %void { + fn make(step: &Step) !void { const self = @fieldParentPtr(InstallFileStep, "step", step); try self.builder.copyFile(self.src_path, self.dest_path); } @@ -1889,7 +1882,7 @@ pub const WriteFileStep = struct { }; } - fn make(step: &Step) %void { + fn make(step: &Step) !void { const self = @fieldParentPtr(WriteFileStep, "step", step); const full_path = self.builder.pathFromRoot(self.file_path); const full_path_dir = os.path.dirname(full_path); @@ -1917,7 +1910,7 @@ pub const LogStep = struct { }; } - fn make(step: &Step) %void { + fn make(step: &Step) !void { const self = @fieldParentPtr(LogStep, "step", step); warn("{}", self.data); } @@ -1936,7 +1929,7 @@ pub const RemoveDirStep = struct { }; } - fn make(step: &Step) %void { + fn make(step: &Step) !void { const self = @fieldParentPtr(RemoveDirStep, "step", step); const full_path = self.builder.pathFromRoot(self.dir_path); @@ -1967,7 +1960,7 @@ pub const Step = struct { return init(name, allocator, makeNoOp); } - pub fn make(self: &Step) %void { + pub fn make(self: &Step) !void { if (self.done_flag) return; diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig index 1ebe64d5a4..60610411b5 100644 --- a/std/crypto/throughput_test.zig +++ b/std/crypto/throughput_test.zig @@ -18,7 +18,7 @@ const c = @cImport({ const Mb = 1024 * 1024; -pub fn main() %void { +pub fn main() !void { var stdout_file = try std.io.getStdOut(); var stdout_out_stream = std.io.FileOutStream.init(&stdout_file); const stdout = &stdout_out_stream.stream; diff --git a/std/cstr.zig b/std/cstr.zig index 987c6d3341..86d59b11dd 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -42,7 +42,7 @@ fn testCStrFnsImpl() void { /// Returns a mutable slice with exactly the same size which is guaranteed to /// have a null byte after it. /// Caller owns the returned memory. -pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) %[]u8 { +pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) ![]u8 { const result = try allocator.alloc(u8, slice.len + 1); mem.copy(u8, result, slice); result[slice.len] = 0; @@ -56,7 +56,7 @@ pub const NullTerminated2DArray = struct { /// Takes N lists of strings, concatenates the lists together, and adds a null terminator /// Caller must deinit result - pub fn fromSlices(allocator: &mem.Allocator, slices: []const []const []const u8) %NullTerminated2DArray { + pub fn fromSlices(allocator: &mem.Allocator, slices: []const []const []const u8) !NullTerminated2DArray { var new_len: usize = 1; // 1 for the list null var byte_count: usize = 0; for (slices) |slice| { diff --git a/std/debug/failing_allocator.zig b/std/debug/failing_allocator.zig index cc5a8bc045..f876b7902d 100644 --- a/std/debug/failing_allocator.zig +++ b/std/debug/failing_allocator.zig @@ -28,7 +28,7 @@ pub const FailingAllocator = struct { }; } - fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) %[]u8 { + fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); if (self.index == self.fail_index) { return error.OutOfMemory; @@ -39,7 +39,7 @@ pub const FailingAllocator = struct { return result; } - fn realloc(allocator: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) %[]u8 { + fn realloc(allocator: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(FailingAllocator, "allocator", allocator); if (new_size <= old_mem.len) { self.freed_bytes += old_mem.len - new_size; diff --git a/std/elf.zig b/std/elf.zig index 59e2150c69..2e139870b1 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -6,8 +6,6 @@ const mem = std.mem; const debug = std.debug; const InStream = std.stream.InStream; -error InvalidFormat; - pub const SHT_NULL = 0; pub const SHT_PROGBITS = 1; pub const SHT_SYMTAB = 2; @@ -81,14 +79,14 @@ pub const Elf = struct { prealloc_file: io.File, /// Call close when done. - pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) %void { + pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) !void { try elf.prealloc_file.open(path); try elf.openFile(allocator, &elf.prealloc_file); elf.auto_close_stream = true; } /// Call close when done. - pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &io.File) %void { + pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &io.File) !void { elf.allocator = allocator; elf.in_file = file; elf.auto_close_stream = false; @@ -239,7 +237,7 @@ pub const Elf = struct { elf.in_file.close(); } - pub fn findSection(elf: &Elf, name: []const u8) %?&SectionHeader { + pub fn findSection(elf: &Elf, name: []const u8) !?&SectionHeader { var file_stream = io.FileInStream.init(elf.in_file); const in = &file_stream.stream; @@ -263,7 +261,7 @@ pub const Elf = struct { return null; } - pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) %void { + pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) !void { try elf.in_file.seekTo(elf_section.offset); } }; diff --git a/std/fmt/index.zig b/std/fmt/index.zig index b7ae018bdc..60acf6d0ef 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -24,8 +24,8 @@ const State = enum { // TODO put inside format function and make sure the name a /// Renders fmt string with args, calling output with slices of bytes. /// If `output` returns an error, the error is returned from `format` and /// `output` is not called again. -pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void, - comptime fmt: []const u8, args: ...) %void +pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void, + comptime fmt: []const u8, args: ...) Errors!void { comptime var start_index = 0; comptime var state = State.Start; @@ -58,7 +58,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void, start_index = i; }, '}' => { - try formatValue(args[next_arg], context, output); + try formatValue(args[next_arg], context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; @@ -110,7 +110,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void, }, State.Integer => switch (c) { '}' => { - try formatInt(args[next_arg], radix, uppercase, width, context, output); + try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; @@ -124,7 +124,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void, State.IntegerWidth => switch (c) { '}' => { width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatInt(args[next_arg], radix, uppercase, width, context, output); + try formatInt(args[next_arg], radix, uppercase, width, context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; @@ -134,7 +134,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void, }, State.Float => switch (c) { '}' => { - try formatFloatDecimal(args[next_arg], 0, context, output); + try formatFloatDecimal(args[next_arg], 0, context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; @@ -148,7 +148,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void, State.FloatWidth => switch (c) { '}' => { width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatFloatDecimal(args[next_arg], width, context, output); + try formatFloatDecimal(args[next_arg], width, context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; @@ -159,7 +159,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void, State.BufWidth => switch (c) { '}' => { width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable); - try formatBuf(args[next_arg], width, context, output); + try formatBuf(args[next_arg], width, context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; @@ -169,7 +169,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void, }, State.Character => switch (c) { '}' => { - try formatAsciiChar(args[next_arg], context, output); + try formatAsciiChar(args[next_arg], context, Errors, output); next_arg += 1; state = State.Start; start_index = i + 1; @@ -191,7 +191,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)%void, } } -pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []const u8)%void) %void { +pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { const T = @typeOf(value); switch (@typeId(T)) { builtin.TypeId.Int => { @@ -208,16 +208,16 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons }, builtin.TypeId.Nullable => { if (value) |payload| { - return formatValue(payload, context, output); + return formatValue(payload, context, Errors, output); } else { return output(context, "null"); } }, builtin.TypeId.ErrorUnion => { if (value) |payload| { - return formatValue(payload, context, output); + return formatValue(payload, context, Errors, output); } else |err| { - return formatValue(err, context, output); + return formatValue(err, context, Errors, output); } }, builtin.TypeId.Error => { @@ -240,12 +240,12 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons } } -pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const u8)%void) %void { +pub fn formatAsciiChar(c: u8, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { return output(context, (&c)[0..1]); } pub fn formatBuf(buf: []const u8, width: usize, - context: var, output: fn(@typeOf(context), []const u8)%void) %void + context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { try output(context, buf); @@ -256,7 +256,7 @@ pub fn formatBuf(buf: []const u8, width: usize, } } -pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []const u8)%void) %void { +pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { var x = f64(value); // Errol doesn't handle these special cases. @@ -294,7 +294,7 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons } } -pub fn formatFloatDecimal(value: var, precision: usize, context: var, output: fn(@typeOf(context), []const u8)%void) %void { +pub fn formatFloatDecimal(value: var, precision: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { var x = f64(value); // Errol doesn't handle these special cases. @@ -336,7 +336,7 @@ pub fn formatFloatDecimal(value: var, precision: usize, context: var, output: fn pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, - context: var, output: fn(@typeOf(context), []const u8)%void) %void + context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)errors!void) errors!void { if (@typeOf(value).is_signed) { return formatIntSigned(value, base, uppercase, width, context, output); @@ -346,7 +346,7 @@ pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, } fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, - context: var, output: fn(@typeOf(context), []const u8)%void) %void + context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { const uint = @IntType(false, @typeOf(value).bit_count); if (value < 0) { @@ -367,7 +367,7 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, } fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, - context: var, output: fn(@typeOf(context), []const u8)%void) %void + context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { // max_int_digits accounts for the minus sign. when printing an unsigned // number we don't need to do that. @@ -417,12 +417,12 @@ const FormatIntBuf = struct { out_buf: []u8, index: usize, }; -fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) %void { +fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) !void { mem.copy(u8, context.out_buf[context.index..], bytes); context.index += bytes.len; } -pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) %T { +pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { if (!T.is_signed) return parseUnsigned(T, buf, radix); if (buf.len == 0) @@ -446,7 +446,7 @@ test "fmt.parseInt" { assert(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); } -pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) %T { +pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) !T { var x: T = 0; for (buf) |c| { @@ -458,8 +458,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) %T { return x; } -error InvalidChar; -fn charToDigit(c: u8, radix: u8) %u8 { +fn charToDigit(c: u8, radix: u8) !u8 { const value = switch (c) { '0' ... '9' => c - '0', 'A' ... 'Z' => c - 'A' + 10, @@ -485,28 +484,26 @@ const BufPrintContext = struct { remaining: []u8, }; -error BufferTooSmall; -fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) %void { +fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) !void { if (context.remaining.len < bytes.len) return error.BufferTooSmall; mem.copy(u8, context.remaining, bytes); context.remaining = context.remaining[bytes.len..]; } -pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) %[]u8 { +pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { var context = BufPrintContext { .remaining = buf, }; try format(&context, bufPrintWrite, fmt, args); return buf[0..buf.len - context.remaining.len]; } -pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) %[]u8 { +pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { var size: usize = 0; - // Cannot fail because `countSize` cannot fail. - format(&size, countSize, fmt, args) catch unreachable; + format(&size, error{}, countSize, fmt, args); const buf = try allocator.alloc(u8, size); return bufPrint(buf, fmt, args); } -fn countSize(size: &usize, bytes: []const u8) %void { +fn countSize(size: &usize, bytes: []const u8) void { *size += bytes.len; } @@ -561,13 +558,13 @@ test "fmt.format" { } { var buf1: [32]u8 = undefined; - const value: %i32 = 1234; + const value: error!i32 = 1234; const result = try bufPrint(buf1[0..], "error union: {}\n", value); assert(mem.eql(u8, result, "error union: 1234\n")); } { var buf1: [32]u8 = undefined; - const value: %i32 = error.InvalidChar; + const value: error!i32 = error.InvalidChar; const result = try bufPrint(buf1[0..], "error union: {}\n", value); assert(mem.eql(u8, result, "error union: error.InvalidChar\n")); } diff --git a/std/hash_map.zig b/std/hash_map.zig index 96ec10b933..659783bc84 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -80,7 +80,7 @@ pub fn HashMap(comptime K: type, comptime V: type, } /// Returns the value that was already there. - pub fn put(hm: &Self, key: K, value: &const V) %?V { + pub fn put(hm: &Self, key: K, value: &const V) !?V { if (hm.entries.len == 0) { try hm.initCapacity(16); } @@ -151,7 +151,7 @@ pub fn HashMap(comptime K: type, comptime V: type, }; } - fn initCapacity(hm: &Self, capacity: usize) %void { + fn initCapacity(hm: &Self, capacity: usize) !void { hm.entries = try hm.allocator.alloc(Entry, capacity); hm.size = 0; hm.max_distance_from_start_index = 0; diff --git a/std/heap.zig b/std/heap.zig index f023e7376d..2ff0e665c9 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -9,8 +9,6 @@ const c = std.c; const Allocator = mem.Allocator; -error OutOfMemory; - pub const c_allocator = &c_allocator_state; var c_allocator_state = Allocator { .allocFn = cAlloc, @@ -18,14 +16,14 @@ var c_allocator_state = Allocator { .freeFn = cFree, }; -fn cAlloc(self: &Allocator, n: usize, alignment: u29) %[]u8 { +fn cAlloc(self: &Allocator, n: usize, alignment: u29) ![]u8 { return if (c.malloc(usize(n))) |buf| @ptrCast(&u8, buf)[0..n] else error.OutOfMemory; } -fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) %[]u8 { +fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { const old_ptr = @ptrCast(&c_void, old_mem.ptr); if (c.realloc(old_ptr, new_size)) |buf| { return @ptrCast(&u8, buf)[0..new_size]; @@ -47,7 +45,7 @@ pub const IncrementingAllocator = struct { end_index: usize, heap_handle: if (builtin.os == Os.windows) os.windows.HANDLE else void, - fn init(capacity: usize) %IncrementingAllocator { + fn init(capacity: usize) !IncrementingAllocator { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const p = os.posix; @@ -105,7 +103,7 @@ pub const IncrementingAllocator = struct { return self.bytes.len - self.end_index; } - fn alloc(allocator: &Allocator, n: usize, alignment: u29) %[]u8 { + fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator); const addr = @ptrToInt(&self.bytes[self.end_index]); const rem = @rem(addr, alignment); @@ -120,7 +118,7 @@ pub const IncrementingAllocator = struct { return result; } - fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) %[]u8 { + fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { if (new_size <= old_mem.len) { return old_mem[0..new_size]; } else { diff --git a/std/io.zig b/std/io.zig index 41f32622f8..2fdb564f15 100644 --- a/std/io.zig +++ b/std/io.zig @@ -26,31 +26,7 @@ test "import io tests" { } } -/// The function received invalid input at runtime. An Invalid error means a -/// bug in the program that called the function. -error Invalid; - -error DiskQuota; -error FileTooBig; -error Io; -error NoSpaceLeft; -error BadPerm; -error BrokenPipe; -error BadFd; -error IsDir; -error NotDir; -error SymLinkLoop; -error ProcessFdQuotaExceeded; -error SystemFdQuotaExceeded; -error NameTooLong; -error NoDevice; -error PathNotFound; -error OutOfMemory; -error Unseekable; -error EndOfFile; -error FilePosLargerThanPointerRange; - -pub fn getStdErr() %File { +pub fn getStdErr() !File { const handle = if (is_windows) try os.windowsGetStdHandle(system.STD_ERROR_HANDLE) else if (is_posix) @@ -60,7 +36,7 @@ pub fn getStdErr() %File { return File.openHandle(handle); } -pub fn getStdOut() %File { +pub fn getStdOut() !File { const handle = if (is_windows) try os.windowsGetStdHandle(system.STD_OUTPUT_HANDLE) else if (is_posix) @@ -70,7 +46,7 @@ pub fn getStdOut() %File { return File.openHandle(handle); } -pub fn getStdIn() %File { +pub fn getStdIn() !File { const handle = if (is_windows) try os.windowsGetStdHandle(system.STD_INPUT_HANDLE) else if (is_posix) @@ -94,7 +70,7 @@ pub const FileInStream = struct { }; } - fn readFn(in_stream: &InStream, buffer: []u8) %usize { + fn readFn(in_stream: &InStream, buffer: []u8) !usize { const self = @fieldParentPtr(FileInStream, "stream", in_stream); return self.file.read(buffer); } @@ -114,7 +90,7 @@ pub const FileOutStream = struct { }; } - fn writeFn(out_stream: &OutStream, bytes: []const u8) %void { + fn writeFn(out_stream: &OutStream, bytes: []const u8) !void { const self = @fieldParentPtr(FileOutStream, "stream", out_stream); return self.file.write(bytes); } @@ -129,7 +105,7 @@ pub const File = struct { /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned. /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. /// Call close to clean up. - pub fn openRead(path: []const u8, allocator: ?&mem.Allocator) %File { + pub fn openRead(path: []const u8, allocator: ?&mem.Allocator) !File { if (is_posix) { const flags = system.O_LARGEFILE|system.O_RDONLY; const fd = try os.posixOpen(path, flags, 0, allocator); @@ -144,7 +120,7 @@ pub const File = struct { } /// Calls `openWriteMode` with 0o666 for the mode. - pub fn openWrite(path: []const u8, allocator: ?&mem.Allocator) %File { + pub fn openWrite(path: []const u8, allocator: ?&mem.Allocator) !File { return openWriteMode(path, 0o666, allocator); } @@ -154,7 +130,7 @@ pub const File = struct { /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned. /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. /// Call close to clean up. - pub fn openWriteMode(path: []const u8, mode: usize, allocator: ?&mem.Allocator) %File { + pub fn openWriteMode(path: []const u8, mode: usize, allocator: ?&mem.Allocator) !File { if (is_posix) { const flags = system.O_LARGEFILE|system.O_WRONLY|system.O_CREAT|system.O_CLOEXEC|system.O_TRUNC; const fd = try os.posixOpen(path, flags, mode, allocator); @@ -189,7 +165,7 @@ pub const File = struct { return os.isTty(self.handle); } - pub fn seekForward(self: &File, amount: isize) %void { + pub fn seekForward(self: &File, amount: isize) !void { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const result = system.lseek(self.handle, amount, system.SEEK_CUR); @@ -218,7 +194,7 @@ pub const File = struct { } } - pub fn seekTo(self: &File, pos: usize) %void { + pub fn seekTo(self: &File, pos: usize) !void { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const ipos = try math.cast(isize, pos); @@ -249,7 +225,7 @@ pub const File = struct { } } - pub fn getPos(self: &File) %usize { + pub fn getPos(self: &File) !usize { switch (builtin.os) { Os.linux, Os.macosx, Os.ios => { const result = system.lseek(self.handle, 0, system.SEEK_CUR); @@ -289,7 +265,7 @@ pub const File = struct { } } - pub fn getEndPos(self: &File) %usize { + pub fn getEndPos(self: &File) !usize { if (is_posix) { var stat: system.Stat = undefined; const err = system.getErrno(system.fstat(self.handle, &stat)); @@ -318,7 +294,7 @@ pub const File = struct { } } - pub fn read(self: &File, buffer: []u8) %usize { + pub fn read(self: &File, buffer: []u8) !usize { if (is_posix) { var index: usize = 0; while (index < buffer.len) { @@ -360,7 +336,7 @@ pub const File = struct { } } - fn write(self: &File, bytes: []const u8) %void { + fn write(self: &File, bytes: []const u8) !void { if (is_posix) { try os.posixWrite(self.handle, bytes); } else if (is_windows) { @@ -371,19 +347,16 @@ pub const File = struct { } }; -error StreamTooLong; -error EndOfStream; - pub const InStream = struct { /// Return the number of bytes read. If the number read is smaller than buf.len, it /// means the stream reached the end. Reaching the end of a stream is not an error /// condition. - readFn: fn(self: &InStream, buffer: []u8) %usize, + readFn: fn(self: &InStream, buffer: []u8) !usize, /// Replaces `buffer` contents by reading from the stream until it is finished. /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and /// the contents read from the stream are lost. - pub fn readAllBuffer(self: &InStream, buffer: &Buffer, max_size: usize) %void { + pub fn readAllBuffer(self: &InStream, buffer: &Buffer, max_size: usize) !void { try buffer.resize(0); var actual_buf_len: usize = 0; @@ -408,7 +381,7 @@ pub const InStream = struct { /// memory would be greater than `max_size`, returns `error.StreamTooLong`. /// Caller owns returned memory. /// If this function returns an error, the contents from the stream read so far are lost. - pub fn readAllAlloc(self: &InStream, allocator: &mem.Allocator, max_size: usize) %[]u8 { + pub fn readAllAlloc(self: &InStream, allocator: &mem.Allocator, max_size: usize) ![]u8 { var buf = Buffer.initNull(allocator); defer buf.deinit(); @@ -420,7 +393,7 @@ pub const InStream = struct { /// Does not include the delimiter in the result. /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents /// read from the stream so far are lost. - pub fn readUntilDelimiterBuffer(self: &InStream, buffer: &Buffer, delimiter: u8, max_size: usize) %void { + pub fn readUntilDelimiterBuffer(self: &InStream, buffer: &Buffer, delimiter: u8, max_size: usize) !void { try buf.resize(0); while (true) { @@ -443,7 +416,7 @@ pub const InStream = struct { /// Caller owns returned memory. /// If this function returns an error, the contents from the stream read so far are lost. pub fn readUntilDelimiterAlloc(self: &InStream, allocator: &mem.Allocator, - delimiter: u8, max_size: usize) %[]u8 + delimiter: u8, max_size: usize) ![]u8 { var buf = Buffer.initNull(allocator); defer buf.deinit(); @@ -455,43 +428,43 @@ pub const InStream = struct { /// Returns the number of bytes read. If the number read is smaller than buf.len, it /// means the stream reached the end. Reaching the end of a stream is not an error /// condition. - pub fn read(self: &InStream, buffer: []u8) %usize { + pub fn read(self: &InStream, buffer: []u8) !usize { return self.readFn(self, buffer); } /// Same as `read` but end of stream returns `error.EndOfStream`. - pub fn readNoEof(self: &InStream, buf: []u8) %void { + pub fn readNoEof(self: &InStream, buf: []u8) !void { const amt_read = try self.read(buf); if (amt_read < buf.len) return error.EndOfStream; } /// Reads 1 byte from the stream or returns `error.EndOfStream`. - pub fn readByte(self: &InStream) %u8 { + pub fn readByte(self: &InStream) !u8 { var result: [1]u8 = undefined; try self.readNoEof(result[0..]); return result[0]; } /// Same as `readByte` except the returned byte is signed. - pub fn readByteSigned(self: &InStream) %i8 { + pub fn readByteSigned(self: &InStream) !i8 { return @bitCast(i8, try self.readByte()); } - pub fn readIntLe(self: &InStream, comptime T: type) %T { + pub fn readIntLe(self: &InStream, comptime T: type) !T { return self.readInt(builtin.Endian.Little, T); } - pub fn readIntBe(self: &InStream, comptime T: type) %T { + pub fn readIntBe(self: &InStream, comptime T: type) !T { return self.readInt(builtin.Endian.Big, T); } - pub fn readInt(self: &InStream, endian: builtin.Endian, comptime T: type) %T { + pub fn readInt(self: &InStream, endian: builtin.Endian, comptime T: type) !T { var bytes: [@sizeOf(T)]u8 = undefined; try self.readNoEof(bytes[0..]); return mem.readInt(bytes, T, endian); } - pub fn readVarInt(self: &InStream, endian: builtin.Endian, comptime T: type, size: usize) %T { + pub fn readVarInt(self: &InStream, endian: builtin.Endian, comptime T: type, size: usize) !T { assert(size <= @sizeOf(T)); assert(size <= 8); var input_buf: [8]u8 = undefined; @@ -504,22 +477,23 @@ pub const InStream = struct { }; pub const OutStream = struct { - writeFn: fn(self: &OutStream, bytes: []const u8) %void, + // TODO allow specifying the error set + writeFn: fn(self: &OutStream, bytes: []const u8) error!void, - pub fn print(self: &OutStream, comptime format: []const u8, args: ...) %void { - return std.fmt.format(self, self.writeFn, format, args); + pub fn print(self: &OutStream, comptime format: []const u8, args: ...) !void { + return std.fmt.format(self, error, self.writeFn, format, args); } - pub fn write(self: &OutStream, bytes: []const u8) %void { + pub fn write(self: &OutStream, bytes: []const u8) !void { return self.writeFn(self, bytes); } - pub fn writeByte(self: &OutStream, byte: u8) %void { + pub fn writeByte(self: &OutStream, byte: u8) !void { const slice = (&byte)[0..1]; return self.writeFn(self, slice); } - pub fn writeByteNTimes(self: &OutStream, byte: u8, n: usize) %void { + pub fn writeByteNTimes(self: &OutStream, byte: u8, n: usize) !void { const slice = (&byte)[0..1]; var i: usize = 0; while (i < n) : (i += 1) { @@ -532,19 +506,19 @@ pub const OutStream = struct { /// a fixed size buffer of size `std.os.max_noalloc_path_len` is an attempted solution. If the fixed /// size buffer is too small, and the provided allocator is null, `error.NameTooLong` is returned. /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. -pub fn writeFile(path: []const u8, data: []const u8, allocator: ?&mem.Allocator) %void { +pub fn writeFile(path: []const u8, data: []const u8, allocator: ?&mem.Allocator) !void { var file = try File.openWrite(path, allocator); defer file.close(); try file.write(data); } /// On success, caller owns returned buffer. -pub fn readFileAlloc(path: []const u8, allocator: &mem.Allocator) %[]u8 { +pub fn readFileAlloc(path: []const u8, allocator: &mem.Allocator) ![]u8 { return readFileAllocExtra(path, allocator, 0); } /// On success, caller owns returned buffer. /// Allocates extra_len extra bytes at the end of the file buffer, which are uninitialized. -pub fn readFileAllocExtra(path: []const u8, allocator: &mem.Allocator, extra_len: usize) %[]u8 { +pub fn readFileAllocExtra(path: []const u8, allocator: &mem.Allocator, extra_len: usize) ![]u8 { var file = try File.openRead(path, allocator); defer file.close(); @@ -589,7 +563,7 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize) type { }; } - fn readFn(in_stream: &InStream, dest: []u8) %usize { + fn readFn(in_stream: &InStream, dest: []u8) !usize { const self = @fieldParentPtr(Self, "stream", in_stream); var dest_index: usize = 0; @@ -652,7 +626,7 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize) type { }; } - pub fn flush(self: &Self) %void { + pub fn flush(self: &Self) !void { if (self.index == 0) return; @@ -660,7 +634,7 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize) type { self.index = 0; } - fn writeFn(out_stream: &OutStream, bytes: []const u8) %void { + fn writeFn(out_stream: &OutStream, bytes: []const u8) !void { const self = @fieldParentPtr(Self, "stream", out_stream); if (bytes.len >= self.buffer.len) { @@ -698,7 +672,7 @@ pub const BufferOutStream = struct { }; } - fn writeFn(out_stream: &OutStream, bytes: []const u8) %void { + fn writeFn(out_stream: &OutStream, bytes: []const u8) !void { const self = @fieldParentPtr(BufferOutStream, "stream", out_stream); return self.buffer.append(bytes); } diff --git a/std/linked_list.zig b/std/linked_list.zig index ffd68d5147..a6ab093341 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -190,7 +190,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Returns: /// A pointer to the new node. - pub fn allocateNode(list: &Self, allocator: &Allocator) %&Node { + pub fn allocateNode(list: &Self, allocator: &Allocator) !&Node { comptime assert(!isIntrusive()); return allocator.create(Node); } @@ -213,7 +213,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na /// /// Returns: /// A pointer to the new node. - pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) %&Node { + pub fn createNode(list: &Self, data: &const T, allocator: &Allocator) !&Node { comptime assert(!isIntrusive()); var node = try list.allocateNode(allocator); *node = Node.init(data); diff --git a/std/math/index.zig b/std/math/index.zig index 64d24a4dfd..9c5e35ef17 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -191,30 +191,26 @@ test "math.max" { assert(max(i32(-1), i32(2)) == 2); } -error Overflow; -pub fn mul(comptime T: type, a: T, b: T) %T { +pub fn mul(comptime T: type, a: T, b: T) !T { var answer: T = undefined; return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer; } -error Overflow; -pub fn add(comptime T: type, a: T, b: T) %T { +pub fn add(comptime T: type, a: T, b: T) !T { var answer: T = undefined; return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer; } -error Overflow; -pub fn sub(comptime T: type, a: T, b: T) %T { +pub fn sub(comptime T: type, a: T, b: T) !T { var answer: T = undefined; return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer; } -pub fn negate(x: var) %@typeOf(x) { +pub fn negate(x: var) !@typeOf(x) { return sub(@typeOf(x), 0, x); } -error Overflow; -pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) %T { +pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T { var answer: T = undefined; return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer; } @@ -323,8 +319,7 @@ fn testOverflow() void { } -error Overflow; -pub fn absInt(x: var) %@typeOf(x) { +pub fn absInt(x: var) !@typeOf(x) { const T = @typeOf(x); comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt comptime assert(T.is_signed); // must pass a signed integer to absInt @@ -347,9 +342,7 @@ fn testAbsInt() void { pub const absFloat = @import("fabs.zig").fabs; -error DivisionByZero; -error Overflow; -pub fn divTrunc(comptime T: type, numerator: T, denominator: T) %T { +pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); if (denominator == 0) return error.DivisionByZero; @@ -372,9 +365,7 @@ fn testDivTrunc() void { assert((divTrunc(f32, -5.0, 3.0) catch unreachable) == -1.0); } -error DivisionByZero; -error Overflow; -pub fn divFloor(comptime T: type, numerator: T, denominator: T) %T { +pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); if (denominator == 0) return error.DivisionByZero; @@ -397,10 +388,7 @@ fn testDivFloor() void { assert((divFloor(f32, -5.0, 3.0) catch unreachable) == -2.0); } -error DivisionByZero; -error Overflow; -error UnexpectedRemainder; -pub fn divExact(comptime T: type, numerator: T, denominator: T) %T { +pub fn divExact(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); if (denominator == 0) return error.DivisionByZero; @@ -428,9 +416,7 @@ fn testDivExact() void { if (divExact(f32, 5.0, 2.0)) |_| unreachable else |err| assert(err == error.UnexpectedRemainder); } -error DivisionByZero; -error NegativeDenominator; -pub fn mod(comptime T: type, numerator: T, denominator: T) %T { +pub fn mod(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); if (denominator == 0) return error.DivisionByZero; @@ -455,9 +441,7 @@ fn testMod() void { if (mod(f32, 10, 0)) |_| unreachable else |err| assert(err == error.DivisionByZero); } -error DivisionByZero; -error NegativeDenominator; -pub fn rem(comptime T: type, numerator: T, denominator: T) %T { +pub fn rem(comptime T: type, numerator: T, denominator: T) !T { @setRuntimeSafety(false); if (denominator == 0) return error.DivisionByZero; @@ -505,8 +489,7 @@ test "math.absCast" { /// Returns the negation of the integer parameter. /// Result is a signed integer. -error Overflow; -pub fn negateCast(x: var) %@IntType(true, @typeOf(x).bit_count) { +pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) { if (@typeOf(x).is_signed) return negate(x); @@ -532,8 +515,7 @@ test "math.negateCast" { /// Cast an integer to a different integer type. If the value doesn't fit, /// return an error. -error Overflow; -pub fn cast(comptime T: type, x: var) %T { +pub fn cast(comptime T: type, x: var) !T { comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer if (x > @maxValue(T)) { return error.Overflow; diff --git a/std/mem.zig b/std/mem.zig index 86bf5e2f3d..30b45e7629 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -4,13 +4,13 @@ const assert = debug.assert; const math = std.math; const builtin = @import("builtin"); -error OutOfMemory; - pub const Allocator = struct { + const Errors = error {OutOfMemory}; + /// Allocate byte_count bytes and return them in a slice, with the /// slice's pointer aligned at least to alignment bytes. /// The returned newly allocated memory is undefined. - allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) %[]u8, + allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Errors![]u8, /// If `new_byte_count > old_mem.len`: /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn. @@ -21,12 +21,12 @@ pub const Allocator = struct { /// * alignment <= alignment of old_mem.ptr /// /// The returned newly allocated memory is undefined. - reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) %[]u8, + reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Errors![]u8, /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` freeFn: fn (self: &Allocator, old_mem: []u8) void, - fn create(self: &Allocator, comptime T: type) %&T { + fn create(self: &Allocator, comptime T: type) !&T { const slice = try self.alloc(T, 1); return &slice[0]; } @@ -35,7 +35,7 @@ pub const Allocator = struct { self.free(ptr[0..1]); } - fn alloc(self: &Allocator, comptime T: type, n: usize) %[]T { + fn alloc(self: &Allocator, comptime T: type, n: usize) ![]T { return self.alignedAlloc(T, @alignOf(T), n); } @@ -51,7 +51,7 @@ pub const Allocator = struct { return ([]align(alignment) T)(@alignCast(alignment, byte_slice)); } - fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) %[]T { + fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) ![]T { return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n); } @@ -123,7 +123,7 @@ pub const FixedBufferAllocator = struct { }; } - fn alloc(allocator: &Allocator, n: usize, alignment: u29) %[]u8 { + fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); const addr = @ptrToInt(&self.buffer[self.end_index]); const rem = @rem(addr, alignment); @@ -138,7 +138,7 @@ pub const FixedBufferAllocator = struct { return result; } - fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) %[]u8 { + fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { if (new_size <= old_mem.len) { return old_mem[0..new_size]; } else { @@ -197,7 +197,7 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool { } /// Copies ::m to newly allocated memory. Caller is responsible to free it. -pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) %[]T { +pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T { const new_buf = try allocator.alloc(T, m.len); copy(T, new_buf, m); return new_buf; @@ -428,7 +428,7 @@ const SplitIterator = struct { /// Naively combines a series of strings with a separator. /// Allocates memory for the result, which must be freed by the caller. -pub fn join(allocator: &Allocator, sep: u8, strings: ...) %[]u8 { +pub fn join(allocator: &Allocator, sep: u8, strings: ...) ![]u8 { comptime assert(strings.len >= 1); var total_strings_len: usize = strings.len; // 1 sep per string { diff --git a/std/net.zig b/std/net.zig index 4fbfd9b9aa..1140b6449b 100644 --- a/std/net.zig +++ b/std/net.zig @@ -5,19 +5,10 @@ const endian = std.endian; // TODO don't trust this file, it bit rotted. start over -error SigInterrupt; -error Io; -error TimedOut; -error ConnectionReset; -error ConnectionRefused; -error OutOfMemory; -error NotSocket; -error BadFd; - const Connection = struct { socket_fd: i32, - pub fn send(c: Connection, buf: []const u8) %usize { + pub fn send(c: Connection, buf: []const u8) !usize { const send_ret = linux.sendto(c.socket_fd, buf.ptr, buf.len, 0, null, 0); const send_err = linux.getErrno(send_ret); switch (send_err) { @@ -31,7 +22,7 @@ const Connection = struct { } } - pub fn recv(c: Connection, buf: []u8) %[]u8 { + pub fn recv(c: Connection, buf: []u8) ![]u8 { const recv_ret = linux.recvfrom(c.socket_fd, buf.ptr, buf.len, 0, null, null); const recv_err = linux.getErrno(recv_ret); switch (recv_err) { @@ -48,7 +39,7 @@ const Connection = struct { } } - pub fn close(c: Connection) %void { + pub fn close(c: Connection) !void { switch (linux.getErrno(linux.close(c.socket_fd))) { 0 => return, linux.EBADF => unreachable, @@ -66,7 +57,7 @@ const Address = struct { sort_key: i32, }; -pub fn lookup(hostname: []const u8, out_addrs: []Address) %[]Address { +pub fn lookup(hostname: []const u8, out_addrs: []Address) ![]Address { if (hostname.len == 0) { unreachable; // TODO @@ -75,7 +66,7 @@ pub fn lookup(hostname: []const u8, out_addrs: []Address) %[]Address { unreachable; // TODO } -pub fn connectAddr(addr: &Address, port: u16) %Connection { +pub fn connectAddr(addr: &Address, port: u16) !Connection { const socket_ret = linux.socket(addr.family, linux.SOCK_STREAM, linux.PROTO_tcp); const socket_err = linux.getErrno(socket_ret); if (socket_err > 0) { @@ -118,7 +109,7 @@ pub fn connectAddr(addr: &Address, port: u16) %Connection { }; } -pub fn connect(hostname: []const u8, port: u16) %Connection { +pub fn connect(hostname: []const u8, port: u16) !Connection { var addrs_buf: [1]Address = undefined; const addrs_slice = try lookup(hostname, addrs_buf[0..]); const main_addr = &addrs_slice[0]; @@ -126,9 +117,7 @@ pub fn connect(hostname: []const u8, port: u16) %Connection { return connectAddr(main_addr, port); } -error InvalidIpLiteral; - -pub fn parseIpLiteral(buf: []const u8) %Address { +pub fn parseIpLiteral(buf: []const u8) !Address { return error.InvalidIpLiteral; } @@ -146,12 +135,7 @@ fn hexDigit(c: u8) u8 { } } -error InvalidChar; -error Overflow; -error JunkAtEnd; -error Incomplete; - -fn parseIp6(buf: []const u8) %Address { +fn parseIp6(buf: []const u8) !Address { var result: Address = undefined; result.family = linux.AF_INET6; result.scope_id = 0; @@ -232,7 +216,7 @@ fn parseIp6(buf: []const u8) %Address { return error.Incomplete; } -fn parseIp4(buf: []const u8) %u32 { +fn parseIp4(buf: []const u8) !u32 { var result: u32 = undefined; const out_ptr = ([]u8)((&result)[0..1]); diff --git a/std/os/child_process.zig b/std/os/child_process.zig index f4709ce75a..c86db9d69c 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -13,10 +13,6 @@ const builtin = @import("builtin"); const Os = builtin.Os; const LinkedList = std.LinkedList; -error PermissionDenied; -error ProcessNotFound; -error InvalidName; - var children_nodes = LinkedList(&ChildProcess).init(); const is_windows = builtin.os == Os.windows; @@ -74,7 +70,7 @@ pub const ChildProcess = struct { /// First argument in argv is the executable. /// On success must call deinit. - pub fn init(argv: []const []const u8, allocator: &mem.Allocator) %&ChildProcess { + pub fn init(argv: []const []const u8, allocator: &mem.Allocator) !&ChildProcess { const child = try allocator.create(ChildProcess); errdefer allocator.destroy(child); @@ -103,7 +99,7 @@ pub const ChildProcess = struct { return child; } - pub fn setUserName(self: &ChildProcess, name: []const u8) %void { + pub fn setUserName(self: &ChildProcess, name: []const u8) !void { const user_info = try os.getUserInfo(name); self.uid = user_info.uid; self.gid = user_info.gid; @@ -111,7 +107,7 @@ pub const ChildProcess = struct { /// onTerm can be called before `spawn` returns. /// On success must call `kill` or `wait`. - pub fn spawn(self: &ChildProcess) %void { + pub fn spawn(self: &ChildProcess) !void { if (is_windows) { return self.spawnWindows(); } else { @@ -119,13 +115,13 @@ pub const ChildProcess = struct { } } - pub fn spawnAndWait(self: &ChildProcess) %Term { + pub fn spawnAndWait(self: &ChildProcess) !Term { try self.spawn(); return self.wait(); } /// Forcibly terminates child process and then cleans up all resources. - pub fn kill(self: &ChildProcess) %Term { + pub fn kill(self: &ChildProcess) !Term { if (is_windows) { return self.killWindows(1); } else { @@ -133,7 +129,7 @@ pub const ChildProcess = struct { } } - pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) %Term { + pub fn killWindows(self: &ChildProcess, exit_code: windows.UINT) !Term { if (self.term) |term| { self.cleanupStreams(); return term; @@ -149,7 +145,7 @@ pub const ChildProcess = struct { return ??self.term; } - pub fn killPosix(self: &ChildProcess) %Term { + pub fn killPosix(self: &ChildProcess) !Term { block_SIGCHLD(); defer restore_SIGCHLD(); @@ -172,7 +168,7 @@ pub const ChildProcess = struct { } /// Blocks until child process terminates and then cleans up all resources. - pub fn wait(self: &ChildProcess) %Term { + pub fn wait(self: &ChildProcess) !Term { if (is_windows) { return self.waitWindows(); } else { @@ -220,7 +216,7 @@ pub const ChildProcess = struct { }; } - fn waitWindows(self: &ChildProcess) %Term { + fn waitWindows(self: &ChildProcess) !Term { if (self.term) |term| { self.cleanupStreams(); return term; @@ -230,7 +226,7 @@ pub const ChildProcess = struct { return ??self.term; } - fn waitPosix(self: &ChildProcess) %Term { + fn waitPosix(self: &ChildProcess) !Term { block_SIGCHLD(); defer restore_SIGCHLD(); @@ -247,7 +243,7 @@ pub const ChildProcess = struct { self.allocator.destroy(self); } - fn waitUnwrappedWindows(self: &ChildProcess) %void { + fn waitUnwrappedWindows(self: &ChildProcess) !void { const result = os.windowsWaitSingle(self.handle, windows.INFINITE); self.term = (%Term)(x: { @@ -295,7 +291,7 @@ pub const ChildProcess = struct { if (self.stderr) |*stderr| { stderr.close(); self.stderr = null; } } - fn cleanupAfterWait(self: &ChildProcess, status: i32) %Term { + fn cleanupAfterWait(self: &ChildProcess, status: i32) !Term { children_nodes.remove(&self.llnode); defer { @@ -331,7 +327,7 @@ pub const ChildProcess = struct { ; } - fn spawnPosix(self: &ChildProcess) %void { + fn spawnPosix(self: &ChildProcess) !void { // TODO atomically set a flag saying that we already did this install_SIGCHLD_handler(); @@ -440,7 +436,7 @@ pub const ChildProcess = struct { if (self.stderr_behavior == StdIo.Pipe) { os.close(stderr_pipe[1]); } } - fn spawnWindows(self: &ChildProcess) %void { + fn spawnWindows(self: &ChildProcess) !void { const saAttr = windows.SECURITY_ATTRIBUTES { .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), .bInheritHandle = windows.TRUE, @@ -623,7 +619,7 @@ pub const ChildProcess = struct { if (self.stdout_behavior == StdIo.Pipe) { os.close(??g_hChildStd_OUT_Wr); } } - fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) %void { + fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void { switch (stdio) { StdIo.Pipe => try os.posixDup2(pipe_fd, std_fileno), StdIo.Close => os.close(std_fileno), @@ -655,7 +651,7 @@ fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ? /// Caller must dealloc. /// Guarantees a null byte at result[result.len]. -fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) %[]u8 { +fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) ![]u8 { var buf = try Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -700,7 +696,7 @@ fn windowsDestroyPipe(rd: ?windows.HANDLE, wr: ?windows.HANDLE) void { // a namespace field lookup const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES; -fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) %void { +fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void { if (windows.CreatePipe(rd, wr, sattr, 0) == 0) { const err = windows.GetLastError(); return switch (err) { @@ -709,7 +705,7 @@ fn windowsMakePipe(rd: &windows.HANDLE, wr: &windows.HANDLE, sattr: &const SECUR } } -fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.DWORD) %void { +fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.DWORD) !void { if (windows.SetHandleInformation(h, mask, flags) == 0) { const err = windows.GetLastError(); return switch (err) { @@ -718,7 +714,7 @@ fn windowsSetHandleInfo(h: windows.HANDLE, mask: windows.DWORD, flags: windows.D } } -fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) %void { +fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void { var rd_h: windows.HANDLE = undefined; var wr_h: windows.HANDLE = undefined; try windowsMakePipe(&rd_h, &wr_h, sattr); @@ -728,7 +724,7 @@ fn windowsMakePipeIn(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const S *wr = wr_h; } -fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) %void { +fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const SECURITY_ATTRIBUTES) !void { var rd_h: windows.HANDLE = undefined; var wr_h: windows.HANDLE = undefined; try windowsMakePipe(&rd_h, &wr_h, sattr); @@ -738,7 +734,7 @@ fn windowsMakePipeOut(rd: &?windows.HANDLE, wr: &?windows.HANDLE, sattr: &const *wr = wr_h; } -fn makePipe() %[2]i32 { +fn makePipe() ![2]i32 { var fds: [2]i32 = undefined; const err = posix.getErrno(posix.pipe(&fds)); if (err > 0) { @@ -764,13 +760,13 @@ fn forkChildErrReport(fd: i32, err: error) noreturn { const ErrInt = @IntType(false, @sizeOf(error) * 8); -fn writeIntFd(fd: i32, value: ErrInt) %void { +fn writeIntFd(fd: i32, value: ErrInt) !void { var bytes: [@sizeOf(ErrInt)]u8 = undefined; mem.writeInt(bytes[0..], value, builtin.endian); os.posixWrite(fd, bytes[0..]) catch return error.SystemResources; } -fn readIntFd(fd: i32) %ErrInt { +fn readIntFd(fd: i32) !ErrInt { var bytes: [@sizeOf(ErrInt)]u8 = undefined; os.posixRead(fd, bytes[0..]) catch return error.SystemResources; return mem.readInt(bytes[0..], ErrInt, builtin.endian); diff --git a/std/os/get_user_id.zig b/std/os/get_user_id.zig index 68cb268169..11410ffa64 100644 --- a/std/os/get_user_id.zig +++ b/std/os/get_user_id.zig @@ -9,7 +9,7 @@ pub const UserInfo = struct { }; /// POSIX function which gets a uid from username. -pub fn getUserInfo(name: []const u8) %UserInfo { +pub fn getUserInfo(name: []const u8) !UserInfo { return switch (builtin.os) { Os.linux, Os.macosx, Os.ios => posixGetUserInfo(name), else => @compileError("Unsupported OS"), @@ -24,13 +24,10 @@ const State = enum { ReadGroupId, }; -error UserNotFound; -error CorruptPasswordFile; - // TODO this reads /etc/passwd. But sometimes the user/id mapping is in something else // like NIS, AD, etc. See `man nss` or look at an strace for `id myuser`. -pub fn posixGetUserInfo(name: []const u8) %UserInfo { +pub fn posixGetUserInfo(name: []const u8) !UserInfo { var in_stream = try io.InStream.open("/etc/passwd", null); defer in_stream.close(); diff --git a/std/os/index.zig b/std/os/index.zig index 6592facf51..3e39d5c2ea 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1470,8 +1470,6 @@ test "std.os" { } -error Unexpected; - // TODO make this a build variable that you can set const unexpected_error_tracing = false; diff --git a/std/os/linux.zig b/std/os/linux.zig index 04cdafb0ac..549b31727d 100644 --- a/std/os/linux.zig +++ b/std/os/linux.zig @@ -720,7 +720,7 @@ pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags: // error SystemResources; // error Io; // -// pub fn if_nametoindex(name: []u8) %u32 { +// pub fn if_nametoindex(name: []u8) !u32 { // var ifr: ifreq = undefined; // // if (name.len >= ifr.ifr_name.len) { diff --git a/std/os/path.zig b/std/os/path.zig index eb95f83f45..5a7428c220 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -32,7 +32,7 @@ pub fn isSep(byte: u8) bool { /// Naively combines a series of paths with the native path seperator. /// Allocates memory for the result, which must be freed by the caller. -pub fn join(allocator: &Allocator, paths: ...) %[]u8 { +pub fn join(allocator: &Allocator, paths: ...) ![]u8 { if (is_windows) { return joinWindows(allocator, paths); } else { @@ -40,11 +40,11 @@ pub fn join(allocator: &Allocator, paths: ...) %[]u8 { } } -pub fn joinWindows(allocator: &Allocator, paths: ...) %[]u8 { +pub fn joinWindows(allocator: &Allocator, paths: ...) ![]u8 { return mem.join(allocator, sep_windows, paths); } -pub fn joinPosix(allocator: &Allocator, paths: ...) %[]u8 { +pub fn joinPosix(allocator: &Allocator, paths: ...) ![]u8 { return mem.join(allocator, sep_posix, paths); } @@ -313,7 +313,7 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool { } /// Converts the command line arguments into a slice and calls `resolveSlice`. -pub fn resolve(allocator: &Allocator, args: ...) %[]u8 { +pub fn resolve(allocator: &Allocator, args: ...) ![]u8 { var paths: [args.len][]const u8 = undefined; comptime var arg_i = 0; inline while (arg_i < args.len) : (arg_i += 1) { @@ -323,7 +323,7 @@ pub fn resolve(allocator: &Allocator, args: ...) %[]u8 { } /// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`. -pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) %[]u8 { +pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) ![]u8 { if (is_windows) { return resolveWindows(allocator, paths); } else { @@ -337,7 +337,7 @@ pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) %[]u8 { /// If all paths are relative it uses the current working directory as a starting point. /// Each drive has its own current working directory. /// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters. -pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) %[]u8 { +pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { assert(is_windows); // resolveWindows called on non windows can't use getCwd return os.getCwd(allocator); @@ -520,7 +520,7 @@ pub fn resolveWindows(allocator: &Allocator, paths: []const []const u8) %[]u8 { /// It resolves "." and "..". /// The result does not have a trailing path separator. /// If all paths are relative it uses the current working directory as a starting point. -pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) %[]u8 { +pub fn resolvePosix(allocator: &Allocator, paths: []const []const u8) ![]u8 { if (paths.len == 0) { assert(!is_windows); // resolvePosix called on windows can't use getCwd return os.getCwd(allocator); @@ -890,7 +890,7 @@ fn testBasenameWindows(input: []const u8, expected_output: []const u8) void { /// resolve to the same path (after calling `resolve` on each), a zero-length /// string is returned. /// On Windows this canonicalizes the drive to a capital letter and paths to `\\`. -pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 { +pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 { if (is_windows) { return relativeWindows(allocator, from, to); } else { @@ -898,7 +898,7 @@ pub fn relative(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 { } } -pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 { +pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 { const resolved_from = try resolveWindows(allocator, [][]const u8{from}); defer allocator.free(resolved_from); @@ -971,7 +971,7 @@ pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8) return []u8{}; } -pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) %[]u8 { +pub fn relativePosix(allocator: &Allocator, from: []const u8, to: []const u8) ![]u8 { const resolved_from = try resolvePosix(allocator, [][]const u8{from}); defer allocator.free(resolved_from); @@ -1066,18 +1066,11 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons assert(mem.eql(u8, result, expected_output)); } -error AccessDenied; -error FileNotFound; -error NotSupported; -error NotDir; -error NameTooLong; -error SymLinkLoop; -error InputOutput; /// Return the canonicalized absolute pathname. /// Expands all symbolic links and resolves references to `.`, `..`, and /// extra `/` characters in ::pathname. /// Caller must deallocate result. -pub fn real(allocator: &Allocator, pathname: []const u8) %[]u8 { +pub fn real(allocator: &Allocator, pathname: []const u8) ![]u8 { switch (builtin.os) { Os.windows => { const pathname_buf = try allocator.alloc(u8, pathname.len + 1); diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index a8a2e3fcd5..08da342e0b 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -6,11 +6,7 @@ const mem = std.mem; const BufMap = std.BufMap; const cstr = std.cstr; -error WaitAbandoned; -error WaitTimeOut; -error Unexpected; - -pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) %void { +pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) !void { const result = windows.WaitForSingleObject(handle, milliseconds); return switch (result) { windows.WAIT_ABANDONED => error.WaitAbandoned, @@ -30,12 +26,7 @@ pub fn windowsClose(handle: windows.HANDLE) void { assert(windows.CloseHandle(handle) != 0); } -error SystemResources; -error OperationAborted; -error IoPending; -error BrokenPipe; - -pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) %void { +pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) !void { if (windows.WriteFile(handle, @ptrCast(&const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { const err = windows.GetLastError(); return switch (err) { @@ -75,9 +66,6 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { mem.indexOf(u16, name_wide, []u16{'-','p','t','y'}) != null; } -error SharingViolation; -error PipeBusy; - /// `file_path` may need to be copied in memory to add a null terminating byte. In this case /// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed /// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned. @@ -120,7 +108,7 @@ pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_m } /// Caller must free result. -pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) %[]u8 { +pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) ![]u8 { // count bytes needed const bytes_needed = x: { var bytes_needed: usize = 1; // 1 for the final null byte @@ -151,8 +139,7 @@ pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) return result; } -error DllNotFound; -pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) %windows.HMODULE { +pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) !windows.HMODULE { const padded_buff = try cstr.addNullByte(allocator, dll_path); defer allocator.free(padded_buff); return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound; diff --git a/std/special/build_file_template.zig b/std/special/build_file_template.zig index 282759bedb..2edfdadf50 100644 --- a/std/special/build_file_template.zig +++ b/std/special/build_file_template.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) %void { +pub fn build(b: &Builder) !void { const mode = b.standardReleaseOptions(); const exe = b.addExecutable("YOUR_NAME_HERE", "src/main.zig"); exe.setBuildMode(mode); diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index e1648276aa..198199b941 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -8,9 +8,7 @@ const mem = std.mem; const ArrayList = std.ArrayList; const warn = std.debug.warn; -error InvalidArgs; - -pub fn main() %void { +pub fn main() !void { var arg_it = os.args(); // TODO use a more general purpose allocator here @@ -125,7 +123,7 @@ pub fn main() %void { }; } -fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) %void { +fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) !void { // run the build script to collect the options if (!already_ran_build) { builder.setInstallPrefix(null); @@ -188,7 +186,7 @@ fn usageAndErr(builder: &Builder, already_ran_build: bool, out_stream: &io.OutSt return error.InvalidArgs; } -fn unwrapArg(arg: %[]u8) %[]u8 { +fn unwrapArg(arg: %[]u8) ![]u8 { return arg catch |err| { warn("Unable to parse command line: {}\n", err); return err; diff --git a/std/unicode.zig b/std/unicode.zig index 235ac4ceac..df62e9162f 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -1,11 +1,9 @@ const std = @import("./index.zig"); -error Utf8InvalidStartByte; - /// Given the first byte of a UTF-8 codepoint, /// returns a number 1-4 indicating the total length of the codepoint in bytes. /// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte. -pub fn utf8ByteSequenceLength(first_byte: u8) %u3 { +pub fn utf8ByteSequenceLength(first_byte: u8) !u3 { if (first_byte < 0b10000000) return u3(1); if (first_byte & 0b11100000 == 0b11000000) return u3(2); if (first_byte & 0b11110000 == 0b11100000) return u3(3); @@ -13,16 +11,11 @@ pub fn utf8ByteSequenceLength(first_byte: u8) %u3 { return error.Utf8InvalidStartByte; } -error Utf8OverlongEncoding; -error Utf8ExpectedContinuation; -error Utf8EncodesSurrogateHalf; -error Utf8CodepointTooLarge; - /// Decodes the UTF-8 codepoint encoded in the given slice of bytes. /// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable. /// If you already know the length at comptime, you can call one of /// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function. -pub fn utf8Decode(bytes: []const u8) %u32 { +pub fn utf8Decode(bytes: []const u8) !u32 { return switch (bytes.len) { 1 => u32(bytes[0]), 2 => utf8Decode2(bytes), @@ -31,7 +24,7 @@ pub fn utf8Decode(bytes: []const u8) %u32 { else => unreachable, }; } -pub fn utf8Decode2(bytes: []const u8) %u32 { +pub fn utf8Decode2(bytes: []const u8) !u32 { std.debug.assert(bytes.len == 2); std.debug.assert(bytes[0] & 0b11100000 == 0b11000000); var value: u32 = bytes[0] & 0b00011111; @@ -44,7 +37,7 @@ pub fn utf8Decode2(bytes: []const u8) %u32 { return value; } -pub fn utf8Decode3(bytes: []const u8) %u32 { +pub fn utf8Decode3(bytes: []const u8) !u32 { std.debug.assert(bytes.len == 3); std.debug.assert(bytes[0] & 0b11110000 == 0b11100000); var value: u32 = bytes[0] & 0b00001111; @@ -62,7 +55,7 @@ pub fn utf8Decode3(bytes: []const u8) %u32 { return value; } -pub fn utf8Decode4(bytes: []const u8) %u32 { +pub fn utf8Decode4(bytes: []const u8) !u32 { std.debug.assert(bytes.len == 4); std.debug.assert(bytes[0] & 0b11111000 == 0b11110000); var value: u32 = bytes[0] & 0b00000111; @@ -85,7 +78,6 @@ pub fn utf8Decode4(bytes: []const u8) %u32 { return value; } -error UnexpectedEof; test "valid utf8" { testValid("\x00", 0x0); testValid("\x20", 0x20); @@ -161,7 +153,7 @@ fn testValid(bytes: []const u8, expected_codepoint: u32) void { std.debug.assert((testDecode(bytes) catch unreachable) == expected_codepoint); } -fn testDecode(bytes: []const u8) %u32 { +fn testDecode(bytes: []const u8) !u32 { const length = try utf8ByteSequenceLength(bytes[0]); if (bytes.len < length) return error.UnexpectedEof; std.debug.assert(bytes.len == length); diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 2455179c89..994bf2a8b1 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -32,7 +32,6 @@ fn funcWithConstPtrPtr(x: &const &i32) void { **x += 1; } -error ItBroke; test "explicit cast from integer to error type" { testCastIntToErr(error.ItBroke); comptime testCastIntToErr(error.ItBroke); @@ -110,11 +109,11 @@ test "return null from fn() %?&T" { const b = returnNullLitFromMaybeTypeErrorRef(); assert((try a) == null and (try b) == null); } -fn returnNullFromMaybeTypeErrorRef() %?&A { +fn returnNullFromMaybeTypeErrorRef() !?&A { const a: ?&A = null; return a; } -fn returnNullLitFromMaybeTypeErrorRef() %?&A { +fn returnNullLitFromMaybeTypeErrorRef() !?&A { return null; } @@ -170,7 +169,7 @@ fn testCastZeroArrayToErrSliceMut() void { assert((gimmeErrOrSlice() catch unreachable).len == 0); } -fn gimmeErrOrSlice() %[]u8 { +fn gimmeErrOrSlice() ![]u8 { return []u8{}; } @@ -188,7 +187,7 @@ test "peer type resolution: [0]u8, []const u8, and %[]u8" { assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } } -fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) %[]u8 { +fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) ![]u8 { if (a) { return []u8{}; } @@ -238,14 +237,13 @@ test "peer type resolution: error and [N]T" { comptime assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); } -error BadValue; -//fn testPeerErrorAndArray(x: u8) %[]const u8 { +//fn testPeerErrorAndArray(x: u8) ![]const u8 { // return switch (x) { // 0x00 => "OK", // else => error.BadValue, // }; //} -fn testPeerErrorAndArray2(x: u8) %[]const u8 { +fn testPeerErrorAndArray2(x: u8) ![]const u8 { return switch (x) { 0x00 => "OK", 0x01 => "OKK", diff --git a/test/cases/defer.zig b/test/cases/defer.zig index 6490ec0acd..a989af18c2 100644 --- a/test/cases/defer.zig +++ b/test/cases/defer.zig @@ -3,9 +3,7 @@ const assert = @import("std").debug.assert; var result: [3]u8 = undefined; var index: usize = undefined; -error FalseNotAllowed; - -fn runSomeErrorDefers(x: bool) %bool { +fn runSomeErrorDefers(x: bool) !bool { index = 0; defer {result[index] = 'a'; index += 1;} errdefer {result[index] = 'b'; index += 1;} diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index 9abc15d129..9fc8104cf8 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -6,7 +6,7 @@ const ET = union(enum) { SINT: i32, UINT: u32, - pub fn print(a: &const ET, buf: []u8) %usize { + pub fn print(a: &const ET, buf: []u8) !usize { return switch (*a) { ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), diff --git a/test/cases/error.zig b/test/cases/error.zig index 47e5f25be9..3564ce60aa 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -1,16 +1,16 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; -pub fn foo() %i32 { +pub fn foo() !i32 { const x = try bar(); return x + 1; } -pub fn bar() %i32 { +pub fn bar() !i32 { return 13; } -pub fn baz() %i32 { +pub fn baz() !i32 { const y = foo() catch 1234; return y + 1; } @@ -19,7 +19,6 @@ test "error wrapping" { assert((baz() catch unreachable) == 15); } -error ItBroke; fn gimmeItBroke() []const u8 { return @errorName(error.ItBroke); } @@ -28,8 +27,6 @@ test "@errorName" { assert(mem.eql(u8, @errorName(error.AnError), "AnError")); assert(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); } -error AnError; -error ALongerErrorName; test "error values" { @@ -37,16 +34,11 @@ test "error values" { const b = i32(error.err2); assert(a != b); } -error err1; -error err2; test "redefinition of error values allowed" { shouldBeNotEqual(error.AnError, error.SecondError); } -error AnError; -error AnError; -error SecondError; fn shouldBeNotEqual(a: error, b: error) void { if (a == b) unreachable; } @@ -58,8 +50,7 @@ test "error binary operator" { assert(a == 3); assert(b == 10); } -error ItBroke; -fn errBinaryOperatorG(x: bool) %isize { +fn errBinaryOperatorG(x: bool) !isize { return if (x) error.ItBroke else isize(10); } @@ -75,11 +66,11 @@ test "error return in assignment" { doErrReturnInAssignment() catch unreachable; } -fn doErrReturnInAssignment() %void { +fn doErrReturnInAssignment() !void { var x : i32 = undefined; x = try makeANonErr(); } -fn makeANonErr() %i32 { +fn makeANonErr() !i32 { return 1; } diff --git a/test/cases/ir_block_deps.zig b/test/cases/ir_block_deps.zig index 44dfa330a1..a762e6e01f 100644 --- a/test/cases/ir_block_deps.zig +++ b/test/cases/ir_block_deps.zig @@ -1,6 +1,6 @@ const assert = @import("std").debug.assert; -fn foo(id: u64) %i32 { +fn foo(id: u64) !i32 { return switch (id) { 1 => getErrInt(), 2 => { @@ -13,8 +13,6 @@ fn foo(id: u64) %i32 { fn getErrInt() %i32 { return 0; } -error ItBroke; - test "ir block deps" { assert((foo(1) catch unreachable) == 0); assert((foo(2) catch unreachable) == 0); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 85757efbb8..314d7d518e 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -262,7 +262,7 @@ test "generic malloc free" { memFree(u8, a); } const some_mem : [100]u8 = undefined; -fn memAlloc(comptime T: type, n: usize) %[]T { +fn memAlloc(comptime T: type, n: usize) ![]T { return @ptrCast(&T, &some_mem[0])[0..n]; } fn memFree(comptime T: type, memory: []T) void { } @@ -419,7 +419,7 @@ test "cast slice to u8 slice" { test "pointer to void return type" { testPointerToVoidReturnType() catch unreachable; } -fn testPointerToVoidReturnType() %void { +fn testPointerToVoidReturnType() !void { const a = testPointerToVoidReturnType2(); return *a; } diff --git a/test/cases/switch.zig b/test/cases/switch.zig index f742057e68..156333c571 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -225,7 +225,7 @@ fn switchWithUnreachable(x: i32) i32 { return 10; } -fn return_a_number() %i32 { +fn return_a_number() !i32 { return 1; } diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig index 2da5b5f09a..aa9deea28d 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/cases/switch_prong_err_enum.zig @@ -2,19 +2,17 @@ const assert = @import("std").debug.assert; var read_count: u64 = 0; -fn readOnce() %u64 { +fn readOnce() !u64 { read_count += 1; return read_count; } -error InvalidDebugInfo; - const FormValue = union(enum) { Address: u64, Other: bool, }; -fn doThing(form_id: u64) %FormValue { +fn doThing(form_id: u64) !FormValue { return switch (form_id) { 17 => FormValue { .Address = try readOnce() }, else => error.InvalidDebugInfo, diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/cases/switch_prong_implicit_cast.zig index 300b8c21a1..335feeef43 100644 --- a/test/cases/switch_prong_implicit_cast.zig +++ b/test/cases/switch_prong_implicit_cast.zig @@ -5,9 +5,7 @@ const FormValue = union(enum) { Two: bool, }; -error Whatever; - -fn foo(id: u64) %FormValue { +fn foo(id: u64) !FormValue { return switch (id) { 2 => FormValue { .Two = true }, 1 => FormValue { .One = {} }, diff --git a/test/cases/try.zig b/test/cases/try.zig index 00259721b1..6878a7c98d 100644 --- a/test/cases/try.zig +++ b/test/cases/try.zig @@ -17,10 +17,7 @@ fn tryOnErrorUnionImpl() void { assert(x == 11); } -error ItBroke; -error NoMem; -error CrappedOut; -fn returnsTen() %i32 { +fn returnsTen() !i32 { return 10; } @@ -32,7 +29,7 @@ test "try without vars" { assert(result2 == 1); } -fn failIfTrue(ok: bool) %void { +fn failIfTrue(ok: bool) !void { if (ok) { return error.ItBroke; } else { diff --git a/test/cases/while.zig b/test/cases/while.zig index 5970e742ea..0b3d2660ed 100644 --- a/test/cases/while.zig +++ b/test/cases/while.zig @@ -50,7 +50,7 @@ fn runContinueAndBreakTest() void { test "return with implicit cast from while loop" { returnWithImplicitCastFromWhileLoopTest() catch unreachable; } -fn returnWithImplicitCastFromWhileLoopTest() %void { +fn returnWithImplicitCastFromWhileLoopTest() !void { while (true) { return; } @@ -116,8 +116,7 @@ test "while with error union condition" { } var numbers_left: i32 = undefined; -error OutOfNumbers; -fn getNumberOrErr() %i32 { +fn getNumberOrErr() !i32 { return if (numbers_left == 0) error.OutOfNumbers else x: { @@ -205,7 +204,6 @@ fn testContinueOuter() void { fn returnNull() ?i32 { return null; } fn returnMaybe(x: i32) ?i32 { return x; } -error YouWantedAnError; fn returnError() %i32 { return error.YouWantedAnError; } fn returnSuccess(x: i32) %i32 { return x; } fn returnFalse() bool { return false; } diff --git a/test/compare_output.zig b/test/compare_output.zig index 6e379c8d1e..7467bcc089 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -15,7 +15,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\use @import("std").io; \\use @import("foo.zig"); \\ - \\pub fn main() %void { + \\pub fn main() !void { \\ privateFunction(); \\ const stdout = &(FileOutStream.init(&(getStdOut() catch unreachable)).stream); \\ stdout.print("OK 2\n") catch unreachable; @@ -49,7 +49,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\use @import("foo.zig"); \\use @import("bar.zig"); \\ - \\pub fn main() %void { + \\pub fn main() !void { \\ foo_function(); \\ bar_function(); \\} @@ -89,7 +89,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { var tc = cases.create("two files use import each other", \\use @import("a.zig"); \\ - \\pub fn main() %void { + \\pub fn main() !void { \\ ok(); \\} , "OK\n"); @@ -118,7 +118,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { cases.add("hello world without libc", \\const io = @import("std").io; \\ - \\pub fn main() %void { + \\pub fn main() !void { \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); \\ stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable; \\} @@ -268,7 +268,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\const z = io.stdin_fileno; \\const x : @typeOf(y) = 1234; \\const y : u16 = 5678; - \\pub fn main() %void { + \\pub fn main() !void { \\ var x_local : i32 = print_ok(x); \\} \\fn print_ok(val: @typeOf(x)) @typeOf(foo) { @@ -351,7 +351,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ fn method(b: &const Bar) bool { return true; } \\}; \\ - \\pub fn main() %void { + \\pub fn main() !void { \\ const bar = Bar {.field2 = 13,}; \\ const foo = Foo {.field1 = bar,}; \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); @@ -367,7 +367,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { cases.add("defer with only fallthrough", \\const io = @import("std").io; - \\pub fn main() %void { + \\pub fn main() !void { \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); \\ stdout.print("before\n") catch unreachable; \\ defer stdout.print("defer1\n") catch unreachable; @@ -380,7 +380,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { cases.add("defer with return", \\const io = @import("std").io; \\const os = @import("std").os; - \\pub fn main() %void { + \\pub fn main() !void { \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); \\ stdout.print("before\n") catch unreachable; \\ defer stdout.print("defer1\n") catch unreachable; @@ -394,10 +394,10 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { cases.add("errdefer and it fails", \\const io = @import("std").io; - \\pub fn main() %void { + \\pub fn main() !void { \\ do_test() catch return; \\} - \\fn do_test() %void { + \\fn do_test() !void { \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); \\ stdout.print("before\n") catch unreachable; \\ defer stdout.print("defer1\n") catch unreachable; @@ -407,17 +407,17 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ stdout.print("after\n") catch unreachable; \\} \\error IToldYouItWouldFail; - \\fn its_gonna_fail() %void { + \\fn its_gonna_fail() !void { \\ return error.IToldYouItWouldFail; \\} , "before\ndeferErr\ndefer1\n"); cases.add("errdefer and it passes", \\const io = @import("std").io; - \\pub fn main() %void { + \\pub fn main() !void { \\ do_test() catch return; \\} - \\fn do_test() %void { + \\fn do_test() !void { \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); \\ stdout.print("before\n") catch unreachable; \\ defer stdout.print("defer1\n") catch unreachable; @@ -434,7 +434,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\const foo_txt = @embedFile("foo.txt"); \\const io = @import("std").io; \\ - \\pub fn main() %void { + \\pub fn main() !void { \\ const stdout = &(io.FileOutStream.init(&(io.getStdOut() catch unreachable)).stream); \\ stdout.print(foo_txt) catch unreachable; \\} @@ -452,7 +452,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\const os = std.os; \\const allocator = std.debug.global_allocator; \\ - \\pub fn main() %void { + \\pub fn main() !void { \\ var args_it = os.args(); \\ var stdout_file = try io.getStdOut(); \\ var stdout_adapter = io.FileOutStream.init(&stdout_file); @@ -493,7 +493,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\const os = std.os; \\const allocator = std.debug.global_allocator; \\ - \\pub fn main() %void { + \\pub fn main() !void { \\ var args_it = os.args(); \\ var stdout_file = try io.getStdOut(); \\ var stdout_adapter = io.FileOutStream.init(&stdout_file); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 223646e767..0c02fccbfe 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1383,7 +1383,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { , ".tmp_source.zig:6:13: error: cannot assign to constant"); cases.add("return from defer expression", - \\pub fn testTrickyDefer() %void { + \\pub fn testTrickyDefer() !void { \\ defer canFail() catch {}; \\ \\ defer try canFail(); @@ -1970,7 +1970,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\fn foo1(args: ...) void {} \\fn foo2(args: ...) void {} \\ - \\pub fn main() %void { + \\pub fn main() !void { \\ foos[0](); \\} , @@ -1982,7 +1982,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\fn foo1(arg: var) void {} \\fn foo2(arg: var) void {} \\ - \\pub fn main() %void { + \\pub fn main() !void { \\ foos[0](true); \\} , diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index 3fb2a91544..20b905b59b 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -5,7 +5,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} - \\pub fn main() %void { + \\pub fn main() !void { \\ @panic("oh no"); \\} ); @@ -14,7 +14,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} - \\pub fn main() %void { + \\pub fn main() !void { \\ const a = []i32{1, 2, 3, 4}; \\ baz(bar(a)); \\} @@ -29,7 +29,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = add(65530, 10); \\ if (x == 0) return error.Whatever; \\} @@ -43,7 +43,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = sub(10, 20); \\ if (x == 0) return error.Whatever; \\} @@ -57,7 +57,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = mul(300, 6000); \\ if (x == 0) return error.Whatever; \\} @@ -71,7 +71,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = neg(-32768); \\ if (x == 32767) return error.Whatever; \\} @@ -85,7 +85,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = div(-32768, -1); \\ if (x == 32767) return error.Whatever; \\} @@ -99,7 +99,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = shl(-16385, 1); \\ if (x == 0) return error.Whatever; \\} @@ -113,7 +113,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = shl(0b0010111111111111, 3); \\ if (x == 0) return error.Whatever; \\} @@ -127,7 +127,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = shr(-16385, 1); \\ if (x == 0) return error.Whatever; \\} @@ -141,7 +141,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = shr(0b0010111111111111, 3); \\ if (x == 0) return error.Whatever; \\} @@ -155,7 +155,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = div0(999, 0); \\} \\fn div0(a: i32, b: i32) i32 { @@ -168,7 +168,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = divExact(10, 3); \\ if (x == 0) return error.Whatever; \\} @@ -182,7 +182,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = widenSlice([]u8{1, 2, 3, 4, 5}); \\ if (x.len == 0) return error.Whatever; \\} @@ -196,7 +196,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = shorten_cast(200); \\ if (x == 0) return error.Whatever; \\} @@ -210,7 +210,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ const x = unsigned_cast(-10); \\ if (x == 0) return error.Whatever; \\} @@ -227,10 +227,10 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(0); // test failed \\} \\error Whatever; - \\pub fn main() %void { + \\pub fn main() !void { \\ bar() catch unreachable; \\} - \\fn bar() %void { + \\fn bar() !void { \\ return error.Whatever; \\} ); @@ -239,7 +239,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} - \\pub fn main() %void { + \\pub fn main() !void { \\ _ = bar(9999); \\} \\fn bar(x: u32) error { @@ -252,7 +252,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ @import("std").os.exit(126); \\} \\error Wrong; - \\pub fn main() %void { + \\pub fn main() !void { \\ var array align(4) = []u32{0x11111111, 0x11111111}; \\ const bytes = ([]u8)(array[0..]); \\ if (foo(bytes) != 0x11111111) return error.Wrong; @@ -274,7 +274,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ int: u32, \\}; \\ - \\pub fn main() %void { + \\pub fn main() !void { \\ var f = Foo { .int = 42 }; \\ bar(&f); \\} diff --git a/test/standalone/brace_expansion/build.zig b/test/standalone/brace_expansion/build.zig index af3160a8c6..1a72a9a0bc 100644 --- a/test/standalone/brace_expansion/build.zig +++ b/test/standalone/brace_expansion/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) %void { +pub fn build(b: &Builder) !void { const main = b.addTest("main.zig"); main.setBuildMode(b.standardReleaseOptions()); diff --git a/test/standalone/brace_expansion/main.zig b/test/standalone/brace_expansion/main.zig index 995da8bb56..3596086138 100644 --- a/test/standalone/brace_expansion/main.zig +++ b/test/standalone/brace_expansion/main.zig @@ -6,9 +6,6 @@ const assert = debug.assert; const Buffer = std.Buffer; const ArrayList = std.ArrayList; -error InvalidInput; -error OutOfMem; - const Token = union(enum) { Word: []const u8, OpenBrace, @@ -19,7 +16,7 @@ const Token = union(enum) { var global_allocator: &mem.Allocator = undefined; -fn tokenize(input:[] const u8) %ArrayList(Token) { +fn tokenize(input:[] const u8) !ArrayList(Token) { const State = enum { Start, Word, @@ -71,7 +68,7 @@ const Node = union(enum) { Combine: []Node, }; -fn parse(tokens: &const ArrayList(Token), token_index: &usize) %Node { +fn parse(tokens: &const ArrayList(Token), token_index: &usize) !Node { const first_token = tokens.items[*token_index]; *token_index += 1; @@ -107,7 +104,7 @@ fn parse(tokens: &const ArrayList(Token), token_index: &usize) %Node { } } -fn expandString(input: []const u8, output: &Buffer) %void { +fn expandString(input: []const u8, output: &Buffer) !void { const tokens = try tokenize(input); if (tokens.len == 1) { return output.resize(0); @@ -135,7 +132,7 @@ fn expandString(input: []const u8, output: &Buffer) %void { } } -fn expandNode(node: &const Node, output: &ArrayList(Buffer)) %void { +fn expandNode(node: &const Node, output: &ArrayList(Buffer)) !void { assert(output.len == 0); switch (*node) { Node.Scalar => |scalar| { @@ -172,7 +169,7 @@ fn expandNode(node: &const Node, output: &ArrayList(Buffer)) %void { } } -pub fn main() %void { +pub fn main() !void { var stdin_file = try io.getStdIn(); var stdout_file = try io.getStdOut(); diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig index 8cf4cfae77..940d1bc24d 100644 --- a/test/standalone/issue_339/build.zig +++ b/test/standalone/issue_339/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) %void { +pub fn build(b: &Builder) !void { const obj = b.addObject("test", "test.zig"); const test_step = b.step("test", "Test the program"); diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig index 2caea10bfe..2cc63bf30b 100644 --- a/test/standalone/pkg_import/build.zig +++ b/test/standalone/pkg_import/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) %void { +pub fn build(b: &Builder) !void { const exe = b.addExecutable("test", "test.zig"); exe.addPackagePath("my_pkg", "pkg.zig"); diff --git a/test/standalone/pkg_import/test.zig b/test/standalone/pkg_import/test.zig index 9575671f2a..b9a0f50776 100644 --- a/test/standalone/pkg_import/test.zig +++ b/test/standalone/pkg_import/test.zig @@ -1,6 +1,6 @@ const my_pkg = @import("my_pkg"); const assert = @import("std").debug.assert; -pub fn main() %void { +pub fn main() !void { assert(my_pkg.add(10, 20) == 30); } diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig index 912058d5a9..452966789c 100644 --- a/test/standalone/use_alias/build.zig +++ b/test/standalone/use_alias/build.zig @@ -1,6 +1,6 @@ const Builder = @import("std").build.Builder; -pub fn build(b: &Builder) %void { +pub fn build(b: &Builder) !void { b.addCIncludePath("."); const main = b.addTest("main.zig"); diff --git a/test/tests.zig b/test/tests.zig index 554bb6a2bc..c12775aa85 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -45,9 +45,6 @@ const test_targets = []TestTarget { }, }; -error TestFailed; -error CompilationIncorrectlySucceeded; - const max_stdout_size = 1 * 1024 * 1024; // 1 MB pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) &build.Step { @@ -248,7 +245,7 @@ pub const CompareOutputContext = struct { return ptr; } - fn make(step: &build.Step) %void { + fn make(step: &build.Step) !void { const self = @fieldParentPtr(RunCompareOutputStep, "step", step); const b = self.context.b; @@ -337,7 +334,7 @@ pub const CompareOutputContext = struct { return ptr; } - fn make(step: &build.Step) %void { + fn make(step: &build.Step) !void { const self = @fieldParentPtr(RuntimeSafetyRunStep, "step", step); const b = self.context.b; @@ -563,7 +560,7 @@ pub const CompileErrorContext = struct { return ptr; } - fn make(step: &build.Step) %void { + fn make(step: &build.Step) !void { const self = @fieldParentPtr(CompileCmpOutputStep, "step", step); const b = self.context.b; @@ -847,7 +844,7 @@ pub const TranslateCContext = struct { return ptr; } - fn make(step: &build.Step) %void { + fn make(step: &build.Step) !void { const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step); const b = self.context.b; @@ -1045,7 +1042,7 @@ pub const GenHContext = struct { return ptr; } - fn make(step: &build.Step) %void { + fn make(step: &build.Step) !void { const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); const b = self.context.b; -- cgit v1.2.3 From 13b36d458f6ba45fdda1c1510e056a7012fb3fff Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 1 Feb 2018 10:23:25 -0500 Subject: *WIP* error sets - fix implicit cast --- TODO | 3 ++- src/analyze.cpp | 47 ++++++++++++++++++++++++++++------- src/analyze.hpp | 3 ++- src/ir.cpp | 73 +++++++++++++++++++++++++++---------------------------- std/fmt/index.zig | 2 +- std/io.zig | 8 +++--- std/mem.zig | 4 +-- std/os/index.zig | 7 +++++- 8 files changed, 92 insertions(+), 55 deletions(-) (limited to 'src/analyze.cpp') diff --git a/TODO b/TODO index 47272138fe..838aaba485 100644 --- a/TODO +++ b/TODO @@ -2,4 +2,5 @@ sed -i 's/\(\bfn .*) \)%\(.*{\)$/\1!\2/g' $(find .. -name "*.zig") comptime assert(error{} ! i32 == i32); - +// TODO this is an explicit cast and should actually coerce the type + erorr set casting diff --git a/src/analyze.cpp b/src/analyze.cpp index f574892ebc..f69b131626 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1274,7 +1274,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { zig_unreachable(); } -static TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry) { +TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry) { TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); buf_resize(&err_set_type->name, 0); buf_appendf(&err_set_type->name, "%s.errors", buf_ptr(&fn_entry->symbol_name)); @@ -3366,7 +3366,7 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *so g->tld_ref_source_node_stack.pop(); } -bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) { +bool types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type) { if (expected_type == actual_type) return true; @@ -3379,7 +3379,7 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count && actual_type->data.pointer.alignment >= expected_type->data.pointer.alignment) { - return types_match_const_cast_only(expected_type->data.pointer.child_type, + return types_match_const_cast_only(g, expected_type->data.pointer.child_type, actual_type->data.pointer.child_type); } @@ -3397,7 +3397,7 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * actual_ptr_type->data.pointer.unaligned_bit_count == expected_ptr_type->data.pointer.unaligned_bit_count && actual_ptr_type->data.pointer.alignment >= expected_ptr_type->data.pointer.alignment) { - return types_match_const_cast_only(expected_ptr_type->data.pointer.child_type, + return types_match_const_cast_only(g, expected_ptr_type->data.pointer.child_type, actual_ptr_type->data.pointer.child_type); } } @@ -3406,7 +3406,7 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { - return types_match_const_cast_only( + return types_match_const_cast_only(g, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type); } @@ -3415,14 +3415,43 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) { - return types_match_const_cast_only( + return types_match_const_cast_only(g, expected_type->data.error_union.payload_type, actual_type->data.error_union.payload_type) && - types_match_const_cast_only( + types_match_const_cast_only(g, expected_type->data.error_union.err_set_type, actual_type->data.error_union.err_set_type); } + // error set + if (expected_type->id == TypeTableEntryIdErrorSet && + actual_type->id == TypeTableEntryIdErrorSet) + { + TypeTableEntry *contained_set = actual_type; + TypeTableEntry *container_set = expected_type; + + if (container_set == g->builtin_types.entry_global_error_set || + container_set->data.error_set.infer_fn != nullptr) + { + return true; + } + + ErrorTableEntry **errors = allocate(g->errors_by_index.length); + for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; + errors[error_entry->value] = error_entry; + } + for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + return false; + } + } + free(errors); + return true; + } + // fn if (expected_type->id == TypeTableEntryIdFn && actual_type->id == TypeTableEntryIdFn) @@ -3441,7 +3470,7 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * } if (!expected_type->data.fn.is_generic && actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable && - !types_match_const_cast_only( + !types_match_const_cast_only(g, expected_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type)) { @@ -3460,7 +3489,7 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i]; - if (!types_match_const_cast_only(actual_param_info->type, expected_param_info->type)) { + if (!types_match_const_cast_only(g, actual_param_info->type, expected_param_info->type)) { return false; } diff --git a/src/analyze.hpp b/src/analyze.hpp index 06c582fb77..9af259b10d 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -46,7 +46,7 @@ bool type_has_bits(TypeTableEntry *type_entry); ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code); -bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type); +bool types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type); VariableTableEntry *find_variable(CodeGen *g, Scope *orig_context, Buf *name); Tld *find_decl(CodeGen *g, Scope *scope, Buf *name); void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *source_node); @@ -189,5 +189,6 @@ ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name); TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g); void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry); +TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry); #endif diff --git a/src/ir.cpp b/src/ir.cpp index cdae4b1511..e6899da48b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6375,7 +6375,7 @@ enum ImplicitCastMatchResult { static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type, TypeTableEntry *actual_type, IrInstruction *value) { - if (types_match_const_cast_only(expected_type, actual_type)) { + if (types_match_const_cast_only(ira->codegen, expected_type, actual_type)) { return ImplicitCastMatchResultYes; } @@ -6412,13 +6412,6 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, return ImplicitCastMatchResultYes; } - // implicit conversion from error set to another error set - if (expected_type->id == TypeTableEntryIdErrorSet && - actual_type->id == TypeTableEntryIdErrorSet) - { - return ImplicitCastMatchResultYes; - } - // implicit conversion from T to U!?T if (expected_type->id == TypeTableEntryIdErrorUnion && expected_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe && @@ -6463,7 +6456,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) { return ImplicitCastMatchResultYes; } @@ -6482,7 +6475,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *array_type = actual_type->data.pointer.child_type; if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ptr_type->data.pointer.child_type, array_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, array_type->data.array.child_type)) { return ImplicitCastMatchResultYes; } @@ -6498,7 +6491,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, expected_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) { return ImplicitCastMatchResultYes; } @@ -6513,7 +6506,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, expected_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) { return ImplicitCastMatchResultYes; } @@ -6593,7 +6586,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicitly take a const pointer to something if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (types_match_const_cast_only(expected_type, const_ptr_actual)) { + if (types_match_const_cast_only(ira->codegen, expected_type, const_ptr_actual)) { return ImplicitCastMatchResultYes; } } @@ -6769,11 +6762,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } - if (types_match_const_cast_only(prev_type, cur_type)) { + if (types_match_const_cast_only(ira->codegen, prev_type, cur_type)) { continue; } - if (types_match_const_cast_only(cur_type, prev_type)) { + if (types_match_const_cast_only(ira->codegen, cur_type, prev_type)) { prev_inst = cur_inst; continue; } @@ -6796,26 +6789,26 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } if (prev_type->id == TypeTableEntryIdErrorUnion && - types_match_const_cast_only(prev_type->data.error_union.payload_type, cur_type)) + types_match_const_cast_only(ira->codegen, prev_type->data.error_union.payload_type, cur_type)) { continue; } if (cur_type->id == TypeTableEntryIdErrorUnion && - types_match_const_cast_only(cur_type->data.error_union.payload_type, prev_type)) + types_match_const_cast_only(ira->codegen, cur_type->data.error_union.payload_type, prev_type)) { prev_inst = cur_inst; continue; } if (prev_type->id == TypeTableEntryIdMaybe && - types_match_const_cast_only(prev_type->data.maybe.child_type, cur_type)) + types_match_const_cast_only(ira->codegen, prev_type->data.maybe.child_type, cur_type)) { continue; } if (cur_type->id == TypeTableEntryIdMaybe && - types_match_const_cast_only(cur_type->data.maybe.child_type, prev_type)) + types_match_const_cast_only(ira->codegen, cur_type->data.maybe.child_type, prev_type)) { prev_inst = cur_inst; continue; @@ -6853,7 +6846,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && cur_type->data.array.len != prev_type->data.array.len && - types_match_const_cast_only(cur_type->data.array.child_type, prev_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, cur_type->data.array.child_type, prev_type->data.array.child_type)) { convert_to_const_slice = true; prev_inst = cur_inst; @@ -6862,7 +6855,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && cur_type->data.array.len != prev_type->data.array.len && - types_match_const_cast_only(prev_type->data.array.child_type, cur_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, prev_type->data.array.child_type, cur_type->data.array.child_type)) { convert_to_const_slice = true; continue; @@ -6871,7 +6864,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) && (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || cur_type->data.array.len == 0) && - types_match_const_cast_only(prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, + types_match_const_cast_only(ira->codegen, prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, cur_type->data.array.child_type)) { convert_to_const_slice = false; @@ -6881,7 +6874,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) && (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || prev_type->data.array.len == 0) && - types_match_const_cast_only(cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, + types_match_const_cast_only(ira->codegen, cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, prev_type->data.array.child_type)) { prev_inst = cur_inst; @@ -7449,6 +7442,7 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction return result; } +// TODO this is an explicit cast and should actually coerce the type static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) { @@ -7999,7 +7993,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return value; // explicit match or non-const to const - if (types_match_const_cast_only(wanted_type, actual_type)) { + if (types_match_const_cast_only(ira->codegen, wanted_type, actual_type)) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } @@ -8045,7 +8039,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) { return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); } @@ -8063,7 +8057,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *array_type = actual_type->data.pointer.child_type; if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ptr_type->data.pointer.child_type, array_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, array_type->data.array.child_type)) { return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); } @@ -8079,7 +8073,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value); if (type_is_invalid(cast1->value.type)) @@ -8102,7 +8096,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value); if (type_is_invalid(cast1->value.type)) @@ -8164,7 +8158,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from child type of maybe type to maybe type if (wanted_type->id == TypeTableEntryIdMaybe) { - if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) { + if (types_match_const_cast_only(ira->codegen, wanted_type->data.maybe.child_type, actual_type)) { return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) @@ -8186,7 +8180,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // 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_union.payload_type, actual_type)) { + if (types_match_const_cast_only(ira->codegen, wanted_type->data.error_union.payload_type, actual_type)) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) @@ -8208,7 +8202,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); if (type_is_invalid(cast1->value.type)) @@ -8235,7 +8229,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->id != TypeTableEntryIdMaybe) { TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; - if (types_match_const_cast_only(wanted_child_type, actual_type) || + if (types_match_const_cast_only(ira->codegen, wanted_child_type, actual_type) || actual_type->id == TypeTableEntryIdNullLit || actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) @@ -8385,7 +8379,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from something to const pointer of it if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (types_match_const_cast_only(wanted_type, const_ptr_actual)) { + if (types_match_const_cast_only(ira->codegen, wanted_type, const_ptr_actual)) { return ir_analyze_cast_ref(ira, source_instr, value, wanted_type); } } @@ -10386,12 +10380,17 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal { AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; - TypeTableEntry *return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node); - if (type_is_invalid(return_type)) + TypeTableEntry *specified_return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node); + if (type_is_invalid(specified_return_type)) return ira->codegen->builtin_types.entry_invalid; - inst_fn_type_id.return_type = return_type; + if (fn_proto_node->data.fn_proto.auto_err_set) { + TypeTableEntry *inferred_err_set_type = get_auto_err_set_type(ira->codegen, impl_fn); + inst_fn_type_id.return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type); + } else { + inst_fn_type_id.return_type = specified_return_type; + } - if (type_requires_comptime(return_type)) { + if (type_requires_comptime(specified_return_type)) { // Throw out our work and call the function as if it were comptime. return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto); } diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 60acf6d0ef..a5c1c854b7 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -220,7 +220,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ return formatValue(err, context, Errors, output); } }, - builtin.TypeId.Error => { + builtin.TypeId.ErrorSet => { try output(context, "error."); return output(context, @errorName(value)); }, diff --git a/std/io.zig b/std/io.zig index 2fdb564f15..b9f7157101 100644 --- a/std/io.zig +++ b/std/io.zig @@ -26,7 +26,9 @@ test "import io tests" { } } -pub fn getStdErr() !File { +const GetStdIoErrs = os.WindowsGetStdHandleErrs; + +pub fn getStdErr() GetStdIoErrs!File { const handle = if (is_windows) try os.windowsGetStdHandle(system.STD_ERROR_HANDLE) else if (is_posix) @@ -36,7 +38,7 @@ pub fn getStdErr() !File { return File.openHandle(handle); } -pub fn getStdOut() !File { +pub fn getStdOut() GetStdIoErrs!File { const handle = if (is_windows) try os.windowsGetStdHandle(system.STD_OUTPUT_HANDLE) else if (is_posix) @@ -46,7 +48,7 @@ pub fn getStdOut() !File { return File.openHandle(handle); } -pub fn getStdIn() !File { +pub fn getStdIn() GetStdIoErrs!File { const handle = if (is_windows) try os.windowsGetStdHandle(system.STD_INPUT_HANDLE) else if (is_posix) diff --git a/std/mem.zig b/std/mem.zig index 30b45e7629..1c75d92541 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -40,7 +40,7 @@ pub const Allocator = struct { } fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29, - n: usize) %[]align(alignment) T + n: usize) ![]align(alignment) T { const byte_count = try math.mul(usize, @sizeOf(T), n); const byte_slice = try self.allocFn(self, byte_count, alignment); @@ -56,7 +56,7 @@ pub const Allocator = struct { } fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29, - old_mem: []align(alignment) T, n: usize) %[]align(alignment) T + old_mem: []align(alignment) T, n: usize) ![]align(alignment) T { if (old_mem.len == 0) { return self.alloc(T, n); diff --git a/std/os/index.zig b/std/os/index.zig index 3e39d5c2ea..224c4ba328 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1158,7 +1158,12 @@ pub fn posix_setregid(rgid: u32, egid: u32) !void { }; } -pub fn windowsGetStdHandle(handle_id: windows.DWORD) !windows.HANDLE { +pub const WindowsGetStdHandleErrs = error { + NoStdHandles, + Unexpected, +}; + +pub fn windowsGetStdHandle(handle_id: windows.DWORD) WindowsGetStdHandleErrs!windows.HANDLE { if (windows.GetStdHandle(handle_id)) |handle| { if (handle == windows.INVALID_HANDLE_VALUE) { const err = windows.GetLastError(); -- cgit v1.2.3 From 406496ca3371b7f50aee141fa37c52e86d96783f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 1 Feb 2018 23:32:09 -0500 Subject: *WIP* error sets - allow peer type resolution to create new error set --- src/analyze.cpp | 2 +- src/ir.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++++---------- std/fmt/index.zig | 4 +-- std/io.zig | 3 +- 4 files changed, 88 insertions(+), 23 deletions(-) (limited to 'src/analyze.cpp') diff --git a/src/analyze.cpp b/src/analyze.cpp index f69b131626..36aac9b29e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1277,7 +1277,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry) { TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); buf_resize(&err_set_type->name, 0); - buf_appendf(&err_set_type->name, "%s.errors", buf_ptr(&fn_entry->symbol_name)); + buf_appendf(&err_set_type->name, "@typeOf(%s).ReturnType.ErrorSet", buf_ptr(&fn_entry->symbol_name)); err_set_type->is_copyable = true; err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref; err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type; diff --git a/src/ir.cpp b/src/ir.cpp index e6899da48b..d2b2c4fc82 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5380,12 +5380,61 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope, return ir_build_const_type(irb, parent_scope, node, container_type); } +// errors should be populated with set1's values +static TypeTableEntry *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, TypeTableEntry *set1, TypeTableEntry *set2) { + assert(set1->id == TypeTableEntryIdErrorSet); + assert(set2->id == TypeTableEntryIdErrorSet); + + TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); + buf_resize(&err_set_type->name, 0); + buf_appendf(&err_set_type->name, "error{"); + + uint32_t count = set1->data.error_set.err_count; + for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; + if (errors[error_entry->value] == nullptr) { + count += 1; + } + } + + err_set_type->is_copyable = true; + err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref; + err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type; + err_set_type->data.error_set.err_count = count; + err_set_type->data.error_set.errors = allocate(count); + + for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set1->data.error_set.errors[i]; + buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name)); + err_set_type->data.error_set.errors[i] = error_entry; + } + + uint32_t index = set1->data.error_set.err_count; + for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; + if (errors[error_entry->value] == nullptr) { + errors[error_entry->value] = error_entry; + buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name)); + err_set_type->data.error_set.errors[index] = error_entry; + index += 1; + } + } + assert(index == count); + + buf_appendf(&err_set_type->name, "}"); + + g->error_di_types.append(&err_set_type->di_type); + + return err_set_type; + +} + static TypeTableEntry *make_err_set_with_one_item(CodeGen *g, Scope *parent_scope, AstNode *node, ErrorTableEntry *err_entry) { TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); buf_resize(&err_set_type->name, 0); - buf_appendf(&err_set_type->name, "@typeOf(error.%s)", buf_ptr(&err_entry->name)); + buf_appendf(&err_set_type->name, "error{%s}", buf_ptr(&err_entry->name)); err_set_type->is_copyable = true; err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref; err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type; @@ -6656,7 +6705,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } // if err_set_type is a superset of cur_type, keep err_set_type. // if cur_type is a superset of err_set_type, switch err_set_type to cur_type - // otherwise emit a compile error bool prev_is_superset = true; for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; @@ -6689,12 +6737,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } if (cur_is_superset) { - err_set_type = cur_inst->value.type; + err_set_type = cur_type; prev_inst = cur_inst; continue; } + + // neither of them are supersets. so we invent a new error set type that is a union of both of them + err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type); + continue; } else if (cur_type->id == TypeTableEntryIdErrorUnion) { - // err_set_type must be a subset of cur_type's error set + // test if err_set_type is a subset of cur_type's error set // unset everything in errors for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; @@ -6719,6 +6771,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod prev_inst = cur_inst; continue; } + + // not a subset. invent new error set type, union of both of them + err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type); + prev_inst = cur_inst; + continue; } else { prev_inst = cur_inst; continue; @@ -6746,7 +6803,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } if (prev_type->id == TypeTableEntryIdErrorUnion) { - // the cur type error set must be a subset + // check if the cur type error set must be a subset bool prev_is_superset = true; for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; @@ -6759,6 +6816,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (prev_is_superset) { continue; } + // not a subset. invent new error set type, union of both of them + err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type); + continue; } } @@ -6927,21 +6987,25 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else { return slice_type; } - } else if (err_set_type != nullptr && prev_inst->value.type->id != TypeTableEntryIdErrorSet) { - if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || - prev_inst->value.type->id == TypeTableEntryIdNumLitFloat) - { - ir_add_error_node(ira, source_node, - buf_sprintf("unable to make error union out of number literal")); - return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value.type->id == TypeTableEntryIdNullLit) { - ir_add_error_node(ira, source_node, - buf_sprintf("unable to make error union out of null literal")); - return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) { - return prev_inst->value.type; + } else if (err_set_type != nullptr) { + if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { + return err_set_type; } else { - return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type); + if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || + prev_inst->value.type->id == TypeTableEntryIdNumLitFloat) + { + ir_add_error_node(ira, source_node, + buf_sprintf("unable to make error union out of number literal")); + return ira->codegen->builtin_types.entry_invalid; + } else if (prev_inst->value.type->id == TypeTableEntryIdNullLit) { + ir_add_error_node(ira, source_node, + buf_sprintf("unable to make error union out of null literal")); + return ira->codegen->builtin_types.entry_invalid; + } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) { + return prev_inst->value.type; + } else { + return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type); + } } } else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNullLit) { if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || diff --git a/std/fmt/index.zig b/std/fmt/index.zig index a5c1c854b7..99a8f69c24 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -498,12 +498,12 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { var size: usize = 0; - format(&size, error{}, countSize, fmt, args); + format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; const buf = try allocator.alloc(u8, size); return bufPrint(buf, fmt, args); } -fn countSize(size: &usize, bytes: []const u8) void { +fn countSize(size: &usize, bytes: []const u8) !void { *size += bytes.len; } diff --git a/std/io.zig b/std/io.zig index b9f7157101..e110d4ddf5 100644 --- a/std/io.zig +++ b/std/io.zig @@ -350,10 +350,11 @@ pub const File = struct { }; pub const InStream = struct { + // TODO allow specifying the error set /// Return the number of bytes read. If the number read is smaller than buf.len, it /// means the stream reached the end. Reaching the end of a stream is not an error /// condition. - readFn: fn(self: &InStream, buffer: []u8) !usize, + readFn: fn(self: &InStream, buffer: []u8) error!usize, /// Replaces `buffer` contents by reading from the stream until it is finished. /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and -- cgit v1.2.3 From cfb2c676925d77887e46631dcafa783e6c65e61d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 2 Feb 2018 11:50:19 -0500 Subject: *WIP* error sets - rewrite "const cast only" function --- src/analyze.cpp | 136 +++++++++++++++++++++++++++++++++++--------------------- src/analyze.hpp | 51 ++++++++++++++++++++- src/ir.cpp | 10 +++-- 3 files changed, 142 insertions(+), 55 deletions(-) (limited to 'src/analyze.cpp') diff --git a/src/analyze.cpp b/src/analyze.cpp index 36aac9b29e..ac504a3ea5 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3366,9 +3366,12 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *so g->tld_ref_source_node_stack.pop(); } -bool types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type) { +ConstCastOnly types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type) { + ConstCastOnly result = {0}; + result.id = ConstCastResultIdOk; + if (expected_type == actual_type) - return true; + return result; // pointer const if (expected_type->id == TypeTableEntryIdPointer && @@ -3379,15 +3382,18 @@ bool types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, Type actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count && actual_type->data.pointer.alignment >= expected_type->data.pointer.alignment) { - return types_match_const_cast_only(g, expected_type->data.pointer.child_type, - actual_type->data.pointer.child_type); + ConstCastOnly child = types_match_const_cast_only(g, expected_type->data.pointer.child_type, actual_type->data.pointer.child_type); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdPointerChild; + result.data.pointer_child = allocate_nonzero(1); + *result.data.pointer_child = child; + } + return result; } // slice const - if (expected_type->id == TypeTableEntryIdStruct && - actual_type->id == TypeTableEntryIdStruct && - expected_type->data.structure.is_slice && - actual_type->data.structure.is_slice) + if (expected_type->id == TypeTableEntryIdStruct && actual_type->id == TypeTableEntryIdStruct && + expected_type->data.structure.is_slice && actual_type->data.structure.is_slice) { TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; TypeTableEntry *expected_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry; @@ -3397,43 +3403,54 @@ bool types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, Type actual_ptr_type->data.pointer.unaligned_bit_count == expected_ptr_type->data.pointer.unaligned_bit_count && actual_ptr_type->data.pointer.alignment >= expected_ptr_type->data.pointer.alignment) { - return types_match_const_cast_only(g, expected_ptr_type->data.pointer.child_type, + ConstCastOnly child = types_match_const_cast_only(g, expected_ptr_type->data.pointer.child_type, actual_ptr_type->data.pointer.child_type); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdSliceChild; + result.data.slice_child = allocate_nonzero(1); + *result.data.slice_child = child; + } + return result; } } // maybe - if (expected_type->id == TypeTableEntryIdMaybe && - actual_type->id == TypeTableEntryIdMaybe) - { - return types_match_const_cast_only(g, - expected_type->data.maybe.child_type, - actual_type->data.maybe.child_type); + if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { + ConstCastOnly child = types_match_const_cast_only(g, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdNullableChild; + result.data.nullable_child = allocate_nonzero(1); + *result.data.nullable_child = child; + } + return result; } // error union - if (expected_type->id == TypeTableEntryIdErrorUnion && - actual_type->id == TypeTableEntryIdErrorUnion) - { - return types_match_const_cast_only(g, - expected_type->data.error_union.payload_type, - actual_type->data.error_union.payload_type) && - types_match_const_cast_only(g, - expected_type->data.error_union.err_set_type, - actual_type->data.error_union.err_set_type); + if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) { + ConstCastOnly payload_child = types_match_const_cast_only(g, expected_type->data.error_union.payload_type, actual_type->data.error_union.payload_type); + if (payload_child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdErrorUnionPayload; + result.data.error_union_payload = allocate_nonzero(1); + *result.data.error_union_payload = payload_child; + return result; + } + ConstCastOnly error_set_child = types_match_const_cast_only(g, expected_type->data.error_union.err_set_type, actual_type->data.error_union.err_set_type); + if (error_set_child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdErrorUnionErrorSet; + result.data.error_union_error_set = allocate_nonzero(1); + *result.data.error_union_error_set = error_set_child; + return result; + } + return result; } // error set - if (expected_type->id == TypeTableEntryIdErrorSet && - actual_type->id == TypeTableEntryIdErrorSet) - { + if (expected_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdErrorSet) { TypeTableEntry *contained_set = actual_type; TypeTableEntry *container_set = expected_type; - if (container_set == g->builtin_types.entry_global_error_set || - container_set->data.error_set.infer_fn != nullptr) - { - return true; + if (container_set == g->builtin_types.entry_global_error_set || container_set->data.error_set.infer_fn != nullptr) { + return result; } ErrorTableEntry **errors = allocate(g->errors_by_index.length); @@ -3445,11 +3462,14 @@ bool types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, Type ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i]; ErrorTableEntry *error_entry = errors[contained_error_entry->value]; if (error_entry == nullptr) { - return false; + if (result.id == ConstCastResultIdOk) { + result.id = ConstCastResultIdErrSet; + } + result.data.error_set.errors.append(contained_error_entry); } } free(errors); - return true; + return result; } // fn @@ -3457,30 +3477,39 @@ bool types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, Type actual_type->id == TypeTableEntryIdFn) { if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) { - return false; + result.id = ConstCastResultIdFnAlign; + return result; } if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { - return false; + result.id = ConstCastResultIdFnCC; + return result; } if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { - return false; + result.id = ConstCastResultIdFnVarArgs; + return result; } if (expected_type->data.fn.is_generic != actual_type->data.fn.is_generic) { - return false; + result.id = ConstCastResultIdFnIsGeneric; + return result; } if (!expected_type->data.fn.is_generic && - actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable && - !types_match_const_cast_only(g, - expected_type->data.fn.fn_type_id.return_type, - actual_type->data.fn.fn_type_id.return_type)) + actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable) { - return false; + ConstCastOnly child = types_match_const_cast_only(g, expected_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdFnReturnType; + result.data.return_type = allocate_nonzero(1); + *result.data.return_type = child; + } + return result; } if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { - return false; + result.id = ConstCastResultIdFnArgCount; + return result; } if (expected_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) { - return false; + result.id = ConstCastResultIdFnGenericArgCount; + return result; } assert(expected_type->data.fn.is_generic || expected_type->data.fn.fn_type_id.next_param_index == expected_type->data.fn.fn_type_id.param_count); @@ -3489,19 +3518,26 @@ bool types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, Type FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i]; - if (!types_match_const_cast_only(g, actual_param_info->type, expected_param_info->type)) { - return false; + ConstCastOnly arg_child = types_match_const_cast_only(g, actual_param_info->type, expected_param_info->type); + if (arg_child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdFnArg; + result.data.fn_arg.arg_index = i; + result.data.fn_arg.child = allocate_nonzero(1); + *result.data.fn_arg.child = arg_child; + return result; } if (expected_param_info->is_noalias != actual_param_info->is_noalias) { - return false; + result.id = ConstCastResultIdFnArgNoAlias; + result.data.arg_no_alias.arg_index = i; + return result; } } - return true; + return result; } - - return false; + result.id = ConstCastResultIdType; + return result; } Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) { diff --git a/src/analyze.hpp b/src/analyze.hpp index 9af259b10d..551b892791 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -46,7 +46,6 @@ bool type_has_bits(TypeTableEntry *type_entry); ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code); -bool types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type); VariableTableEntry *find_variable(CodeGen *g, Scope *orig_context, Buf *name); Tld *find_decl(CodeGen *g, Scope *scope, Buf *name); void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *source_node); @@ -191,4 +190,54 @@ void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry); TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry); +enum ConstCastResultId { + ConstCastResultIdOk, + ConstCastResultIdErrSet, + ConstCastResultIdPointerChild, + ConstCastResultIdSliceChild, + ConstCastResultIdNullableChild, + ConstCastResultIdErrorUnionPayload, + ConstCastResultIdErrorUnionErrorSet, + ConstCastResultIdFnAlign, + ConstCastResultIdFnCC, + ConstCastResultIdFnVarArgs, + ConstCastResultIdFnIsGeneric, + ConstCastResultIdFnReturnType, + ConstCastResultIdFnArgCount, + ConstCastResultIdFnGenericArgCount, + ConstCastResultIdFnArg, + ConstCastResultIdFnArgNoAlias, + ConstCastResultIdType, +}; + +struct ConstCastErrSetMismatch { + ZigList missing_errors; +}; + +struct ConstCastArg { + size_t arg_index; + ConstCastOnly *child; +}; + +struct ConstCastArgNoAlias { + size_t arg_index; +}; + +struct ConstCastOnly { + ConstCastResultId id; + union { + ConstCastErrSetMismatch error_set; + ConstCastOnly *pointer_child; + ConstCastOnly *slice_child; + ConstCastOnly *nullable_child; + ConstCastOnly *error_union_payload; + ConstCastOnly *error_union_error_set; + ConstCastOnly *return_type; + ConstCastArg fn_arg; + ConstCastArgNoAlias arg_no_alias; + } data; +}; + +bool types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type); + #endif diff --git a/src/ir.cpp b/src/ir.cpp index d2b2c4fc82..00bde702d2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6428,6 +6428,9 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, return ImplicitCastMatchResultYes; } + // if we got here with error sets, make an error showing the incompatibilities + if (expected_typek + // implicit conversion from anything to var if (expected_type->id == TypeTableEntryIdVar) { return ImplicitCastMatchResultYes; @@ -6801,9 +6804,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod errors[error_entry->value] = error_entry; } continue; - } - if (prev_type->id == TypeTableEntryIdErrorUnion) { - // check if the cur type error set must be a subset + } else { + // check if the cur type error set is a subset bool prev_is_superset = true; for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; @@ -8471,7 +8473,7 @@ static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, Typ ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, expected_type, value->value.type, value); switch (result) { case ImplicitCastMatchResultNo: - ir_add_error(ira, value, + ErrorMsg *msg = ir_add_error(ira, value, buf_sprintf("expected type '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&value->value.type->name))); -- cgit v1.2.3 From 39d5f44863aafa77163b2a7e32f2553a589dbb2c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 2 Feb 2018 14:26:14 -0500 Subject: *WI* error sets - basic support working --- TODO | 13 +++++- src/analyze.cpp | 4 +- src/analyze.hpp | 4 +- src/ir.cpp | 83 +++++++++++++++++++++++------------- std/debug/index.zig | 3 +- std/fmt/index.zig | 33 ++++++++------ std/math/index.zig | 6 +-- test/cases/cast.zig | 32 +++++++------- test/cases/enum_with_members.zig | 2 +- test/cases/error.zig | 14 +++--- test/cases/ir_block_deps.zig | 2 +- test/cases/misc.zig | 8 ++-- test/cases/reflection.zig | 2 +- test/cases/switch.zig | 2 +- test/cases/switch_prong_err_enum.zig | 4 +- test/cases/try.zig | 4 +- test/cases/union.zig | 2 +- test/cases/while.zig | 8 ++-- 18 files changed, 134 insertions(+), 92 deletions(-) (limited to 'src/analyze.cpp') diff --git a/TODO b/TODO index 838aaba485..963169d5aa 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,15 @@ -sed -i 's/\(\bfn .*) \)%\(.*{\)$/\1!\2/g' $(find .. -name "*.zig") +sed -i 's/\(\bfn .*) \)%\(.*{\)$/\1!\2/g' $(find . -name "*.zig") -comptime assert(error{} ! i32 == i32); +the literal translation of `%T` to this new code is `error!T`. +however this would not take advantage of error sets. It's +recommended to generally have all your functions which return possible +errors to use error set inference, like this: + +fn foo() !void { + +} + +then you can return void, or any error, and the error set is inferred. // TODO this is an explicit cast and should actually coerce the type erorr set casting diff --git a/src/analyze.cpp b/src/analyze.cpp index ac504a3ea5..8ed5706a27 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3367,7 +3367,7 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *so } ConstCastOnly types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type) { - ConstCastOnly result = {0}; + ConstCastOnly result = {}; result.id = ConstCastResultIdOk; if (expected_type == actual_type) @@ -3465,7 +3465,7 @@ ConstCastOnly types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_t if (result.id == ConstCastResultIdOk) { result.id = ConstCastResultIdErrSet; } - result.data.error_set.errors.append(contained_error_entry); + result.data.error_set.missing_errors.append(contained_error_entry); } } free(errors); diff --git a/src/analyze.hpp b/src/analyze.hpp index 551b892791..b7869d3a79 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -214,6 +214,8 @@ struct ConstCastErrSetMismatch { ZigList missing_errors; }; +struct ConstCastOnly; + struct ConstCastArg { size_t arg_index; ConstCastOnly *child; @@ -238,6 +240,6 @@ struct ConstCastOnly { } data; }; -bool types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type); +ConstCastOnly types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type); #endif diff --git a/src/ir.cpp b/src/ir.cpp index 00bde702d2..2e4a13927b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6424,12 +6424,23 @@ enum ImplicitCastMatchResult { static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type, TypeTableEntry *actual_type, IrInstruction *value) { - if (types_match_const_cast_only(ira->codegen, expected_type, actual_type)) { + ConstCastOnly const_cast_result = types_match_const_cast_only(ira->codegen, expected_type, actual_type); + if (const_cast_result.id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } // if we got here with error sets, make an error showing the incompatibilities - if (expected_typek + if (const_cast_result.id == ConstCastResultIdErrSet) { + ErrorMsg *msg = ir_add_error(ira, value, + buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name))); + for (size_t i = 0; i < const_cast_result.data.error_set.missing_errors.length; i += 1) { + ErrorTableEntry *error_entry = const_cast_result.data.error_set.missing_errors.at(i); + add_error_note(ira->codegen, msg, error_entry->decl_node, + buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name))); + } + + return ImplicitCastMatchResultReportedError; + } // implicit conversion from anything to var if (expected_type->id == TypeTableEntryIdVar) { @@ -6508,7 +6519,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6527,7 +6538,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *array_type = actual_type->data.pointer.child_type; if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, array_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, array_type->data.array.child_type).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6543,7 +6554,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, expected_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6558,7 +6569,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, expected_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6638,7 +6649,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicitly take a const pointer to something if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (types_match_const_cast_only(ira->codegen, expected_type, const_ptr_actual)) { + if (types_match_const_cast_only(ira->codegen, expected_type, const_ptr_actual).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } } @@ -6742,20 +6753,31 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_is_superset) { err_set_type = cur_type; prev_inst = cur_inst; + assert(errors != nullptr); continue; } // neither of them are supersets. so we invent a new error set type that is a union of both of them err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type); + assert(errors != nullptr); continue; } else if (cur_type->id == TypeTableEntryIdErrorUnion) { + if (err_set_type == ira->codegen->builtin_types.entry_global_error_set) { + prev_inst = cur_inst; + continue; + } + TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type; + if (cur_err_set_type == ira->codegen->builtin_types.entry_global_error_set) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + prev_inst = cur_inst; + continue; + } // test if err_set_type is a subset of cur_type's error set // unset everything in errors for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = nullptr; } - TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type; for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; errors[error_entry->value] = error_entry; @@ -6772,12 +6794,14 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_is_superset) { err_set_type = cur_err_set_type; prev_inst = cur_inst; + assert(errors != nullptr); continue; } // not a subset. invent new error set type, union of both of them err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type); prev_inst = cur_inst; + assert(errors != nullptr); continue; } else { prev_inst = cur_inst; @@ -6820,15 +6844,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } // not a subset. invent new error set type, union of both of them err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type); + assert(errors != nullptr); continue; } } - if (types_match_const_cast_only(ira->codegen, prev_type, cur_type)) { + if (types_match_const_cast_only(ira->codegen, prev_type, cur_type).id == ConstCastResultIdOk) { continue; } - if (types_match_const_cast_only(ira->codegen, cur_type, prev_type)) { + if (types_match_const_cast_only(ira->codegen, cur_type, prev_type).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } @@ -6851,26 +6876,26 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } if (prev_type->id == TypeTableEntryIdErrorUnion && - types_match_const_cast_only(ira->codegen, prev_type->data.error_union.payload_type, cur_type)) + types_match_const_cast_only(ira->codegen, prev_type->data.error_union.payload_type, cur_type).id == ConstCastResultIdOk) { continue; } if (cur_type->id == TypeTableEntryIdErrorUnion && - types_match_const_cast_only(ira->codegen, cur_type->data.error_union.payload_type, prev_type)) + types_match_const_cast_only(ira->codegen, cur_type->data.error_union.payload_type, prev_type).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } if (prev_type->id == TypeTableEntryIdMaybe && - types_match_const_cast_only(ira->codegen, prev_type->data.maybe.child_type, cur_type)) + types_match_const_cast_only(ira->codegen, prev_type->data.maybe.child_type, cur_type).id == ConstCastResultIdOk) { continue; } if (cur_type->id == TypeTableEntryIdMaybe && - types_match_const_cast_only(ira->codegen, cur_type->data.maybe.child_type, prev_type)) + types_match_const_cast_only(ira->codegen, cur_type->data.maybe.child_type, prev_type).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; @@ -6908,7 +6933,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && cur_type->data.array.len != prev_type->data.array.len && - types_match_const_cast_only(ira->codegen, cur_type->data.array.child_type, prev_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, cur_type->data.array.child_type, prev_type->data.array.child_type).id == ConstCastResultIdOk) { convert_to_const_slice = true; prev_inst = cur_inst; @@ -6917,7 +6942,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && cur_type->data.array.len != prev_type->data.array.len && - types_match_const_cast_only(ira->codegen, prev_type->data.array.child_type, cur_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, prev_type->data.array.child_type, cur_type->data.array.child_type).id == ConstCastResultIdOk) { convert_to_const_slice = true; continue; @@ -6927,7 +6952,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || cur_type->data.array.len == 0) && types_match_const_cast_only(ira->codegen, prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, - cur_type->data.array.child_type)) + cur_type->data.array.child_type).id == ConstCastResultIdOk) { convert_to_const_slice = false; continue; @@ -6937,7 +6962,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || prev_type->data.array.len == 0) && types_match_const_cast_only(ira->codegen, cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, - prev_type->data.array.child_type)) + prev_type->data.array.child_type).id == ConstCastResultIdOk) { prev_inst = cur_inst; convert_to_const_slice = false; @@ -8059,7 +8084,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return value; // explicit match or non-const to const - if (types_match_const_cast_only(ira->codegen, wanted_type, actual_type)) { + if (types_match_const_cast_only(ira->codegen, wanted_type, actual_type).id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } @@ -8105,7 +8130,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) { return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); } @@ -8123,7 +8148,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *array_type = actual_type->data.pointer.child_type; if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, array_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, array_type->data.array.child_type).id == ConstCastResultIdOk) { return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); } @@ -8139,7 +8164,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value); if (type_is_invalid(cast1->value.type)) @@ -8162,7 +8187,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value); if (type_is_invalid(cast1->value.type)) @@ -8224,7 +8249,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from child type of maybe type to maybe type if (wanted_type->id == TypeTableEntryIdMaybe) { - if (types_match_const_cast_only(ira->codegen, wanted_type->data.maybe.child_type, actual_type)) { + if (types_match_const_cast_only(ira->codegen, wanted_type->data.maybe.child_type, actual_type).id == ConstCastResultIdOk) { return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) @@ -8246,7 +8271,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from child type of error type to error type if (wanted_type->id == TypeTableEntryIdErrorUnion) { - if (types_match_const_cast_only(ira->codegen, wanted_type->data.error_union.payload_type, actual_type)) { + if (types_match_const_cast_only(ira->codegen, wanted_type->data.error_union.payload_type, actual_type).id == ConstCastResultIdOk) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) @@ -8268,7 +8293,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type)) + types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); if (type_is_invalid(cast1->value.type)) @@ -8295,7 +8320,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->id != TypeTableEntryIdMaybe) { TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; - if (types_match_const_cast_only(ira->codegen, wanted_child_type, actual_type) || + if (types_match_const_cast_only(ira->codegen, wanted_child_type, actual_type).id == ConstCastResultIdOk || actual_type->id == TypeTableEntryIdNullLit || actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) @@ -8445,7 +8470,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from something to const pointer of it if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (types_match_const_cast_only(ira->codegen, wanted_type, const_ptr_actual)) { + if (types_match_const_cast_only(ira->codegen, wanted_type, const_ptr_actual).id == ConstCastResultIdOk) { return ir_analyze_cast_ref(ira, source_instr, value, wanted_type); } } @@ -8473,7 +8498,7 @@ static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, Typ ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, expected_type, value->value.type, value); switch (result) { case ImplicitCastMatchResultNo: - ErrorMsg *msg = ir_add_error(ira, value, + ir_add_error(ira, value, buf_sprintf("expected type '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&value->value.type->name))); diff --git a/std/debug/index.zig b/std/debug/index.zig index 6bb578e5bf..4778d63dd2 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -209,8 +209,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); } } else |err| switch (err) { - error.EndOfFile, error.PathNotFound => {}, - else => return err, + error.EndOfFile => {}, } } else |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 99a8f69c24..26583817e6 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -195,7 +195,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ const T = @typeOf(value); switch (@typeId(T)) { builtin.TypeId.Int => { - return formatInt(value, 10, false, 0, context, output); + return formatInt(value, 10, false, 0, context, Errors, output); }, builtin.TypeId.Float => { return formatFloat(value, context, output); @@ -290,7 +290,7 @@ pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@ if (float_decimal.exp != 1) { try output(context, "e"); - try formatInt(float_decimal.exp - 1, 10, false, 0, context, output); + try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output); } } @@ -336,12 +336,12 @@ pub fn formatFloatDecimal(value: var, precision: usize, context: var, comptime E pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, - context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)errors!void) errors!void + context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { if (@typeOf(value).is_signed) { - return formatIntSigned(value, base, uppercase, width, context, output); + return formatIntSigned(value, base, uppercase, width, context, Errors, output); } else { - return formatIntUnsigned(value, base, uppercase, width, context, output); + return formatIntUnsigned(value, base, uppercase, width, context, Errors, output); } } @@ -354,15 +354,15 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, try output(context, (&minus_sign)[0..1]); const new_value = uint(-(value + 1)) + 1; const new_width = if (width == 0) 0 else (width - 1); - return formatIntUnsigned(new_value, base, uppercase, new_width, context, output); + return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); } else if (width == 0) { - return formatIntUnsigned(uint(value), base, uppercase, width, context, output); + return formatIntUnsigned(uint(value), base, uppercase, width, context, Errors, output); } else { const plus_sign: u8 = '+'; try output(context, (&plus_sign)[0..1]); const new_value = uint(value); const new_width = if (width == 0) 0 else (width - 1); - return formatIntUnsigned(new_value, base, uppercase, new_width, context, output); + return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); } } @@ -410,7 +410,7 @@ pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: .out_buf = out_buf, .index = 0, }; - formatInt(value, base, uppercase, width, &context, formatIntCallback) catch unreachable; + formatInt(value, base, uppercase, width, &context, error{}, formatIntCallback) catch unreachable; return context.index; } const FormatIntBuf = struct { @@ -446,7 +446,14 @@ test "fmt.parseInt" { assert(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); } -pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) !T { +const ParseUnsignedError = error { + /// The result cannot fit in the type specified + Overflow, + /// The input had a byte that was not a digit + InvalidCharacter, +}; + +pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T { var x: T = 0; for (buf) |c| { @@ -458,16 +465,16 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) !T { return x; } -fn charToDigit(c: u8, radix: u8) !u8 { +fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { const value = switch (c) { '0' ... '9' => c - '0', 'A' ... 'Z' => c - 'A' + 10, 'a' ... 'z' => c - 'a' + 10, - else => return error.InvalidChar, + else => return error.InvalidCharacter, }; if (value >= radix) - return error.InvalidChar; + return error.InvalidCharacter; return value; } diff --git a/std/math/index.zig b/std/math/index.zig index 9c5e35ef17..f8668cc00d 100644 --- a/std/math/index.zig +++ b/std/math/index.zig @@ -191,17 +191,17 @@ test "math.max" { assert(max(i32(-1), i32(2)) == 2); } -pub fn mul(comptime T: type, a: T, b: T) !T { +pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) { var answer: T = undefined; return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer; } -pub fn add(comptime T: type, a: T, b: T) !T { +pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) { var answer: T = undefined; return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer; } -pub fn sub(comptime T: type, a: T, b: T) !T { +pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) { var answer: T = undefined; return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer; } diff --git a/test/cases/cast.zig b/test/cases/cast.zig index 994bf2a8b1..6ffb558174 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -74,7 +74,7 @@ test "string literal to &const []const u8" { assert(mem.eql(u8, *x, "hello")); } -test "implicitly cast from T to %?T" { +test "implicitly cast from T to error!?T" { castToMaybeTypeError(1); comptime castToMaybeTypeError(1); } @@ -83,37 +83,37 @@ const A = struct { }; fn castToMaybeTypeError(z: i32) void { const x = i32(1); - const y: %?i32 = x; + const y: error!?i32 = x; assert(??(try y) == 1); const f = z; - const g: %?i32 = f; + const g: error!?i32 = f; const a = A{ .a = z }; - const b: %?A = a; + const b: error!?A = a; assert((??(b catch unreachable)).a == 1); } -test "implicitly cast from int to %?T" { +test "implicitly cast from int to error!?T" { implicitIntLitToMaybe(); comptime implicitIntLitToMaybe(); } fn implicitIntLitToMaybe() void { const f: ?i32 = 1; - const g: %?i32 = 1; + const g: error!?i32 = 1; } -test "return null from fn() %?&T" { +test "return null from fn() error!?&T" { const a = returnNullFromMaybeTypeErrorRef(); const b = returnNullLitFromMaybeTypeErrorRef(); assert((try a) == null and (try b) == null); } -fn returnNullFromMaybeTypeErrorRef() !?&A { +fn returnNullFromMaybeTypeErrorRef() error!?&A { const a: ?&A = null; return a; } -fn returnNullLitFromMaybeTypeErrorRef() !?&A { +fn returnNullLitFromMaybeTypeErrorRef() error!?&A { return null; } @@ -160,7 +160,7 @@ fn castToMaybeSlice() ?[]const u8 { } -test "implicitly cast from [0]T to %[]T" { +test "implicitly cast from [0]T to error![]T" { testCastZeroArrayToErrSliceMut(); comptime testCastZeroArrayToErrSliceMut(); } @@ -169,11 +169,11 @@ fn testCastZeroArrayToErrSliceMut() void { assert((gimmeErrOrSlice() catch unreachable).len == 0); } -fn gimmeErrOrSlice() ![]u8 { +fn gimmeErrOrSlice() error![]u8 { return []u8{}; } -test "peer type resolution: [0]u8, []const u8, and %[]u8" { +test "peer type resolution: [0]u8, []const u8, and error![]u8" { { var data = "hi"; const slice = data[0..]; @@ -187,7 +187,7 @@ test "peer type resolution: [0]u8, []const u8, and %[]u8" { assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } } -fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) ![]u8 { +fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) error![]u8 { if (a) { return []u8{}; } @@ -229,7 +229,7 @@ fn foo(args: ...) void { test "peer type resolution: error and [N]T" { - // TODO: implicit %T to %U where T can implicitly cast to U + // TODO: implicit error!T to error!U where T can implicitly cast to U //assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); //comptime assert(mem.eql(u8, try testPeerErrorAndArray(0), "OK")); @@ -237,13 +237,13 @@ test "peer type resolution: error and [N]T" { comptime assert(mem.eql(u8, try testPeerErrorAndArray2(1), "OKK")); } -//fn testPeerErrorAndArray(x: u8) ![]const u8 { +//fn testPeerErrorAndArray(x: u8) error![]const u8 { // return switch (x) { // 0x00 => "OK", // else => error.BadValue, // }; //} -fn testPeerErrorAndArray2(x: u8) ![]const u8 { +fn testPeerErrorAndArray2(x: u8) error![]const u8 { return switch (x) { 0x00 => "OK", 0x01 => "OKK", diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index 9fc8104cf8..0c2ae1c383 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -6,7 +6,7 @@ const ET = union(enum) { SINT: i32, UINT: u32, - pub fn print(a: &const ET, buf: []u8) !usize { + pub fn print(a: &const ET, buf: []u8) error!usize { return switch (*a) { ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), diff --git a/test/cases/error.zig b/test/cases/error.zig index 3564ce60aa..f35f2e7ad6 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -1,16 +1,16 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; -pub fn foo() !i32 { +pub fn foo() error!i32 { const x = try bar(); return x + 1; } -pub fn bar() !i32 { +pub fn bar() error!i32 { return 13; } -pub fn baz() !i32 { +pub fn baz() error!i32 { const y = foo() catch 1234; return y + 1; } @@ -50,7 +50,7 @@ test "error binary operator" { assert(a == 3); assert(b == 10); } -fn errBinaryOperatorG(x: bool) !isize { +fn errBinaryOperatorG(x: bool) error!isize { return if (x) error.ItBroke else isize(10); } @@ -59,18 +59,18 @@ test "unwrap simple value from error" { const i = unwrapSimpleValueFromErrorDo() catch unreachable; assert(i == 13); } -fn unwrapSimpleValueFromErrorDo() %isize { return 13; } +fn unwrapSimpleValueFromErrorDo() error!isize { return 13; } test "error return in assignment" { doErrReturnInAssignment() catch unreachable; } -fn doErrReturnInAssignment() !void { +fn doErrReturnInAssignment() error!void { var x : i32 = undefined; x = try makeANonErr(); } -fn makeANonErr() !i32 { +fn makeANonErr() error!i32 { return 1; } diff --git a/test/cases/ir_block_deps.zig b/test/cases/ir_block_deps.zig index a762e6e01f..202df19f62 100644 --- a/test/cases/ir_block_deps.zig +++ b/test/cases/ir_block_deps.zig @@ -11,7 +11,7 @@ fn foo(id: u64) !i32 { }; } -fn getErrInt() %i32 { return 0; } +fn getErrInt() error!i32 { return 0; } test "ir block deps" { assert((foo(1) catch unreachable) == 0); diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 314d7d518e..964c5babc1 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -262,7 +262,7 @@ test "generic malloc free" { memFree(u8, a); } const some_mem : [100]u8 = undefined; -fn memAlloc(comptime T: type, n: usize) ![]T { +fn memAlloc(comptime T: type, n: usize) error![]T { return @ptrCast(&T, &some_mem[0])[0..n]; } fn memFree(comptime T: type, memory: []T) void { } @@ -419,7 +419,7 @@ test "cast slice to u8 slice" { test "pointer to void return type" { testPointerToVoidReturnType() catch unreachable; } -fn testPointerToVoidReturnType() !void { +fn testPointerToVoidReturnType() error!void { const a = testPointerToVoidReturnType2(); return *a; } @@ -475,8 +475,8 @@ test "@typeId" { assert(@typeId(@typeOf(undefined)) == Tid.UndefinedLiteral); assert(@typeId(@typeOf(null)) == Tid.NullLiteral); assert(@typeId(?i32) == Tid.Nullable); - assert(@typeId(%i32) == Tid.ErrorUnion); - assert(@typeId(error) == Tid.Error); + assert(@typeId(error!i32) == Tid.ErrorUnion); + assert(@typeId(error) == Tid.ErrorSet); assert(@typeId(AnEnum) == Tid.Enum); assert(@typeId(@typeOf(AUnionEnum.One)) == Tid.Enum); assert(@typeId(AUnionEnum) == Tid.Union); diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig index 8e103a3bc7..18a766d9fc 100644 --- a/test/cases/reflection.zig +++ b/test/cases/reflection.zig @@ -5,7 +5,7 @@ test "reflection: array, pointer, nullable, error union type child" { comptime { assert(([10]u8).Child == u8); assert((&u8).Child == u8); - assert((%u8).Child == u8); + assert((error!u8).Payload == u8); assert((?u8).Child == u8); } } diff --git a/test/cases/switch.zig b/test/cases/switch.zig index 156333c571..a0ac646160 100644 --- a/test/cases/switch.zig +++ b/test/cases/switch.zig @@ -225,7 +225,7 @@ fn switchWithUnreachable(x: i32) i32 { return 10; } -fn return_a_number() !i32 { +fn return_a_number() error!i32 { return 1; } diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig index aa9deea28d..136e8834e6 100644 --- a/test/cases/switch_prong_err_enum.zig +++ b/test/cases/switch_prong_err_enum.zig @@ -2,7 +2,7 @@ const assert = @import("std").debug.assert; var read_count: u64 = 0; -fn readOnce() !u64 { +fn readOnce() error!u64 { read_count += 1; return read_count; } @@ -12,7 +12,7 @@ const FormValue = union(enum) { Other: bool, }; -fn doThing(form_id: u64) !FormValue { +fn doThing(form_id: u64) error!FormValue { return switch (form_id) { 17 => FormValue { .Address = try readOnce() }, else => error.InvalidDebugInfo, diff --git a/test/cases/try.zig b/test/cases/try.zig index 6878a7c98d..4a0425e22e 100644 --- a/test/cases/try.zig +++ b/test/cases/try.zig @@ -17,7 +17,7 @@ fn tryOnErrorUnionImpl() void { assert(x == 11); } -fn returnsTen() !i32 { +fn returnsTen() error!i32 { return 10; } @@ -29,7 +29,7 @@ test "try without vars" { assert(result2 == 1); } -fn failIfTrue(ok: bool) !void { +fn failIfTrue(ok: bool) error!void { if (ok) { return error.ItBroke; } else { diff --git a/test/cases/union.zig b/test/cases/union.zig index ea8a9da188..dc2a7c3414 100644 --- a/test/cases/union.zig +++ b/test/cases/union.zig @@ -13,7 +13,7 @@ const Agg = struct { const v1 = Value { .Int = 1234 }; const v2 = Value { .Array = []u8{3} ** 9 }; -const err = (%Agg)(Agg { +const err = (error!Agg)(Agg { .val1 = v1, .val2 = v2, }); diff --git a/test/cases/while.zig b/test/cases/while.zig index 0b3d2660ed..33d5a5623a 100644 --- a/test/cases/while.zig +++ b/test/cases/while.zig @@ -50,7 +50,7 @@ fn runContinueAndBreakTest() void { test "return with implicit cast from while loop" { returnWithImplicitCastFromWhileLoopTest() catch unreachable; } -fn returnWithImplicitCastFromWhileLoopTest() !void { +fn returnWithImplicitCastFromWhileLoopTest() error!void { while (true) { return; } @@ -116,7 +116,7 @@ test "while with error union condition" { } var numbers_left: i32 = undefined; -fn getNumberOrErr() !i32 { +fn getNumberOrErr() error!i32 { return if (numbers_left == 0) error.OutOfNumbers else x: { @@ -204,7 +204,7 @@ fn testContinueOuter() void { fn returnNull() ?i32 { return null; } fn returnMaybe(x: i32) ?i32 { return x; } -fn returnError() %i32 { return error.YouWantedAnError; } -fn returnSuccess(x: i32) %i32 { return x; } +fn returnError() error!i32 { return error.YouWantedAnError; } +fn returnSuccess(x: i32) error!i32 { return x; } fn returnFalse() bool { return false; } fn returnTrue() bool { return true; } -- cgit v1.2.3 From b8f59e14cdbf90cf724ed9e721c1909293f41b3b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 2 Feb 2018 18:13:32 -0500 Subject: *WIP* error sets - correctly resolve inferred error sets --- TODO | 12 ++++ doc/langref.html.in | 2 +- src/all_types.hpp | 5 +- src/analyze.cpp | 42 ++++++++++-- src/ast_render.cpp | 4 +- src/codegen.cpp | 22 ++++++- src/ir.cpp | 167 +++++++++++++++++++++++++++++++++++++++--------- src/ir_print.cpp | 2 + src/parser.cpp | 3 +- src/tokenizer.cpp | 29 +++++++-- src/tokenizer.hpp | 2 + std/debug/index.zig | 1 + std/io.zig | 8 ++- std/mem.zig | 10 +-- std/os/index.zig | 85 ++++++++++++++++-------- std/os/windows/util.zig | 45 +++++++++---- 16 files changed, 350 insertions(+), 89 deletions(-) (limited to 'src/analyze.cpp') diff --git a/TODO b/TODO index 963169d5aa..c6fcfa939a 100644 --- a/TODO +++ b/TODO @@ -13,3 +13,15 @@ then you can return void, or any error, and the error set is inferred. // TODO this is an explicit cast and should actually coerce the type erorr set casting + + +test err should be comptime if error set has 0 members + +comptime calling fn with inferred error set should give empty error set but still you can use try + +comptime err to int of empty err set and of size 1 err set + +comptime test for err + + +undefined in infer error diff --git a/doc/langref.html.in b/doc/langref.html.in index 72d1f946af..24c4b324ba 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5682,7 +5682,7 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression = TypeExpr option(ContainerInitExpression) -MultiplyOperator = "*" | "/" | "%" | "**" | "*%" +MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression diff --git a/src/all_types.hpp b/src/all_types.hpp index 304fa5651a..c94c158046 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -510,8 +510,7 @@ enum BinOpType { BinOpTypeAssignBitAnd, BinOpTypeAssignBitXor, BinOpTypeAssignBitOr, - BinOpTypeAssignBoolAnd, - BinOpTypeAssignBoolOr, + BinOpTypeAssignMergeErrorSets, BinOpTypeBoolOr, BinOpTypeBoolAnd, BinOpTypeCmpEq, @@ -537,6 +536,7 @@ enum BinOpType { BinOpTypeArrayCat, BinOpTypeArrayMult, BinOpTypeErrorUnion, + BinOpTypeMergeErrorSets, }; struct AstNodeBinOpExpr { @@ -2054,6 +2054,7 @@ enum IrBinOp { IrBinOpRemMod, IrBinOpArrayCat, IrBinOpArrayMult, + IrBinOpMergeErrorSets, }; struct IrInstructionBinOp { diff --git a/src/analyze.cpp b/src/analyze.cpp index 8ed5706a27..073a91aeff 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -530,7 +530,6 @@ TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, T TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion); entry->is_copyable = true; - assert(payload_type->type_ref); assert(payload_type->di_type); ensure_complete_type(g, payload_type); @@ -541,9 +540,16 @@ TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, T entry->data.error_union.payload_type = payload_type; if (!type_has_bits(payload_type)) { - entry->type_ref = err_set_type->type_ref; - entry->di_type = err_set_type->di_type; - + if (type_has_bits(err_set_type)) { + entry->type_ref = err_set_type->type_ref; + entry->di_type = err_set_type->di_type; + } else { + entry->zero_bits = true; + entry->di_type = g->builtin_types.entry_void->di_type; + } + } else if (!type_has_bits(err_set_type)) { + entry->type_ref = payload_type->type_ref; + entry->di_type = payload_type->di_type; } else { LLVMTypeRef elem_types[] = { err_set_type->type_ref, @@ -3841,6 +3847,27 @@ void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, Vari } } +static bool analyze_resolve_inferred_error_set(CodeGen *g, TypeTableEntry *err_set_type, AstNode *source_node) { + FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn; + if (infer_fn != nullptr) { + if (infer_fn->anal_state == FnAnalStateInvalid) { + return false; + } else if (infer_fn->anal_state == FnAnalStateReady) { + analyze_fn_body(g, infer_fn); + if (err_set_type->data.error_set.infer_fn != nullptr) { + assert(g->errors.length != 0); + return false; + } + } else { + add_node_error(g, source_node, + buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet", + buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name))); + return false; + } + } + return true; +} + void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_type_node) { TypeTableEntry *fn_type = fn_table_entry->type_entry; assert(!fn_type->data.fn.is_generic); @@ -3871,6 +3898,13 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ return; } + if (inferred_err_set_type->data.error_set.infer_fn != nullptr) { + if (!analyze_resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) { + fn_table_entry->anal_state = FnAnalStateInvalid; + return; + } + } + return_err_set_type->data.error_set.infer_fn = nullptr; return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count; return_err_set_type->data.error_set.errors = allocate(inferred_err_set_type->data.error_set.err_count); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 88895d2d36..aed4b3e6db 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -49,12 +49,12 @@ static const char *bin_op_str(BinOpType bin_op) { case BinOpTypeAssignBitAnd: return "&="; case BinOpTypeAssignBitXor: return "^="; case BinOpTypeAssignBitOr: return "|="; - case BinOpTypeAssignBoolAnd: return "&&="; - case BinOpTypeAssignBoolOr: return "||="; + case BinOpTypeAssignMergeErrorSets: return "||="; case BinOpTypeUnwrapMaybe: return "??"; case BinOpTypeArrayCat: return "++"; case BinOpTypeArrayMult: return "**"; case BinOpTypeErrorUnion: return "!"; + case BinOpTypeMergeErrorSets: return "||"; } zig_unreachable(); } diff --git a/src/codegen.cpp b/src/codegen.cpp index ea82b3fcd6..acf14474ee 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1799,6 +1799,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, case IrBinOpArrayCat: case IrBinOpArrayMult: case IrBinOpRemUnspecified: + case IrBinOpMergeErrorSets: zig_unreachable(); case IrBinOpBoolOr: return LLVMBuildOr(g->builder, op1_value, op2_value, ""); @@ -2188,6 +2189,9 @@ static LLVMValueRef ir_render_err_to_int(CodeGen *g, IrExecutable *executable, I return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), g->err_tag_type, wanted_type, target_val); } else if (actual_type->id == TypeTableEntryIdErrorUnion) { + // this should have been a compile time constant + assert(type_has_bits(actual_type->data.error_union.err_set_type)); + if (!type_has_bits(actual_type->data.error_union.payload_type)) { return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), g->err_tag_type, wanted_type, target_val); @@ -3428,6 +3432,10 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); + if (!type_has_bits(err_union_type->data.error_union.err_set_type)) { + return err_union_handle; + } + if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->errors_by_index.length > 1) { LLVMValueRef err_val; if (type_has_bits(payload_type)) { @@ -3490,9 +3498,11 @@ static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable assert(wanted_type->id == TypeTableEntryIdErrorUnion); TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type; + TypeTableEntry *err_set_type = wanted_type->data.error_union.err_set_type; + LLVMValueRef err_val = ir_llvm_value(g, instruction->value); - if (!type_has_bits(payload_type)) + if (!type_has_bits(payload_type) || !type_has_bits(err_set_type)) return err_val; assert(instruction->tmp_ptr); @@ -3509,6 +3519,11 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa assert(wanted_type->id == TypeTableEntryIdErrorUnion); TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type; + TypeTableEntry *err_set_type = wanted_type->data.error_union.err_set_type; + + if (!type_has_bits(err_set_type)) { + return ir_llvm_value(g, instruction->value); + } LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref); @@ -4328,9 +4343,14 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { case TypeTableEntryIdErrorUnion: { TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; + TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type; if (!type_has_bits(payload_type)) { + assert(type_has_bits(err_set_type)); uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0; return LLVMConstInt(g->err_tag_type->type_ref, value, false); + } else if (!type_has_bits(err_set_type)) { + assert(type_has_bits(payload_type)); + return gen_const_val(g, const_val->data.x_err_union.payload); } else { LLVMValueRef err_tag_value; LLVMValueRef err_payload_value; diff --git a/src/ir.cpp b/src/ir.cpp index 2e4a13927b..12dc76e972 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2869,10 +2869,8 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor); case BinOpTypeAssignBitOr: return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr); - case BinOpTypeAssignBoolAnd: - return ir_gen_assign_op(irb, scope, node, IrBinOpBoolAnd); - case BinOpTypeAssignBoolOr: - return ir_gen_assign_op(irb, scope, node, IrBinOpBoolOr); + case BinOpTypeAssignMergeErrorSets: + return ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets); case BinOpTypeBoolOr: return ir_gen_bool_or(irb, scope, node); case BinOpTypeBoolAnd: @@ -2919,6 +2917,8 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat); case BinOpTypeArrayMult: return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult); + case BinOpTypeMergeErrorSets: + return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets); case BinOpTypeUnwrapMaybe: return ir_gen_maybe_ok_or(irb, scope, node); case BinOpTypeErrorUnion: @@ -5420,6 +5420,7 @@ static TypeTableEntry *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, } } assert(index == count); + assert(count != 0); buf_appendf(&err_set_type->name, "}"); @@ -5453,21 +5454,21 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A uint32_t err_count = node->data.err_set_decl.decls.length; - if (err_count == 0) { - add_node_error(irb->codegen, node, buf_sprintf("empty error set")); - return irb->codegen->invalid_instruction; - } - Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error set", node); TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); buf_init_from_buf(&err_set_type->name, type_name); err_set_type->is_copyable = true; - err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref; - err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type; err_set_type->data.error_set.err_count = err_count; - err_set_type->data.error_set.errors = allocate(err_count); - irb->codegen->error_di_types.append(&err_set_type->di_type); + if (err_count == 0) { + err_set_type->zero_bits = true; + err_set_type->di_type = irb->codegen->builtin_types.entry_void->di_type; + } else { + err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref; + err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type; + irb->codegen->error_di_types.append(&err_set_type->di_type); + err_set_type->data.error_set.errors = allocate(err_count); + } for (uint32_t i = 0; i < err_count; i += 1) { AstNode *symbol_node = node->data.err_set_decl.decls.at(i); @@ -6657,6 +6658,27 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, return ImplicitCastMatchResultNo; } +static bool resolve_inferred_error_set(IrAnalyze *ira, TypeTableEntry *err_set_type, AstNode *source_node) { + FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn; + if (infer_fn != nullptr) { + if (infer_fn->anal_state == FnAnalStateInvalid) { + return false; + } else if (infer_fn->anal_state == FnAnalStateReady) { + analyze_fn_body(ira->codegen, infer_fn); + if (err_set_type->data.error_set.infer_fn != nullptr) { + assert(ira->codegen->errors.length != 0); + return false; + } + } else { + ir_add_error_node(ira, source_node, + buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet", + buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name))); + return false; + } + } + return true; +} + static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) { assert(instruction_count >= 1); IrInstruction *prev_inst = instructions[0]; @@ -6670,6 +6692,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { err_set_type = prev_inst->value.type; errors = allocate(ira->codegen->errors_by_index.length); + if (!resolve_inferred_error_set(ira, err_set_type, prev_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = error_entry; @@ -6717,6 +6742,10 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod prev_inst = cur_inst; continue; } + + if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } // if err_set_type is a superset of cur_type, keep err_set_type. // if cur_type is a superset of err_set_type, switch err_set_type to cur_type bool prev_is_superset = true; @@ -6778,6 +6807,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = nullptr; } + if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; errors[error_entry->value] = error_entry; @@ -6820,6 +6852,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (err_set_type == ira->codegen->builtin_types.entry_global_error_set) { continue; } + if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } if (err_set_type == nullptr) { err_set_type = cur_type; errors = allocate(ira->codegen->errors_by_index.length); @@ -7543,6 +7578,8 @@ static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *sou assert(contained_set->id == TypeTableEntryIdErrorSet); assert(container_set->id == TypeTableEntryIdErrorSet); + zig_panic("TODO explicit error set cast"); + if (container_set->data.error_set.infer_fn == nullptr && container_set != ira->codegen->builtin_types.entry_global_error_set) { @@ -8058,6 +8095,34 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc return result; } + TypeTableEntry *err_set_type; + if (err_type->id == TypeTableEntryIdErrorUnion) { + err_set_type = err_type->data.error_union.err_set_type; + } else if (err_type->id == TypeTableEntryIdErrorSet) { + err_set_type = err_type; + } else { + zig_unreachable(); + } + if (err_set_type != ira->codegen->builtin_types.entry_global_error_set) { + if (!resolve_inferred_error_set(ira, err_set_type, source_instr->source_node)) { + return ira->codegen->invalid_instruction; + } + if (err_set_type->data.error_set.err_count == 0) { + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + result->value.type = wanted_type; + bigint_init_unsigned(&result->value.data.x_bigint, 0); + return result; + } else if (err_set_type->data.error_set.err_count == 1) { + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + result->value.type = wanted_type; + ErrorTableEntry *err = err_set_type->data.error_set.errors[0]; + bigint_init_unsigned(&result->value.data.x_bigint, err->value); + return result; + } + } + BigInt bn; bigint_init_unsigned(&bn, ira->codegen->errors_by_index.length); if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { @@ -9053,6 +9118,7 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val, case IrBinOpArrayCat: case IrBinOpArrayMult: case IrBinOpRemUnspecified: + case IrBinOpMergeErrorSets: zig_unreachable(); case IrBinOpBinOr: assert(is_int); @@ -9625,6 +9691,45 @@ static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp return get_array_type(ira->codegen, child_type, new_array_len); } +static TypeTableEntry *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) { + TypeTableEntry *op1_type = ir_resolve_type(ira, instruction->op1->other); + if (type_is_invalid(op1_type)) + return ira->codegen->builtin_types.entry_invalid; + + TypeTableEntry *op2_type = ir_resolve_type(ira, instruction->op2->other); + if (type_is_invalid(op2_type)) + return ira->codegen->builtin_types.entry_invalid; + + if (op1_type == ira->codegen->builtin_types.entry_global_error_set || + op2_type == ira->codegen->builtin_types.entry_global_error_set) + { + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = ira->codegen->builtin_types.entry_global_error_set; + return ira->codegen->builtin_types.entry_type; + } + + if (!resolve_inferred_error_set(ira, op1_type, instruction->op1->other->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + + if (!resolve_inferred_error_set(ira, op2_type, instruction->op2->other->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + + ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length); + for (uint32_t i = 0; i < op1_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i]; + errors[error_entry->value] = error_entry; + } + TypeTableEntry *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type); + free(errors); + + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = result_type; + return ira->codegen->builtin_types.entry_type; +} + static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrBinOp op_id = bin_op_instruction->op_id; switch (op_id) { @@ -9666,6 +9771,8 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi return ir_analyze_array_cat(ira, bin_op_instruction); case IrBinOpArrayMult: return ir_analyze_array_mult(ira, bin_op_instruction); + case IrBinOpMergeErrorSets: + return ir_analyze_merge_error_sets(ira, bin_op_instruction); } zig_unreachable(); } @@ -11605,6 +11712,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru } err_set_type = err_entry->set_with_only_this_in_it; } else { + if (!resolve_inferred_error_set(ira, child_type, field_ptr_instruction->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } ErrorTableEntry *err_entry = find_err_table_entry(child_type, field_name); if (err_entry == nullptr) { ir_add_error(ira, &field_ptr_instruction->base, @@ -14623,6 +14733,19 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc } } + TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type; + if (!resolve_inferred_error_set(ira, err_set_type, instruction->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (err_set_type != ira->codegen->builtin_types.entry_global_error_set && + err_set_type->data.error_set.err_count == 0) + { + assert(err_set_type->data.error_set.infer_fn == nullptr); + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_bool = false; + return ira->codegen->builtin_types.entry_bool; + } + ir_build_test_err_from(&ira->new_irb, &instruction->base, value); return ira->codegen->builtin_types.entry_bool; } else if (type_entry->id == TypeTableEntryIdErrorSet) { @@ -14861,22 +14984,8 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira } } } else if (switch_type->id == TypeTableEntryIdErrorSet) { - FnTableEntry *infer_fn = switch_type->data.error_set.infer_fn; - if (infer_fn != nullptr) { - if (infer_fn->anal_state == FnAnalStateInvalid) { - return ira->codegen->builtin_types.entry_invalid; - } else if (infer_fn->anal_state == FnAnalStateReady) { - analyze_fn_body(ira->codegen, infer_fn); - if (switch_type->data.error_set.infer_fn != nullptr) { - assert(ira->codegen->errors.length != 0); - return ira->codegen->builtin_types.entry_invalid; - } - } else { - ir_add_error(ira, &instruction->base, - buf_sprintf("cannot switch on inferred error set '%s': function '%s' not fully analyzed yet", - buf_ptr(&switch_type->name), buf_ptr(&switch_type->data.error_set.infer_fn->symbol_name))); - return ira->codegen->builtin_types.entry_invalid; - } + if (!resolve_inferred_error_set(ira, switch_type, target_value->source_node)) { + return ira->codegen->builtin_types.entry_invalid; } AstNode **field_prev_uses = allocate(ira->codegen->errors_by_index.length); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 781cd4a1e3..f2c0d6a5b4 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -130,6 +130,8 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) { return "++"; case IrBinOpArrayMult: return "**"; + case IrBinOpMergeErrorSets: + return "||"; } zig_unreachable(); } diff --git a/src/parser.cpp b/src/parser.cpp index 5034a95567..1a3b568a2e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1088,12 +1088,13 @@ static BinOpType tok_to_mult_op(Token *token) { case TokenIdSlash: return BinOpTypeDiv; case TokenIdPercent: return BinOpTypeMod; case TokenIdBang: return BinOpTypeErrorUnion; + case TokenIdBarBar: return BinOpTypeMergeErrorSets; default: return BinOpTypeInvalid; } } /* -MultiplyOperator = "!" | "*" | "/" | "%" | "**" | "*%" +MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" */ static BinOpType ast_parse_mult_op(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index f98c0c8344..44d838a723 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -195,7 +195,8 @@ enum TokenizeState { TokenizeStateSawMinusPercent, TokenizeStateSawAmpersand, TokenizeStateSawCaret, - TokenizeStateSawPipe, + TokenizeStateSawBar, + TokenizeStateSawBarBar, TokenizeStateLineComment, TokenizeStateLineString, TokenizeStateLineStringEnd, @@ -594,7 +595,7 @@ void tokenize(Buf *buf, Tokenization *out) { break; case '|': begin_token(&t, TokenIdBinOr); - t.state = TokenizeStateSawPipe; + t.state = TokenizeStateSawBar; break; case '=': begin_token(&t, TokenIdEq); @@ -888,13 +889,17 @@ void tokenize(Buf *buf, Tokenization *out) { continue; } break; - case TokenizeStateSawPipe: + case TokenizeStateSawBar: switch (c) { case '=': set_token_id(&t, t.cur_tok, TokenIdBitOrEq); end_token(&t); t.state = TokenizeStateStart; break; + case '|': + set_token_id(&t, t.cur_tok, TokenIdBarBar); + t.state = TokenizeStateSawBarBar; + break; default: t.pos -= 1; end_token(&t); @@ -902,6 +907,19 @@ void tokenize(Buf *buf, Tokenization *out) { continue; } break; + case TokenizeStateSawBarBar: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdBarBarEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } case TokenizeStateSawSlash: switch (c) { case '/': @@ -1428,7 +1446,7 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateSawDash: case TokenizeStateSawAmpersand: case TokenizeStateSawCaret: - case TokenizeStateSawPipe: + case TokenizeStateSawBar: case TokenizeStateSawEq: case TokenizeStateSawBang: case TokenizeStateSawLessThan: @@ -1443,6 +1461,7 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateSawMinusPercent: case TokenizeStateLineString: case TokenizeStateLineStringEnd: + case TokenizeStateSawBarBar: end_token(&t); break; case TokenizeStateSawDotDot: @@ -1475,6 +1494,7 @@ const char * token_name(TokenId id) { case TokenIdArrow: return "->"; case TokenIdAtSign: return "@"; case TokenIdBang: return "!"; + case TokenIdBarBar: return "||"; case TokenIdBinOr: return "|"; case TokenIdBinXor: return "^"; case TokenIdBitAndEq: return "&="; @@ -1577,6 +1597,7 @@ const char * token_name(TokenId id) { case TokenIdTimesEq: return "*="; case TokenIdTimesPercent: return "*%"; case TokenIdTimesPercentEq: return "*%="; + case TokenIdBarBarEq: return "||="; } return "(invalid token)"; } diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 749f72f419..92a3b8de0d 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -17,6 +17,8 @@ enum TokenId { TokenIdArrow, TokenIdAtSign, TokenIdBang, + TokenIdBarBar, + TokenIdBarBarEq, TokenIdBinOr, TokenIdBinXor, TokenIdBitAndEq, diff --git a/std/debug/index.zig b/std/debug/index.zig index 4778d63dd2..50ab1a2216 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -210,6 +210,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a } } else |err| switch (err) { error.EndOfFile => {}, + else => return err, } } else |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { diff --git a/std/io.zig b/std/io.zig index e110d4ddf5..dbca37745a 100644 --- a/std/io.zig +++ b/std/io.zig @@ -102,12 +102,14 @@ pub const File = struct { /// The OS-specific file descriptor or file handle. handle: os.FileHandle, + const OpenError = os.WindowsOpenError || os.PosixOpenError; + /// `path` may need to be copied in memory to add a null terminating byte. In this case /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned. /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. /// Call close to clean up. - pub fn openRead(path: []const u8, allocator: ?&mem.Allocator) !File { + pub fn openRead(path: []const u8, allocator: ?&mem.Allocator) OpenError!File { if (is_posix) { const flags = system.O_LARGEFILE|system.O_RDONLY; const fd = try os.posixOpen(path, flags, 0, allocator); @@ -338,7 +340,9 @@ pub const File = struct { } } - fn write(self: &File, bytes: []const u8) !void { + const WriteError = os.WindowsWriteError || os.PosixWriteError; + + fn write(self: &File, bytes: []const u8) WriteError!void { if (is_posix) { try os.posixWrite(self.handle, bytes); } else if (is_windows) { diff --git a/std/mem.zig b/std/mem.zig index 1c75d92541..25c0648888 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -5,12 +5,12 @@ const math = std.math; const builtin = @import("builtin"); pub const Allocator = struct { - const Errors = error {OutOfMemory}; + const Error = error {OutOfMemory}; /// Allocate byte_count bytes and return them in a slice, with the /// slice's pointer aligned at least to alignment bytes. /// The returned newly allocated memory is undefined. - allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Errors![]u8, + allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8, /// If `new_byte_count > old_mem.len`: /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn. @@ -21,7 +21,7 @@ pub const Allocator = struct { /// * alignment <= alignment of old_mem.ptr /// /// The returned newly allocated memory is undefined. - reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Errors![]u8, + reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` freeFn: fn (self: &Allocator, old_mem: []u8) void, @@ -42,7 +42,7 @@ pub const Allocator = struct { fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { - const byte_count = try math.mul(usize, @sizeOf(T), n); + const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; const byte_slice = try self.allocFn(self, byte_count, alignment); // This loop should get optimized out in ReleaseFast mode for (byte_slice) |*byte| { @@ -63,7 +63,7 @@ pub const Allocator = struct { } const old_byte_slice = ([]u8)(old_mem); - const byte_count = try math.mul(usize, @sizeOf(T), n); + const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment); // This loop should get optimized out in ReleaseFast mode for (byte_slice[old_byte_slice.len..]) |*byte| { diff --git a/std/os/index.zig b/std/os/index.zig index 224c4ba328..a303ee4be1 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -38,6 +38,9 @@ pub const windowsLoadDll = windows_util.windowsLoadDll; pub const windowsUnloadDll = windows_util.windowsUnloadDll; pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; +pub const WindowsOpenError = windows_util.OpenError; +pub const WindowsWriteError = windows_util.WriteError; + pub const FileHandle = if (is_windows) windows.HANDLE else i32; const debug = std.debug; @@ -188,8 +191,21 @@ pub fn posixRead(fd: i32, buf: []u8) !void { } } +pub const PosixWriteError = error { + WouldBlock, + FileClosed, + DestinationAddressRequired, + DiskQuota, + FileTooBig, + InputOutput, + NoSpaceLeft, + AccessDenied, + BrokenPipe, + Unexpected, +}; + /// Calls POSIX write, and keeps trying if it gets interrupted. -pub fn posixWrite(fd: i32, bytes: []const u8) !void { +pub fn posixWrite(fd: i32, bytes: []const u8) PosixWriteError!void { while (true) { const write_ret = posix.write(fd, bytes.ptr, bytes.len); const write_err = posix.getErrno(write_ret); @@ -197,15 +213,15 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { return switch (write_err) { posix.EINTR => continue, posix.EINVAL, posix.EFAULT => unreachable, - posix.EAGAIN => error.WouldBlock, - posix.EBADF => error.FileClosed, - posix.EDESTADDRREQ => error.DestinationAddressRequired, - posix.EDQUOT => error.DiskQuota, - posix.EFBIG => error.FileTooBig, - posix.EIO => error.InputOutput, - posix.ENOSPC => error.NoSpaceLeft, - posix.EPERM => error.AccessDenied, - posix.EPIPE => error.BrokenPipe, + posix.EAGAIN => PosixWriteError.WouldBlock, + posix.EBADF => PosixWriteError.FileClosed, + posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired, + posix.EDQUOT => PosixWriteError.DiskQuota, + posix.EFBIG => PosixWriteError.FileTooBig, + posix.EIO => PosixWriteError.InputOutput, + posix.ENOSPC => PosixWriteError.NoSpaceLeft, + posix.EPERM => PosixWriteError.AccessDenied, + posix.EPIPE => PosixWriteError.BrokenPipe, else => unexpectedErrorPosix(write_err), }; } @@ -213,13 +229,32 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { } } +pub const PosixOpenError = error { + OutOfMemory, + AccessDenied, + FileTooBig, + IsDir, + SymLinkLoop, + ProcessFdQuotaExceeded, + NameTooLong, + SystemFdQuotaExceeded, + NoDevice, + PathNotFound, + SystemResources, + NoSpaceLeft, + NotDir, + AccessDenied, + PathAlreadyExists, + Unexpected, +}; + /// ::file_path may need to be copied in memory to add a null terminating byte. In this case /// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed /// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned. /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. /// Calls POSIX open, keeps trying if it gets interrupted, and translates /// the return value into zig errors. -pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Allocator) !i32 { +pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Allocator) PosixOpenError!i32 { var stack_buf: [max_noalloc_path_len]u8 = undefined; var path0: []u8 = undefined; var need_free = false; @@ -247,20 +282,20 @@ pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Al posix.EFAULT => unreachable, posix.EINVAL => unreachable, - posix.EACCES => error.AccessDenied, - posix.EFBIG, posix.EOVERFLOW => error.FileTooBig, - posix.EISDIR => error.IsDir, - posix.ELOOP => error.SymLinkLoop, - posix.EMFILE => error.ProcessFdQuotaExceeded, - posix.ENAMETOOLONG => error.NameTooLong, - posix.ENFILE => error.SystemFdQuotaExceeded, - posix.ENODEV => error.NoDevice, - posix.ENOENT => error.PathNotFound, - posix.ENOMEM => error.SystemResources, - posix.ENOSPC => error.NoSpaceLeft, - posix.ENOTDIR => error.NotDir, - posix.EPERM => error.AccessDenied, - posix.EEXIST => error.PathAlreadyExists, + posix.EACCES => PosixOpenError.AccessDenied, + posix.EFBIG, posix.EOVERFLOW => PosixOpenError.FileTooBig, + posix.EISDIR => PosixOpenError.IsDir, + posix.ELOOP => PosixOpenError.SymLinkLoop, + posix.EMFILE => PosixOpenError.ProcessFdQuotaExceeded, + posix.ENAMETOOLONG => PosixOpenError.NameTooLong, + posix.ENFILE => PosixOpenError.SystemFdQuotaExceeded, + posix.ENODEV => PosixOpenError.NoDevice, + posix.ENOENT => PosixOpenError.PathNotFound, + posix.ENOMEM => PosixOpenError.SystemResources, + posix.ENOSPC => PosixOpenError.NoSpaceLeft, + posix.ENOTDIR => PosixOpenError.NotDir, + posix.EPERM => PosixOpenError.AccessDenied, + posix.EEXIST => PosixOpenError.PathAlreadyExists, else => unexpectedErrorPosix(err), }; } diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 08da342e0b..d03092ff14 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -26,16 +26,25 @@ pub fn windowsClose(handle: windows.HANDLE) void { assert(windows.CloseHandle(handle) != 0); } -pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) !void { +pub const WriteError = error { + SystemResources, + OperationAborted, + SystemResources, + IoPending, + BrokenPipe, + Unexpected, +}; + +pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { if (windows.WriteFile(handle, @ptrCast(&const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { const err = windows.GetLastError(); return switch (err) { - windows.ERROR.INVALID_USER_BUFFER => error.SystemResources, - windows.ERROR.NOT_ENOUGH_MEMORY => error.SystemResources, - windows.ERROR.OPERATION_ABORTED => error.OperationAborted, - windows.ERROR.NOT_ENOUGH_QUOTA => error.SystemResources, - windows.ERROR.IO_PENDING => error.IoPending, - windows.ERROR.BROKEN_PIPE => error.BrokenPipe, + windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, + windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources, + windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted, + windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources, + windows.ERROR.IO_PENDING => WriteError.IoPending, + windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe, else => os.unexpectedErrorWindows(err), }; } @@ -66,12 +75,22 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { mem.indexOf(u16, name_wide, []u16{'-','p','t','y'}) != null; } +pub const OpenError = error { + SharingViolation, + PathAlreadyExists, + FileNotFound, + AccessDenied, + PipeBusy, + Unexpected, +}; + /// `file_path` may need to be copied in memory to add a null terminating byte. In this case /// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed /// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned. /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_mode: windows.DWORD, - creation_disposition: windows.DWORD, flags_and_attrs: windows.DWORD, allocator: ?&mem.Allocator) %windows.HANDLE + creation_disposition: windows.DWORD, flags_and_attrs: windows.DWORD, allocator: ?&mem.Allocator) + OpenError!windows.HANDLE { var stack_buf: [os.max_noalloc_path_len]u8 = undefined; var path0: []u8 = undefined; @@ -95,11 +114,11 @@ pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_m if (result == windows.INVALID_HANDLE_VALUE) { const err = windows.GetLastError(); return switch (err) { - windows.ERROR.SHARING_VIOLATION => error.SharingViolation, - windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => error.PathAlreadyExists, - windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, - windows.ERROR.ACCESS_DENIED => error.AccessDenied, - windows.ERROR.PIPE_BUSY => error.PipeBusy, + windows.ERROR.SHARING_VIOLATION => OpenError.SharingViolation, + windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => OpenError.PathAlreadyExists, + windows.ERROR.FILE_NOT_FOUND => OpenError.FileNotFound, + windows.ERROR.ACCESS_DENIED => OpenError.AccessDenied, + windows.ERROR.PIPE_BUSY => OpenError.PipeBusy, else => os.unexpectedErrorWindows(err), }; } -- cgit v1.2.3 From ef5e7bb4693ed7e1582fb0b68cab01e00638d615 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 3 Feb 2018 14:06:37 -0500 Subject: *WIP* error sets - an inferred error set can end up being the global one --- TODO | 2 - src/analyze.cpp | 193 ++------------------------ src/analyze.hpp | 53 +------- src/codegen.cpp | 1 + src/ir.cpp | 406 ++++++++++++++++++++++++++++++++++++++++++++----------- std/os/index.zig | 2 +- 6 files changed, 346 insertions(+), 311 deletions(-) (limited to 'src/analyze.cpp') diff --git a/TODO b/TODO index 69521fe204..67e32779d7 100644 --- a/TODO +++ b/TODO @@ -26,6 +26,4 @@ comptime test for err undefined in infer error -change readlink back to inferred error - syntax - ?a!b should be ?(a!b) but it's (?a)!b diff --git a/src/analyze.cpp b/src/analyze.cpp index 073a91aeff..be6057a309 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3372,180 +3372,6 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *so g->tld_ref_source_node_stack.pop(); } -ConstCastOnly types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type) { - ConstCastOnly result = {}; - result.id = ConstCastResultIdOk; - - if (expected_type == actual_type) - return result; - - // pointer const - if (expected_type->id == TypeTableEntryIdPointer && - actual_type->id == TypeTableEntryIdPointer && - (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) && - (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) && - actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset && - actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count && - actual_type->data.pointer.alignment >= expected_type->data.pointer.alignment) - { - ConstCastOnly child = types_match_const_cast_only(g, expected_type->data.pointer.child_type, actual_type->data.pointer.child_type); - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdPointerChild; - result.data.pointer_child = allocate_nonzero(1); - *result.data.pointer_child = child; - } - return result; - } - - // slice const - if (expected_type->id == TypeTableEntryIdStruct && actual_type->id == TypeTableEntryIdStruct && - expected_type->data.structure.is_slice && actual_type->data.structure.is_slice) - { - TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; - TypeTableEntry *expected_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry; - if ((!actual_ptr_type->data.pointer.is_const || expected_ptr_type->data.pointer.is_const) && - (!actual_ptr_type->data.pointer.is_volatile || expected_ptr_type->data.pointer.is_volatile) && - actual_ptr_type->data.pointer.bit_offset == expected_ptr_type->data.pointer.bit_offset && - actual_ptr_type->data.pointer.unaligned_bit_count == expected_ptr_type->data.pointer.unaligned_bit_count && - actual_ptr_type->data.pointer.alignment >= expected_ptr_type->data.pointer.alignment) - { - ConstCastOnly child = types_match_const_cast_only(g, expected_ptr_type->data.pointer.child_type, - actual_ptr_type->data.pointer.child_type); - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdSliceChild; - result.data.slice_child = allocate_nonzero(1); - *result.data.slice_child = child; - } - return result; - } - } - - // maybe - if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { - ConstCastOnly child = types_match_const_cast_only(g, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type); - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdNullableChild; - result.data.nullable_child = allocate_nonzero(1); - *result.data.nullable_child = child; - } - return result; - } - - // error union - if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) { - ConstCastOnly payload_child = types_match_const_cast_only(g, expected_type->data.error_union.payload_type, actual_type->data.error_union.payload_type); - if (payload_child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdErrorUnionPayload; - result.data.error_union_payload = allocate_nonzero(1); - *result.data.error_union_payload = payload_child; - return result; - } - ConstCastOnly error_set_child = types_match_const_cast_only(g, expected_type->data.error_union.err_set_type, actual_type->data.error_union.err_set_type); - if (error_set_child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdErrorUnionErrorSet; - result.data.error_union_error_set = allocate_nonzero(1); - *result.data.error_union_error_set = error_set_child; - return result; - } - return result; - } - - // error set - if (expected_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdErrorSet) { - TypeTableEntry *contained_set = actual_type; - TypeTableEntry *container_set = expected_type; - - if (container_set == g->builtin_types.entry_global_error_set || container_set->data.error_set.infer_fn != nullptr) { - return result; - } - - ErrorTableEntry **errors = allocate(g->errors_by_index.length); - for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; - errors[error_entry->value] = error_entry; - } - for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) { - ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i]; - ErrorTableEntry *error_entry = errors[contained_error_entry->value]; - if (error_entry == nullptr) { - if (result.id == ConstCastResultIdOk) { - result.id = ConstCastResultIdErrSet; - } - result.data.error_set.missing_errors.append(contained_error_entry); - } - } - free(errors); - return result; - } - - // fn - if (expected_type->id == TypeTableEntryIdFn && - actual_type->id == TypeTableEntryIdFn) - { - if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) { - result.id = ConstCastResultIdFnAlign; - return result; - } - if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { - result.id = ConstCastResultIdFnCC; - return result; - } - if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { - result.id = ConstCastResultIdFnVarArgs; - return result; - } - if (expected_type->data.fn.is_generic != actual_type->data.fn.is_generic) { - result.id = ConstCastResultIdFnIsGeneric; - return result; - } - if (!expected_type->data.fn.is_generic && - actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable) - { - ConstCastOnly child = types_match_const_cast_only(g, expected_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type); - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdFnReturnType; - result.data.return_type = allocate_nonzero(1); - *result.data.return_type = child; - } - return result; - } - if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { - result.id = ConstCastResultIdFnArgCount; - return result; - } - if (expected_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) { - result.id = ConstCastResultIdFnGenericArgCount; - return result; - } - assert(expected_type->data.fn.is_generic || - expected_type->data.fn.fn_type_id.next_param_index == expected_type->data.fn.fn_type_id.param_count); - for (size_t i = 0; i < expected_type->data.fn.fn_type_id.next_param_index; i += 1) { - // note it's reversed for parameters - FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; - FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i]; - - ConstCastOnly arg_child = types_match_const_cast_only(g, actual_param_info->type, expected_param_info->type); - if (arg_child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdFnArg; - result.data.fn_arg.arg_index = i; - result.data.fn_arg.child = allocate_nonzero(1); - *result.data.fn_arg.child = arg_child; - return result; - } - - if (expected_param_info->is_noalias != actual_param_info->is_noalias) { - result.id = ConstCastResultIdFnArgNoAlias; - result.data.arg_no_alias.arg_index = i; - return result; - } - } - return result; - } - - result.id = ConstCastResultIdType; - return result; -} - Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) { // we must resolve all the use decls ImportTableEntry *import = get_scope_import(scope); @@ -3906,10 +3732,16 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ } return_err_set_type->data.error_set.infer_fn = nullptr; - return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count; - return_err_set_type->data.error_set.errors = allocate(inferred_err_set_type->data.error_set.err_count); - for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) { - return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i]; + if (type_is_global_error_set(inferred_err_set_type)) { + return_err_set_type->data.error_set.err_count = UINT32_MAX; + } else { + return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count; + if (inferred_err_set_type->data.error_set.err_count > 0) { + return_err_set_type->data.error_set.errors = allocate(inferred_err_set_type->data.error_set.err_count); + for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) { + return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i]; + } + } } } } @@ -5833,3 +5665,8 @@ ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { return var_value; } +bool type_is_global_error_set(TypeTableEntry *err_set_type) { + assert(err_set_type->id == TypeTableEntryIdErrorSet); + assert(err_set_type->data.error_set.infer_fn == nullptr); + return err_set_type->data.error_set.err_count == UINT32_MAX; +} diff --git a/src/analyze.hpp b/src/analyze.hpp index b7869d3a79..82ad05972b 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -56,6 +56,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt TypeTableEntry *container_ref_type(TypeTableEntry *type_entry); bool type_is_complete(TypeTableEntry *type_entry); bool type_is_invalid(TypeTableEntry *type_entry); +bool type_is_global_error_set(TypeTableEntry *err_set_type); bool type_has_zero_bits_known(TypeTableEntry *type_entry); void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry); ScopeDecls *get_container_scope(TypeTableEntry *type_entry); @@ -190,56 +191,4 @@ void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry); TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry); -enum ConstCastResultId { - ConstCastResultIdOk, - ConstCastResultIdErrSet, - ConstCastResultIdPointerChild, - ConstCastResultIdSliceChild, - ConstCastResultIdNullableChild, - ConstCastResultIdErrorUnionPayload, - ConstCastResultIdErrorUnionErrorSet, - ConstCastResultIdFnAlign, - ConstCastResultIdFnCC, - ConstCastResultIdFnVarArgs, - ConstCastResultIdFnIsGeneric, - ConstCastResultIdFnReturnType, - ConstCastResultIdFnArgCount, - ConstCastResultIdFnGenericArgCount, - ConstCastResultIdFnArg, - ConstCastResultIdFnArgNoAlias, - ConstCastResultIdType, -}; - -struct ConstCastErrSetMismatch { - ZigList missing_errors; -}; - -struct ConstCastOnly; - -struct ConstCastArg { - size_t arg_index; - ConstCastOnly *child; -}; - -struct ConstCastArgNoAlias { - size_t arg_index; -}; - -struct ConstCastOnly { - ConstCastResultId id; - union { - ConstCastErrSetMismatch error_set; - ConstCastOnly *pointer_child; - ConstCastOnly *slice_child; - ConstCastOnly *nullable_child; - ConstCastOnly *error_union_payload; - ConstCastOnly *error_union_error_set; - ConstCastOnly *return_type; - ConstCastArg fn_arg; - ConstCastArgNoAlias arg_no_alias; - } data; -}; - -ConstCastOnly types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type); - #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index acf14474ee..e43060f195 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5216,6 +5216,7 @@ static void define_builtin_types(CodeGen *g) { { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorSet); buf_init_from_str(&entry->name, "error"); + entry->data.error_set.err_count = UINT32_MAX; // TODO allow overriding this type and keep track of max value and emit an // error if there are too many errors declared diff --git a/src/ir.cpp b/src/ir.cpp index 721bd70f12..06b46646e8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -45,6 +45,59 @@ static LVal make_lval_addr(bool is_const, bool is_volatile) { return { true, is_const, is_volatile }; } +enum ConstCastResultId { + ConstCastResultIdOk, + ConstCastResultIdErrSet, + ConstCastResultIdErrSetGlobal, + ConstCastResultIdPointerChild, + ConstCastResultIdSliceChild, + ConstCastResultIdNullableChild, + ConstCastResultIdErrorUnionPayload, + ConstCastResultIdErrorUnionErrorSet, + ConstCastResultIdFnAlign, + ConstCastResultIdFnCC, + ConstCastResultIdFnVarArgs, + ConstCastResultIdFnIsGeneric, + ConstCastResultIdFnReturnType, + ConstCastResultIdFnArgCount, + ConstCastResultIdFnGenericArgCount, + ConstCastResultIdFnArg, + ConstCastResultIdFnArgNoAlias, + ConstCastResultIdType, + ConstCastResultIdUnresolvedInferredErrSet, +}; + +struct ConstCastErrSetMismatch { + ZigList missing_errors; +}; + +struct ConstCastOnly; + +struct ConstCastArg { + size_t arg_index; + ConstCastOnly *child; +}; + +struct ConstCastArgNoAlias { + size_t arg_index; +}; + +struct ConstCastOnly { + ConstCastResultId id; + union { + ConstCastErrSetMismatch error_set; + ConstCastOnly *pointer_child; + ConstCastOnly *slice_child; + ConstCastOnly *nullable_child; + ConstCastOnly *error_union_payload; + ConstCastOnly *error_union_error_set; + ConstCastOnly *return_type; + ConstCastArg fn_arg; + ConstCastArgNoAlias arg_no_alias; + } data; +}; + + static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope); static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval); static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction); @@ -6416,6 +6469,220 @@ static bool slice_is_const(TypeTableEntry *type) { return type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; } +static bool resolve_inferred_error_set(IrAnalyze *ira, TypeTableEntry *err_set_type, AstNode *source_node) { + assert(err_set_type->id == TypeTableEntryIdErrorSet); + FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn; + if (infer_fn != nullptr) { + if (infer_fn->anal_state == FnAnalStateInvalid) { + return false; + } else if (infer_fn->anal_state == FnAnalStateReady) { + analyze_fn_body(ira->codegen, infer_fn); + if (err_set_type->data.error_set.infer_fn != nullptr) { + assert(ira->codegen->errors.length != 0); + return false; + } + } else { + ir_add_error_node(ira, source_node, + buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet", + buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name))); + return false; + } + } + return true; +} + +static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry *expected_type, + TypeTableEntry *actual_type, AstNode *source_node) +{ + CodeGen *g = ira->codegen; + ConstCastOnly result = {}; + result.id = ConstCastResultIdOk; + + if (expected_type == actual_type) + return result; + + // pointer const + if (expected_type->id == TypeTableEntryIdPointer && + actual_type->id == TypeTableEntryIdPointer && + (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) && + (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) && + actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset && + actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count && + actual_type->data.pointer.alignment >= expected_type->data.pointer.alignment) + { + ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.pointer.child_type, actual_type->data.pointer.child_type, source_node); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdPointerChild; + result.data.pointer_child = allocate_nonzero(1); + *result.data.pointer_child = child; + } + return result; + } + + // slice const + if (expected_type->id == TypeTableEntryIdStruct && actual_type->id == TypeTableEntryIdStruct && + expected_type->data.structure.is_slice && actual_type->data.structure.is_slice) + { + TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; + TypeTableEntry *expected_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry; + if ((!actual_ptr_type->data.pointer.is_const || expected_ptr_type->data.pointer.is_const) && + (!actual_ptr_type->data.pointer.is_volatile || expected_ptr_type->data.pointer.is_volatile) && + actual_ptr_type->data.pointer.bit_offset == expected_ptr_type->data.pointer.bit_offset && + actual_ptr_type->data.pointer.unaligned_bit_count == expected_ptr_type->data.pointer.unaligned_bit_count && + actual_ptr_type->data.pointer.alignment >= expected_ptr_type->data.pointer.alignment) + { + ConstCastOnly child = types_match_const_cast_only(ira, expected_ptr_type->data.pointer.child_type, + actual_ptr_type->data.pointer.child_type, source_node); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdSliceChild; + result.data.slice_child = allocate_nonzero(1); + *result.data.slice_child = child; + } + return result; + } + } + + // maybe + if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { + ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type, source_node); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdNullableChild; + result.data.nullable_child = allocate_nonzero(1); + *result.data.nullable_child = child; + } + return result; + } + + // error union + if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) { + ConstCastOnly payload_child = types_match_const_cast_only(ira, expected_type->data.error_union.payload_type, actual_type->data.error_union.payload_type, source_node); + if (payload_child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdErrorUnionPayload; + result.data.error_union_payload = allocate_nonzero(1); + *result.data.error_union_payload = payload_child; + return result; + } + ConstCastOnly error_set_child = types_match_const_cast_only(ira, expected_type->data.error_union.err_set_type, actual_type->data.error_union.err_set_type, source_node); + if (error_set_child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdErrorUnionErrorSet; + result.data.error_union_error_set = allocate_nonzero(1); + *result.data.error_union_error_set = error_set_child; + return result; + } + return result; + } + + // error set + if (expected_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdErrorSet) { + TypeTableEntry *contained_set = actual_type; + TypeTableEntry *container_set = expected_type; + + if (!resolve_inferred_error_set(ira, container_set, source_node)) { + result.id = ConstCastResultIdUnresolvedInferredErrSet; + return result; + } + + if (type_is_global_error_set(container_set)) { + return result; + } + + if (!resolve_inferred_error_set(ira, contained_set, source_node)) { + result.id = ConstCastResultIdUnresolvedInferredErrSet; + return result; + } + + if (type_is_global_error_set(contained_set)) { + result.id = ConstCastResultIdErrSetGlobal; + return result; + } + + ErrorTableEntry **errors = allocate(g->errors_by_index.length); + for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; + errors[error_entry->value] = error_entry; + } + for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + if (result.id == ConstCastResultIdOk) { + result.id = ConstCastResultIdErrSet; + } + result.data.error_set.missing_errors.append(contained_error_entry); + } + } + free(errors); + return result; + } + + // fn + if (expected_type->id == TypeTableEntryIdFn && + actual_type->id == TypeTableEntryIdFn) + { + if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) { + result.id = ConstCastResultIdFnAlign; + return result; + } + if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { + result.id = ConstCastResultIdFnCC; + return result; + } + if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { + result.id = ConstCastResultIdFnVarArgs; + return result; + } + if (expected_type->data.fn.is_generic != actual_type->data.fn.is_generic) { + result.id = ConstCastResultIdFnIsGeneric; + return result; + } + if (!expected_type->data.fn.is_generic && + actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable) + { + ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type, source_node); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdFnReturnType; + result.data.return_type = allocate_nonzero(1); + *result.data.return_type = child; + } + return result; + } + if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { + result.id = ConstCastResultIdFnArgCount; + return result; + } + if (expected_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) { + result.id = ConstCastResultIdFnGenericArgCount; + return result; + } + assert(expected_type->data.fn.is_generic || + expected_type->data.fn.fn_type_id.next_param_index == expected_type->data.fn.fn_type_id.param_count); + for (size_t i = 0; i < expected_type->data.fn.fn_type_id.next_param_index; i += 1) { + // note it's reversed for parameters + FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; + FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i]; + + ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type, expected_param_info->type, source_node); + if (arg_child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdFnArg; + result.data.fn_arg.arg_index = i; + result.data.fn_arg.child = allocate_nonzero(1); + *result.data.fn_arg.child = arg_child; + return result; + } + + if (expected_param_info->is_noalias != actual_param_info->is_noalias) { + result.id = ConstCastResultIdFnArgNoAlias; + result.data.arg_no_alias.arg_index = i; + return result; + } + } + return result; + } + + result.id = ConstCastResultIdType; + return result; +} + enum ImplicitCastMatchResult { ImplicitCastMatchResultNo, ImplicitCastMatchResultYes, @@ -6425,7 +6692,8 @@ enum ImplicitCastMatchResult { static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type, TypeTableEntry *actual_type, IrInstruction *value) { - ConstCastOnly const_cast_result = types_match_const_cast_only(ira->codegen, expected_type, actual_type); + AstNode *source_node = value->source_node; + ConstCastOnly const_cast_result = types_match_const_cast_only(ira, expected_type, actual_type, source_node); if (const_cast_result.id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6520,7 +6788,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6539,7 +6807,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *array_type = actual_type->data.pointer.child_type; if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, array_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6555,7 +6823,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, expected_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6570,7 +6838,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, expected_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6650,7 +6918,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicitly take a const pointer to something if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (types_match_const_cast_only(ira->codegen, expected_type, const_ptr_actual).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, expected_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } } @@ -6658,27 +6926,6 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, return ImplicitCastMatchResultNo; } -static bool resolve_inferred_error_set(IrAnalyze *ira, TypeTableEntry *err_set_type, AstNode *source_node) { - FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn; - if (infer_fn != nullptr) { - if (infer_fn->anal_state == FnAnalStateInvalid) { - return false; - } else if (infer_fn->anal_state == FnAnalStateReady) { - analyze_fn_body(ira->codegen, infer_fn); - if (err_set_type->data.error_set.infer_fn != nullptr) { - assert(ira->codegen->errors.length != 0); - return false; - } - } else { - ir_add_error_node(ira, source_node, - buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet", - buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name))); - return false; - } - } - return true; -} - static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) { assert(instruction_count >= 1); IrInstruction *prev_inst = instructions[0]; @@ -6687,17 +6934,19 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } ErrorTableEntry **errors = nullptr; TypeTableEntry *err_set_type = nullptr; - if (prev_inst->value.type == ira->codegen->builtin_types.entry_global_error_set) { - err_set_type = ira->codegen->builtin_types.entry_global_error_set; - } else if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { - err_set_type = prev_inst->value.type; - errors = allocate(ira->codegen->errors_by_index.length); - if (!resolve_inferred_error_set(ira, err_set_type, prev_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } - for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; - errors[error_entry->value] = error_entry; + if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { + if (type_is_global_error_set(prev_inst->value.type)) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + } else { + err_set_type = prev_inst->value.type; + if (!resolve_inferred_error_set(ira, err_set_type, prev_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + errors = allocate(ira->codegen->errors_by_index.length); + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; + errors[error_entry->value] = error_entry; + } } } @@ -6734,18 +6983,18 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (prev_type->id == TypeTableEntryIdErrorSet) { assert(err_set_type != nullptr); if (cur_type->id == TypeTableEntryIdErrorSet) { - if (err_set_type == ira->codegen->builtin_types.entry_global_error_set) { + if (type_is_global_error_set(err_set_type)) { continue; } - if (cur_type == ira->codegen->builtin_types.entry_global_error_set) { + if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; } - if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } // if err_set_type is a superset of cur_type, keep err_set_type. // if cur_type is a superset of err_set_type, switch err_set_type to cur_type bool prev_is_superset = true; @@ -6791,12 +7040,15 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod assert(errors != nullptr); continue; } else if (cur_type->id == TypeTableEntryIdErrorUnion) { - if (err_set_type == ira->codegen->builtin_types.entry_global_error_set) { + if (type_is_global_error_set(err_set_type)) { prev_inst = cur_inst; continue; } TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type; - if (cur_err_set_type == ira->codegen->builtin_types.entry_global_error_set) { + if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (type_is_global_error_set(cur_err_set_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; @@ -6807,9 +7059,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = nullptr; } - if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; errors[error_entry->value] = error_entry; @@ -6845,11 +7094,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (prev_type->id == TypeTableEntryIdArray) { convert_to_const_slice = true; } - if (cur_type == ira->codegen->builtin_types.entry_global_error_set) { + if (type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; } - if (err_set_type == ira->codegen->builtin_types.entry_global_error_set) { + if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) { continue; } if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { @@ -6884,11 +7133,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } - if (types_match_const_cast_only(ira->codegen, prev_type, cur_type).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) { continue; } - if (types_match_const_cast_only(ira->codegen, cur_type, prev_type).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, cur_type, prev_type, source_node).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } @@ -6911,26 +7160,26 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } if (prev_type->id == TypeTableEntryIdErrorUnion && - types_match_const_cast_only(ira->codegen, prev_type->data.error_union.payload_type, cur_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type, source_node).id == ConstCastResultIdOk) { continue; } if (cur_type->id == TypeTableEntryIdErrorUnion && - types_match_const_cast_only(ira->codegen, cur_type->data.error_union.payload_type, prev_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, source_node).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } if (prev_type->id == TypeTableEntryIdMaybe && - types_match_const_cast_only(ira->codegen, prev_type->data.maybe.child_type, cur_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, source_node).id == ConstCastResultIdOk) { continue; } if (cur_type->id == TypeTableEntryIdMaybe && - types_match_const_cast_only(ira->codegen, cur_type->data.maybe.child_type, prev_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, source_node).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; @@ -6968,7 +7217,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && cur_type->data.array.len != prev_type->data.array.len && - types_match_const_cast_only(ira->codegen, cur_type->data.array.child_type, prev_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, cur_type->data.array.child_type, prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { convert_to_const_slice = true; prev_inst = cur_inst; @@ -6977,7 +7226,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && cur_type->data.array.len != prev_type->data.array.len && - types_match_const_cast_only(ira->codegen, prev_type->data.array.child_type, cur_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, prev_type->data.array.child_type, cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { convert_to_const_slice = true; continue; @@ -6986,8 +7235,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) && (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || cur_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, - cur_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, + cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { convert_to_const_slice = false; continue; @@ -6996,8 +7245,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) && (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || prev_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, - prev_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, + prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { prev_inst = cur_inst; convert_to_const_slice = false; @@ -7581,7 +7830,7 @@ static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *sou zig_panic("TODO explicit error set cast"); if (container_set->data.error_set.infer_fn == nullptr && - container_set != ira->codegen->builtin_types.entry_global_error_set) + !type_is_global_error_set(container_set)) { ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length); for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { @@ -8103,7 +8352,7 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc } else { zig_unreachable(); } - if (err_set_type != ira->codegen->builtin_types.entry_global_error_set) { + if (!type_is_global_error_set(err_set_type)) { if (!resolve_inferred_error_set(ira, err_set_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; } @@ -8140,6 +8389,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *wanted_type, IrInstruction *value) { TypeTableEntry *actual_type = value->value.type; + AstNode *source_node = source_instr->source_node; if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) { return ira->codegen->invalid_instruction; @@ -8149,7 +8399,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return value; // explicit match or non-const to const - if (types_match_const_cast_only(ira->codegen, wanted_type, actual_type).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } @@ -8195,7 +8445,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); } @@ -8213,7 +8463,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *array_type = actual_type->data.pointer.child_type; if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, array_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); } @@ -8229,7 +8479,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value); if (type_is_invalid(cast1->value.type)) @@ -8252,7 +8502,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value); if (type_is_invalid(cast1->value.type)) @@ -8314,7 +8564,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from child type of maybe type to maybe type if (wanted_type->id == TypeTableEntryIdMaybe) { - if (types_match_const_cast_only(ira->codegen, wanted_type->data.maybe.child_type, actual_type).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) @@ -8336,7 +8586,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from child type of error type to error type if (wanted_type->id == TypeTableEntryIdErrorUnion) { - if (types_match_const_cast_only(ira->codegen, wanted_type->data.error_union.payload_type, actual_type).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) @@ -8358,7 +8608,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); if (type_is_invalid(cast1->value.type)) @@ -8385,7 +8635,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->id != TypeTableEntryIdMaybe) { TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; - if (types_match_const_cast_only(ira->codegen, wanted_child_type, actual_type).id == ConstCastResultIdOk || + if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk || actual_type->id == TypeTableEntryIdNullLit || actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) @@ -8535,7 +8785,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from something to const pointer of it if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (types_match_const_cast_only(ira->codegen, wanted_type, const_ptr_actual).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) { return ir_analyze_cast_ref(ira, source_instr, value, wanted_type); } } @@ -9700,8 +9950,8 @@ static TypeTableEntry *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstruction if (type_is_invalid(op2_type)) return ira->codegen->builtin_types.entry_invalid; - if (op1_type == ira->codegen->builtin_types.entry_global_error_set || - op2_type == ira->codegen->builtin_types.entry_global_error_set) + if (type_is_global_error_set(op1_type) || + type_is_global_error_set(op2_type)) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = ira->codegen->builtin_types.entry_global_error_set; @@ -11716,7 +11966,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru } else if (child_type->id == TypeTableEntryIdErrorSet) { ErrorTableEntry *err_entry; TypeTableEntry *err_set_type; - if (child_type == ira->codegen->builtin_types.entry_global_error_set) { + if (type_is_global_error_set(child_type)) { auto existing_entry = ira->codegen->error_table.maybe_get(field_name); if (existing_entry) { err_entry = existing_entry->value; @@ -14764,7 +15014,7 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc if (!resolve_inferred_error_set(ira, err_set_type, instruction->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } - if (err_set_type != ira->codegen->builtin_types.entry_global_error_set && + if (!type_is_global_error_set(err_set_type) && err_set_type->data.error_set.err_count == 0) { assert(err_set_type->data.error_set.infer_fn == nullptr); diff --git a/std/os/index.zig b/std/os/index.zig index aad24cf996..e57968dcad 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1072,7 +1072,7 @@ pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) !void { } /// Read value of a symbolic link. -pub fn readLink(allocator: &Allocator, pathname: []const u8) error![]u8 { +pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 { const path_buf = try allocator.alloc(u8, pathname.len + 1); defer allocator.free(path_buf); -- cgit v1.2.3 From 21ce559c9c923e62a76ffda6c26d31ac2e062acb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 4 Feb 2018 22:06:03 -0500 Subject: add --forbid-library to help track down accidentally linking against a library --- src/all_types.hpp | 2 ++ src/analyze.cpp | 11 ----------- src/analyze.hpp | 1 - src/codegen.cpp | 5 +++++ src/codegen.hpp | 1 + src/ir.cpp | 27 +++++++++++++++++++++++---- src/main.cpp | 8 ++++++++ 7 files changed, 39 insertions(+), 16 deletions(-) (limited to 'src/analyze.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index c94c158046..869d17f0b2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1631,6 +1631,8 @@ struct CodeGen { TypeTableEntry *ptr_to_stack_trace_type; ZigList error_di_types; + + ZigList forbidden_libs; }; enum VarLinkage { diff --git a/src/analyze.cpp b/src/analyze.cpp index be6057a309..a7adbf4a3a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5609,17 +5609,6 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) { return link_lib; } -void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name) { - LinkLib *link_lib = add_link_lib(g, lib_name); - for (size_t i = 0; i < link_lib->symbols.length; i += 1) { - Buf *existing_symbol_name = link_lib->symbols.at(i); - if (buf_eql_buf(existing_symbol_name, symbol_name)) { - return; - } - } - link_lib->symbols.append(symbol_name); -} - uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) { type_ensure_zero_bits_known(g, type_entry); if (type_entry->zero_bits) return 0; diff --git a/src/analyze.hpp b/src/analyze.hpp index 82ad05972b..c0c89cf36b 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -175,7 +175,6 @@ bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry); LinkLib *create_link_lib(Buf *name); bool calling_convention_does_first_arg_return(CallingConvention cc); LinkLib *add_link_lib(CodeGen *codegen, Buf *lib); -void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name); uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry); TypeTableEntry *get_align_amt_type(CodeGen *g); diff --git a/src/codegen.cpp b/src/codegen.cpp index e43060f195..c533e66d61 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -253,6 +253,10 @@ LinkLib *codegen_add_link_lib(CodeGen *g, Buf *name) { return add_link_lib(g, name); } +void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib) { + codegen->forbidden_libs.append(lib); +} + void codegen_add_framework(CodeGen *g, const char *framework) { g->darwin_frameworks.append(buf_create_from_str(framework)); } @@ -6311,3 +6315,4 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir, } return pkg; } + diff --git a/src/codegen.hpp b/src/codegen.hpp index b29cadee55..a7a4b748c4 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -36,6 +36,7 @@ void codegen_set_kernel32_lib_dir(CodeGen *codegen, Buf *kernel32_lib_dir); void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker); void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole); void codegen_add_lib_dir(CodeGen *codegen, const char *dir); +void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib); LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib); void codegen_add_framework(CodeGen *codegen, const char *name); void codegen_add_rpath(CodeGen *codegen, const char *name); diff --git a/src/ir.cpp b/src/ir.cpp index 56ead2b906..27b39b868f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11762,6 +11762,25 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field } } +static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) { + LinkLib *link_lib = add_link_lib(ira->codegen, lib_name); + for (size_t i = 0; i < link_lib->symbols.length; i += 1) { + Buf *existing_symbol_name = link_lib->symbols.at(i); + if (buf_eql_buf(existing_symbol_name, symbol_name)) { + return; + } + } + for (size_t i = 0; i < ira->codegen->forbidden_libs.length; i += 1) { + Buf *forbidden_lib_name = ira->codegen->forbidden_libs.at(i); + if (buf_eql_buf(lib_name, forbidden_lib_name)) { + ir_add_error_node(ira, source_node, + buf_sprintf("linking against forbidden library '%s'", buf_ptr(symbol_name))); + } + } + link_lib->symbols.append(symbol_name); +} + + static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld) { bool pointer_only = false; resolve_top_level_decl(ira->codegen, tld, pointer_only, source_instruction->source_node); @@ -11777,7 +11796,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source TldVar *tld_var = (TldVar *)tld; VariableTableEntry *var = tld_var->var; if (tld_var->extern_lib_name != nullptr) { - add_link_lib_symbol(ira->codegen, tld_var->extern_lib_name, &var->name); + add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, source_instruction->source_node); } return ir_analyze_var_ptr(ira, source_instruction, var, false, false); @@ -11799,7 +11818,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source const_val->data.x_fn.fn_entry = fn_entry; if (tld_fn->extern_lib_name != nullptr) { - add_link_lib_symbol(ira->codegen, tld_fn->extern_lib_name, &fn_entry->symbol_name); + add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, source_instruction->source_node); } bool ptr_is_const = true; @@ -15839,7 +15858,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; if (tld_var->extern_lib_name != nullptr) { - add_link_lib_symbol(ira->codegen, tld_var->extern_lib_name, &var->name); + add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node); } if (lval.is_ptr) { @@ -15858,7 +15877,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, assert(fn_entry->type_entry); if (tld_fn->extern_lib_name != nullptr) { - add_link_lib_symbol(ira->codegen, tld_fn->extern_lib_name, &fn_entry->symbol_name); + add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, instruction->base.source_node); } IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope, diff --git a/src/main.cpp b/src/main.cpp index e3ef62be07..a533973de9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -66,6 +66,7 @@ static int usage(const char *arg0) { " --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides\n" " --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides\n" " --library [lib] link against lib\n" + " --forbid-library [lib] make it an error to link against lib\n" " --library-path [dir] add a directory to the library search path\n" " --linker-script [path] use a custom linker script\n" " --object [obj] add object file to build\n" @@ -309,6 +310,7 @@ int main(int argc, char **argv) { ZigList llvm_argv = {0}; ZigList lib_dirs = {0}; ZigList link_libs = {0}; + ZigList forbidden_link_libs = {0}; ZigList frameworks = {0}; int err; const char *target_arch = nullptr; @@ -592,6 +594,8 @@ int main(int argc, char **argv) { lib_dirs.append(argv[i]); } else if (strcmp(arg, "--library") == 0) { link_libs.append(argv[i]); + } else if (strcmp(arg, "--forbid-library") == 0) { + forbidden_link_libs.append(argv[i]); } else if (strcmp(arg, "--object") == 0) { objects.append(argv[i]); } else if (strcmp(arg, "--assembly") == 0) { @@ -804,6 +808,10 @@ int main(int argc, char **argv) { LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i))); link_lib->provided_explicitly = true; } + for (size_t i = 0; i < forbidden_link_libs.length; i += 1) { + Buf *forbidden_link_lib = buf_create_from_str(forbidden_link_libs.at(i)); + codegen_add_forbidden_lib(g, forbidden_link_lib); + } for (size_t i = 0; i < frameworks.length; i += 1) { codegen_add_framework(g, frameworks.at(i)); } -- cgit v1.2.3 From 32c988a2d7920fcd3da50a13a1ae9abcd57daf50 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 9 Feb 2018 00:24:23 -0500 Subject: fix build runner on windows --- src/analyze.cpp | 1 + src/codegen.cpp | 4 ++-- std/os/child_process.zig | 2 +- std/os/index.zig | 30 +++++++++++++++++++++++++++--- std/os/windows/util.zig | 8 +++++++- 5 files changed, 38 insertions(+), 7 deletions(-) (limited to 'src/analyze.cpp') diff --git a/src/analyze.cpp b/src/analyze.cpp index b6f08b7aec..bf7b6e363f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -543,6 +543,7 @@ TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, T if (type_has_bits(err_set_type)) { entry->type_ref = err_set_type->type_ref; entry->di_type = err_set_type->di_type; + g->error_di_types.append(&entry->di_type); } else { entry->zero_bits = true; entry->di_type = g->builtin_types.entry_void->di_type; diff --git a/src/codegen.cpp b/src/codegen.cpp index 2f8ebf768d..25c115b6f1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4622,6 +4622,8 @@ static void validate_inline_fns(CodeGen *g) { static void do_code_gen(CodeGen *g) { assert(!g->errors.length); + codegen_add_time_event(g, "Code Generation"); + { // create debug type for error sets assert(g->err_enumerators.length == g->errors_by_index.length); @@ -4644,8 +4646,6 @@ static void do_code_gen(CodeGen *g) { } } - codegen_add_time_event(g, "Code Generation"); - generate_error_name_table(g); generate_enum_name_tables(g); diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 1b7639fb71..0b3040cdc9 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -160,7 +160,7 @@ pub const ChildProcess = struct { else => os.unexpectedErrorWindows(err), }; } - self.waitUnwrappedWindows(); + try self.waitUnwrappedWindows(); return ??self.term; } diff --git a/std/os/index.zig b/std/os/index.zig index d8b2303981..a2da2ec673 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -38,6 +38,7 @@ pub const windowsLoadDll = windows_util.windowsLoadDll; pub const windowsUnloadDll = windows_util.windowsUnloadDll; pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; +pub const WindowsWaitError = windows_util.WaitError; pub const WindowsOpenError = windows_util.OpenError; pub const WindowsWriteError = windows_util.WriteError; @@ -605,7 +606,9 @@ test "os.getCwd" { _ = getCwd(debug.global_allocator); } -pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { +pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; + +pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void { if (is_windows) { return symLinkWindows(allocator, existing_path, new_path); } else { @@ -613,7 +616,12 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con } } -pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { +pub const WindowsSymLinkError = error { + OutOfMemory, + Unexpected, +}; + +pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { const existing_with_null = try cstr.addNullByte(allocator, existing_path); defer allocator.free(existing_with_null); const new_with_null = try cstr.addNullByte(allocator, new_path); @@ -627,7 +635,23 @@ pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path } } -pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { +pub const PosixSymLinkError = error { + OutOfMemory, + AccessDenied, + DiskQuota, + PathAlreadyExists, + FileSystem, + SymLinkLoop, + NameTooLong, + FileNotFound, + SystemResources, + NoSpaceLeft, + ReadOnlyFileSystem, + NotDir, + Unexpected, +}; + +pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2); defer allocator.free(full_buf); diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index e2d7c14149..c19a5789e7 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -7,7 +7,13 @@ const mem = std.mem; const BufMap = std.BufMap; const cstr = std.cstr; -pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) !void { +pub const WaitError = error { + WaitAbandoned, + WaitTimeOut, + Unexpected, +}; + +pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) WaitError!void { const result = windows.WaitForSingleObject(handle, milliseconds); return switch (result) { windows.WAIT_ABANDONED => error.WaitAbandoned, -- cgit v1.2.3