diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2018-02-09 00:47:57 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-02-09 00:47:57 -0500 |
| commit | 59119628425566691a6c580a3da378798bc6c648 (patch) | |
| tree | 7eeeaf4620f40e5a6203c9de91edbdab5e733a51 /src/ir.cpp | |
| parent | 1c236b0766bbc68f1b04e32a95683e273b26714c (diff) | |
| parent | 8e554561df7823e2aba0076f2fc98278df6cb8f2 (diff) | |
| download | zig-59119628425566691a6c580a3da378798bc6c648.tar.gz zig-59119628425566691a6c580a3da378798bc6c648.zip | |
Merge pull request #759 from zig-lang/error-sets
Error Sets
Diffstat (limited to 'src/ir.cpp')
| -rw-r--r-- | src/ir.cpp | 1654 |
1 files changed, 1425 insertions, 229 deletions
diff --git a/src/ir.cpp b/src/ir.cpp index ae8ef00f2f..5f7e02848a 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<ErrorTableEntry *> 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); @@ -580,6 +633,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorReturnTrace return IrInstructionIdErrorReturnTrace; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionErrorUnion *) { + return IrInstructionIdErrorUnion; +} + template<typename T> static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate<T>(1); @@ -2326,6 +2383,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<IrInstructionErrorUnion>(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 +2870,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); @@ -2835,10 +2922,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: @@ -2885,8 +2970,12 @@ 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: + return ir_gen_error_union(irb, scope, node); } zig_unreachable(); } @@ -3990,8 +4079,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); } @@ -4713,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); @@ -4727,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); @@ -5165,7 +5249,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 +5333,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 +5340,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 +5430,135 @@ 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{"); + + 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]; + 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<ErrorTableEntry *>(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); + assert(count != 0); + + 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, "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<ErrorTableEntry *>(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; + + 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->data.error_set.err_count = err_count; + + 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<ErrorTableEntry *>(err_count); + } + + ErrorTableEntry **errors = allocate<ErrorTableEntry *>(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); + Buf *err_name = symbol_node->data.symbol_expr.symbol; + ErrorTableEntry *err = allocate<ErrorTableEntry>(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; + + 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); +} + static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNode *node) { assert(node->type == NodeTypeFnProto); @@ -5401,7 +5612,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 +5692,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(); } @@ -6287,6 +6499,274 @@ 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 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<ErrorTableEntry *>(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<ErrorTableEntry *> 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) +{ + 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<ConstCastOnly>(1); + *result.data.pointer_child = child; + } + return result; + } + + // slice const + if (is_slice(expected_type) && is_slice(actual_type)) { + 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<ConstCastOnly>(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<ConstCastOnly>(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<ConstCastOnly>(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<ConstCastOnly>(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 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; + } + + 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<ErrorTableEntry *>(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) { + 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<ConstCastOnly>(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<ConstCastOnly>(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, @@ -6296,10 +6776,46 @@ 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)) { + 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; } + // if we got here with error sets, make an error showing the incompatibilities + ZigList<ErrorTableEntry *> *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; + } + } 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, + buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name))); + 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))); + } + + return ImplicitCastMatchResultReportedError; + } + // implicit conversion from anything to var if (expected_type->id == TypeTableEntryIdVar) { return ImplicitCastMatchResultYes; @@ -6319,25 +6835,25 @@ 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 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; @@ -6374,7 +6890,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, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6392,7 +6908,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, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6408,7 +6924,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, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6423,7 +6939,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, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6503,7 +7019,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, expected_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } } @@ -6511,13 +7027,39 @@ 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]; 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; + 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)) { + 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; + } + 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; + } + } + } + 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) { @@ -6538,34 +7080,280 @@ 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 == TypeTableEntryIdErrorSet) { + assert(err_set_type != nullptr); + if (cur_type->id == TypeTableEntryIdErrorSet) { + if (type_is_global_error_set(err_set_type)) { + continue; + } + 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; + } + + // 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; + 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, 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; + 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_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 (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 (!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; + } + + 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; + 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; + 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; + continue; + } + } + + if (cur_type->id == TypeTableEntryIdErrorSet) { if (prev_type->id == TypeTableEntryIdArray) { convert_to_const_slice = true; } - any_are_pure_error = true; + if (type_is_global_error_set(cur_type)) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + continue; + } + 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)) { + 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; + } + 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) { + continue; + } + } + // 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 (cur_type->id == TypeTableEntryIdNullLit) { - any_are_null = true; - continue; + 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; + } + + update_errors_helper(ira->codegen, &errors, &errors_count); + + if (err_set_type == nullptr) { + err_set_type = prev_err_set_type; + 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; + } + } + 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 < 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; + 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(prev_type, cur_type)) { + if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) { continue; } - if (types_match_const_cast_only(cur_type, prev_type)) { + if (types_match_const_cast_only(ira, cur_type, prev_type, source_node).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } @@ -6588,26 +7376,41 @@ 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(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(cur_type->data.error.child_type, prev_type)) + 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; + } + + 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; 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, prev_type->data.maybe.child_type, cur_type, source_node).id == ConstCastResultIdOk) { 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, cur_type->data.maybe.child_type, prev_type, source_node).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; @@ -6645,7 +7448,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, 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; @@ -6654,7 +7457,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, prev_type->data.array.child_type, cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { convert_to_const_slice = true; continue; @@ -6663,8 +7466,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(prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, - cur_type->data.array.child_type)) + 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; @@ -6673,8 +7476,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(cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, - prev_type->data.array.child_type)) + 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; @@ -6714,30 +7517,37 @@ 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) { - 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_type(ira->codegen, 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 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); + } } } else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNullLit) { if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || @@ -6783,6 +7593,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); @@ -7213,7 +8025,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; @@ -7238,19 +8050,64 @@ 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) +{ + assert(value->value.type->id == TypeTableEntryIdErrorSet); + assert(wanted_type->id == TypeTableEntryIdErrorSet); if (instr_is_comptime(value)) { ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); if (!val) return ira->codegen->invalid_instruction; + if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) { + return ira->codegen->invalid_instruction; + } + 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; + } + } + 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; + } + } + + IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&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_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, CastOpErrSet); + 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<IrInstructionConst>(&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_pure_err; + 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; } @@ -7630,9 +8487,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); @@ -7640,26 +8500,55 @@ 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); - 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); - AstNode *error_decl_node = ira->codegen->error_decls.at(index); - result->value.data.x_pure_err = error_decl_node->data.error_value_decl.err; - 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); - result->value.type = ira->codegen->builtin_types.entry_pure_error; + result->value.type = wanted_type; return result; } @@ -7681,8 +8570,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(); } @@ -7702,8 +8591,36 @@ 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 (!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; + } + 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->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))); @@ -7719,6 +8636,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; @@ -7728,7 +8646,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, wanted_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } @@ -7748,6 +8666,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) @@ -7767,7 +8692,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, 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); } @@ -7785,7 +8710,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, 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); } @@ -7801,7 +8726,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, 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)) @@ -7824,7 +8749,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, 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)) @@ -7886,7 +8811,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, 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) @@ -7908,12 +8833,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(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) { - 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; @@ -7923,16 +8848,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)) + 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.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; @@ -7944,25 +8869,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; - if (types_match_const_cast_only(wanted_child_type, actual_type) || + TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; + 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) { - 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; @@ -8031,21 +8956,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 @@ -8109,7 +9032,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, wanted_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) { return ir_analyze_cast_ref(ira, source_instr, value, wanted_type); } } @@ -8471,6 +9394,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); @@ -8503,7 +9427,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) { @@ -8514,8 +9438,88 @@ 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; + } + + // 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; + } + + 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); @@ -8523,7 +9527,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 @@ -8538,7 +9541,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: @@ -8692,6 +9695,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); @@ -9264,6 +10268,46 @@ 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 (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; + 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<ErrorTableEntry *>(ira->codegen->errors_by_index.length); + 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); + 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) { @@ -9305,6 +10349,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(); } @@ -9326,7 +10372,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: @@ -9352,7 +10398,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: @@ -9587,7 +10633,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: @@ -9610,7 +10656,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: @@ -9644,6 +10690,31 @@ 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; + + 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); + 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) { @@ -9926,9 +10997,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; @@ -9942,6 +11021,23 @@ 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) { + 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<ErrorTableEntry *>(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; + } 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)) @@ -10092,12 +11188,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); } @@ -10128,7 +11229,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; } @@ -10138,7 +11239,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; } @@ -10257,58 +11358,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; @@ -10364,7 +11413,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: @@ -10474,8 +11523,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(); } @@ -10633,6 +11680,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); @@ -11048,6 +12098,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); @@ -11063,7 +12132,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); @@ -11085,7 +12154,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; @@ -11097,6 +12166,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)) @@ -11238,23 +12318,52 @@ 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 (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; + } else { + err_entry = allocate<ErrorTableEntry>(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 { + if (!resolve_inferred_error_set(ira, child_type, field_ptr_instruction->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + 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; @@ -11337,11 +12446,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 { @@ -11528,7 +12644,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: @@ -11795,7 +12911,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: @@ -11903,7 +13019,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: @@ -11956,7 +13072,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: @@ -12291,7 +13407,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); @@ -12361,8 +13477,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: @@ -12887,7 +14001,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: @@ -12975,7 +14089,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); @@ -13956,6 +15070,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; @@ -14120,7 +15243,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: @@ -14251,9 +15374,22 @@ 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 (!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); + 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 == 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; @@ -14289,13 +15425,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))); @@ -14319,10 +15455,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) @@ -14332,7 +15468,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; } @@ -14357,6 +15493,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); @@ -14482,6 +15624,57 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira } } } + } else if (switch_type->id == TypeTableEntryIdErrorSet) { + if (!resolve_inferred_error_set(ira, switch_type, target_value->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + + AstNode **field_prev_uses = allocate<AstNode *>(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) { @@ -14774,7 +15967,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"); @@ -14832,7 +16025,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"); @@ -15010,7 +16203,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) { @@ -15029,7 +16222,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, @@ -15443,6 +16636,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(); } @@ -15628,6 +16823,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdArgType: case IrInstructionIdTagType: case IrInstructionIdErrorReturnTrace: + case IrInstructionIdErrorUnion: return false; case IrInstructionIdAsm: { |
