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/ir.cpp | 696 ++++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 533 insertions(+), 163 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index 9d5f59d187..cdae4b1511 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -580,6 +580,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace return IrInstructionIdErrorReturnTrace; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnion *) { + return IrInstructionIdErrorUnion; +} + template static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(1); @@ -2326,6 +2330,19 @@ static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, return &instruction->base; } +static IrInstruction *ir_build_error_union(IrBuilder *irb, Scope *scope, AstNode *source_node, + IrInstruction *err_set, IrInstruction *payload) +{ + IrInstructionErrorUnion *instruction = ir_build_instruction(irb, scope, source_node); + instruction->err_set = err_set; + instruction->payload = payload; + + ir_ref_instruction(err_set, irb->current_basic_block); + ir_ref_instruction(payload, irb->current_basic_block); + + return &instruction->base; +} + static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { results[ReturnKindUnconditional] = 0; results[ReturnKindError] = 0; @@ -2800,6 +2817,23 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As return ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); } +static IrInstruction *ir_gen_error_union(IrBuilder *irb, Scope *parent_scope, AstNode *node) { + assert(node->type == NodeTypeBinOpExpr); + + AstNode *op1_node = node->data.bin_op_expr.op1; + AstNode *op2_node = node->data.bin_op_expr.op2; + + IrInstruction *err_set = ir_gen_node(irb, op1_node, parent_scope); + if (err_set == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + IrInstruction *payload = ir_gen_node(irb, op2_node, parent_scope); + if (payload == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + + return ir_build_error_union(irb, parent_scope, node, err_set, payload); +} + static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeBinOpExpr); @@ -2887,6 +2921,8 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult); case BinOpTypeUnwrapMaybe: return ir_gen_maybe_ok_or(irb, scope, node); + case BinOpTypeErrorUnion: + return ir_gen_error_union(irb, scope, node); } zig_unreachable(); } @@ -3990,8 +4026,6 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod return ir_gen_prefix_op_id_lval(irb, scope, node, IrUnOpDereference, lval); case PrefixOpMaybe: return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe), lval); - case PrefixOpError: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpError), lval); case PrefixOpUnwrapMaybe: return ir_gen_maybe_assert_ok(irb, scope, node, lval); } @@ -5165,7 +5199,7 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) { assert(node->type == NodeTypeErrorType); - return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_pure_error); + return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_global_error_set); } static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode *node) { @@ -5249,8 +5283,6 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN Scope *err_scope; if (var_node) { assert(var_node->type == NodeTypeSymbol); - IrInstruction *var_type = ir_build_const_type(irb, parent_scope, node, - irb->codegen->builtin_types.entry_pure_error); Buf *var_name = var_node->data.symbol_expr.symbol; bool is_const = true; bool is_shadowable = false; @@ -5258,7 +5290,7 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN is_const, is_const, is_shadowable, is_comptime); err_scope = var->child_scope; IrInstruction *err_val = ir_build_unwrap_err_code(irb, err_scope, node, err_union_ptr); - ir_build_var_decl(irb, err_scope, var_node, var, var_type, nullptr, err_val); + ir_build_var_decl(irb, err_scope, var_node, var, nullptr, nullptr, err_val); } else { err_scope = parent_scope; } @@ -5348,6 +5380,70 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope, return ir_build_const_type(irb, parent_scope, node, container_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)); + 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 = 1; + err_set_type->data.error_set.errors = allocate(1); + + g->error_di_types.append(&err_set_type->di_type); + + err_set_type->data.error_set.errors[0] = err_entry; + + return err_set_type; +} + +static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, AstNode *node) { + assert(node->type == NodeTypeErrorSetDecl); + + 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); + + for (uint32_t i = 0; i < err_count; i += 1) { + AstNode *symbol_node = node->data.err_set_decl.decls.at(i); + assert(symbol_node->type == NodeTypeSymbol); + Buf *err_name = symbol_node->data.symbol_expr.symbol; + ErrorTableEntry *err = allocate(1); + err->decl_node = symbol_node; + buf_init_from_buf(&err->name, err_name); + + auto existing_entry = irb->codegen->error_table.put_unique(err_name, err); + if (existing_entry) { + err->value = existing_entry->value->value; + } else { + size_t error_value_count = irb->codegen->errors_by_index.length; + assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)irb->codegen->err_tag_type->data.integral.bit_count)); + err->value = error_value_count; + irb->codegen->errors_by_index.append(err); + irb->codegen->err_enumerators.append(ZigLLVMCreateDebugEnumerator(irb->codegen->dbuilder, + buf_ptr(err_name), error_value_count)); + } + err_set_type->data.error_set.errors[i] = err; + } + return ir_build_const_type(irb, parent_scope, node, err_set_type); +} + static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeFnProto); @@ -5401,7 +5497,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop case NodeTypeStructField: case NodeTypeFnDef: case NodeTypeFnDecl: - case NodeTypeErrorValueDecl: case NodeTypeTestDecl: zig_unreachable(); case NodeTypeBlock: @@ -5482,6 +5577,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval); case NodeTypeFnProto: return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval); + case NodeTypeErrorSetDecl: + return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval); } zig_unreachable(); } @@ -6301,25 +6398,32 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, return ImplicitCastMatchResultYes; } - // implicit T to %T + // implicit T to U!T if (expected_type->id == TypeTableEntryIdErrorUnion && - ir_types_match_with_implicit_cast(ira, expected_type->data.error.child_type, actual_type, value)) + ir_types_match_with_implicit_cast(ira, expected_type->data.error_union.payload_type, actual_type, value)) { return ImplicitCastMatchResultYes; } - // implicit conversion from pure error to error union type + // implicit conversion from error set to error union type if (expected_type->id == TypeTableEntryIdErrorUnion && - actual_type->id == TypeTableEntryIdPureError) + actual_type->id == TypeTableEntryIdErrorSet) + { + 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 %?T + // implicit conversion from T to U!?T if (expected_type->id == TypeTableEntryIdErrorUnion && - expected_type->data.error.child_type->id == TypeTableEntryIdMaybe && + expected_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe && ir_types_match_with_implicit_cast(ira, - expected_type->data.error.child_type->data.maybe.child_type, + expected_type->data.error_union.payload_type->data.maybe.child_type, actual_type, value)) { return ImplicitCastMatchResultYes; @@ -6503,7 +6607,19 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (type_is_invalid(prev_inst->value.type)) { return ira->codegen->builtin_types.entry_invalid; } - bool any_are_pure_error = (prev_inst->value.type->id == TypeTableEntryIdPureError); + 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); + 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; + } + } + bool any_are_null = (prev_inst->value.type->id == TypeTableEntryIdNullLit); bool convert_to_const_slice = false; for (size_t i = 1; i < instruction_count; i += 1) { @@ -6524,27 +6640,133 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } - if (prev_type->id == TypeTableEntryIdPureError) { + if (prev_type->id == TypeTableEntryIdNullLit) { prev_inst = cur_inst; continue; } - if (prev_type->id == TypeTableEntryIdNullLit) { - prev_inst = cur_inst; + if (cur_type->id == TypeTableEntryIdNullLit) { + any_are_null = true; continue; } - if (cur_type->id == TypeTableEntryIdPureError) { - if (prev_type->id == TypeTableEntryIdArray) { - convert_to_const_slice = true; + 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) { + continue; + } + if (cur_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; + } + // 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]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + prev_is_superset = false; + break; + } + } + if (prev_is_superset) { + continue; + } + + // 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; + } + for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = cur_type->data.error_set.errors[i]; + errors[error_entry->value] = error_entry; + } + bool cur_is_superset = true; + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + cur_is_superset = false; + break; + } + } + if (cur_is_superset) { + err_set_type = cur_inst->value.type; + prev_inst = cur_inst; + continue; + } + } else if (cur_type->id == TypeTableEntryIdErrorUnion) { + // err_set_type must be 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; + } + bool cur_is_superset = true; + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + cur_is_superset = false; + break; + } + } + if (cur_is_superset) { + err_set_type = cur_err_set_type; + prev_inst = cur_inst; + continue; + } + } else { + prev_inst = cur_inst; + continue; } - any_are_pure_error = true; - continue; } - if (cur_type->id == TypeTableEntryIdNullLit) { - any_are_null = true; - continue; + if (cur_type->id == TypeTableEntryIdErrorSet) { + if (prev_type->id == TypeTableEntryIdArray) { + convert_to_const_slice = true; + } + if (cur_type == ira->codegen->builtin_types.entry_global_error_set) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + continue; + } + if (err_set_type == ira->codegen->builtin_types.entry_global_error_set) { + continue; + } + if (err_set_type == nullptr) { + err_set_type = cur_type; + 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; + } + continue; + } + if (prev_type->id == TypeTableEntryIdErrorUnion) { + // 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]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + prev_is_superset = false; + break; + } + } + if (prev_is_superset) { + continue; + } + } } if (types_match_const_cast_only(prev_type, cur_type)) { @@ -6574,20 +6796,20 @@ 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.child_type, cur_type)) + types_match_const_cast_only(prev_type->data.error_union.payload_type, cur_type)) { continue; } if (cur_type->id == TypeTableEntryIdErrorUnion && - types_match_const_cast_only(cur_type->data.error.child_type, prev_type)) + types_match_const_cast_only(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(prev_type->data.maybe.child_type, cur_type)) { continue; } @@ -6700,16 +6922,19 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod return ira->codegen->builtin_types.entry_invalid; } + + free(errors); + if (convert_to_const_slice) { assert(prev_inst->value.type->id == TypeTableEntryIdArray); TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, prev_inst->value.type->data.array.child_type, true); TypeTableEntry *slice_type = get_slice_type(ira->codegen, ptr_type); - if (any_are_pure_error) { - return get_error_type(ira->codegen, slice_type); + if (err_set_type != nullptr) { + return get_error_union_type(ira->codegen, err_set_type, slice_type); } else { return slice_type; } - } else if (any_are_pure_error && prev_inst->value.type->id != TypeTableEntryIdPureError) { + } 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) { @@ -6723,7 +6948,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) { return prev_inst->value.type; } else { - return get_error_type(ira->codegen, prev_inst->value.type); + 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 || @@ -7199,7 +7424,7 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction assert(wanted_type->id == TypeTableEntryIdErrorUnion); if (instr_is_comptime(value)) { - TypeTableEntry *payload_type = wanted_type->data.error.child_type; + TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type; IrInstruction *casted_payload = ir_implicit_cast(ira, value, payload_type); if (type_is_invalid(casted_payload->value.type)) return ira->codegen->invalid_instruction; @@ -7224,8 +7449,43 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction return result; } -static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) { - assert(wanted_type->id == TypeTableEntryIdErrorUnion); +static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, + TypeTableEntry *wanted_type) +{ + TypeTableEntry *contained_set = value->value.type; + TypeTableEntry *container_set = wanted_type; + + assert(contained_set->id == TypeTableEntryIdErrorSet); + assert(container_set->id == TypeTableEntryIdErrorSet); + + if (container_set->data.error_set.infer_fn == nullptr && + container_set != ira->codegen->builtin_types.entry_global_error_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) { + ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; + errors[error_entry->value] = error_entry; + } + ErrorMsg *err_msg = nullptr; + 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 (err_msg == nullptr) { + err_msg = ir_add_error(ira, source_instr, + buf_sprintf("invalid cast of error set '%s' to error set '%s'", + buf_ptr(&contained_set->name), buf_ptr(&container_set->name))); + } + add_error_note(ira->codegen, err_msg, contained_error_entry->decl_node, + buf_sprintf("'%s.%s' not present in '%s'", buf_ptr(&contained_set->name), + buf_ptr(&contained_error_entry->name), buf_ptr(&container_set->name))); + } + } + free(errors); + if (err_msg != nullptr) { + return ira->codegen->invalid_instruction; + } + } if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); @@ -7236,7 +7496,30 @@ static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *so source_instr->scope, source_instr->source_node); const_instruction->base.value.type = wanted_type; const_instruction->base.value.special = ConstValSpecialStatic; - const_instruction->base.value.data.x_err_union.err = val->data.x_pure_err; + const_instruction->base.value.data.x_err_set = val->data.x_err_set; + return &const_instruction->base; + } + + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpNoop); + result->value.type = wanted_type; + return result; +} + +static IrInstruction *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) { + assert(wanted_type->id == TypeTableEntryIdErrorUnion); + + IrInstruction *casted_value = ir_implicit_cast(ira, value, wanted_type->data.error_union.err_set_type); + + if (instr_is_comptime(casted_value)) { + ConstExprValue *val = ir_resolve_const(ira, casted_value, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + + IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, + source_instr->scope, source_instr->source_node); + const_instruction->base.value.type = wanted_type; + const_instruction->base.value.special = ConstValSpecialStatic; + const_instruction->base.value.data.x_err_union.err = val->data.x_err_set; const_instruction->base.value.data.x_err_union.payload = nullptr; return &const_instruction->base; } @@ -7616,9 +7899,12 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction return result; } -static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target) { +static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, + TypeTableEntry *wanted_type) +{ assert(target->value.type->id == TypeTableEntryIdInt); assert(!target->value.type->data.integral.is_signed); + assert(wanted_type->id == TypeTableEntryIdErrorSet); if (instr_is_comptime(target)) { ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); @@ -7626,10 +7912,10 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc return ira->codegen->invalid_instruction; IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, - source_instr->source_node, ira->codegen->builtin_types.entry_pure_error); + source_instr->source_node, wanted_type); BigInt err_count; - bigint_init_unsigned(&err_count, ira->codegen->error_decls.length); + bigint_init_unsigned(&err_count, ira->codegen->errors_by_index.length); if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) { Buf *val_buf = buf_alloc(); bigint_append_buf(val_buf, &val->data.x_bigint, 10); @@ -7639,13 +7925,12 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc } size_t index = bigint_as_unsigned(&val->data.x_bigint); - AstNode *error_decl_node = ira->codegen->error_decls.at(index); - result->value.data.x_pure_err = error_decl_node->data.error_value_decl.err; + result->value.data.x_err_set = ira->codegen->errors_by_index.at(index); return result; } IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target); - result->value.type = ira->codegen->builtin_types.entry_pure_error; + result->value.type = wanted_type; return result; } @@ -7667,8 +7952,8 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc ErrorTableEntry *err; if (err_type->id == TypeTableEntryIdErrorUnion) { err = val->data.x_err_union.err; - } else if (err_type->id == TypeTableEntryIdPureError) { - err = val->data.x_pure_err; + } else if (err_type->id == TypeTableEntryIdErrorSet) { + err = val->data.x_err_set; } else { zig_unreachable(); } @@ -7689,7 +7974,7 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc } BigInt bn; - bigint_init_unsigned(&bn, ira->codegen->error_decls.length); + 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)) { ir_add_error_node(ira, source_instr->source_node, buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); @@ -7734,6 +8019,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); } + // explicit error set cast + if (wanted_type->id == TypeTableEntryIdErrorSet && + actual_type->id == TypeTableEntryIdErrorSet) + { + return ir_analyze_err_set_cast(ira, source_instr, value, wanted_type); + } + // explicit cast from int to float if (wanted_type->id == TypeTableEntryIdFloat && actual_type->id == TypeTableEntryIdInt) @@ -7894,12 +8186,12 @@ 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.child_type, actual_type)) { + if (types_match_const_cast_only(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) { - if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error.child_type, true)) { + if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); } else { return ira->codegen->invalid_instruction; @@ -7909,16 +8201,16 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from [N]T to %[]const T if (wanted_type->id == TypeTableEntryIdErrorUnion && - is_slice(wanted_type->data.error.child_type) && + is_slice(wanted_type->data.error_union.payload_type) && actual_type->id == TypeTableEntryIdArray) { TypeTableEntry *ptr_type = - wanted_type->data.error.child_type->data.structure.fields[slice_ptr_index].type_entry; + 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)) { - IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error.child_type, value); + IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); if (type_is_invalid(cast1->value.type)) return ira->codegen->invalid_instruction; @@ -7930,25 +8222,25 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } - // explicit cast from pure error to error union type + // explicit cast from error set to error union type if (wanted_type->id == TypeTableEntryIdErrorUnion && - actual_type->id == TypeTableEntryIdPureError) + actual_type->id == TypeTableEntryIdErrorSet) { return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type); } // explicit cast from T to %?T if (wanted_type->id == TypeTableEntryIdErrorUnion && - wanted_type->data.error.child_type->id == TypeTableEntryIdMaybe && + wanted_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe && actual_type->id != TypeTableEntryIdMaybe) { - TypeTableEntry *wanted_child_type = wanted_type->data.error.child_type->data.maybe.child_type; + 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) || actual_type->id == TypeTableEntryIdNullLit || actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) { - IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error.child_type, value); + IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); if (type_is_invalid(cast1->value.type)) return ira->codegen->invalid_instruction; @@ -8017,21 +8309,19 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); } - // explicit cast from %void to integer type which can fit it + // explicit cast from T!void to integer type which can fit it bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion && - !type_has_bits(actual_type->data.error.child_type); - bool actual_type_is_pure_err = actual_type->id == TypeTableEntryIdPureError; - if ((actual_type_is_void_err || actual_type_is_pure_err) && - wanted_type->id == TypeTableEntryIdInt) - { + !type_has_bits(actual_type->data.error_union.payload_type); + bool actual_type_is_err_set = actual_type->id == TypeTableEntryIdErrorSet; + if ((actual_type_is_void_err || actual_type_is_err_set) && wanted_type->id == TypeTableEntryIdInt) { return ir_analyze_err_to_int(ira, source_instr, value, wanted_type); } - // explicit cast from integer to pure error - if (wanted_type->id == TypeTableEntryIdPureError && actual_type->id == TypeTableEntryIdInt && + // explicit cast from integer to error set + if (wanted_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed) { - return ir_analyze_int_to_err(ira, source_instr, value); + return ir_analyze_int_to_err(ira, source_instr, value, wanted_type); } // explicit cast from integer to enum type with no payload @@ -8524,7 +8814,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdPointer: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: case TypeTableEntryIdOpaque: case TypeTableEntryIdNamespace: @@ -9312,7 +9602,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { case TypeTableEntryIdInt: case TypeTableEntryIdFloat: case TypeTableEntryIdVoid: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: return VarClassRequiredAny; case TypeTableEntryIdNumLitFloat: @@ -9338,7 +9628,7 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { case TypeTableEntryIdMaybe: return get_var_class_required(type_entry->data.maybe.child_type); case TypeTableEntryIdErrorUnion: - return get_var_class_required(type_entry->data.error.child_type); + return get_var_class_required(type_entry->data.error_union.payload_type); case TypeTableEntryIdStruct: case TypeTableEntryIdEnum: @@ -9573,7 +9863,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -9596,7 +9886,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name)); case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: @@ -9630,6 +9920,24 @@ static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, return nullable_type; } +static TypeTableEntry *ir_analyze_instruction_error_union(IrAnalyze *ira, + IrInstructionErrorUnion *instruction) +{ + TypeTableEntry *err_set_type = ir_resolve_type(ira, instruction->err_set->other); + if (type_is_invalid(err_set_type)) + return ira->codegen->builtin_types.entry_invalid; + + TypeTableEntry *payload_type = ir_resolve_type(ira, instruction->payload->other); + if (type_is_invalid(payload_type)) + return ira->codegen->builtin_types.entry_invalid; + + TypeTableEntry *result_type = get_error_union_type(ira->codegen, err_set_type, payload_type); + + 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 bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i) { @@ -10114,7 +10422,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type; ir_add_alloca(ira, new_call_instruction, return_type); - if (return_type->id == TypeTableEntryIdPureError || return_type->id == TypeTableEntryIdErrorUnion) { + if (return_type->id == TypeTableEntryIdErrorSet || return_type->id == TypeTableEntryIdErrorUnion) { parent_fn_entry->calls_errorable_function = true; } @@ -10124,7 +10432,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal FnTableEntry *parent_fn_entry = exec_fn_entry(ira->new_irb.exec); assert(fn_type_id->return_type != nullptr); assert(parent_fn_entry != nullptr); - if (fn_type_id->return_type->id == TypeTableEntryIdPureError || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) { + if (fn_type_id->return_type->id == TypeTableEntryIdErrorSet || fn_type_id->return_type->id == TypeTableEntryIdErrorUnion) { parent_fn_entry->calls_errorable_function = true; } @@ -10243,58 +10551,6 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction } } -static TypeTableEntry *ir_analyze_unary_prefix_op_err(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { - assert(un_op_instruction->op_id == IrUnOpError); - IrInstruction *value = un_op_instruction->value->other; - - TypeTableEntry *meta_type = ir_resolve_type(ira, value); - if (type_is_invalid(meta_type)) - return ira->codegen->builtin_types.entry_invalid; - - - switch (meta_type->id) { - case TypeTableEntryIdInvalid: // handled above - zig_unreachable(); - - case TypeTableEntryIdVoid: - case TypeTableEntryIdBool: - case TypeTableEntryIdInt: - case TypeTableEntryIdFloat: - case TypeTableEntryIdPointer: - case TypeTableEntryIdArray: - case TypeTableEntryIdStruct: - case TypeTableEntryIdMaybe: - case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: - case TypeTableEntryIdEnum: - case TypeTableEntryIdUnion: - case TypeTableEntryIdFn: - case TypeTableEntryIdBoundFn: - { - ConstExprValue *out_val = ir_build_const_from(ira, &un_op_instruction->base); - TypeTableEntry *result_type = get_error_type(ira->codegen, meta_type); - out_val->data.x_type = result_type; - return ira->codegen->builtin_types.entry_type; - } - case TypeTableEntryIdMetaType: - case TypeTableEntryIdNumLitFloat: - case TypeTableEntryIdNumLitInt: - case TypeTableEntryIdUndefLit: - case TypeTableEntryIdNullLit: - case TypeTableEntryIdNamespace: - case TypeTableEntryIdBlock: - case TypeTableEntryIdUnreachable: - case TypeTableEntryIdVar: - case TypeTableEntryIdArgTuple: - case TypeTableEntryIdOpaque: - ir_add_error_node(ira, un_op_instruction->base.source_node, - buf_sprintf("unable to wrap type '%s' in error type", buf_ptr(&meta_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - zig_unreachable(); -} - - static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) { IrInstruction *value = un_op_instruction->value->other; @@ -10350,7 +10606,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: @@ -10460,8 +10716,6 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio return ir_analyze_dereference(ira, un_op_instruction); case IrUnOpMaybe: return ir_analyze_maybe(ira, un_op_instruction); - case IrUnOpError: - return ir_analyze_unary_prefix_op_err(ira, un_op_instruction); } zig_unreachable(); } @@ -11083,6 +11337,17 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source zig_unreachable(); } +static ErrorTableEntry *find_err_table_entry(TypeTableEntry *err_set_type, Buf *field_name) { + assert(err_set_type->id == TypeTableEntryIdErrorSet); + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *err_table_entry = err_set_type->data.error_set.errors[i]; + if (buf_eql_buf(&err_table_entry->name, field_name)) { + return err_table_entry; + } + } + return nullptr; +} + static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) { IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other; if (type_is_invalid(container_ptr->value.type)) @@ -11224,23 +11489,49 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru buf_sprintf("container '%s' has no member called '%s'", buf_ptr(&child_type->name), buf_ptr(field_name))); return ira->codegen->builtin_types.entry_invalid; - } else if (child_type->id == TypeTableEntryIdPureError) { - auto err_table_entry = ira->codegen->error_table.maybe_get(field_name); - if (err_table_entry) { - ConstExprValue *const_val = create_const_vals(1); - const_val->special = ConstValSpecialStatic; - const_val->type = child_type; - const_val->data.x_pure_err = err_table_entry->value; - - bool ptr_is_const = true; - bool ptr_is_volatile = false; - return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val, - child_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } else if (child_type->id == TypeTableEntryIdErrorSet) { + ErrorTableEntry *err_entry; + TypeTableEntry *err_set_type; + if (child_type == ira->codegen->builtin_types.entry_global_error_set) { + auto existing_entry = ira->codegen->error_table.maybe_get(field_name); + if (existing_entry) { + err_entry = existing_entry->value; + } else { + err_entry = allocate(1); + err_entry->decl_node = field_ptr_instruction->base.source_node; + buf_init_from_buf(&err_entry->name, field_name); + size_t error_value_count = ira->codegen->errors_by_index.length; + assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count)); + err_entry->value = error_value_count; + ira->codegen->errors_by_index.append(err_entry); + ira->codegen->err_enumerators.append(ZigLLVMCreateDebugEnumerator(ira->codegen->dbuilder, + buf_ptr(field_name), error_value_count)); + ira->codegen->error_table.put(field_name, err_entry); + } + if (err_entry->set_with_only_this_in_it == nullptr) { + err_entry->set_with_only_this_in_it = make_err_set_with_one_item(ira->codegen, + field_ptr_instruction->base.scope, field_ptr_instruction->base.source_node, + err_entry); + } + err_set_type = err_entry->set_with_only_this_in_it; + } else { + ErrorTableEntry *err_entry = find_err_table_entry(child_type, field_name); + if (err_entry == nullptr) { + ir_add_error(ira, &field_ptr_instruction->base, + buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + err_set_type = child_type; } + ConstExprValue *const_val = create_const_vals(1); + const_val->special = ConstValSpecialStatic; + const_val->type = err_set_type; + const_val->data.x_err_set = err_entry; - ir_add_error(ira, &field_ptr_instruction->base, - buf_sprintf("use of undeclared error value '%s'", buf_ptr(field_name))); - return ira->codegen->builtin_types.entry_invalid; + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, const_val, + err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else if (child_type->id == TypeTableEntryIdInt) { if (buf_eql_str(field_name, "bit_count")) { bool ptr_is_const = true; @@ -11323,11 +11614,18 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru return ira->codegen->builtin_types.entry_invalid; } } else if (child_type->id == TypeTableEntryIdErrorUnion) { - if (buf_eql_str(field_name, "Child")) { + if (buf_eql_str(field_name, "Payload")) { + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, + create_const_type(ira->codegen, child_type->data.error_union.payload_type), + ira->codegen->builtin_types.entry_type, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); + } else if (buf_eql_str(field_name, "ErrorSet")) { bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, - create_const_type(ira->codegen, child_type->data.error.child_type), + create_const_type(ira->codegen, child_type->data.error_union.err_set_type), ira->codegen->builtin_types.entry_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile); } else { @@ -11514,7 +11812,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: @@ -11781,7 +12079,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, case TypeTableEntryIdNumLitInt: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: @@ -11889,7 +12187,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, case TypeTableEntryIdNumLitInt: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: @@ -11942,7 +12240,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: @@ -12277,7 +12575,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, case TypeTableEntryIdPointer: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: if (pointee_val) { ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base); copy_const_val(out_val, pointee_val, true); @@ -12347,8 +12645,6 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, return target_type; } case TypeTableEntryIdErrorUnion: - // see https://github.com/andrewrk/zig/issues/632 - zig_panic("TODO switch on error union"); case TypeTableEntryIdUnreachable: case TypeTableEntryIdArray: case TypeTableEntryIdStruct: @@ -12873,7 +13169,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ case TypeTableEntryIdNullLit: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: case TypeTableEntryIdNamespace: @@ -12961,7 +13257,7 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc TypeTableEntry *u8_ptr_type = get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); TypeTableEntry *str_type = get_slice_type(ira->codegen, u8_ptr_type); if (casted_value->value.special == ConstValSpecialStatic) { - ErrorTableEntry *err = casted_value->value.data.x_pure_err; + ErrorTableEntry *err = casted_value->value.data.x_err_set; if (!err->cached_error_name_val) { ConstExprValue *array_val = create_const_str_lit(ira->codegen, &err->name); err->cached_error_name_val = create_const_slice(ira->codegen, array_val, 0, buf_len(&err->name), true); @@ -14106,7 +14402,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdEnum: case TypeTableEntryIdUnion: case TypeTableEntryIdFn: @@ -14239,7 +14535,7 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc ir_build_test_err_from(&ira->new_irb, &instruction->base, value); return ira->codegen->builtin_types.entry_bool; - } else if (type_entry->id == TypeTableEntryIdPureError) { + } else if (type_entry->id == TypeTableEntryIdErrorSet) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_bool = true; return ira->codegen->builtin_types.entry_bool; @@ -14275,13 +14571,13 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, assert(err); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); - out_val->data.x_pure_err = err; - return ira->codegen->builtin_types.entry_pure_error; + out_val->data.x_err_set = err; + return type_entry->data.error_union.err_set_type; } } ir_build_unwrap_err_code_from(&ira->new_irb, &instruction->base, value); - return ira->codegen->builtin_types.entry_pure_error; + return type_entry->data.error_union.err_set_type; } else { ir_add_error(ira, value, buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); @@ -14305,10 +14601,10 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, if (type_is_invalid(type_entry)) { return ira->codegen->builtin_types.entry_invalid; } else if (type_entry->id == TypeTableEntryIdErrorUnion) { - TypeTableEntry *child_type = type_entry->data.error.child_type; - TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, child_type, + TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; + TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - get_abi_alignment(ira->codegen, child_type), 0, 0); + get_abi_alignment(ira->codegen, payload_type), 0, 0); if (instr_is_comptime(value)) { ConstExprValue *ptr_val = ir_resolve_const(ira, value, UndefBad); if (!ptr_val) @@ -14343,6 +14639,12 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc AstNode *proto_node = instruction->base.source_node; assert(proto_node->type == NodeTypeFnProto); + if (proto_node->data.fn_proto.auto_err_set) { + ir_add_error(ira, &instruction->base, + buf_sprintf("inferring error set of return type valid only for function definitions")); + return ira->codegen->builtin_types.entry_invalid; + } + FnTypeId fn_type_id = {0}; init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length); @@ -14468,6 +14770,71 @@ 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; + } + } + + AstNode **field_prev_uses = allocate(ira->codegen->errors_by_index.length); + + for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { + IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; + + IrInstruction *start_value = range->start->other; + if (type_is_invalid(start_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + IrInstruction *end_value = range->end->other; + if (type_is_invalid(end_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + + assert(start_value->value.type->id == TypeTableEntryIdErrorSet); + uint32_t start_index = start_value->value.data.x_err_set->value; + + assert(end_value->value.type->id == TypeTableEntryIdErrorSet); + uint32_t end_index = end_value->value.data.x_err_set->value; + + if (start_index != end_index) { + ir_add_error(ira, end_value, buf_sprintf("ranges not allowed when switching on errors")); + return ira->codegen->builtin_types.entry_invalid; + } + + AstNode *prev_node = field_prev_uses[start_index]; + if (prev_node != nullptr) { + Buf *err_name = &ira->codegen->errors_by_index.at(start_index)->name; + ErrorMsg *msg = ir_add_error(ira, start_value, + buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name), buf_ptr(err_name))); + add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here")); + } + field_prev_uses[start_index] = start_value->source_node; + } + if (!instruction->have_else_prong) { + for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i]; + + AstNode *prev_node = field_prev_uses[err_entry->value]; + if (prev_node == nullptr) { + ir_add_error(ira, &instruction->base, + buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name))); + } + } + } + + free(field_prev_uses); } else if (switch_type->id == TypeTableEntryIdInt) { RangeSet rs = {0}; for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { @@ -14760,7 +15127,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_panic("TODO buf_write_value_bytes maybe type"); case TypeTableEntryIdErrorUnion: zig_panic("TODO buf_write_value_bytes error union"); - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: zig_panic("TODO buf_write_value_bytes pure error type"); case TypeTableEntryIdEnum: zig_panic("TODO buf_write_value_bytes enum type"); @@ -14818,7 +15185,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue zig_panic("TODO buf_read_value_bytes maybe type"); case TypeTableEntryIdErrorUnion: zig_panic("TODO buf_read_value_bytes error union"); - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: zig_panic("TODO buf_read_value_bytes pure error type"); case TypeTableEntryIdEnum: zig_panic("TODO buf_read_value_bytes enum type"); @@ -15429,6 +15796,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction); case IrInstructionIdErrorReturnTrace: return ir_analyze_instruction_error_return_trace(ira, (IrInstructionErrorReturnTrace *)instruction); + case IrInstructionIdErrorUnion: + return ir_analyze_instruction_error_union(ira, (IrInstructionErrorUnion *)instruction); } zig_unreachable(); } @@ -15614,6 +15983,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdArgType: case IrInstructionIdTagType: case IrInstructionIdErrorReturnTrace: + case IrInstructionIdErrorUnion: return false; case IrInstructionIdAsm: { -- 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/ir.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/ir.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/ir.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/ir.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/ir.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 abf5ae6897bb23e49e4232ab8be7ed61ea9520b6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 3 Feb 2018 11:51:29 -0500 Subject: *WIP* error sets - support fns called at comptime --- TODO | 4 ++++ src/ir.cpp | 31 +++++++++++++++++++++++++++++-- src/parser.cpp | 2 +- std/base64.zig | 6 +++--- std/build.zig | 10 +++++----- std/fmt/index.zig | 6 +++--- std/os/child_process.zig | 12 ++++++++---- std/os/index.zig | 4 ++-- 8 files changed, 55 insertions(+), 20 deletions(-) (limited to 'src/ir.cpp') diff --git a/TODO b/TODO index c6fcfa939a..69521fe204 100644 --- a/TODO +++ b/TODO @@ -25,3 +25,7 @@ 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/ir.cpp b/src/ir.cpp index 12dc76e972..721bd70f12 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -10123,6 +10123,13 @@ static TypeTableEntry *ir_analyze_instruction_error_union(IrAnalyze *ira, if (type_is_invalid(payload_type)) return ira->codegen->builtin_types.entry_invalid; + if (err_set_type->id != TypeTableEntryIdErrorSet) { + ir_add_error(ira, instruction->err_set->other, + buf_sprintf("expected error set type, found type '%s'", + buf_ptr(&err_set_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + TypeTableEntry *result_type = get_error_union_type(ira->codegen, err_set_type, payload_type); ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); @@ -10412,9 +10419,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, exec_scope, return_type_node); - if (type_is_invalid(return_type)) + TypeTableEntry *specified_return_type = analyze_type_expr(ira->codegen, exec_scope, return_type_node); + if (type_is_invalid(specified_return_type)) return ira->codegen->builtin_types.entry_invalid; + TypeTableEntry *return_type; + TypeTableEntry *inferred_err_set_type = nullptr; + if (fn_proto_node->data.fn_proto.auto_err_set) { + inferred_err_set_type = get_auto_err_set_type(ira->codegen, fn_entry); + return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type); + } else { + return_type = specified_return_type; + } IrInstruction *result; @@ -10428,6 +10443,18 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry, nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec); + if (inferred_err_set_type != nullptr) { + inferred_err_set_type->data.error_set.infer_fn = nullptr; + if (result->value.type->id == TypeTableEntryIdErrorUnion) { + TypeTableEntry *fn_inferred_err_set_type = result->value.type->data.error_union.err_set_type; + inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count; + inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors; + } else if (result->value.type->id == TypeTableEntryIdErrorSet) { + inferred_err_set_type->data.error_set.err_count = result->value.type->data.error_set.err_count; + inferred_err_set_type->data.error_set.errors = result->value.type->data.error_set.errors; + } + } + ira->codegen->memoized_fn_eval_table.put(exec_scope, result); if (type_is_invalid(result->value.type)) diff --git a/src/parser.cpp b/src/parser.cpp index 1a3b568a2e..b72de374ba 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2589,7 +2589,7 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index, Token *colon_token = &pc->tokens->at(*token_index); if (colon_token->id == TokenIdColon) { *token_index += 1; - field_node->data.struct_field.type = ast_parse_prefix_op_expr(pc, token_index, true); + field_node->data.struct_field.type = ast_parse_type_expr(pc, token_index, true); } Token *eq_token = &pc->tokens->at(*token_index); if (eq_token->id == TokenIdEq) { diff --git a/std/base64.zig b/std/base64.zig index f91c1e56d6..d9e1d2f908 100644 --- a/std/base64.zig +++ b/std/base64.zig @@ -181,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; } @@ -430,7 +430,7 @@ fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init( standard_alphabet_chars, standard_pad_char, ""); var buffer: [0x100]u8 = undefined; - var decoded = buffer[0..try Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)]; + var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)]; var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded); assert(written <= decoded.len); assert(mem.eql(u8, decoded[0..written], expected_decoded)); @@ -449,7 +449,7 @@ fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !voi const standard_decoder_ignore_space = Base64DecoderWithIgnore.init( standard_alphabet_chars, standard_pad_char, " "); var buffer: [0x100]u8 = undefined; - var decoded = buffer[0..try Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)]; + var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)]; var written = try standard_decoder_ignore_space.decode(decoded, encoded); assert(mem.eql(u8, decoded[0..written], expected_decoded)); } diff --git a/std/build.zig b/std/build.zig index 1a5c48e684..216699f355 100644 --- a/std/build.zig +++ b/std/build.zig @@ -554,7 +554,7 @@ pub const Builder = struct { } fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap, - argv: []const []const u8) %void + argv: []const []const u8) !void { if (self.verbose) { printCmd(cwd, argv); @@ -1942,12 +1942,12 @@ pub const RemoveDirStep = struct { pub const Step = struct { name: []const u8, - makeFn: fn(self: &Step) %void, + makeFn: fn(self: &Step) error!void, dependencies: ArrayList(&Step), loop_flag: bool, done_flag: bool, - pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn (&Step)%void) Step { + pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn (&Step)error!void) Step { return Step { .name = name, .makeFn = makeFn, @@ -1972,11 +1972,11 @@ pub const Step = struct { self.dependencies.append(other) catch unreachable; } - fn makeNoOp(self: &Step) %void {} + fn makeNoOp(self: &Step) (error{}!void) {} }; fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_major_only: []const u8, - filename_name_only: []const u8) %void + filename_name_only: []const u8) !void { const out_dir = os.path.dirname(output_path); const out_basename = os.path.basename(output_path); diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 26583817e6..50f0013852 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -198,7 +198,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ return formatInt(value, 10, false, 0, context, Errors, output); }, builtin.TypeId.Float => { - return formatFloat(value, context, output); + return formatFloat(value, context, Errors, output); }, builtin.TypeId.Void => { return output(context, "void"); @@ -417,7 +417,7 @@ const FormatIntBuf = struct { out_buf: []u8, index: usize, }; -fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) !void { +fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) (error{}!void) { mem.copy(u8, context.out_buf[context.index..], bytes); context.index += bytes.len; } @@ -499,7 +499,7 @@ fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) !void { pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { var context = BufPrintContext { .remaining = buf, }; - try format(&context, bufPrintWrite, fmt, args); + try format(&context, error{BufferTooSmall}, bufPrintWrite, fmt, args); return buf[0..buf.len - context.remaining.len]; } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index c86db9d69c..48f638e323 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -28,7 +28,7 @@ pub const ChildProcess = struct { pub stdout: ?io.File, pub stderr: ?io.File, - pub term: ?%Term, + pub term: ?SpawnError!Term, pub argv: []const []const u8, @@ -54,6 +54,10 @@ pub const ChildProcess = struct { err_pipe: if (is_windows) void else [2]i32, llnode: if (is_windows) void else LinkedList(&ChildProcess).Node, + pub const SpawnError = error { + + }; + pub const Term = union(enum) { Exited: i32, Signal: i32, @@ -185,7 +189,7 @@ pub const ChildProcess = struct { /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. /// If it succeeds, the caller owns result.stdout and result.stderr memory. pub fn exec(allocator: &mem.Allocator, argv: []const []const u8, cwd: ?[]const u8, - env_map: ?&const BufMap, max_output_size: usize) %ExecResult + env_map: ?&const BufMap, max_output_size: usize) !ExecResult { const child = try ChildProcess.init(argv, allocator); defer child.deinit(); @@ -246,7 +250,7 @@ pub const ChildProcess = struct { fn waitUnwrappedWindows(self: &ChildProcess) !void { const result = os.windowsWaitSingle(self.handle, windows.INFINITE); - self.term = (%Term)(x: { + self.term = (SpawnError!Term)(x: { var exit_code: windows.DWORD = undefined; if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) { break :x Term { .Unknown = 0 }; @@ -631,7 +635,7 @@ pub const ChildProcess = struct { }; fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?&u8, - lpStartupInfo: &windows.STARTUPINFOA, lpProcessInformation: &windows.PROCESS_INFORMATION) %void + lpStartupInfo: &windows.STARTUPINFOA, lpProcessInformation: &windows.PROCESS_INFORMATION) !void { if (windows.CreateProcessA(app_name, cmd_line, null, null, windows.TRUE, 0, @ptrCast(?&c_void, envp_ptr), cwd_ptr, lpStartupInfo, lpProcessInformation) == 0) diff --git a/std/os/index.zig b/std/os/index.zig index a303ee4be1..aad24cf996 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) ![]u8 { +pub fn readLink(allocator: &Allocator, pathname: []const u8) error![]u8 { const path_buf = try allocator.alloc(u8, pathname.len + 1); defer allocator.free(path_buf); @@ -1267,7 +1267,7 @@ pub const ArgIteratorWindows = struct { } /// You must free the returned memory when done. - pub fn next(self: &ArgIteratorWindows, allocator: &Allocator) ?internalNext.errors![]u8 { + pub fn next(self: &ArgIteratorWindows, allocator: &Allocator) ?(@typeOf(internalNext).ReturnType.ErrorSet![]u8) { // march forward over whitespace while (true) : (self.index += 1) { const byte = self.cmd_line[self.index]; -- 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/ir.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 61718742f7f20cde1469d4db811cd6dd6c3f8266 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 3 Feb 2018 14:42:20 -0500 Subject: *WIP* error sets - std lib test compile but try to link against windows --- src/ir.cpp | 6 +++--- std/os/child_process.zig | 2 +- std/os/index.zig | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index 06b46646e8..56ead2b906 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6577,11 +6577,11 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry TypeTableEntry *contained_set = actual_type; TypeTableEntry *container_set = expected_type; - if (!resolve_inferred_error_set(ira, container_set, source_node)) { - result.id = ConstCastResultIdUnresolvedInferredErrSet; + // if the container set is inferred, then this will always work. + if (container_set->data.error_set.infer_fn != nullptr) { return result; } - + // if the container set is the global one, it will always work. if (type_is_global_error_set(container_set)) { return result; } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 48f638e323..360a1bfb93 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -28,7 +28,7 @@ pub const ChildProcess = struct { pub stdout: ?io.File, pub stderr: ?io.File, - pub term: ?SpawnError!Term, + pub term: ?(SpawnError!Term), pub argv: []const []const u8, diff --git a/std/os/index.zig b/std/os/index.zig index e57968dcad..33707676bb 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1515,7 +1515,7 @@ const unexpected_error_tracing = false; /// Call this when you made a syscall or something that sets errno /// and you get an unexpected error. -pub fn unexpectedErrorPosix(errno: usize) error { +pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) { if (unexpected_error_tracing) { debug.warn("unexpected errno: {}\n", errno); debug.dumpStackTrace(); @@ -1525,7 +1525,7 @@ pub fn unexpectedErrorPosix(errno: usize) error { /// Call this when you made a windows DLL call or something that does SetLastError /// and you get an unexpected error. -pub fn unexpectedErrorWindows(err: windows.DWORD) error { +pub fn unexpectedErrorWindows(err: windows.DWORD) (error{Unexpected}) { if (unexpected_error_tracing) { debug.warn("unexpected GetLastError(): {}\n", err); debug.dumpStackTrace(); -- 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/ir.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 15075d2c3d7e34fe6c75d7072cfa7f4138bf0910 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 5 Feb 2018 00:05:04 -0500 Subject: error sets - compile error for equality with no common errors --- src/ir.cpp | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 122 insertions(+), 4 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index 27b39b868f..a534bc4be0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6491,6 +6491,60 @@ static bool resolve_inferred_error_set(IrAnalyze *ira, TypeTableEntry *err_set_t return true; } +static TypeTableEntry *get_error_set_intersection(IrAnalyze *ira, TypeTableEntry *set1, TypeTableEntry *set2, + AstNode *source_node) +{ + assert(set1->id == TypeTableEntryIdErrorSet); + assert(set2->id == TypeTableEntryIdErrorSet); + + if (!resolve_inferred_error_set(ira, set1, source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (!resolve_inferred_error_set(ira, set2, source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (type_is_global_error_set(set1)) { + return set2; + } + if (type_is_global_error_set(set2)) { + return set1; + } + ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length); + for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set1->data.error_set.errors[i]; + errors[error_entry->value] = error_entry; + } + ZigList intersection_list = {}; + + TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); + buf_resize(&err_set_type->name, 0); + buf_appendf(&err_set_type->name, "error{"); + + for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; + ErrorTableEntry *existing_entry = errors[error_entry->value]; + if (existing_entry != nullptr) { + intersection_list.append(existing_entry); + buf_appendf(&err_set_type->name, "%s,", buf_ptr(&existing_entry->name)); + } + } + free(errors); + + err_set_type->is_copyable = true; + err_set_type->type_ref = ira->codegen->builtin_types.entry_global_error_set->type_ref; + err_set_type->di_type = ira->codegen->builtin_types.entry_global_error_set->di_type; + err_set_type->data.error_set.err_count = intersection_list.length; + err_set_type->data.error_set.errors = intersection_list.items; + err_set_type->zero_bits = intersection_list.length == 0; + + buf_appendf(&err_set_type->name, "}"); + + ira->codegen->error_di_types.append(&err_set_type->di_type); + + return err_set_type; +} + + static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry *expected_type, TypeTableEntry *actual_type, AstNode *source_node) { @@ -7313,7 +7367,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod 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; + return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type); } else { return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type); } @@ -9147,6 +9201,7 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) { static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrInstruction *op1 = bin_op_instruction->op1->other; IrInstruction *op2 = bin_op_instruction->op2->other; + AstNode *source_node = bin_op_instruction->base.source_node; IrBinOp op_id = bin_op_instruction->op_id; bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); @@ -9179,7 +9234,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp } IrInstruction *is_non_null = ir_build_test_nonnull(&ira->new_irb, bin_op_instruction->base.scope, - bin_op_instruction->base.source_node, maybe_op); + source_node, maybe_op); is_non_null->value.type = ira->codegen->builtin_types.entry_bool; if (op_id == IrBinOpCmpEq) { @@ -9190,8 +9245,69 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp return ira->codegen->builtin_types.entry_bool; } + if (op1->value.type->id == TypeTableEntryIdErrorSet && op2->value.type->id == TypeTableEntryIdErrorSet) { + if (!is_equality_cmp) { + ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for errors")); + return ira->codegen->builtin_types.entry_invalid; + } + TypeTableEntry *intersect_type = get_error_set_intersection(ira, op1->value.type, op2->value.type, source_node); + if (type_is_invalid(intersect_type)) { + return ira->codegen->builtin_types.entry_invalid; + } + + if (!resolve_inferred_error_set(ira, intersect_type, source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + + if (!type_is_global_error_set(intersect_type)) { + if (intersect_type->data.error_set.err_count == 0) { + ir_add_error_node(ira, source_node, + buf_sprintf("error sets '%s' and '%s' have no common errors", + buf_ptr(&op1->value.type->name), buf_ptr(&op2->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + if (op1->value.type->data.error_set.err_count == 1 && op2->value.type->data.error_set.err_count == 1) { + bool are_equal = true; + bool answer; + if (op_id == IrBinOpCmpEq) { + answer = are_equal; + } else if (op_id == IrBinOpCmpNotEq) { + answer = !are_equal; + } else { + zig_unreachable(); + } + ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); + out_val->data.x_bool = answer; + return ira->codegen->builtin_types.entry_bool; + } + } + + ConstExprValue *op1_val = &op1->value; + ConstExprValue *op2_val = &op2->value; + if (value_is_comptime(op1_val) && value_is_comptime(op2_val)) { + bool answer; + bool are_equal = op1_val->data.x_err_set->value == op2_val->data.x_err_set->value; + if (op_id == IrBinOpCmpEq) { + answer = are_equal; + } else if (op_id == IrBinOpCmpNotEq) { + answer = !are_equal; + } else { + zig_unreachable(); + } + + ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); + out_val->data.x_bool = answer; + return ira->codegen->builtin_types.entry_bool; + } + + ir_build_bin_op_from(&ira->new_irb, &bin_op_instruction->base, op_id, + op1, op2, bin_op_instruction->safety_check_on); + + return ira->codegen->builtin_types.entry_bool; + } + IrInstruction *instructions[] = {op1, op2}; - TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2); + TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, instructions, 2); if (type_is_invalid(resolved_type)) return resolved_type; type_ensure_zero_bits_known(ira->codegen, resolved_type); @@ -9199,7 +9315,6 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp return resolved_type; - AstNode *source_node = bin_op_instruction->base.source_node; switch (resolved_type->id) { case TypeTableEntryIdInvalid: zig_unreachable(); // handled above @@ -11347,6 +11462,9 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP IrInstruction *branch_instruction = predecessor->instruction_list.pop(); ir_set_cursor_at_end(&ira->new_irb, predecessor); IrInstruction *casted_value = ir_implicit_cast(ira, new_value, resolved_type); + if (casted_value == ira->codegen->invalid_instruction) { + return ira->codegen->builtin_types.entry_invalid; + } new_incoming_values.items[i] = casted_value; predecessor->instruction_list.append(branch_instruction); -- cgit v1.2.3 From 893f1088dfe40b0141ac6988a1ae6b165f7cc643 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 5 Feb 2018 01:49:14 -0500 Subject: error sets - peer resolution for error unions --- src/ir.cpp | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++-- std/debug/index.zig | 6 +++- 2 files changed, 100 insertions(+), 3 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index a534bc4be0..aa3bc62988 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6753,11 +6753,26 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } // if we got here with error sets, make an error showing the incompatibilities + ZigList *missing_errors = nullptr; if (const_cast_result.id == ConstCastResultIdErrSet) { + missing_errors = &const_cast_result.data.error_set.missing_errors; + } + if (const_cast_result.id == ConstCastResultIdErrorUnionErrorSet) { + if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSet) { + missing_errors = &const_cast_result.data.error_union_error_set->data.error_set.missing_errors; + } else if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSetGlobal) { + ErrorMsg *msg = ir_add_error(ira, value, + buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name))); + add_error_note(ira->codegen, msg, value->source_node, + buf_sprintf("unable to cast global error set into smaller set")); + return ImplicitCastMatchResultReportedError; + } + } + if (missing_errors != nullptr) { 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); + for (size_t i = 0; i < missing_errors->length; i += 1) { + ErrorTableEntry *error_entry = 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))); } @@ -7187,6 +7202,84 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } + if (prev_type->id == TypeTableEntryIdErrorUnion && cur_type->id == TypeTableEntryIdErrorUnion) { + TypeTableEntry *prev_payload_type = prev_type->data.error_union.payload_type; + TypeTableEntry *cur_payload_type = cur_type->data.error_union.payload_type; + + bool const_cast_prev = types_match_const_cast_only(ira, prev_payload_type, cur_payload_type, + source_node).id == ConstCastResultIdOk; + bool const_cast_cur = types_match_const_cast_only(ira, cur_payload_type, prev_payload_type, + source_node).id == ConstCastResultIdOk; + + if (const_cast_prev || const_cast_cur) { + if (const_cast_cur) { + prev_inst = cur_inst; + } + + TypeTableEntry *prev_err_set_type = prev_type->data.error_union.err_set_type; + TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type; + + if (!resolve_inferred_error_set(ira, prev_err_set_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + + 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(prev_err_set_type) || type_is_global_error_set(cur_err_set_type)) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + continue; + } + + if (err_set_type == nullptr) { + err_set_type = prev_err_set_type; + errors = allocate(ira->codegen->errors_by_index.length); + for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i]; + errors[error_entry->value] = error_entry; + } + } + bool prev_is_superset = true; + for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = cur_err_set_type->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + prev_is_superset = false; + break; + } + } + if (prev_is_superset) { + continue; + } + // unset all the errors + for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i]; + errors[error_entry->value] = nullptr; + } + 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; + } + bool cur_is_superset = true; + for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = prev_err_set_type->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + cur_is_superset = false; + break; + } + } + if (cur_is_superset) { + err_set_type = cur_err_set_type; + continue; + } + + err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, prev_err_set_type); + continue; + } + } + if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) { continue; } diff --git a/std/debug/index.zig b/std/debug/index.zig index 50ab1a2216..326610bebb 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -594,7 +594,11 @@ fn parseFormValueRef(allocator: &mem.Allocator, in_stream: &io.InStream, comptim return parseFormValueRefLen(allocator, in_stream, block_len); } -fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u64, is_64: bool) !FormValue { +const ParseFormValueError = error {}; + +fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u64, is_64: bool) + ParseFormValueError!FormValue +{ return switch (form_id) { DW.FORM_addr => FormValue { .Address = try parseFormValueTargetAddrSize(in_stream) }, DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), -- cgit v1.2.3 From b7bc259093ccad98cdc5661c493c0bdb4771e899 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 5 Feb 2018 07:38:24 -0500 Subject: make OutStream and InStream take an error set param --- src/ir.cpp | 39 ++++---- std/debug/index.zig | 48 +++++---- std/io.zig | 284 +++++++++++++++++++++++++++------------------------- 3 files changed, 198 insertions(+), 173 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index aa3bc62988..3f3dfb1ad7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7174,32 +7174,37 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod return ira->codegen->builtin_types.entry_invalid; } if (err_set_type == nullptr) { - err_set_type = cur_type; + if (prev_type->id == TypeTableEntryIdErrorUnion) { + err_set_type = prev_type->data.error_union.err_set_type; + } else { + err_set_type = cur_type; + } 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; } - continue; - } 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]; - ErrorTableEntry *error_entry = errors[contained_error_entry->value]; - if (error_entry == nullptr) { - prev_is_superset = false; - break; - } - } - if (prev_is_superset) { + if (err_set_type == cur_type) { 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); - assert(errors != nullptr); + } + // 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]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + prev_is_superset = false; + break; + } + } + 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); + assert(errors != nullptr); + continue; } if (prev_type->id == TypeTableEntryIdErrorUnion && cur_type->id == TypeTableEntryIdErrorUnion) { diff --git a/std/debug/index.zig b/std/debug/index.zig index 326610bebb..a759a2af16 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -15,12 +15,12 @@ pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; /// TODO atomic/multithread support var stderr_file: io.File = undefined; var stderr_file_out_stream: io.FileOutStream = undefined; -var stderr_stream: ?&io.OutStream = null; +var stderr_stream: ?&io.OutStream(io.FileOutStream.Error) = null; pub fn warn(comptime fmt: []const u8, args: ...) void { const stderr = getStderrStream() catch return; stderr.print(fmt, args) catch return; } -fn getStderrStream() !&io.OutStream { +fn getStderrStream() !&io.OutStream(io.FileOutStream.Error) { if (stderr_stream) |st| { return st; } else { @@ -140,7 +140,7 @@ const WHITE = "\x1b[37;1m"; const DIM = "\x1b[2m"; const RESET = "\x1b[0m"; -pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.OutStream, allocator: &mem.Allocator, +pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: var, allocator: &mem.Allocator, debug_info: &ElfStackTrace, tty_color: bool) !void { var frame_index: usize = undefined; @@ -162,7 +162,7 @@ pub fn writeStackTrace(stack_trace: &const builtin.StackTrace, out_stream: &io.O } } -pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, +pub fn writeCurrentStackTrace(out_stream: var, allocator: &mem.Allocator, debug_info: &ElfStackTrace, tty_color: bool, ignore_frame_count: usize) !void { var ignored_count: usize = 0; @@ -179,7 +179,7 @@ pub fn writeCurrentStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocat } } -fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, address: usize) !void { +fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: var, address: usize) !void { if (builtin.os == builtin.Os.windows) { return error.UnsupportedDebugInfo; } @@ -532,7 +532,7 @@ const LineNumberProgram = struct { } }; -fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) ![]u8 { +fn readStringRaw(allocator: &mem.Allocator, in_stream: var) ![]u8 { var buf = ArrayList(u8).init(allocator); while (true) { const byte = try in_stream.readByte(); @@ -549,54 +549,62 @@ fn getString(st: &ElfStackTrace, offset: u64) ![]u8 { return st.readString(); } -fn readAllocBytes(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) ![]u8 { +fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 { const buf = try global_allocator.alloc(u8, size); errdefer global_allocator.free(buf); if ((try in_stream.read(buf)) < size) return error.EndOfFile; return buf; } -fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) !FormValue { +fn parseFormValueBlockLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { const buf = try readAllocBytes(allocator, in_stream, size); return FormValue { .Block = buf }; } -fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) !FormValue { +fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { const block_len = try in_stream.readVarInt(builtin.Endian.Little, usize, size); return parseFormValueBlockLen(allocator, in_stream, block_len); } -fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: &io.InStream, signed: bool, size: usize) !FormValue { +fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue { return FormValue { .Const = Constant { .signed = signed, .payload = try readAllocBytes(allocator, in_stream, size), }}; } -fn parseFormValueDwarfOffsetSize(in_stream: &io.InStream, is_64: bool) !u64 { +fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 { return if (is_64) try in_stream.readIntLe(u64) else u64(try in_stream.readIntLe(u32)) ; } -fn parseFormValueTargetAddrSize(in_stream: &io.InStream) !u64 { +fn parseFormValueTargetAddrSize(in_stream: var) !u64 { return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLe(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLe(u64) else unreachable; } -fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) !FormValue { +fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: var, size: usize) !FormValue { const buf = try readAllocBytes(allocator, in_stream, size); return FormValue { .Ref = buf }; } -fn parseFormValueRef(allocator: &mem.Allocator, in_stream: &io.InStream, comptime T: type) !FormValue { +fn parseFormValueRef(allocator: &mem.Allocator, in_stream: var, comptime T: type) !FormValue { const block_len = try in_stream.readIntLe(T); return parseFormValueRefLen(allocator, in_stream, block_len); } -const ParseFormValueError = error {}; +const ParseFormValueError = error { + EndOfStream, + Io, + BadFd, + Unexpected, + InvalidDebugInfo, + EndOfFile, + OutOfMemory, +}; -fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u64, is_64: bool) +fn parseFormValue(allocator: &mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue { return switch (form_id) { @@ -739,7 +747,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe try in_file.seekTo(this_offset); var is_64: bool = undefined; - const unit_length = try readInitialLength(in_stream, &is_64); + const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64); if (unit_length == 0) return error.MissingDebugInfo; const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); @@ -914,7 +922,7 @@ fn scanAllCompileUnits(st: &ElfStackTrace) !void { try st.self_exe_file.seekTo(this_unit_offset); var is_64: bool = undefined; - const unit_length = try readInitialLength(in_stream, &is_64); + const unit_length = try readInitialLength(@typeOf(in_stream.readFn).ReturnType.ErrorSet, in_stream, &is_64); if (unit_length == 0) return; const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); @@ -1014,7 +1022,7 @@ fn findCompileUnit(st: &ElfStackTrace, target_address: u64) !&const CompileUnit return error.MissingDebugInfo; } -fn readInitialLength(in_stream: &io.InStream, is_64: &bool) !u64 { +fn readInitialLength(comptime E: type, in_stream: &io.InStream(E), is_64: &bool) !u64 { const first_32_bits = try in_stream.readIntLe(u32); *is_64 = (first_32_bits == 0xffffffff); if (*is_64) { @@ -1025,7 +1033,7 @@ fn readInitialLength(in_stream: &io.InStream, is_64: &bool) !u64 { } } -fn readULeb128(in_stream: &io.InStream) !u64 { +fn readULeb128(in_stream: var) !u64 { var result: u64 = 0; var shift: usize = 0; diff --git a/std/io.zig b/std/io.zig index dbca37745a..50b70db645 100644 --- a/std/io.zig +++ b/std/io.zig @@ -61,18 +61,21 @@ pub fn getStdIn() GetStdIoErrs!File { /// Implementation of InStream trait for File pub const FileInStream = struct { file: &File, - stream: InStream, + stream: Stream, + + pub const Error = @typeOf(File.read).ReturnType.ErrorSet; + pub const Stream = InStream(Error); pub fn init(file: &File) FileInStream { return FileInStream { .file = file, - .stream = InStream { + .stream = Stream { .readFn = readFn, }, }; } - fn readFn(in_stream: &InStream, buffer: []u8) !usize { + fn readFn(in_stream: &Stream, buffer: []u8) Error!usize { const self = @fieldParentPtr(FileInStream, "stream", in_stream); return self.file.read(buffer); } @@ -81,18 +84,21 @@ pub const FileInStream = struct { /// Implementation of OutStream trait for File pub const FileOutStream = struct { file: &File, - stream: OutStream, + stream: Stream, + + pub const Error = File.WriteError; + pub const Stream = OutStream(Error); pub fn init(file: &File) FileOutStream { return FileOutStream { .file = file, - .stream = OutStream { + .stream = Stream { .writeFn = writeFn, }, }; } - fn writeFn(out_stream: &OutStream, bytes: []const u8) !void { + fn writeFn(out_stream: &Stream, bytes: []const u8) !void { const self = @fieldParentPtr(FileOutStream, "stream", out_stream); return self.file.write(bytes); } @@ -298,6 +304,8 @@ pub const File = struct { } } + pub const ReadError = error {}; + pub fn read(self: &File, buffer: []u8) !usize { if (is_posix) { var index: usize = 0; @@ -340,7 +348,7 @@ pub const File = struct { } } - const WriteError = os.WindowsWriteError || os.PosixWriteError; + pub const WriteError = os.WindowsWriteError || os.PosixWriteError; fn write(self: &File, bytes: []const u8) WriteError!void { if (is_posix) { @@ -353,161 +361,165 @@ 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) 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 - /// the contents read from the stream are lost. - pub fn readAllBuffer(self: &InStream, buffer: &Buffer, max_size: usize) !void { - try buffer.resize(0); - - var actual_buf_len: usize = 0; - while (true) { - const dest_slice = buffer.toSlice()[actual_buf_len..]; - const bytes_read = try self.readFn(self, dest_slice); - actual_buf_len += bytes_read; - - if (bytes_read != dest_slice.len) { - buffer.shrink(actual_buf_len); - return; - } - - const new_buf_size = math.min(max_size, actual_buf_len + os.page_size); - if (new_buf_size == actual_buf_len) - return error.StreamTooLong; - try buffer.resize(new_buf_size); - } - } +pub fn InStream(comptime Error: type) type { + return struct { + const Self = this; - /// Allocates enough memory to hold all the contents of the stream. If the allocated - /// 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 { - var buf = Buffer.initNull(allocator); - defer buf.deinit(); + /// 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: &Self, buffer: []u8) Error!usize, - try self.readAllBuffer(&buf, max_size); - return buf.toOwnedSlice(); - } + /// 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: &Self, buffer: &Buffer, max_size: usize) !void { + try buffer.resize(0); - /// Replaces `buffer` contents by reading from the stream until `delimiter` is found. - /// 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 { - try buf.resize(0); + var actual_buf_len: usize = 0; + while (true) { + const dest_slice = buffer.toSlice()[actual_buf_len..]; + const bytes_read = try self.readFn(self, dest_slice); + actual_buf_len += bytes_read; - while (true) { - var byte: u8 = try self.readByte(); + if (bytes_read != dest_slice.len) { + buffer.shrink(actual_buf_len); + return; + } - if (byte == delimiter) { - return; + const new_buf_size = math.min(max_size, actual_buf_len + os.page_size); + if (new_buf_size == actual_buf_len) + return error.StreamTooLong; + try buffer.resize(new_buf_size); } + } - if (buf.len() == max_size) { - return error.StreamTooLong; - } + /// Allocates enough memory to hold all the contents of the stream. If the allocated + /// 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: &Self, allocator: &mem.Allocator, max_size: usize) ![]u8 { + var buf = Buffer.initNull(allocator); + defer buf.deinit(); - try buf.appendByte(byte); + try self.readAllBuffer(&buf, max_size); + return buf.toOwnedSlice(); } - } - /// Allocates enough memory to read until `delimiter`. If the allocated - /// 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 readUntilDelimiterAlloc(self: &InStream, allocator: &mem.Allocator, - delimiter: u8, max_size: usize) ![]u8 - { - var buf = Buffer.initNull(allocator); - defer buf.deinit(); - - try self.readUntilDelimiterBuffer(self, &buf, delimiter, max_size); - return buf.toOwnedSlice(); - } + /// Replaces `buffer` contents by reading from the stream until `delimiter` is found. + /// 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: &Self, buffer: &Buffer, delimiter: u8, max_size: usize) !void { + try buf.resize(0); - /// 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 { - return self.readFn(self, buffer); - } + while (true) { + var byte: u8 = try self.readByte(); - /// Same as `read` but end of stream returns `error.EndOfStream`. - pub fn readNoEof(self: &InStream, buf: []u8) !void { - const amt_read = try self.read(buf); - if (amt_read < buf.len) return error.EndOfStream; - } + if (byte == delimiter) { + return; + } - /// Reads 1 byte from the stream or returns `error.EndOfStream`. - pub fn readByte(self: &InStream) !u8 { - var result: [1]u8 = undefined; - try self.readNoEof(result[0..]); - return result[0]; - } + if (buf.len() == max_size) { + return error.StreamTooLong; + } - /// Same as `readByte` except the returned byte is signed. - pub fn readByteSigned(self: &InStream) !i8 { - return @bitCast(i8, try self.readByte()); - } + try buf.appendByte(byte); + } + } - pub fn readIntLe(self: &InStream, comptime T: type) !T { - return self.readInt(builtin.Endian.Little, T); - } + /// Allocates enough memory to read until `delimiter`. If the allocated + /// 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 readUntilDelimiterAlloc(self: &Self, allocator: &mem.Allocator, + delimiter: u8, max_size: usize) ![]u8 + { + var buf = Buffer.initNull(allocator); + defer buf.deinit(); + + try self.readUntilDelimiterBuffer(self, &buf, delimiter, max_size); + return buf.toOwnedSlice(); + } - pub fn readIntBe(self: &InStream, comptime T: type) !T { - return self.readInt(builtin.Endian.Big, T); - } + /// 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: &Self, buffer: []u8) !usize { + return self.readFn(self, buffer); + } - 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); - } + /// Same as `read` but end of stream returns `error.EndOfStream`. + pub fn readNoEof(self: &Self, buf: []u8) !void { + const amt_read = try self.read(buf); + if (amt_read < buf.len) return error.EndOfStream; + } - 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; - const input_slice = input_buf[0..size]; - try self.readNoEof(input_slice); - return mem.readInt(input_slice, T, endian); - } + /// Reads 1 byte from the stream or returns `error.EndOfStream`. + pub fn readByte(self: &Self) !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: &Self) !i8 { + return @bitCast(i8, try self.readByte()); + } -}; + pub fn readIntLe(self: &Self, comptime T: type) !T { + return self.readInt(builtin.Endian.Little, T); + } -pub const OutStream = struct { - // TODO allow specifying the error set - writeFn: fn(self: &OutStream, bytes: []const u8) error!void, + pub fn readIntBe(self: &Self, comptime T: type) !T { + return self.readInt(builtin.Endian.Big, T); + } - pub fn print(self: &OutStream, comptime format: []const u8, args: ...) !void { - return std.fmt.format(self, error, self.writeFn, format, args); - } + pub fn readInt(self: &Self, 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 write(self: &OutStream, bytes: []const u8) !void { - return self.writeFn(self, bytes); - } + pub fn readVarInt(self: &Self, endian: builtin.Endian, comptime T: type, size: usize) !T { + assert(size <= @sizeOf(T)); + assert(size <= 8); + var input_buf: [8]u8 = undefined; + const input_slice = input_buf[0..size]; + try self.readNoEof(input_slice); + return mem.readInt(input_slice, T, endian); + } + }; +} - pub fn writeByte(self: &OutStream, byte: u8) !void { - const slice = (&byte)[0..1]; - return self.writeFn(self, slice); - } +pub fn OutStream(comptime Error: type) type { + return struct { + const Self = this; + + writeFn: fn(self: &Self, bytes: []const u8) Error!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) { - try self.writeFn(self, slice); + pub fn print(self: &Self, comptime format: []const u8, args: ...) !void { + return std.fmt.format(self, error, self.writeFn, format, args); } - } -}; + + pub fn write(self: &Self, bytes: []const u8) !void { + return self.writeFn(self, bytes); + } + + pub fn writeByte(self: &Self, byte: u8) !void { + const slice = (&byte)[0..1]; + return self.writeFn(self, slice); + } + + pub fn writeByteNTimes(self: &Self, byte: u8, n: usize) !void { + const slice = (&byte)[0..1]; + var i: usize = 0; + while (i < n) : (i += 1) { + try self.writeFn(self, slice); + } + } + }; +} /// `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 -- cgit v1.2.3 From 6940212ecbef349e449441e1fd813116865d3a5f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 5 Feb 2018 17:42:13 -0500 Subject: error sets: fix peer resolution of error unions --- src/codegen.cpp | 3 ++- src/ir.cpp | 17 +++++++++++++++-- std/debug/index.zig | 4 ++-- 3 files changed, 19 insertions(+), 5 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index c533e66d61..e3f182e17c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1790,7 +1790,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, assert(op1->value.type == op2->value.type || op_id == IrBinOpBitShiftLeftLossy || op_id == IrBinOpBitShiftLeftExact || op_id == IrBinOpBitShiftRightLossy || - op_id == IrBinOpBitShiftRightExact); + op_id == IrBinOpBitShiftRightExact || + (op1->value.type->id == TypeTableEntryIdErrorSet && op2->value.type->id == TypeTableEntryIdErrorSet)); TypeTableEntry *type_entry = op1->value.type; bool want_runtime_safety = bin_op_instruction->safety_check_on && diff --git a/src/ir.cpp b/src/ir.cpp index 3f3dfb1ad7..0f9020c9dc 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7258,8 +7258,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } // unset all the errors - for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i]; + 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; } for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { @@ -7320,6 +7320,19 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdErrorUnion && types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, source_node).id == ConstCastResultIdOk) { + if (err_set_type != nullptr) { + TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type; + 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) || type_is_global_error_set(err_set_type)) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + prev_inst = cur_inst; + continue; + } + + err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type); + } prev_inst = cur_inst; continue; } diff --git a/std/debug/index.zig b/std/debug/index.zig index a759a2af16..2bb03a6706 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -264,7 +264,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { } } -fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_info: &const LineInfo) !void { +fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &const LineInfo) !void { var f = try io.File.openRead(line_info.file_name, allocator); defer f.close(); // TODO fstat and make sure that the file has the correct size @@ -1054,7 +1054,7 @@ fn readULeb128(in_stream: var) !u64 { } } -fn readILeb128(in_stream: &io.InStream) !i64 { +fn readILeb128(in_stream: var) !i64 { var result: i64 = 0; var shift: usize = 0; -- cgit v1.2.3 From f99b8b006fe458ee717acc6c8de6170fc453acb7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 5 Feb 2018 18:09:13 -0500 Subject: error sets - fix most std lib compile errors --- src/ir.cpp | 20 +++++++++++++++++++- std/fmt/index.zig | 8 ++++---- std/io.zig | 39 ++++++++++++++++++++++++--------------- std/io_test.zig | 4 ++-- 4 files changed, 49 insertions(+), 22 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index 0f9020c9dc..c9c73c66f3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9361,6 +9361,24 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for errors")); return ira->codegen->builtin_types.entry_invalid; } + // exception if one of the operators has the type of the empty error set, we allow the comparison + // (and make it comptime known) + // this is a function which is evaluated at comptime and returns an inferred error set will have an empty + // error set. + if (op1->value.type->data.error_set.err_count == 0 || op2->value.type->data.error_set.err_count == 0) { + bool are_equal = false; + bool answer; + if (op_id == IrBinOpCmpEq) { + answer = are_equal; + } else if (op_id == IrBinOpCmpNotEq) { + answer = !are_equal; + } else { + zig_unreachable(); + } + ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base); + out_val->data.x_bool = answer; + return ira->codegen->builtin_types.entry_bool; + } TypeTableEntry *intersect_type = get_error_set_intersection(ira, op1->value.type, op2->value.type, source_node); if (type_is_invalid(intersect_type)) { return ira->codegen->builtin_types.entry_invalid; @@ -15352,7 +15370,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, ErrorTableEntry *err = err_union_val->data.x_err_union.err; if (err != nullptr) { ir_add_error(ira, &instruction->base, - buf_sprintf("unable to unwrap error '%s'", buf_ptr(&err->name))); + buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); return ira->codegen->builtin_types.entry_invalid; } diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 50f0013852..407ffd901b 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -439,9 +439,9 @@ pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { test "fmt.parseInt" { assert((parseInt(i32, "-10", 10) catch unreachable) == -10); assert((parseInt(i32, "+10", 10) catch unreachable) == 10); - assert(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidChar); - assert(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidChar); - assert(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidChar); + assert(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); + assert(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter); + assert(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter); assert((parseInt(u8, "255", 10) catch unreachable) == 255); assert(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); } @@ -538,7 +538,7 @@ fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: u test "parse u64 digit too big" { _ = parseUnsigned(u64, "123a", 10) catch |err| { - if (err == error.InvalidChar) return; + if (err == error.InvalidCharacter) return; unreachable; }; unreachable; diff --git a/std/io.zig b/std/io.zig index 50b70db645..842c30a0e4 100644 --- a/std/io.zig +++ b/std/io.zig @@ -550,21 +550,24 @@ pub fn readFileAllocExtra(path: []const u8, allocator: &mem.Allocator, extra_len return buf; } -pub const BufferedInStream = BufferedInStreamCustom(os.page_size); +pub fn BufferedInStream(comptime Error: type) type { + return BufferedInStreamCustom(os.page_size, Error); +} -pub fn BufferedInStreamCustom(comptime buffer_size: usize) type { +pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) type { return struct { const Self = this; + const Stream = InStream(Error); - pub stream: InStream, + pub stream: Stream, - unbuffered_in_stream: &InStream, + unbuffered_in_stream: &Stream, buffer: [buffer_size]u8, start_index: usize, end_index: usize, - pub fn init(unbuffered_in_stream: &InStream) Self { + pub fn init(unbuffered_in_stream: &Stream) Self { return Self { .unbuffered_in_stream = unbuffered_in_stream, .buffer = undefined, @@ -576,13 +579,13 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize) type { .start_index = buffer_size, .end_index = buffer_size, - .stream = InStream { + .stream = Stream { .readFn = readFn, }, }; } - fn readFn(in_stream: &InStream, dest: []u8) !usize { + fn readFn(in_stream: &Stream, dest: []u8) !usize { const self = @fieldParentPtr(Self, "stream", in_stream); var dest_index: usize = 0; @@ -621,25 +624,28 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize) type { }; } -pub const BufferedOutStream = BufferedOutStreamCustom(os.page_size); +pub fn BufferedOutStream(comptime Error: type) type { + return BufferedOutStreamCustom(os.page_size, Error); +} -pub fn BufferedOutStreamCustom(comptime buffer_size: usize) type { +pub fn BufferedOutStreamCustom(comptime buffer_size: usize, comptime Error: type) type { return struct { const Self = this; + const Stream = OutStream(Error); - pub stream: OutStream, + pub stream: Stream, - unbuffered_out_stream: &OutStream, + unbuffered_out_stream: &Stream, buffer: [buffer_size]u8, index: usize, - pub fn init(unbuffered_out_stream: &OutStream) Self { + pub fn init(unbuffered_out_stream: &Stream) Self { return Self { .unbuffered_out_stream = unbuffered_out_stream, .buffer = undefined, .index = 0, - .stream = OutStream { + .stream = Stream { .writeFn = writeFn, }, }; @@ -653,7 +659,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: &Stream, bytes: []const u8) !void { const self = @fieldParentPtr(Self, "stream", out_stream); if (bytes.len >= self.buffer.len) { @@ -680,7 +686,10 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize) type { /// Implementation of OutStream trait for Buffer pub const BufferOutStream = struct { buffer: &Buffer, - stream: OutStream, + stream: Stream, + + pub const Error = error{OutOfMemory}; + pub const Stream = OutStream(Error); pub fn init(buffer: &Buffer) BufferOutStream { return BufferOutStream { diff --git a/std/io_test.zig b/std/io_test.zig index 1767a546ea..ea4b816b01 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -17,7 +17,7 @@ test "write a file, read it, then delete it" { defer file.close(); var file_out_stream = io.FileOutStream.init(&file); - var buf_stream = io.BufferedOutStream.init(&file_out_stream.stream); + var buf_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream); const st = &buf_stream.stream; try st.print("begin"); try st.write(data[0..]); @@ -33,7 +33,7 @@ test "write a file, read it, then delete it" { assert(file_size == expected_file_size); var file_in_stream = io.FileInStream.init(&file); - var buf_stream = io.BufferedInStream.init(&file_in_stream.stream); + var buf_stream = io.BufferedInStream(io.FileInStream.Error).init(&file_in_stream.stream); const st = &buf_stream.stream; const contents = try st.readAllAlloc(allocator, 2 * 1024); defer allocator.free(contents); -- cgit v1.2.3 From 68238d5678a4c055bb6f1206254dcac2e0c634f0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 7 Feb 2018 22:33:05 -0500 Subject: fix comptime fn execution not returning error unions properly --- src/ir.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index c9c73c66f3..ccd4567842 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9361,6 +9361,15 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for errors")); return ira->codegen->builtin_types.entry_invalid; } + TypeTableEntry *intersect_type = get_error_set_intersection(ira, op1->value.type, op2->value.type, source_node); + if (type_is_invalid(intersect_type)) { + return ira->codegen->builtin_types.entry_invalid; + } + + if (!resolve_inferred_error_set(ira, intersect_type, source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + // exception if one of the operators has the type of the empty error set, we allow the comparison // (and make it comptime known) // this is a function which is evaluated at comptime and returns an inferred error set will have an empty @@ -9379,14 +9388,6 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp out_val->data.x_bool = answer; return ira->codegen->builtin_types.entry_bool; } - TypeTableEntry *intersect_type = get_error_set_intersection(ira, op1->value.type, op2->value.type, source_node); - if (type_is_invalid(intersect_type)) { - return ira->codegen->builtin_types.entry_invalid; - } - - if (!resolve_inferred_error_set(ira, intersect_type, source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } if (!type_is_global_error_set(intersect_type)) { if (intersect_type->data.error_set.err_count == 0) { @@ -10940,6 +10941,11 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal if (inferred_err_set_type != nullptr) { inferred_err_set_type->data.error_set.infer_fn = nullptr; if (result->value.type->id == TypeTableEntryIdErrorUnion) { + if (result->value.data.x_err_union.err != nullptr) { + inferred_err_set_type->data.error_set.err_count = 1; + inferred_err_set_type->data.error_set.errors = allocate(1); + inferred_err_set_type->data.error_set.errors[0] = result->value.data.x_err_union.err; + } TypeTableEntry *fn_inferred_err_set_type = result->value.type->data.error_union.err_set_type; inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count; inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors; -- cgit v1.2.3 From 0d5ff6f4622a492dddbb1fc2b19b3157237500b1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Feb 2018 02:08:45 -0500 Subject: error sets - most tests passing --- TODO | 11 ++++ doc/docgen.zig | 13 +--- example/cat/main.zig | 2 +- example/mix_o_files/build.zig | 2 +- example/shared_library/build.zig | 2 +- src-self-hosted/main.zig | 6 +- src-self-hosted/module.zig | 3 +- src-self-hosted/parser.zig | 18 ++---- src/ir.cpp | 76 ++++++++++++++++++++++-- src/util.hpp | 19 +++--- std/build.zig | 8 +-- std/fmt/index.zig | 2 +- std/io.zig | 4 +- std/os/child_process.zig | 21 ++++++- std/os/index.zig | 99 +++++++++++++++++++++++++------ std/os/windows/util.zig | 3 +- std/special/bootstrap.zig | 4 +- std/special/build_runner.zig | 25 +++++--- test/cases/error.zig | 38 +++++++++++- test/compare_output.zig | 25 ++++---- test/compile_errors.zig | 48 ++++++++++----- test/standalone/brace_expansion/build.zig | 2 +- test/standalone/brace_expansion/main.zig | 13 +++- test/standalone/issue_339/build.zig | 2 +- test/standalone/issue_339/test.zig | 2 +- test/standalone/pkg_import/build.zig | 2 +- test/standalone/pkg_import/test.zig | 2 +- test/standalone/use_alias/build.zig | 2 +- 28 files changed, 333 insertions(+), 121 deletions(-) (limited to 'src/ir.cpp') diff --git a/TODO b/TODO index 67e32779d7..f470ebaafe 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,6 @@ sed -i 's/\(\bfn .*) \)%\(.*{\)$/\1!\2/g' $(find . -name "*.zig") + 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 @@ -11,6 +12,11 @@ fn foo() !void { then you can return void, or any error, and the error set is inferred. + +you can get the compiler to tell you the possible errors for an inferred error set like this: + +foo() catch |err| switch (err) {}; + // TODO this is an explicit cast and should actually coerce the type erorr set casting @@ -27,3 +33,8 @@ comptime test for err undefined in infer error syntax - ?a!b should be ?(a!b) but it's (?a)!b + +syntax - (error{}!void) as the return type + + +passing a fn()error{}!T to a fn()error!T should be a compile error, they're not compatible diff --git a/doc/docgen.zig b/doc/docgen.zig index c8bae7e084..71a06d719b 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -42,7 +42,7 @@ pub fn main() !void { const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size); var file_out_stream = io.FileOutStream.init(&out_file); - var buffered_out_stream = io.BufferedOutStream.init(&file_out_stream.stream); + var buffered_out_stream = io.BufferedOutStream(io.FileOutStream.Error).init(&file_out_stream.stream); var tokenizer = Tokenizer.init(in_file_name, input_file_bytes); var toc = try genToc(allocator, &tokenizer); @@ -218,8 +218,6 @@ const Tokenizer = struct { } }; -error ParseError; - fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const u8, args: ...) error { const loc = tokenizer.getTokenLocation(token); warn("{}:{}:{}: error: " ++ fmt ++ "\n", tokenizer.source_file_name, loc.line + 1, loc.column + 1, args); @@ -596,8 +594,6 @@ const TermState = enum { ExpectEnd, }; -error UnsupportedEscape; - test "term color" { const input_bytes = "A\x1b[32;1mgreen\x1b[0mB"; const result = try termColor(std.debug.global_allocator, input_bytes); @@ -684,9 +680,7 @@ fn termColor(allocator: &mem.Allocator, input: []const u8) ![]u8 { return buf.toOwnedSlice(); } -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: var, zig_exe: []const u8) !void { var code_progress_index: usize = 0; for (toc.nodes) |node| { switch (node) { @@ -974,9 +968,6 @@ 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 { const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size); switch (result.term) { diff --git a/example/cat/main.zig b/example/cat/main.zig index acd9495a83..203567644d 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -61,7 +61,7 @@ fn cat_file(stdout: &io.File, file: &io.File) !void { } } -fn unwrapArg(arg: %[]u8) ![]u8 { +fn unwrapArg(arg: error![]u8) ![]u8 { return arg catch |err| { warn("Unable to parse command line: {}\n", err); return err; diff --git a/example/mix_o_files/build.zig b/example/mix_o_files/build.zig index 59993012fb..4380486867 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 9a42d3e312..2b5a178b35 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 3e0c00853e..9adf294ad9 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -14,10 +14,6 @@ const builtin = @import("builtin"); const ArrayList = std.ArrayList; const c = @import("c.zig"); -error InvalidCommandLineArguments; -error ZigLibDirNotFound; -error ZigInstallationNotFound; - const default_zig_cache_name = "zig-cache"; pub fn main() !void { @@ -472,7 +468,7 @@ pub fn main2() !void { } } -fn printUsage(stream: &io.OutStream) !void { +fn printUsage(stream: var) !void { try stream.write( \\Usage: zig [command] [options] \\ diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig index 8aec3b06e0..d95e54b269 100644 --- a/src-self-hosted/module.zig +++ b/src-self-hosted/module.zig @@ -110,7 +110,7 @@ pub const Module = struct { }; pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target, - kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) %&Module + kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module { var name_buffer = try Buffer.init(allocator, name); errdefer name_buffer.deinit(); @@ -265,6 +265,7 @@ pub const Module = struct { pub fn link(self: &Module, out_file: ?[]const u8) !void { warn("TODO link"); + return error.Todo; } pub fn addLinkLib(self: &Module, name: []const u8, provided_explicitly: bool) !&LinkLib { diff --git a/src-self-hosted/parser.zig b/src-self-hosted/parser.zig index c18c746cf4..7157cfbaf9 100644 --- a/src-self-hosted/parser.zig +++ b/src-self-hosted/parser.zig @@ -12,8 +12,6 @@ const io = std.io; // get rid of this const warn = std.debug.warn; -error ParseError; - pub const Parser = struct { allocator: &mem.Allocator, tokenizer: &Tokenizer, @@ -555,7 +553,7 @@ pub const Parser = struct { } fn createVarDecl(self: &Parser, visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token, - extern_token: &const ?Token) %&ast.NodeVarDecl + extern_token: &const ?Token) !&ast.NodeVarDecl { const node = try self.allocator.create(ast.NodeVarDecl); @@ -577,7 +575,7 @@ pub const Parser = struct { } fn createFnProto(self: &Parser, fn_token: &const Token, extern_token: &const ?Token, - cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) %&ast.NodeFnProto + cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) !&ast.NodeFnProto { const node = try self.allocator.create(ast.NodeFnProto); @@ -694,7 +692,7 @@ pub const Parser = struct { fn createAttachFnProto(self: &Parser, list: &ArrayList(&ast.Node), fn_token: &const Token, extern_token: &const ?Token, cc_token: &const ?Token, visib_token: &const ?Token, - inline_token: &const ?Token) %&ast.NodeFnProto + inline_token: &const ?Token) !&ast.NodeFnProto { const node = try self.createFnProto(fn_token, extern_token, cc_token, visib_token, inline_token); try list.append(&node.base); @@ -702,7 +700,7 @@ pub const Parser = struct { } fn createAttachVarDecl(self: &Parser, list: &ArrayList(&ast.Node), visib_token: &const ?Token, - mut_token: &const Token, comptime_token: &const ?Token, extern_token: &const ?Token) %&ast.NodeVarDecl + mut_token: &const Token, comptime_token: &const ?Token, extern_token: &const ?Token) !&ast.NodeVarDecl { const node = try self.createVarDecl(visib_token, mut_token, comptime_token, extern_token); try list.append(&node.base); @@ -763,7 +761,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: var, root_node: &ast.NodeRoot) !void { var stack = self.initUtilityArrayList(RenderAstFrame); defer self.deinitUtilityArrayList(stack); @@ -802,7 +800,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: var, root_node: &ast.NodeRoot) !void { var stack = self.initUtilityArrayList(RenderState); defer self.deinitUtilityArrayList(stack); @@ -1058,10 +1056,6 @@ fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 { return buffer.toOwnedSlice(); } -error TestFailed; -error NondeterministicMemoryUsage; -error MemoryLeakDetected; - // TODO test for memory leaks // TODO test for valid frees fn testCanonical(source: []const u8) !void { diff --git a/src/ir.cpp b/src/ir.cpp index ccd4567842..0c30f49d36 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5442,6 +5442,10 @@ static TypeTableEntry *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, buf_resize(&err_set_type->name, 0); buf_appendf(&err_set_type->name, "error{"); + for (uint32_t i = 0, count = set1->data.error_set.err_count; i < count; i += 1) { + assert(errors[set1->data.error_set.errors[i]->value] == set1->data.error_set.errors[i]); + } + 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]; @@ -5523,6 +5527,8 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A err_set_type->data.error_set.errors = allocate(err_count); } + ErrorTableEntry **errors = allocate(irb->codegen->errors_by_index.length + err_count); + for (uint32_t i = 0; i < err_count; i += 1) { AstNode *symbol_node = node->data.err_set_decl.decls.at(i); assert(symbol_node->type == NodeTypeSymbol); @@ -5543,7 +5549,16 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A buf_ptr(err_name), error_value_count)); } err_set_type->data.error_set.errors[i] = err; + + ErrorTableEntry *prev_err = errors[err->value]; + if (prev_err != nullptr) { + ErrorMsg *msg = add_node_error(irb->codegen, err->decl_node, buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name))); + add_error_note(irb->codegen, msg, prev_err->decl_node, buf_sprintf("other error here")); + return irb->codegen->invalid_instruction; + } + errors[err->value] = err; } + free(errors); return ir_build_const_type(irb, parent_scope, node, err_set_type); } @@ -6512,6 +6527,7 @@ static TypeTableEntry *get_error_set_intersection(IrAnalyze *ira, TypeTableEntry ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length); for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = set1->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } ZigList intersection_list = {}; @@ -6653,6 +6669,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry 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]; + assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) { @@ -6767,6 +6784,12 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, buf_sprintf("unable to cast global error set into smaller set")); return ImplicitCastMatchResultReportedError; } + } else if (const_cast_result.id == ConstCastResultIdErrSetGlobal) { + ErrorMsg *msg = ir_add_error(ira, value, + buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name))); + add_error_note(ira->codegen, msg, value->source_node, + buf_sprintf("unable to cast global error set into smaller set")); + return ImplicitCastMatchResultReportedError; } if (missing_errors != nullptr) { ErrorMsg *msg = ir_add_error(ira, value, @@ -6995,6 +7018,12 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, return ImplicitCastMatchResultNo; } +static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *errors_count) { + size_t old_errors_count = *errors_count; + *errors_count = g->errors_by_index.length; + *errors = reallocate(*errors, old_errors_count, *errors_count); +} + 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]; @@ -7002,6 +7031,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod return ira->codegen->builtin_types.entry_invalid; } ErrorTableEntry **errors = nullptr; + size_t errors_count = 0; TypeTableEntry *err_set_type = nullptr; if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { if (type_is_global_error_set(prev_inst->value.type)) { @@ -7011,9 +7041,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod 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); + update_errors_helper(ira->codegen, &errors, &errors_count); + 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]; + assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } } @@ -7064,6 +7096,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } + // number of declared errors might have increased now + update_errors_helper(ira->codegen, &errors, &errors_count); + // 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; @@ -7084,8 +7119,12 @@ 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; } + for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { + assert(errors[i] == nullptr); + } for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = cur_type->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } bool cur_is_superset = true; @@ -7122,14 +7161,21 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod prev_inst = cur_inst; continue; } + + update_errors_helper(ira->codegen, &errors, &errors_count); + // 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; } + for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { + assert(errors[i] == nullptr); + } 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]; + assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } bool cur_is_superset = true; @@ -7173,15 +7219,18 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { return ira->codegen->builtin_types.entry_invalid; } + + update_errors_helper(ira->codegen, &errors, &errors_count); + if (err_set_type == nullptr) { if (prev_type->id == TypeTableEntryIdErrorUnion) { err_set_type = prev_type->data.error_union.err_set_type; } else { err_set_type = cur_type; } - 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]; + assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } if (err_set_type == cur_type) { @@ -7237,11 +7286,13 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } + update_errors_helper(ira->codegen, &errors, &errors_count); + if (err_set_type == nullptr) { err_set_type = prev_err_set_type; - errors = allocate(ira->codegen->errors_by_index.length); for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } } @@ -7262,8 +7313,12 @@ 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; } + for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { + assert(errors[i] == nullptr); + } 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]; + assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } bool cur_is_superset = true; @@ -7331,6 +7386,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } + update_errors_helper(ira->codegen, &errors, &errors_count); + err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type); } prev_inst = cur_inst; @@ -8000,6 +8057,7 @@ static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *sou ErrorTableEntry **errors = allocate(ira->codegen->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]; + assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } ErrorMsg *err_msg = nullptr; @@ -10212,8 +10270,9 @@ static TypeTableEntry *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstruction } ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length); - for (uint32_t i = 0; i < op1_type->data.error_set.err_count; i += 1) { + for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) { ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); errors[error_entry->value] = error_entry; } TypeTableEntry *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type); @@ -14987,6 +15046,15 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns result = container_type->data.structure.src_field_count; } else if (container_type->id == TypeTableEntryIdUnion) { result = container_type->data.unionation.src_field_count; + } else if (container_type->id == TypeTableEntryIdErrorSet) { + if (!resolve_inferred_error_set(ira, container_type, instruction->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (type_is_global_error_set(container_type)) { + ir_add_error(ira, &instruction->base, buf_sprintf("global error set member count not available at comptime")); + return ira->codegen->builtin_types.entry_invalid; + } + result = container_type->data.error_set.err_count; } else { ir_add_error(ira, &instruction->base, buf_sprintf("no value count available for type '%s'", buf_ptr(&container_type->name))); return ira->codegen->builtin_types.entry_invalid; diff --git a/src/util.hpp b/src/util.hpp index ce6cc09a59..ae33cb84af 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -92,19 +92,22 @@ static inline void safe_memcpy(T *dest, const T *src, size_t count) { } template -static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) { -#ifdef NDEBUG +static inline T *reallocate(T *old, size_t old_count, size_t new_count) { T *ptr = reinterpret_cast(realloc(old, new_count * sizeof(T))); if (!ptr) zig_panic("allocation failed"); + if (new_count > old_count) { + memset(&ptr[old_count], 0, (new_count - old_count) * sizeof(T)); + } return ptr; -#else - // manually assign every element to trigger compile error for non-copyable structs - T *ptr = allocate_nonzero(new_count); - safe_memcpy(ptr, old, old_count); - free(old); +} + +template +static inline T *reallocate_nonzero(T *old, size_t old_count, size_t new_count) { + T *ptr = reinterpret_cast(realloc(old, new_count * sizeof(T))); + if (!ptr) + zig_panic("allocation failed"); return ptr; -#endif } template diff --git a/std/build.zig b/std/build.zig index 216699f355..ee1990a349 100644 --- a/std/build.zig +++ b/std/build.zig @@ -271,7 +271,7 @@ pub const Builder = struct { return &self.uninstall_tls.step; } - fn makeUninstall(uninstall_step: &Step) !void { + fn makeUninstall(uninstall_step: &Step) error!void { const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls); @@ -285,7 +285,7 @@ pub const Builder = struct { // TODO remove empty directories } - fn makeOneStep(self: &Builder, s: &Step) !void { + fn makeOneStep(self: &Builder, s: &Step) error!void { if (s.loop_flag) { warn("Dependency loop detected:\n {}\n", s.name); return error.DependencyLoopDetected; @@ -1910,7 +1910,7 @@ pub const LogStep = struct { }; } - fn make(step: &Step) !void { + fn make(step: &Step) error!void { const self = @fieldParentPtr(LogStep, "step", step); warn("{}", self.data); } @@ -1972,7 +1972,7 @@ pub const Step = struct { self.dependencies.append(other) catch unreachable; } - fn makeNoOp(self: &Step) (error{}!void) {} + fn makeNoOp(self: &Step) error!void {} }; fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_major_only: []const u8, diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 407ffd901b..98f8d3d26b 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -510,7 +510,7 @@ pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ... return bufPrint(buf, fmt, args); } -fn countSize(size: &usize, bytes: []const u8) !void { +fn countSize(size: &usize, bytes: []const u8) (error{}!void) { *size += bytes.len; } diff --git a/std/io.zig b/std/io.zig index 842c30a0e4..9f16c5f496 100644 --- a/std/io.zig +++ b/std/io.zig @@ -694,13 +694,13 @@ pub const BufferOutStream = struct { pub fn init(buffer: &Buffer) BufferOutStream { return BufferOutStream { .buffer = buffer, - .stream = OutStream { + .stream = Stream { .writeFn = writeFn, }, }; } - fn writeFn(out_stream: &OutStream, bytes: []const u8) !void { + fn writeFn(out_stream: &Stream, bytes: []const u8) !void { const self = @fieldParentPtr(BufferOutStream, "stream", out_stream); return self.buffer.append(bytes); } diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 360a1bfb93..1b7639fb71 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -55,7 +55,22 @@ pub const ChildProcess = struct { llnode: if (is_windows) void else LinkedList(&ChildProcess).Node, pub const SpawnError = error { - + ProcessFdQuotaExceeded, + Unexpected, + NotDir, + SystemResources, + FileNotFound, + NameTooLong, + SymLinkLoop, + FileSystem, + OutOfMemory, + AccessDenied, + PermissionDenied, + InvalidUserId, + ResourceLimitReached, + InvalidExe, + IsDir, + FileBusy, }; pub const Term = union(enum) { @@ -313,7 +328,7 @@ pub const ChildProcess = struct { // Here we potentially return the fork child's error // from the parent pid. if (err_int != @maxValue(ErrInt)) { - return error(err_int); + return SpawnError(err_int); } return statusToTerm(status); @@ -757,7 +772,7 @@ fn destroyPipe(pipe: &const [2]i32) void { // Child of fork calls this to report an error to the fork parent. // Then the child exits. -fn forkChildErrReport(fd: i32, err: error) noreturn { +fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn { _ = writeIntFd(fd, ErrInt(err)); posix.exit(1); } diff --git a/std/os/index.zig b/std/os/index.zig index 33707676bb..c5191c8d74 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -243,7 +243,6 @@ pub const PosixOpenError = error { SystemResources, NoSpaceLeft, NotDir, - AccessDenied, PathAlreadyExists, Unexpected, }; @@ -411,7 +410,19 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, return posixExecveErrnoToErr(err); } -fn posixExecveErrnoToErr(err: usize) error { +pub const PosixExecveError = error { + SystemResources, + AccessDenied, + InvalidExe, + FileSystem, + IsDir, + FileNotFound, + NotDir, + FileBusy, + Unexpected, +}; + +fn posixExecveErrnoToErr(err: usize) PosixExecveError { assert(err > 0); return switch (err) { posix.EFAULT => unreachable, @@ -904,24 +915,68 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void { /// removes it. If it cannot be removed because it is a non-empty directory, /// this function recursively removes its entries and then tries again. // TODO non-recursive implementation -pub fn deleteTree(allocator: &Allocator, full_path: []const u8) !void { +const DeleteTreeError = error { + OutOfMemory, + AccessDenied, + FileTooBig, + IsDir, + SymLinkLoop, + ProcessFdQuotaExceeded, + NameTooLong, + SystemFdQuotaExceeded, + NoDevice, + PathNotFound, + SystemResources, + NoSpaceLeft, + PathAlreadyExists, + ReadOnlyFileSystem, + NotDir, + FileNotFound, + FileSystem, + FileBusy, + DirNotEmpty, + Unexpected, +}; +pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!void { start_over: while (true) { // First, try deleting the item as a file. This way we don't follow sym links. if (deleteFile(allocator, full_path)) { return; - } else |err| { - if (err == error.FileNotFound) - return; - if (err != error.IsDir) - return err; + } else |err| switch (err) { + error.FileNotFound => return, + error.IsDir => {}, + + error.OutOfMemory, + error.AccessDenied, + error.SymLinkLoop, + error.NameTooLong, + error.SystemResources, + error.ReadOnlyFileSystem, + error.NotDir, + error.FileSystem, + error.FileBusy, + error.Unexpected + => return err, } { - var dir = Dir.open(allocator, full_path) catch |err| { - if (err == error.FileNotFound) - return; - if (err == error.NotDir) - continue :start_over; - return err; + var dir = Dir.open(allocator, full_path) catch |err| switch (err) { + error.NotDir => continue :start_over, + + error.OutOfMemory, + error.AccessDenied, + error.FileTooBig, + error.IsDir, + error.SymLinkLoop, + error.ProcessFdQuotaExceeded, + error.NameTooLong, + error.SystemFdQuotaExceeded, + error.NoDevice, + error.PathNotFound, + error.SystemResources, + error.NoSpaceLeft, + error.PathAlreadyExists, + error.Unexpected + => return err, }; defer dir.close(); @@ -1252,6 +1307,8 @@ pub const ArgIteratorWindows = struct { quote_count: usize, seen_quote_count: usize, + pub const NextError = error{OutOfMemory}; + pub fn init() ArgIteratorWindows { return initWithCmdLine(windows.GetCommandLineA()); } @@ -1267,7 +1324,7 @@ pub const ArgIteratorWindows = struct { } /// You must free the returned memory when done. - pub fn next(self: &ArgIteratorWindows, allocator: &Allocator) ?(@typeOf(internalNext).ReturnType.ErrorSet![]u8) { + pub fn next(self: &ArgIteratorWindows, allocator: &Allocator) ?(NextError![]u8) { // march forward over whitespace while (true) : (self.index += 1) { const byte = self.cmd_line[self.index]; @@ -1320,7 +1377,7 @@ pub const ArgIteratorWindows = struct { } } - fn internalNext(self: &ArgIteratorWindows, allocator: &Allocator) ![]u8 { + fn internalNext(self: &ArgIteratorWindows, allocator: &Allocator) NextError![]u8 { var buf = try Buffer.initSize(allocator, 0); defer buf.deinit(); @@ -1394,16 +1451,20 @@ pub const ArgIteratorWindows = struct { }; pub const ArgIterator = struct { - inner: if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix, + const InnerType = if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix; + + inner: InnerType, pub fn init() ArgIterator { return ArgIterator { - .inner = if (builtin.os == Os.windows) ArgIteratorWindows.init() else ArgIteratorPosix.init(), + .inner = InnerType.init(), }; } + + pub const NextError = ArgIteratorWindows.NextError; /// You must free the returned memory when done. - pub fn next(self: &ArgIterator, allocator: &Allocator) ?![]u8 { + pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) { if (builtin.os == Os.windows) { return self.inner.next(allocator); } else { diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 2569763489..e2d7c14149 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -30,7 +30,6 @@ pub fn windowsClose(handle: windows.HANDLE) void { pub const WriteError = error { SystemResources, OperationAborted, - SystemResources, IoPending, BrokenPipe, Unexpected, @@ -83,6 +82,8 @@ pub const OpenError = error { AccessDenied, PipeBusy, Unexpected, + OutOfMemory, + NameTooLong, }; /// `file_path` may need to be copied in memory to add a null terminating byte. In this case diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index bcb3456353..f5754638b0 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -77,7 +77,7 @@ fn callMain() u8 { }, builtin.TypeId.Int => { if (@typeOf(root.main).ReturnType.bit_count != 8) { - @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"); + @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"); } return root.main(); }, @@ -91,6 +91,6 @@ fn callMain() u8 { }; return 0; }, - else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"), + else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"), } } diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index 198199b941..0661d26a98 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -1,5 +1,6 @@ const root = @import("@build"); const std = @import("std"); +const builtin = @import("builtin"); const io = std.io; const fmt = std.fmt; const os = std.os; @@ -43,14 +44,14 @@ pub fn main() !void { var stderr_file = io.getStdErr(); var stderr_file_stream: io.FileOutStream = undefined; - var stderr_stream: %&io.OutStream = if (stderr_file) |*f| x: { + var stderr_stream = if (stderr_file) |*f| x: { stderr_file_stream = io.FileOutStream.init(f); break :x &stderr_file_stream.stream; } else |err| err; var stdout_file = io.getStdOut(); var stdout_file_stream: io.FileOutStream = undefined; - var stdout_stream: %&io.OutStream = if (stdout_file) |*f| x: { + var stdout_stream = if (stdout_file) |*f| x: { stdout_file_stream = io.FileOutStream.init(f); break :x &stdout_file_stream.stream; } else |err| err; @@ -110,7 +111,7 @@ pub fn main() !void { } builder.setInstallPrefix(prefix); - try root.build(&builder); + try runBuild(&builder); if (builder.validateUserInputDidItFail()) return usageAndErr(&builder, true, try stderr_stream); @@ -123,11 +124,19 @@ pub fn main() !void { }; } -fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) !void { +fn runBuild(builder: &Builder) error!void { + switch (@typeId(@typeOf(root.build).ReturnType)) { + builtin.TypeId.Void => root.build(builder), + builtin.TypeId.ErrorUnion => try root.build(builder), + else => @compileError("expected return type of build to be 'void' or '!void'"), + } +} + +fn usage(builder: &Builder, already_ran_build: bool, out_stream: var) !void { // run the build script to collect the options if (!already_ran_build) { builder.setInstallPrefix(null); - try root.build(builder); + try runBuild(builder); } // This usage text has to be synchronized with src/main.cpp @@ -181,12 +190,14 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) ); } -fn usageAndErr(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) error { +fn usageAndErr(builder: &Builder, already_ran_build: bool, out_stream: var) error { usage(builder, already_ran_build, out_stream) catch {}; return error.InvalidArgs; } -fn unwrapArg(arg: %[]u8) ![]u8 { +const UnwrapArgError = error {OutOfMemory}; + +fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 { return arg catch |err| { warn("Unable to parse command line: {}\n", err); return err; diff --git a/test/cases/error.zig b/test/cases/error.zig index f35f2e7ad6..0322cb9246 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -1,5 +1,7 @@ -const assert = @import("std").debug.assert; -const mem = @import("std").mem; +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; +const builtin = @import("builtin"); pub fn foo() error!i32 { const x = try bar(); @@ -74,3 +76,35 @@ fn doErrReturnInAssignment() error!void { fn makeANonErr() error!i32 { return 1; } + +test "error union type " { + testErrorUnionType(); + comptime testErrorUnionType(); +} + +fn testErrorUnionType() void { + const x: error!i32 = 1234; + if (x) |value| assert(value == 1234) else |_| unreachable; + assert(@typeId(@typeOf(x)) == builtin.TypeId.ErrorUnion); + assert(@typeId(@typeOf(x).ErrorSet) == builtin.TypeId.ErrorSet); + assert(@typeOf(x).ErrorSet == error); +} + +test "error set type " { + testErrorSetType(); + comptime testErrorSetType(); +} + +const MyErrSet = error {OutOfMemory, FileNotFound}; + +fn testErrorSetType() void { + assert(@memberCount(MyErrSet) == 2); + + const a: MyErrSet!i32 = 5678; + const b: MyErrSet!i32 = MyErrSet.OutOfMemory; + + if (a) |value| assert(value == 5678) else |err| switch (err) { + error.OutOfMemory => unreachable, + error.FileNotFound => unreachable, + } +} diff --git a/test/compare_output.zig b/test/compare_output.zig index 7467bcc089..dfdeeba9b3 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,7 +394,7 @@ 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 { @@ -406,7 +406,6 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ defer stdout.print("defer3\n") catch unreachable; \\ stdout.print("after\n") catch unreachable; \\} - \\error IToldYouItWouldFail; \\fn its_gonna_fail() !void { \\ return error.IToldYouItWouldFail; \\} @@ -414,7 +413,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { 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 { @@ -426,7 +425,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\ defer stdout.print("defer3\n") catch unreachable; \\ stdout.print("after\n") catch unreachable; \\} - \\fn its_gonna_pass() %void { } + \\fn its_gonna_pass() error!void { } , "before\nafter\ndefer3\ndefer1\n"); cases.addCase(x: { @@ -434,7 +433,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; \\} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 0c02fccbfe..bf4dad3cc8 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,25 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("@memberCount of error", + \\comptime { + \\ _ = @memberCount(error); + \\} + , + ".tmp_source.zig:2:9: error: global error set member count not available at comptime"); + + cases.add("duplicate error value in error set", + \\const Foo = error { + \\ Bar, + \\ Bar, + \\}; + \\export fn entry() void { + \\ const a: Foo = undefined; + \\} + , + ".tmp_source.zig:3:5: error: duplicate error: 'Bar'", + ".tmp_source.zig:2:5: note: other error here"); + cases.add("duplicate struct field", \\const Foo = struct { \\ Bar: i32, @@ -99,12 +118,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add("wrong return type for main", \\pub fn main() f32 { } - , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"); + , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"); cases.add("double ?? on main return value", \\pub fn main() ??void { \\} - , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '%void'"); + , "error: expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"); cases.add("bad identifier in function with struct defined inside function which references local const", \\export fn entry() void { @@ -1160,7 +1179,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\export fn f() void { \\ try something(); \\} - \\fn something() %void { } + \\fn something() error!void { } , ".tmp_source.zig:2:5: error: expected type 'void', found 'error'"); @@ -1251,7 +1270,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { , ".tmp_source.zig:3:11: error: cannot assign to constant"); cases.add("main function with bogus args type", - \\pub fn main(args: [][]bogus) %void {} + \\pub fn main(args: [][]bogus) !void {} , ".tmp_source.zig:1:23: error: use of undeclared identifier 'bogus'"); cases.add("for loop missing element param", @@ -1391,7 +1410,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\ const a = maybeInt() ?? return; \\} \\ - \\fn canFail() %void { } + \\fn canFail() error!void { } \\ \\pub fn maybeInt() ?i32 { \\ return 0; @@ -1521,7 +1540,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\export fn foo() void { \\ bar() catch unreachable; \\} - \\fn bar() %i32 { return 0; } + \\fn bar() error!i32 { return 0; } , ".tmp_source.zig:2:11: error: expression value is ignored"); cases.add("ignored statement value", @@ -1552,7 +1571,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\export fn foo() void { \\ defer bar(); \\} - \\fn bar() %i32 { return 0; } + \\fn bar() error!i32 { return 0; } , ".tmp_source.zig:2:14: error: expression value is ignored"); cases.add("dereference an array", @@ -1619,13 +1638,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { , ".tmp_source.zig:2:21: error: expected pointer, found 'usize'"); cases.add("too many error values to cast to small integer", - \\error A; error B; error C; error D; error E; error F; error G; error H; - \\const u2 = @IntType(false, 2); - \\fn foo(e: error) u2 { + \\const Error = error { A, B, C, D, E, F, G, H }; + \\fn foo(e: Error) u2 { \\ return u2(e); \\} \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } - , ".tmp_source.zig:4:14: error: too many error values to fit in 'u2'"); + , ".tmp_source.zig:3:14: error: too many error values to fit in 'u2'"); cases.add("asm at compile time", \\comptime { @@ -1808,9 +1826,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\export fn foo() void { \\ while (bar()) {} \\} - \\fn bar() %i32 { return 1; } + \\fn bar() error!i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected type 'bool', found '%i32'"); + ".tmp_source.zig:2:15: error: expected type 'bool', found 'error!i32'"); cases.add("while expected nullable, got bool", \\export fn foo() void { @@ -1824,9 +1842,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\export fn foo() void { \\ while (bar()) |x| {} \\} - \\fn bar() %i32 { return 1; } + \\fn bar() error!i32 { return 1; } , - ".tmp_source.zig:2:15: error: expected nullable type, found '%i32'"); + ".tmp_source.zig:2:15: error: expected nullable type, found 'error!i32'"); cases.add("while expected error union, got bool", \\export fn foo() void { diff --git a/test/standalone/brace_expansion/build.zig b/test/standalone/brace_expansion/build.zig index 1a72a9a0bc..7752f599df 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 3596086138..b8eab68a3e 100644 --- a/test/standalone/brace_expansion/main.zig +++ b/test/standalone/brace_expansion/main.zig @@ -68,7 +68,12 @@ const Node = union(enum) { Combine: []Node, }; -fn parse(tokens: &const ArrayList(Token), token_index: &usize) !Node { +const ParseError = error { + InvalidInput, + OutOfMemory, +}; + +fn parse(tokens: &const ArrayList(Token), token_index: &usize) ParseError!Node { const first_token = tokens.items[*token_index]; *token_index += 1; @@ -132,7 +137,11 @@ fn expandString(input: []const u8, output: &Buffer) !void { } } -fn expandNode(node: &const Node, output: &ArrayList(Buffer)) !void { +const ExpandNodeError = error { + OutOfMemory, +}; + +fn expandNode(node: &const Node, output: &ArrayList(Buffer)) ExpandNodeError!void { assert(output.len == 0); switch (*node) { Node.Scalar => |scalar| { diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig index 940d1bc24d..f3ab327006 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/issue_339/test.zig b/test/standalone/issue_339/test.zig index 1c9480a0a6..f65b9f734e 100644 --- a/test/standalone/issue_339/test.zig +++ b/test/standalone/issue_339/test.zig @@ -1,7 +1,7 @@ const StackTrace = @import("builtin").StackTrace; pub fn panic(msg: []const u8, stack_trace: ?&StackTrace) noreturn { @breakpoint(); while (true) {} } -fn bar() %void {} +fn bar() error!void {} export fn foo() void { bar() catch unreachable; diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig index 2cc63bf30b..bb9416d3c4 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 b9a0f50776..ffd2080022 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 452966789c..ecbba297d8 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"); -- cgit v1.2.3 From fee875770cb8c9363219b736f6c03e15cff39b92 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Feb 2018 11:09:18 -0500 Subject: error set casting building --- TODO | 1 + src/all_types.hpp | 1 + src/codegen.cpp | 3 +++ src/ir.cpp | 61 +++++++++++++++++++++---------------------------------- 4 files changed, 28 insertions(+), 38 deletions(-) (limited to 'src/ir.cpp') diff --git a/TODO b/TODO index f470ebaafe..567d31e39d 100644 --- a/TODO +++ b/TODO @@ -19,6 +19,7 @@ foo() catch |err| switch (err) {}; // TODO this is an explicit cast and should actually coerce the type erorr set casting + // add a runtime safety check test err should be comptime if error set has 0 members diff --git a/src/all_types.hpp b/src/all_types.hpp index 869d17f0b2..04b781a598 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -560,6 +560,7 @@ enum CastOp { CastOpResizeSlice, CastOpBytesToSlice, CastOpNumLitToConcrete, + CastOpErrSet, }; struct AstNodeFnCallExpr { diff --git a/src/codegen.cpp b/src/codegen.cpp index e3f182e17c..69460d723a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2081,6 +2081,9 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, assert(wanted_type->id == TypeTableEntryIdInt); assert(actual_type->id == TypeTableEntryIdBool); return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, ""); + case CastOpErrSet: + // TODO runtime safety for error casting + return expr_val; } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index 0c30f49d36..25723535dd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7584,6 +7584,8 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, switch (cast_op) { case CastOpNoCast: zig_unreachable(); + case CastOpErrSet: + zig_panic("TODO"); case CastOpNoop: { copy_const_val(const_val, other_val, other_val->special == ConstValSpecialStatic); @@ -8039,52 +8041,35 @@ 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) { - TypeTableEntry *contained_set = value->value.type; - TypeTableEntry *container_set = wanted_type; - - assert(contained_set->id == TypeTableEntryIdErrorSet); - assert(container_set->id == TypeTableEntryIdErrorSet); + assert(value->value.type->id == TypeTableEntryIdErrorSet); + assert(wanted_type->id == TypeTableEntryIdErrorSet); - zig_panic("TODO explicit error set cast"); + if (instr_is_comptime(value)) { + ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; - if (container_set->data.error_set.infer_fn == nullptr && - !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) { - ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; - assert(errors[error_entry->value] == nullptr); - errors[error_entry->value] = error_entry; + if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) { + return ira->codegen->invalid_instruction; } - ErrorMsg *err_msg = nullptr; - 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 (err_msg == nullptr) { - err_msg = ir_add_error(ira, source_instr, - buf_sprintf("invalid cast of error set '%s' to error set '%s'", - buf_ptr(&contained_set->name), buf_ptr(&container_set->name))); + if (!type_is_global_error_set(wanted_type)) { + bool subset = false; + for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) { + if (wanted_type->data.error_set.errors[i]->value == val->data.x_err_set->value) { + subset = true; + break; } - add_error_note(ira->codegen, err_msg, contained_error_entry->decl_node, - buf_sprintf("'%s.%s' not present in '%s'", buf_ptr(&contained_set->name), - buf_ptr(&contained_error_entry->name), buf_ptr(&container_set->name))); + } + if (!subset) { + ir_add_error(ira, source_instr, + buf_sprintf("error.%s not a member of error set '%s'", + buf_ptr(&val->data.x_err_set->name), buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_instruction; } } - free(errors); - if (err_msg != nullptr) { - return ira->codegen->invalid_instruction; - } - } - - if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); - if (!val) - return ira->codegen->invalid_instruction; IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); @@ -8094,7 +8079,7 @@ static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *sou return &const_instruction->base; } - IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpNoop); + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpErrSet); result->value.type = wanted_type; return result; } -- cgit v1.2.3 From 57edd4dcb31eeaca69b93d2caf0e1f4eb3772e3e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Feb 2018 18:13:07 -0500 Subject: error sets - fix bad value for constant error literal --- src/ir.cpp | 2 +- test/cases/error.zig | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index 25723535dd..728a3e815c 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12308,7 +12308,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru 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); + err_entry = find_err_table_entry(child_type, field_name); if (err_entry == nullptr) { ir_add_error(ira, &field_ptr_instruction->base, buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name))); diff --git a/test/cases/error.zig b/test/cases/error.zig index 0322cb9246..3d2835e723 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -108,3 +108,18 @@ fn testErrorSetType() void { error.FileNotFound => unreachable, } } + + +test "explicit error set cast" { + testExplicitErrorSetCast(Set1.A); + comptime testExplicitErrorSetCast(Set1.A); +} + +const Set1 = error{A, B}; +const Set2 = error{A, C}; + +fn testExplicitErrorSetCast(set1: Set1) void { + var x = Set2(set1); + var y = Set1(x); + assert(y == error.A); +} -- cgit v1.2.3 From 54c06bf7158ce52c5de8d09f109215c467a3bf6a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Feb 2018 21:54:44 -0500 Subject: error sets: runtime safety for int-to-err and err set cast --- TODO | 4 --- src/codegen.cpp | 79 ++++++++++++++++++++++++++++++++----------------- src/ir.cpp | 50 ++++++++++++++++++++++++------- test/compile_errors.zig | 32 ++++++++++++++++++++ test/runtime_safety.zig | 16 +++++++++- 5 files changed, 139 insertions(+), 42 deletions(-) (limited to 'src/ir.cpp') diff --git a/TODO b/TODO index 567d31e39d..2f3f25c3d3 100644 --- a/TODO +++ b/TODO @@ -17,10 +17,6 @@ you can get the compiler to tell you the possible errors for an inferred error s foo() catch |err| switch (err) {}; -// TODO this is an explicit cast and should actually coerce the type - erorr set casting - // add a runtime safety check - test err should be comptime if error set has 0 members diff --git a/src/codegen.cpp b/src/codegen.cpp index 69460d723a..2f8ebf768d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1958,6 +1958,54 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, zig_unreachable(); } +static void add_error_range_check(CodeGen *g, TypeTableEntry *err_set_type, TypeTableEntry *int_type, LLVMValueRef target_val) { + assert(err_set_type->id == TypeTableEntryIdErrorSet); + + if (type_is_global_error_set(err_set_type)) { + LLVMValueRef zero = LLVMConstNull(int_type->type_ref); + LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, ""); + LLVMValueRef ok_bit; + + BigInt biggest_possible_err_val = {0}; + eval_min_max_value_int(g, int_type, &biggest_possible_err_val, true); + + if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) && + bigint_as_unsigned(&biggest_possible_err_val) < g->errors_by_index.length) + { + ok_bit = neq_zero_bit; + } else { + LLVMValueRef error_value_count = LLVMConstInt(int_type->type_ref, g->errors_by_index.length, false); + LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, ""); + ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, ""); + } + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail"); + + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdInvalidErrorCode); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } else { + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail"); + + uint32_t err_count = err_set_type->data.error_set.err_count; + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_val, fail_block, err_count); + for (uint32_t i = 0; i < err_count; i += 1) { + LLVMValueRef case_value = LLVMConstInt(g->err_tag_type->type_ref, err_set_type->data.error_set.errors[i]->value, false); + LLVMAddCase(switch_instr, case_value, ok_block); + } + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdInvalidErrorCode); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } +} + static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, IrInstructionCast *cast_instruction) { @@ -2082,7 +2130,9 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, assert(actual_type->id == TypeTableEntryIdBool); return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, ""); case CastOpErrSet: - // TODO runtime safety for error casting + if (ir_want_runtime_safety(g, &cast_instruction->base)) { + add_error_range_check(g, wanted_type, g->err_tag_type, expr_val); + } return expr_val; } zig_unreachable(); @@ -2154,32 +2204,7 @@ static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, I LLVMValueRef target_val = ir_llvm_value(g, instruction->target); if (ir_want_runtime_safety(g, &instruction->base)) { - LLVMValueRef zero = LLVMConstNull(actual_type->type_ref); - LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, ""); - LLVMValueRef ok_bit; - - BigInt biggest_possible_err_val = {0}; - eval_min_max_value_int(g, actual_type, &biggest_possible_err_val, true); - - if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) && - bigint_as_unsigned(&biggest_possible_err_val) < g->errors_by_index.length) - { - ok_bit = neq_zero_bit; - } else { - LLVMValueRef error_value_count = LLVMConstInt(actual_type->type_ref, g->errors_by_index.length, false); - LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, ""); - ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, ""); - } - - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail"); - - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdInvalidErrorCode); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); + add_error_range_check(g, wanted_type, actual_type, target_val); } return gen_widen_or_shorten(g, false, actual_type, g->err_tag_type, target_val); diff --git a/src/ir.cpp b/src/ir.cpp index 9cc05a2bc2..fa2815d6dd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8505,19 +8505,49 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - BigInt err_count; - bigint_init_unsigned(&err_count, ira->codegen->errors_by_index.length); - if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) { - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &val->data.x_bigint, 10); - ir_add_error(ira, source_instr, - buf_sprintf("integer value %s represents no error", buf_ptr(val_buf))); + if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; } - size_t index = bigint_as_unsigned(&val->data.x_bigint); - result->value.data.x_err_set = ira->codegen->errors_by_index.at(index); - return result; + if (type_is_global_error_set(wanted_type)) { + BigInt err_count; + bigint_init_unsigned(&err_count, ira->codegen->errors_by_index.length); + + if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &val->data.x_bigint, 10); + ir_add_error(ira, source_instr, + buf_sprintf("integer value %s represents no error", buf_ptr(val_buf))); + return ira->codegen->invalid_instruction; + } + + size_t index = bigint_as_unsigned(&val->data.x_bigint); + result->value.data.x_err_set = ira->codegen->errors_by_index.at(index); + return result; + } else { + ErrorTableEntry *err = nullptr; + BigInt err_int; + + for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) { + ErrorTableEntry *this_err = wanted_type->data.error_set.errors[i]; + bigint_init_unsigned(&err_int, this_err->value); + if (bigint_cmp(&val->data.x_bigint, &err_int) == CmpEQ) { + err = this_err; + break; + } + } + + if (err == nullptr) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &val->data.x_bigint, 10); + ir_add_error(ira, source_instr, + buf_sprintf("integer value %s represents no error in '%s'", buf_ptr(val_buf), buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_instruction; + } + + result->value.data.x_err_set = err; + return result; + } } IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f497f938ed..0378d9366d 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,38 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("implicit cast of error set not a subset", + \\const Set1 = error{A, B}; + \\const Set2 = error{A, C}; + \\export fn entry() void { + \\ foo(Set1.B); + \\} + \\fn foo(set1: Set1) void { + \\ var x: Set2 = set1; + \\} + , + ".tmp_source.zig:7:19: error: expected 'Set2', found 'Set1'", + ".tmp_source.zig:1:23: note: 'error.B' not a member of destination error set"); + + cases.add("int to err global invalid number", + \\const Set1 = error{A, B}; + \\comptime { + \\ var x: usize = 3; + \\ var y = error(x); + \\} + , + ".tmp_source.zig:4:18: error: integer value 3 represents no error"); + + cases.add("int to err non global invalid number", + \\const Set1 = error{A, B}; + \\const Set2 = error{A, C}; + \\comptime { + \\ var x = usize(Set1.B); + \\ var y = Set2(x); + \\} + , + ".tmp_source.zig:5:17: error: integer value 2 represents no error in 'Set2'"); + cases.add("@memberCount of error", \\comptime { \\ _ = @memberCount(error); diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index def6430961..8b8f612056 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -220,7 +220,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\} ); - cases.addRuntimeSafety("cast integer to error and no code matches", + cases.addRuntimeSafety("cast integer to global error and no code matches", \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} @@ -232,6 +232,20 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\} ); + cases.addRuntimeSafety("cast integer to non-global error set and no match", + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\const Set1 = error{A, B}; + \\const Set2 = error{A, C}; + \\pub fn main() void { + \\ _ = foo(Set1.B); + \\} + \\fn foo(set1: Set1) Set2 { + \\ return Set2(set1); + \\} + ); + cases.addRuntimeSafety("@alignCast misaligned", \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); -- cgit v1.2.3 From 0efe441dfd422d4bfe501d5fbfbf15bd5951b494 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Feb 2018 22:18:13 -0500 Subject: if statements support comptime known test error, runtime payload --- TODO | 23 +---------------------- src/ir.cpp | 11 ++++------- test/cases/error.zig | 11 +++++++++++ 3 files changed, 16 insertions(+), 29 deletions(-) (limited to 'src/ir.cpp') diff --git a/TODO b/TODO index 2f3f25c3d3..f67feb1630 100644 --- a/TODO +++ b/TODO @@ -1,25 +1,3 @@ -sed -i 's/\(\bfn .*) \)%\(.*{\)$/\1!\2/g' $(find . -name "*.zig") - - -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. - - -you can get the compiler to tell you the possible errors for an inferred error set like this: - -foo() catch |err| switch (err) {}; - - -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 @@ -35,3 +13,4 @@ syntax - (error{}!void) as the return type passing a fn()error{}!T to a fn()error!T should be a compile error, they're not compatible + diff --git a/src/ir.cpp b/src/ir.cpp index fa2815d6dd..5f7e02848a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4800,12 +4800,8 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "TryElse"); IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "TryEnd"); - IrInstruction *is_comptime; - if (ir_should_inline(irb->exec, scope)) { - is_comptime = ir_build_const_bool(irb, scope, node, true); - } else { - is_comptime = ir_build_test_comptime(irb, scope, node, is_err); - } + bool force_comptime = ir_should_inline(irb->exec, scope); + IrInstruction *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err); ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime); ir_set_cursor_at_end_and_append_block(irb, ok_block); @@ -4814,8 +4810,9 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * if (var_symbol) { IrInstruction *var_type = nullptr; bool is_shadowable = false; + IrInstruction *var_is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, err_val); VariableTableEntry *var = ir_create_var(irb, node, scope, - var_symbol, var_is_const, var_is_const, is_shadowable, is_comptime); + var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime); IrInstruction *var_ptr_value = ir_build_unwrap_err_payload(irb, scope, node, err_val_ptr, false); IrInstruction *var_value = var_is_ptr ? var_ptr_value : ir_build_load_ptr(irb, scope, node, var_ptr_value); diff --git a/test/cases/error.zig b/test/cases/error.zig index 3d2835e723..a8150620b2 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -123,3 +123,14 @@ fn testExplicitErrorSetCast(set1: Set1) void { var y = Set1(x); assert(y == error.A); } + +test "comptime test error for empty error set" { + testComptimeTestErrorEmptySet(1234); + comptime testComptimeTestErrorEmptySet(1234); +} + +const EmptyErrorSet = error {}; + +fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void { + if (x) |v| assert(v == 1234) else |err| @compileError("bad"); +} -- cgit v1.2.3 From 3919afcad26d2359efe52f98cd4f2f0573527369 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 9 Feb 2018 11:16:04 -0500 Subject: fix crash with error peer type resolution closes #765 --- src/ir.cpp | 2 +- test/cases/error.zig | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index 5f7e02848a..61c26cb5ae 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7279,7 +7279,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod prev_inst = cur_inst; } - TypeTableEntry *prev_err_set_type = prev_type->data.error_union.err_set_type; + TypeTableEntry *prev_err_set_type = (err_set_type == nullptr) ? prev_type->data.error_union.err_set_type : err_set_type; TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type; if (!resolve_inferred_error_set(ira, prev_err_set_type, cur_inst->source_node)) { diff --git a/test/cases/error.zig b/test/cases/error.zig index bb1366429b..e64bf02c91 100644 --- a/test/cases/error.zig +++ b/test/cases/error.zig @@ -150,3 +150,28 @@ fn testErrToIntWithOnePossibleValue(x: error{A}, comptime value: u32) void { @compileError("bad"); } } + +test "error union peer type resolution" { + testErrorUnionPeerTypeResolution(1); + comptime testErrorUnionPeerTypeResolution(1); +} + +fn testErrorUnionPeerTypeResolution(x: i32) void { + const y = switch (x) { + 1 => bar_1(), + 2 => baz_1(), + else => quux_1(), + }; +} + +fn bar_1() error { + return error.A; +} + +fn baz_1() !i32 { + return error.B; +} + +fn quux_1() !i32 { + return error.C; +} -- cgit v1.2.3 From e7bf8f3f04efc280a76a3a38b4e6d470d279e41a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 9 Feb 2018 13:49:58 -0500 Subject: fix compiler crash switching on global error with no else --- src/ir.cpp | 18 ++++++++++++------ std/io.zig | 2 +- std/zig/parser.zig | 25 +++++++++++++------------ test/compile_errors.zig | 12 ++++++++++++ 4 files changed, 38 insertions(+), 19 deletions(-) (limited to 'src/ir.cpp') diff --git a/src/ir.cpp b/src/ir.cpp index 61c26cb5ae..2bb40c7e15 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15663,13 +15663,19 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira field_prev_uses[start_index] = start_value->source_node; } if (!instruction->have_else_prong) { - for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i]; + if (type_is_global_error_set(switch_type)) { + ir_add_error(ira, &instruction->base, + buf_sprintf("else prong required when switching on type 'error'")); + return ira->codegen->builtin_types.entry_invalid; + } else { + for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i]; - AstNode *prev_node = field_prev_uses[err_entry->value]; - if (prev_node == nullptr) { - ir_add_error(ira, &instruction->base, - buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name))); + AstNode *prev_node = field_prev_uses[err_entry->value]; + if (prev_node == nullptr) { + ir_add_error(ira, &instruction->base, + buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name))); + } } } } diff --git a/std/io.zig b/std/io.zig index dbaf39ea16..504dcdef80 100644 --- a/std/io.zig +++ b/std/io.zig @@ -499,7 +499,7 @@ pub fn OutStream(comptime Error: type) type { writeFn: fn(self: &Self, bytes: []const u8) Error!void, pub fn print(self: &Self, comptime format: []const u8, args: ...) !void { - return std.fmt.format(self, error, self.writeFn, format, args); + return std.fmt.format(self, Error, self.writeFn, format, args); } pub fn write(self: &Self, bytes: []const u8) !void { diff --git a/std/zig/parser.zig b/std/zig/parser.zig index 4fcdddf63d..9a2044c232 100644 --- a/std/zig/parser.zig +++ b/std/zig/parser.zig @@ -133,7 +133,6 @@ pub const Parser = struct { Token.Id.Eof => return Tree {.root_node = root_node}, else => { self.putBackToken(token); - // TODO shouldn't need this cast stack.append(State { .TopLevelExtern = null }) catch unreachable; continue; }, @@ -707,7 +706,7 @@ pub const Parser = struct { return node; } - fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) error { + fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) { const loc = self.tokenizer.getTokenLocation(token); warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args); warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]); @@ -1082,16 +1081,18 @@ fn testCanonical(source: []const u8) !void { var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index); if (testParse(source, &failing_allocator.allocator)) |_| { return error.NondeterministicMemoryUsage; - } else |err| { - assert(err == error.OutOfMemory); - // TODO make this pass - //if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) { - // warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n", - // fail_index, needed_alloc_count, - // failing_allocator.allocated_bytes, failing_allocator.freed_bytes, - // failing_allocator.index, failing_allocator.deallocations); - // return error.MemoryLeakDetected; - //} + } else |err| switch (err) { + error.OutOfMemory => { + // TODO make this pass + //if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) { + // warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n", + // fail_index, needed_alloc_count, + // failing_allocator.allocated_bytes, failing_allocator.freed_bytes, + // failing_allocator.index, failing_allocator.deallocations); + // return error.MemoryLeakDetected; + //} + }, + error.ParseError => @panic("test failed"), } } } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 3120e3a94a..f60705aa31 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,18 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("no else prong on switch on global error set", + \\export fn entry() void { + \\ foo(error.A); + \\} + \\fn foo(a: error) void { + \\ switch (a) { + \\ error.A => {}, + \\ } + \\} + , + ".tmp_source.zig:5:5: error: else prong required when switching on type 'error'"); + cases.add("inferred error set with no returned error", \\export fn entry() void { \\ foo() catch unreachable; -- cgit v1.2.3