From 5161d70620342749b1995fdaabb39220654cc941 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 31 Jan 2018 01:51:15 -0500 Subject: *WIP* error sets --- src/codegen.cpp | 127 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 51 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index b1412b2b59..ea82b3fcd6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -92,9 +92,6 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib); buf_resize(&g->global_asm, 0); - // reserve index 0 to indicate no error - g->error_decls.append(nullptr); - if (root_src_path) { Buf *src_basename = buf_alloc(); Buf *src_dir = buf_alloc(); @@ -410,7 +407,7 @@ static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_e } TypeTableEntry *fn_type = fn_table_entry->type_entry; TypeTableEntry *return_type = fn_type->data.fn.fn_type_id.return_type; - if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdPureError) { + if (return_type->id != TypeTableEntryIdErrorUnion && return_type->id != TypeTableEntryIdErrorSet) { return UINT32_MAX; } bool first_arg_ret = type_has_bits(return_type) && handle_is_ptr(return_type); @@ -1442,7 +1439,7 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError; // TODO: emit a branch to check if the return value is an error } - } else if (return_type->id == TypeTableEntryIdPureError) { + } else if (return_type->id == TypeTableEntryIdErrorSet) { is_err_return = true; } if (is_err_return) { @@ -1823,7 +1820,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, } else if (type_entry->id == TypeTableEntryIdEnum) { LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); - } else if (type_entry->id == TypeTableEntryIdPureError || + } else if (type_entry->id == TypeTableEntryIdErrorSet || type_entry->id == TypeTableEntryIdPointer || type_entry->id == TypeTableEntryIdBool) { @@ -2139,7 +2136,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) { TypeTableEntry *wanted_type = instruction->base.value.type; - assert(wanted_type->id == TypeTableEntryIdPureError); + assert(wanted_type->id == TypeTableEntryIdErrorSet); TypeTableEntry *actual_type = instruction->target->value.type; assert(actual_type->id == TypeTableEntryIdInt); @@ -2156,11 +2153,11 @@ static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, I eval_min_max_value_int(g, actual_type, &biggest_possible_err_val, true); if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) && - bigint_as_unsigned(&biggest_possible_err_val) < g->error_decls.length) + bigint_as_unsigned(&biggest_possible_err_val) < g->errors_by_index.length) { ok_bit = neq_zero_bit; } else { - LLVMValueRef error_value_count = LLVMConstInt(actual_type->type_ref, g->error_decls.length, false); + LLVMValueRef error_value_count = LLVMConstInt(actual_type->type_ref, g->errors_by_index.length, false); LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, ""); ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, ""); } @@ -2187,15 +2184,15 @@ static LLVMValueRef ir_render_err_to_int(CodeGen *g, IrExecutable *executable, I TypeTableEntry *actual_type = instruction->target->value.type; LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - if (actual_type->id == TypeTableEntryIdPureError) { + if (actual_type->id == TypeTableEntryIdErrorSet) { return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), g->err_tag_type, wanted_type, target_val); } else if (actual_type->id == TypeTableEntryIdErrorUnion) { - if (!type_has_bits(actual_type->data.error.child_type)) { + if (!type_has_bits(actual_type->data.error_union.payload_type)) { return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), g->err_tag_type, wanted_type, target_val); } else { - zig_panic("TODO"); + zig_panic("TODO err to int when error union payload type not void"); } } else { zig_unreachable(); @@ -2235,7 +2232,6 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst switch (op_id) { case IrUnOpInvalid: - case IrUnOpError: case IrUnOpMaybe: case IrUnOpDereference: zig_unreachable(); @@ -2489,7 +2485,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr TypeTableEntry *src_return_type = fn_type_id->return_type; bool ret_has_bits = type_has_bits(src_return_type); bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type); - bool prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdPureError); + bool prefix_arg_err_ret_stack = g->have_err_ret_tracing && (src_return_type->id == TypeTableEntryIdErrorUnion || src_return_type->id == TypeTableEntryIdErrorSet); size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0); bool is_var_args = fn_type_id->is_var_args; LLVMValueRef *gen_param_values = allocate(actual_param_count); @@ -2907,7 +2903,7 @@ static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstru static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrInstructionErrName *instruction) { assert(g->generate_error_name_table); - if (g->error_decls.length == 1) { + if (g->errors_by_index.length == 1) { LLVMBuildUnreachable(g->builder); return nullptr; } @@ -2915,7 +2911,7 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI LLVMValueRef err_val = ir_llvm_value(g, instruction->value); if (ir_want_runtime_safety(g, &instruction->base)) { LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val)); - LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->error_decls.length, false); + LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->errors_by_index.length, false); add_bounds_check(g, err_val, LLVMIntNE, zero, LLVMIntULT, end_val); } @@ -3393,11 +3389,11 @@ static LLVMValueRef ir_render_overflow_op(CodeGen *g, IrExecutable *executable, static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutable *executable, IrInstructionTestErr *instruction) { TypeTableEntry *err_union_type = instruction->value->value.type; - TypeTableEntry *child_type = err_union_type->data.error.child_type; + TypeTableEntry *payload_type = err_union_type->data.error_union.payload_type; LLVMValueRef err_union_handle = ir_llvm_value(g, instruction->value); LLVMValueRef err_val; - if (type_has_bits(child_type)) { + if (type_has_bits(payload_type)) { LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); err_val = gen_load_untyped(g, err_val_ptr, 0, false, ""); } else { @@ -3412,11 +3408,11 @@ static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutable *executab TypeTableEntry *ptr_type = instruction->value->value.type; assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *err_union_type = ptr_type->data.pointer.child_type; - TypeTableEntry *child_type = err_union_type->data.error.child_type; + TypeTableEntry *payload_type = err_union_type->data.error_union.payload_type; LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); - if (type_has_bits(child_type)) { + if (type_has_bits(payload_type)) { LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); return gen_load_untyped(g, err_val_ptr, 0, false, ""); } else { @@ -3428,13 +3424,13 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu TypeTableEntry *ptr_type = instruction->value->value.type; assert(ptr_type->id == TypeTableEntryIdPointer); TypeTableEntry *err_union_type = ptr_type->data.pointer.child_type; - TypeTableEntry *child_type = err_union_type->data.error.child_type; + TypeTableEntry *payload_type = err_union_type->data.error_union.payload_type; LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); - if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->error_decls.length > 1) { + if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->errors_by_index.length > 1) { LLVMValueRef err_val; - if (type_has_bits(child_type)) { + if (type_has_bits(payload_type)) { LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); err_val = gen_load_untyped(g, err_val_ptr, 0, false, ""); } else { @@ -3452,7 +3448,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu LLVMPositionBuilderAtEnd(g->builder, ok_block); } - if (type_has_bits(child_type)) { + if (type_has_bits(payload_type)) { return LLVMBuildStructGEP(g->builder, err_union_handle, err_union_payload_index, ""); } else { return nullptr; @@ -3493,10 +3489,10 @@ static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable assert(wanted_type->id == TypeTableEntryIdErrorUnion); - TypeTableEntry *child_type = wanted_type->data.error.child_type; + TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type; LLVMValueRef err_val = ir_llvm_value(g, instruction->value); - if (!type_has_bits(child_type)) + if (!type_has_bits(payload_type)) return err_val; assert(instruction->tmp_ptr); @@ -3512,11 +3508,11 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa assert(wanted_type->id == TypeTableEntryIdErrorUnion); - TypeTableEntry *child_type = wanted_type->data.error.child_type; + TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type; LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref); - if (!type_has_bits(child_type)) + if (!type_has_bits(payload_type)) return ok_err_val; assert(instruction->tmp_ptr); @@ -3527,7 +3523,7 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false); LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_payload_index, ""); - gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, child_type, false), payload_val); + gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, payload_type, false), payload_val); return instruction->tmp_ptr; } @@ -3700,6 +3696,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdArgType: case IrInstructionIdTagType: case IrInstructionIdExport: + case IrInstructionIdErrorUnion: zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); @@ -3933,7 +3930,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: @@ -4026,10 +4023,10 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { switch (type_entry->id) { case TypeTableEntryIdInt: return bigint_to_llvm_const(type_entry->type_ref, &const_val->data.x_bigint); - case TypeTableEntryIdPureError: - assert(const_val->data.x_pure_err); - return LLVMConstInt(g->builtin_types.entry_pure_error->type_ref, - const_val->data.x_pure_err->value, false); + case TypeTableEntryIdErrorSet: + assert(const_val->data.x_err_set != nullptr); + return LLVMConstInt(g->builtin_types.entry_global_error_set->type_ref, + const_val->data.x_err_set->value, false); case TypeTableEntryIdFloat: switch (type_entry->data.floating.bit_count) { case 32: @@ -4330,8 +4327,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { } case TypeTableEntryIdErrorUnion: { - TypeTableEntry *child_type = type_entry->data.error.child_type; - if (!type_has_bits(child_type)) { + TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; + if (!type_has_bits(payload_type)) { uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0; return LLVMConstInt(g->err_tag_type->type_ref, value, false); } else { @@ -4340,7 +4337,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { bool make_unnamed_struct; if (const_val->data.x_err_union.err) { err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err_union.err->value, false); - err_payload_value = LLVMConstNull(child_type->type_ref); + err_payload_value = LLVMConstNull(payload_type->type_ref); make_unnamed_struct = false; } else { err_tag_value = LLVMConstNull(g->err_tag_type->type_ref); @@ -4410,21 +4407,20 @@ static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const } static void generate_error_name_table(CodeGen *g) { - if (g->err_name_table != nullptr || !g->generate_error_name_table || g->error_decls.length == 1) { + if (g->err_name_table != nullptr || !g->generate_error_name_table || g->errors_by_index.length == 1) { return; } - assert(g->error_decls.length > 0); + assert(g->errors_by_index.length > 0); TypeTableEntry *u8_ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type); - LLVMValueRef *values = allocate(g->error_decls.length); + LLVMValueRef *values = allocate(g->errors_by_index.length); values[0] = LLVMGetUndef(str_type->type_ref); - for (size_t i = 1; i < g->error_decls.length; i += 1) { - AstNode *error_decl_node = g->error_decls.at(i); - assert(error_decl_node->type == NodeTypeErrorValueDecl); - Buf *name = error_decl_node->data.error_value_decl.name; + for (size_t i = 1; i < g->errors_by_index.length; i += 1) { + ErrorTableEntry *err_entry = g->errors_by_index.at(i); + Buf *name = &err_entry->name; g->largest_err_name_len = max(g->largest_err_name_len, buf_len(name)); @@ -4443,7 +4439,7 @@ static void generate_error_name_table(CodeGen *g) { values[i] = LLVMConstNamedStruct(str_type->type_ref, fields, 2); } - LLVMValueRef err_name_table_init = LLVMConstArray(str_type->type_ref, values, (unsigned)g->error_decls.length); + LLVMValueRef err_name_table_init = LLVMConstArray(str_type->type_ref, values, (unsigned)g->errors_by_index.length); g->err_name_table = LLVMAddGlobal(g->module, LLVMTypeOf(err_name_table_init), buf_ptr(get_mangled_name(g, buf_create_from_str("__zig_err_name_table"), false))); @@ -4573,6 +4569,28 @@ static void validate_inline_fns(CodeGen *g) { static void do_code_gen(CodeGen *g) { assert(!g->errors.length); + { + // create debug type for error sets + assert(g->err_enumerators.length == g->errors_by_index.length); + uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, g->err_tag_type->type_ref); + uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, g->err_tag_type->type_ref); + ZigLLVMDIFile *err_set_di_file = nullptr; + ZigLLVMDIType *err_set_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, + ZigLLVMCompileUnitToScope(g->compile_unit), buf_ptr(&g->builtin_types.entry_global_error_set->name), + err_set_di_file, 0, + tag_debug_size_in_bits, + tag_debug_align_in_bits, + g->err_enumerators.items, g->err_enumerators.length, + g->err_tag_type->di_type, ""); + ZigLLVMReplaceTemporary(g->dbuilder, g->builtin_types.entry_global_error_set->di_type, err_set_di_type); + g->builtin_types.entry_global_error_set->di_type = err_set_di_type; + + for (size_t i = 0; i < g->error_di_types.length; i += 1) { + ZigLLVMDIType **di_type_ptr = g->error_di_types.at(i); + *di_type_ptr = err_set_di_type; + } + } + codegen_add_time_event(g, "Code Generation"); generate_error_name_table(g); @@ -5176,16 +5194,23 @@ static void define_builtin_types(CodeGen *g) { } { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorSet); buf_init_from_str(&entry->name, "error"); // TODO allow overriding this type and keep track of max value and emit an // error if there are too many errors declared g->err_tag_type = g->builtin_types.entry_u16; - g->builtin_types.entry_pure_error = entry; + g->builtin_types.entry_global_error_set = entry; entry->type_ref = g->err_tag_type->type_ref; - entry->di_type = g->err_tag_type->di_type; + + entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, + ZigLLVMTag_DW_enumeration_type(), "error", + ZigLLVMCompileUnitToScope(g->compile_unit), nullptr, 0); + + // reserve index 0 to indicate no error + g->err_enumerators.append(ZigLLVMCreateDebugEnumerator(g->dbuilder, "(none)", 0)); + g->errors_by_index.append(nullptr); g->primitive_type_table.put(&entry->name, entry); } @@ -5815,7 +5840,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry case TypeTableEntryIdBoundFn: case TypeTableEntryIdArgTuple: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: zig_unreachable(); case TypeTableEntryIdVoid: case TypeTableEntryIdUnreachable: @@ -5988,7 +6013,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf return; } case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdFn: zig_panic("TODO implement get_c_type for more types"); case TypeTableEntryIdInvalid: @@ -6155,7 +6180,7 @@ static void gen_h_file(CodeGen *g) { case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: case TypeTableEntryIdErrorUnion: - case TypeTableEntryIdPureError: + case TypeTableEntryIdErrorSet: case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: -- cgit v1.2.3 From b8f59e14cdbf90cf724ed9e721c1909293f41b3b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 2 Feb 2018 18:13:32 -0500 Subject: *WIP* error sets - correctly resolve inferred error sets --- TODO | 12 ++++ doc/langref.html.in | 2 +- src/all_types.hpp | 5 +- src/analyze.cpp | 42 ++++++++++-- src/ast_render.cpp | 4 +- src/codegen.cpp | 22 ++++++- src/ir.cpp | 167 +++++++++++++++++++++++++++++++++++++++--------- src/ir_print.cpp | 2 + src/parser.cpp | 3 +- src/tokenizer.cpp | 29 +++++++-- src/tokenizer.hpp | 2 + std/debug/index.zig | 1 + std/io.zig | 8 ++- std/mem.zig | 10 +-- std/os/index.zig | 85 ++++++++++++++++-------- std/os/windows/util.zig | 45 +++++++++---- 16 files changed, 350 insertions(+), 89 deletions(-) (limited to 'src/codegen.cpp') diff --git a/TODO b/TODO index 963169d5aa..c6fcfa939a 100644 --- a/TODO +++ b/TODO @@ -13,3 +13,15 @@ then you can return void, or any error, and the error set is inferred. // TODO this is an explicit cast and should actually coerce the type erorr set casting + + +test err should be comptime if error set has 0 members + +comptime calling fn with inferred error set should give empty error set but still you can use try + +comptime err to int of empty err set and of size 1 err set + +comptime test for err + + +undefined in infer error diff --git a/doc/langref.html.in b/doc/langref.html.in index 72d1f946af..24c4b324ba 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5682,7 +5682,7 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression = TypeExpr option(ContainerInitExpression) -MultiplyOperator = "*" | "/" | "%" | "**" | "*%" +MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression diff --git a/src/all_types.hpp b/src/all_types.hpp index 304fa5651a..c94c158046 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -510,8 +510,7 @@ enum BinOpType { BinOpTypeAssignBitAnd, BinOpTypeAssignBitXor, BinOpTypeAssignBitOr, - BinOpTypeAssignBoolAnd, - BinOpTypeAssignBoolOr, + BinOpTypeAssignMergeErrorSets, BinOpTypeBoolOr, BinOpTypeBoolAnd, BinOpTypeCmpEq, @@ -537,6 +536,7 @@ enum BinOpType { BinOpTypeArrayCat, BinOpTypeArrayMult, BinOpTypeErrorUnion, + BinOpTypeMergeErrorSets, }; struct AstNodeBinOpExpr { @@ -2054,6 +2054,7 @@ enum IrBinOp { IrBinOpRemMod, IrBinOpArrayCat, IrBinOpArrayMult, + IrBinOpMergeErrorSets, }; struct IrInstructionBinOp { diff --git a/src/analyze.cpp b/src/analyze.cpp index 8ed5706a27..073a91aeff 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -530,7 +530,6 @@ TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, T TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion); entry->is_copyable = true; - assert(payload_type->type_ref); assert(payload_type->di_type); ensure_complete_type(g, payload_type); @@ -541,9 +540,16 @@ TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, T entry->data.error_union.payload_type = payload_type; if (!type_has_bits(payload_type)) { - entry->type_ref = err_set_type->type_ref; - entry->di_type = err_set_type->di_type; - + if (type_has_bits(err_set_type)) { + entry->type_ref = err_set_type->type_ref; + entry->di_type = err_set_type->di_type; + } else { + entry->zero_bits = true; + entry->di_type = g->builtin_types.entry_void->di_type; + } + } else if (!type_has_bits(err_set_type)) { + entry->type_ref = payload_type->type_ref; + entry->di_type = payload_type->di_type; } else { LLVMTypeRef elem_types[] = { err_set_type->type_ref, @@ -3841,6 +3847,27 @@ void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, Vari } } +static bool analyze_resolve_inferred_error_set(CodeGen *g, TypeTableEntry *err_set_type, AstNode *source_node) { + FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn; + if (infer_fn != nullptr) { + if (infer_fn->anal_state == FnAnalStateInvalid) { + return false; + } else if (infer_fn->anal_state == FnAnalStateReady) { + analyze_fn_body(g, infer_fn); + if (err_set_type->data.error_set.infer_fn != nullptr) { + assert(g->errors.length != 0); + return false; + } + } else { + add_node_error(g, source_node, + buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet", + buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name))); + return false; + } + } + return true; +} + void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_type_node) { TypeTableEntry *fn_type = fn_table_entry->type_entry; assert(!fn_type->data.fn.is_generic); @@ -3871,6 +3898,13 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ return; } + if (inferred_err_set_type->data.error_set.infer_fn != nullptr) { + if (!analyze_resolve_inferred_error_set(g, inferred_err_set_type, return_type_node)) { + fn_table_entry->anal_state = FnAnalStateInvalid; + return; + } + } + return_err_set_type->data.error_set.infer_fn = nullptr; return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count; return_err_set_type->data.error_set.errors = allocate(inferred_err_set_type->data.error_set.err_count); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 88895d2d36..aed4b3e6db 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -49,12 +49,12 @@ static const char *bin_op_str(BinOpType bin_op) { case BinOpTypeAssignBitAnd: return "&="; case BinOpTypeAssignBitXor: return "^="; case BinOpTypeAssignBitOr: return "|="; - case BinOpTypeAssignBoolAnd: return "&&="; - case BinOpTypeAssignBoolOr: return "||="; + case BinOpTypeAssignMergeErrorSets: return "||="; case BinOpTypeUnwrapMaybe: return "??"; case BinOpTypeArrayCat: return "++"; case BinOpTypeArrayMult: return "**"; case BinOpTypeErrorUnion: return "!"; + case BinOpTypeMergeErrorSets: return "||"; } zig_unreachable(); } diff --git a/src/codegen.cpp b/src/codegen.cpp index ea82b3fcd6..acf14474ee 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1799,6 +1799,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, case IrBinOpArrayCat: case IrBinOpArrayMult: case IrBinOpRemUnspecified: + case IrBinOpMergeErrorSets: zig_unreachable(); case IrBinOpBoolOr: return LLVMBuildOr(g->builder, op1_value, op2_value, ""); @@ -2188,6 +2189,9 @@ static LLVMValueRef ir_render_err_to_int(CodeGen *g, IrExecutable *executable, I return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), g->err_tag_type, wanted_type, target_val); } else if (actual_type->id == TypeTableEntryIdErrorUnion) { + // this should have been a compile time constant + assert(type_has_bits(actual_type->data.error_union.err_set_type)); + if (!type_has_bits(actual_type->data.error_union.payload_type)) { return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), g->err_tag_type, wanted_type, target_val); @@ -3428,6 +3432,10 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); + if (!type_has_bits(err_union_type->data.error_union.err_set_type)) { + return err_union_handle; + } + if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->errors_by_index.length > 1) { LLVMValueRef err_val; if (type_has_bits(payload_type)) { @@ -3490,9 +3498,11 @@ static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutable *executable assert(wanted_type->id == TypeTableEntryIdErrorUnion); TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type; + TypeTableEntry *err_set_type = wanted_type->data.error_union.err_set_type; + LLVMValueRef err_val = ir_llvm_value(g, instruction->value); - if (!type_has_bits(payload_type)) + if (!type_has_bits(payload_type) || !type_has_bits(err_set_type)) return err_val; assert(instruction->tmp_ptr); @@ -3509,6 +3519,11 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa assert(wanted_type->id == TypeTableEntryIdErrorUnion); TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type; + TypeTableEntry *err_set_type = wanted_type->data.error_union.err_set_type; + + if (!type_has_bits(err_set_type)) { + return ir_llvm_value(g, instruction->value); + } LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref); @@ -4328,9 +4343,14 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { case TypeTableEntryIdErrorUnion: { TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; + TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type; if (!type_has_bits(payload_type)) { + assert(type_has_bits(err_set_type)); uint64_t value = const_val->data.x_err_union.err ? const_val->data.x_err_union.err->value : 0; return LLVMConstInt(g->err_tag_type->type_ref, value, false); + } else if (!type_has_bits(err_set_type)) { + assert(type_has_bits(payload_type)); + return gen_const_val(g, const_val->data.x_err_union.payload); } else { LLVMValueRef err_tag_value; LLVMValueRef err_payload_value; diff --git a/src/ir.cpp b/src/ir.cpp index 2e4a13927b..12dc76e972 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2869,10 +2869,8 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor); case BinOpTypeAssignBitOr: return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr); - case BinOpTypeAssignBoolAnd: - return ir_gen_assign_op(irb, scope, node, IrBinOpBoolAnd); - case BinOpTypeAssignBoolOr: - return ir_gen_assign_op(irb, scope, node, IrBinOpBoolOr); + case BinOpTypeAssignMergeErrorSets: + return ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets); case BinOpTypeBoolOr: return ir_gen_bool_or(irb, scope, node); case BinOpTypeBoolAnd: @@ -2919,6 +2917,8 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node) return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat); case BinOpTypeArrayMult: return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult); + case BinOpTypeMergeErrorSets: + return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets); case BinOpTypeUnwrapMaybe: return ir_gen_maybe_ok_or(irb, scope, node); case BinOpTypeErrorUnion: @@ -5420,6 +5420,7 @@ static TypeTableEntry *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, } } assert(index == count); + assert(count != 0); buf_appendf(&err_set_type->name, "}"); @@ -5453,21 +5454,21 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A uint32_t err_count = node->data.err_set_decl.decls.length; - if (err_count == 0) { - add_node_error(irb->codegen, node, buf_sprintf("empty error set")); - return irb->codegen->invalid_instruction; - } - Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error set", node); TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); buf_init_from_buf(&err_set_type->name, type_name); err_set_type->is_copyable = true; - err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref; - err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type; err_set_type->data.error_set.err_count = err_count; - err_set_type->data.error_set.errors = allocate(err_count); - irb->codegen->error_di_types.append(&err_set_type->di_type); + if (err_count == 0) { + err_set_type->zero_bits = true; + err_set_type->di_type = irb->codegen->builtin_types.entry_void->di_type; + } else { + err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref; + err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type; + irb->codegen->error_di_types.append(&err_set_type->di_type); + err_set_type->data.error_set.errors = allocate(err_count); + } for (uint32_t i = 0; i < err_count; i += 1) { AstNode *symbol_node = node->data.err_set_decl.decls.at(i); @@ -6657,6 +6658,27 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, return ImplicitCastMatchResultNo; } +static bool resolve_inferred_error_set(IrAnalyze *ira, TypeTableEntry *err_set_type, AstNode *source_node) { + FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn; + if (infer_fn != nullptr) { + if (infer_fn->anal_state == FnAnalStateInvalid) { + return false; + } else if (infer_fn->anal_state == FnAnalStateReady) { + analyze_fn_body(ira->codegen, infer_fn); + if (err_set_type->data.error_set.infer_fn != nullptr) { + assert(ira->codegen->errors.length != 0); + return false; + } + } else { + ir_add_error_node(ira, source_node, + buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet", + buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name))); + return false; + } + } + return true; +} + static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) { assert(instruction_count >= 1); IrInstruction *prev_inst = instructions[0]; @@ -6670,6 +6692,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { err_set_type = prev_inst->value.type; errors = allocate(ira->codegen->errors_by_index.length); + if (!resolve_inferred_error_set(ira, err_set_type, prev_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = error_entry; @@ -6717,6 +6742,10 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod prev_inst = cur_inst; continue; } + + if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } // if err_set_type is a superset of cur_type, keep err_set_type. // if cur_type is a superset of err_set_type, switch err_set_type to cur_type bool prev_is_superset = true; @@ -6778,6 +6807,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = nullptr; } + if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; errors[error_entry->value] = error_entry; @@ -6820,6 +6852,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (err_set_type == ira->codegen->builtin_types.entry_global_error_set) { continue; } + if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } if (err_set_type == nullptr) { err_set_type = cur_type; errors = allocate(ira->codegen->errors_by_index.length); @@ -7543,6 +7578,8 @@ static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *sou assert(contained_set->id == TypeTableEntryIdErrorSet); assert(container_set->id == TypeTableEntryIdErrorSet); + zig_panic("TODO explicit error set cast"); + if (container_set->data.error_set.infer_fn == nullptr && container_set != ira->codegen->builtin_types.entry_global_error_set) { @@ -8058,6 +8095,34 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc return result; } + TypeTableEntry *err_set_type; + if (err_type->id == TypeTableEntryIdErrorUnion) { + err_set_type = err_type->data.error_union.err_set_type; + } else if (err_type->id == TypeTableEntryIdErrorSet) { + err_set_type = err_type; + } else { + zig_unreachable(); + } + if (err_set_type != ira->codegen->builtin_types.entry_global_error_set) { + if (!resolve_inferred_error_set(ira, err_set_type, source_instr->source_node)) { + return ira->codegen->invalid_instruction; + } + if (err_set_type->data.error_set.err_count == 0) { + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + result->value.type = wanted_type; + bigint_init_unsigned(&result->value.data.x_bigint, 0); + return result; + } else if (err_set_type->data.error_set.err_count == 1) { + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type); + result->value.type = wanted_type; + ErrorTableEntry *err = err_set_type->data.error_set.errors[0]; + bigint_init_unsigned(&result->value.data.x_bigint, err->value); + return result; + } + } + BigInt bn; bigint_init_unsigned(&bn, ira->codegen->errors_by_index.length); if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { @@ -9053,6 +9118,7 @@ static int ir_eval_math_op(TypeTableEntry *type_entry, ConstExprValue *op1_val, case IrBinOpArrayCat: case IrBinOpArrayMult: case IrBinOpRemUnspecified: + case IrBinOpMergeErrorSets: zig_unreachable(); case IrBinOpBinOr: assert(is_int); @@ -9625,6 +9691,45 @@ static TypeTableEntry *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp return get_array_type(ira->codegen, child_type, new_array_len); } +static TypeTableEntry *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstructionBinOp *instruction) { + TypeTableEntry *op1_type = ir_resolve_type(ira, instruction->op1->other); + if (type_is_invalid(op1_type)) + return ira->codegen->builtin_types.entry_invalid; + + TypeTableEntry *op2_type = ir_resolve_type(ira, instruction->op2->other); + if (type_is_invalid(op2_type)) + return ira->codegen->builtin_types.entry_invalid; + + if (op1_type == ira->codegen->builtin_types.entry_global_error_set || + op2_type == ira->codegen->builtin_types.entry_global_error_set) + { + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = ira->codegen->builtin_types.entry_global_error_set; + return ira->codegen->builtin_types.entry_type; + } + + if (!resolve_inferred_error_set(ira, op1_type, instruction->op1->other->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + + if (!resolve_inferred_error_set(ira, op2_type, instruction->op2->other->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + + ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length); + for (uint32_t i = 0; i < op1_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i]; + errors[error_entry->value] = error_entry; + } + TypeTableEntry *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type); + free(errors); + + + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_type = result_type; + return ira->codegen->builtin_types.entry_type; +} + static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { IrBinOp op_id = bin_op_instruction->op_id; switch (op_id) { @@ -9666,6 +9771,8 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi return ir_analyze_array_cat(ira, bin_op_instruction); case IrBinOpArrayMult: return ir_analyze_array_mult(ira, bin_op_instruction); + case IrBinOpMergeErrorSets: + return ir_analyze_merge_error_sets(ira, bin_op_instruction); } zig_unreachable(); } @@ -11605,6 +11712,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru } err_set_type = err_entry->set_with_only_this_in_it; } else { + if (!resolve_inferred_error_set(ira, child_type, field_ptr_instruction->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } ErrorTableEntry *err_entry = find_err_table_entry(child_type, field_name); if (err_entry == nullptr) { ir_add_error(ira, &field_ptr_instruction->base, @@ -14623,6 +14733,19 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc } } + TypeTableEntry *err_set_type = type_entry->data.error_union.err_set_type; + if (!resolve_inferred_error_set(ira, err_set_type, instruction->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (err_set_type != ira->codegen->builtin_types.entry_global_error_set && + err_set_type->data.error_set.err_count == 0) + { + assert(err_set_type->data.error_set.infer_fn == nullptr); + ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); + out_val->data.x_bool = false; + return ira->codegen->builtin_types.entry_bool; + } + ir_build_test_err_from(&ira->new_irb, &instruction->base, value); return ira->codegen->builtin_types.entry_bool; } else if (type_entry->id == TypeTableEntryIdErrorSet) { @@ -14861,22 +14984,8 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira } } } else if (switch_type->id == TypeTableEntryIdErrorSet) { - FnTableEntry *infer_fn = switch_type->data.error_set.infer_fn; - if (infer_fn != nullptr) { - if (infer_fn->anal_state == FnAnalStateInvalid) { - return ira->codegen->builtin_types.entry_invalid; - } else if (infer_fn->anal_state == FnAnalStateReady) { - analyze_fn_body(ira->codegen, infer_fn); - if (switch_type->data.error_set.infer_fn != nullptr) { - assert(ira->codegen->errors.length != 0); - return ira->codegen->builtin_types.entry_invalid; - } - } else { - ir_add_error(ira, &instruction->base, - buf_sprintf("cannot switch on inferred error set '%s': function '%s' not fully analyzed yet", - buf_ptr(&switch_type->name), buf_ptr(&switch_type->data.error_set.infer_fn->symbol_name))); - return ira->codegen->builtin_types.entry_invalid; - } + if (!resolve_inferred_error_set(ira, switch_type, target_value->source_node)) { + return ira->codegen->builtin_types.entry_invalid; } AstNode **field_prev_uses = allocate(ira->codegen->errors_by_index.length); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 781cd4a1e3..f2c0d6a5b4 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -130,6 +130,8 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) { return "++"; case IrBinOpArrayMult: return "**"; + case IrBinOpMergeErrorSets: + return "||"; } zig_unreachable(); } diff --git a/src/parser.cpp b/src/parser.cpp index 5034a95567..1a3b568a2e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1088,12 +1088,13 @@ static BinOpType tok_to_mult_op(Token *token) { case TokenIdSlash: return BinOpTypeDiv; case TokenIdPercent: return BinOpTypeMod; case TokenIdBang: return BinOpTypeErrorUnion; + case TokenIdBarBar: return BinOpTypeMergeErrorSets; default: return BinOpTypeInvalid; } } /* -MultiplyOperator = "!" | "*" | "/" | "%" | "**" | "*%" +MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" */ static BinOpType ast_parse_mult_op(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index f98c0c8344..44d838a723 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -195,7 +195,8 @@ enum TokenizeState { TokenizeStateSawMinusPercent, TokenizeStateSawAmpersand, TokenizeStateSawCaret, - TokenizeStateSawPipe, + TokenizeStateSawBar, + TokenizeStateSawBarBar, TokenizeStateLineComment, TokenizeStateLineString, TokenizeStateLineStringEnd, @@ -594,7 +595,7 @@ void tokenize(Buf *buf, Tokenization *out) { break; case '|': begin_token(&t, TokenIdBinOr); - t.state = TokenizeStateSawPipe; + t.state = TokenizeStateSawBar; break; case '=': begin_token(&t, TokenIdEq); @@ -888,13 +889,17 @@ void tokenize(Buf *buf, Tokenization *out) { continue; } break; - case TokenizeStateSawPipe: + case TokenizeStateSawBar: switch (c) { case '=': set_token_id(&t, t.cur_tok, TokenIdBitOrEq); end_token(&t); t.state = TokenizeStateStart; break; + case '|': + set_token_id(&t, t.cur_tok, TokenIdBarBar); + t.state = TokenizeStateSawBarBar; + break; default: t.pos -= 1; end_token(&t); @@ -902,6 +907,19 @@ void tokenize(Buf *buf, Tokenization *out) { continue; } break; + case TokenizeStateSawBarBar: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdBarBarEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } case TokenizeStateSawSlash: switch (c) { case '/': @@ -1428,7 +1446,7 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateSawDash: case TokenizeStateSawAmpersand: case TokenizeStateSawCaret: - case TokenizeStateSawPipe: + case TokenizeStateSawBar: case TokenizeStateSawEq: case TokenizeStateSawBang: case TokenizeStateSawLessThan: @@ -1443,6 +1461,7 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateSawMinusPercent: case TokenizeStateLineString: case TokenizeStateLineStringEnd: + case TokenizeStateSawBarBar: end_token(&t); break; case TokenizeStateSawDotDot: @@ -1475,6 +1494,7 @@ const char * token_name(TokenId id) { case TokenIdArrow: return "->"; case TokenIdAtSign: return "@"; case TokenIdBang: return "!"; + case TokenIdBarBar: return "||"; case TokenIdBinOr: return "|"; case TokenIdBinXor: return "^"; case TokenIdBitAndEq: return "&="; @@ -1577,6 +1597,7 @@ const char * token_name(TokenId id) { case TokenIdTimesEq: return "*="; case TokenIdTimesPercent: return "*%"; case TokenIdTimesPercentEq: return "*%="; + case TokenIdBarBarEq: return "||="; } return "(invalid token)"; } diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 749f72f419..92a3b8de0d 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -17,6 +17,8 @@ enum TokenId { TokenIdArrow, TokenIdAtSign, TokenIdBang, + TokenIdBarBar, + TokenIdBarBarEq, TokenIdBinOr, TokenIdBinXor, TokenIdBitAndEq, diff --git a/std/debug/index.zig b/std/debug/index.zig index 4778d63dd2..50ab1a2216 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -210,6 +210,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a } } else |err| switch (err) { error.EndOfFile => {}, + else => return err, } } else |err| switch (err) { error.MissingDebugInfo, error.InvalidDebugInfo => { diff --git a/std/io.zig b/std/io.zig index e110d4ddf5..dbca37745a 100644 --- a/std/io.zig +++ b/std/io.zig @@ -102,12 +102,14 @@ pub const File = struct { /// The OS-specific file descriptor or file handle. handle: os.FileHandle, + const OpenError = os.WindowsOpenError || os.PosixOpenError; + /// `path` may need to be copied in memory to add a null terminating byte. In this case /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned. /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. /// Call close to clean up. - pub fn openRead(path: []const u8, allocator: ?&mem.Allocator) !File { + pub fn openRead(path: []const u8, allocator: ?&mem.Allocator) OpenError!File { if (is_posix) { const flags = system.O_LARGEFILE|system.O_RDONLY; const fd = try os.posixOpen(path, flags, 0, allocator); @@ -338,7 +340,9 @@ pub const File = struct { } } - fn write(self: &File, bytes: []const u8) !void { + const WriteError = os.WindowsWriteError || os.PosixWriteError; + + fn write(self: &File, bytes: []const u8) WriteError!void { if (is_posix) { try os.posixWrite(self.handle, bytes); } else if (is_windows) { diff --git a/std/mem.zig b/std/mem.zig index 1c75d92541..25c0648888 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -5,12 +5,12 @@ const math = std.math; const builtin = @import("builtin"); pub const Allocator = struct { - const Errors = error {OutOfMemory}; + const Error = error {OutOfMemory}; /// Allocate byte_count bytes and return them in a slice, with the /// slice's pointer aligned at least to alignment bytes. /// The returned newly allocated memory is undefined. - allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Errors![]u8, + allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8, /// If `new_byte_count > old_mem.len`: /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn. @@ -21,7 +21,7 @@ pub const Allocator = struct { /// * alignment <= alignment of old_mem.ptr /// /// The returned newly allocated memory is undefined. - reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Errors![]u8, + reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8, /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn` freeFn: fn (self: &Allocator, old_mem: []u8) void, @@ -42,7 +42,7 @@ pub const Allocator = struct { fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29, n: usize) ![]align(alignment) T { - const byte_count = try math.mul(usize, @sizeOf(T), n); + const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; const byte_slice = try self.allocFn(self, byte_count, alignment); // This loop should get optimized out in ReleaseFast mode for (byte_slice) |*byte| { @@ -63,7 +63,7 @@ pub const Allocator = struct { } const old_byte_slice = ([]u8)(old_mem); - const byte_count = try math.mul(usize, @sizeOf(T), n); + const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory; const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment); // This loop should get optimized out in ReleaseFast mode for (byte_slice[old_byte_slice.len..]) |*byte| { diff --git a/std/os/index.zig b/std/os/index.zig index 224c4ba328..a303ee4be1 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -38,6 +38,9 @@ pub const windowsLoadDll = windows_util.windowsLoadDll; pub const windowsUnloadDll = windows_util.windowsUnloadDll; pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; +pub const WindowsOpenError = windows_util.OpenError; +pub const WindowsWriteError = windows_util.WriteError; + pub const FileHandle = if (is_windows) windows.HANDLE else i32; const debug = std.debug; @@ -188,8 +191,21 @@ pub fn posixRead(fd: i32, buf: []u8) !void { } } +pub const PosixWriteError = error { + WouldBlock, + FileClosed, + DestinationAddressRequired, + DiskQuota, + FileTooBig, + InputOutput, + NoSpaceLeft, + AccessDenied, + BrokenPipe, + Unexpected, +}; + /// Calls POSIX write, and keeps trying if it gets interrupted. -pub fn posixWrite(fd: i32, bytes: []const u8) !void { +pub fn posixWrite(fd: i32, bytes: []const u8) PosixWriteError!void { while (true) { const write_ret = posix.write(fd, bytes.ptr, bytes.len); const write_err = posix.getErrno(write_ret); @@ -197,15 +213,15 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { return switch (write_err) { posix.EINTR => continue, posix.EINVAL, posix.EFAULT => unreachable, - posix.EAGAIN => error.WouldBlock, - posix.EBADF => error.FileClosed, - posix.EDESTADDRREQ => error.DestinationAddressRequired, - posix.EDQUOT => error.DiskQuota, - posix.EFBIG => error.FileTooBig, - posix.EIO => error.InputOutput, - posix.ENOSPC => error.NoSpaceLeft, - posix.EPERM => error.AccessDenied, - posix.EPIPE => error.BrokenPipe, + posix.EAGAIN => PosixWriteError.WouldBlock, + posix.EBADF => PosixWriteError.FileClosed, + posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired, + posix.EDQUOT => PosixWriteError.DiskQuota, + posix.EFBIG => PosixWriteError.FileTooBig, + posix.EIO => PosixWriteError.InputOutput, + posix.ENOSPC => PosixWriteError.NoSpaceLeft, + posix.EPERM => PosixWriteError.AccessDenied, + posix.EPIPE => PosixWriteError.BrokenPipe, else => unexpectedErrorPosix(write_err), }; } @@ -213,13 +229,32 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { } } +pub const PosixOpenError = error { + OutOfMemory, + AccessDenied, + FileTooBig, + IsDir, + SymLinkLoop, + ProcessFdQuotaExceeded, + NameTooLong, + SystemFdQuotaExceeded, + NoDevice, + PathNotFound, + SystemResources, + NoSpaceLeft, + NotDir, + AccessDenied, + PathAlreadyExists, + Unexpected, +}; + /// ::file_path may need to be copied in memory to add a null terminating byte. In this case /// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed /// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned. /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. /// Calls POSIX open, keeps trying if it gets interrupted, and translates /// the return value into zig errors. -pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Allocator) !i32 { +pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Allocator) PosixOpenError!i32 { var stack_buf: [max_noalloc_path_len]u8 = undefined; var path0: []u8 = undefined; var need_free = false; @@ -247,20 +282,20 @@ pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Al posix.EFAULT => unreachable, posix.EINVAL => unreachable, - posix.EACCES => error.AccessDenied, - posix.EFBIG, posix.EOVERFLOW => error.FileTooBig, - posix.EISDIR => error.IsDir, - posix.ELOOP => error.SymLinkLoop, - posix.EMFILE => error.ProcessFdQuotaExceeded, - posix.ENAMETOOLONG => error.NameTooLong, - posix.ENFILE => error.SystemFdQuotaExceeded, - posix.ENODEV => error.NoDevice, - posix.ENOENT => error.PathNotFound, - posix.ENOMEM => error.SystemResources, - posix.ENOSPC => error.NoSpaceLeft, - posix.ENOTDIR => error.NotDir, - posix.EPERM => error.AccessDenied, - posix.EEXIST => error.PathAlreadyExists, + posix.EACCES => PosixOpenError.AccessDenied, + posix.EFBIG, posix.EOVERFLOW => PosixOpenError.FileTooBig, + posix.EISDIR => PosixOpenError.IsDir, + posix.ELOOP => PosixOpenError.SymLinkLoop, + posix.EMFILE => PosixOpenError.ProcessFdQuotaExceeded, + posix.ENAMETOOLONG => PosixOpenError.NameTooLong, + posix.ENFILE => PosixOpenError.SystemFdQuotaExceeded, + posix.ENODEV => PosixOpenError.NoDevice, + posix.ENOENT => PosixOpenError.PathNotFound, + posix.ENOMEM => PosixOpenError.SystemResources, + posix.ENOSPC => PosixOpenError.NoSpaceLeft, + posix.ENOTDIR => PosixOpenError.NotDir, + posix.EPERM => PosixOpenError.AccessDenied, + posix.EEXIST => PosixOpenError.PathAlreadyExists, else => unexpectedErrorPosix(err), }; } diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 08da342e0b..d03092ff14 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -26,16 +26,25 @@ pub fn windowsClose(handle: windows.HANDLE) void { assert(windows.CloseHandle(handle) != 0); } -pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) !void { +pub const WriteError = error { + SystemResources, + OperationAborted, + SystemResources, + IoPending, + BrokenPipe, + Unexpected, +}; + +pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { if (windows.WriteFile(handle, @ptrCast(&const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { const err = windows.GetLastError(); return switch (err) { - windows.ERROR.INVALID_USER_BUFFER => error.SystemResources, - windows.ERROR.NOT_ENOUGH_MEMORY => error.SystemResources, - windows.ERROR.OPERATION_ABORTED => error.OperationAborted, - windows.ERROR.NOT_ENOUGH_QUOTA => error.SystemResources, - windows.ERROR.IO_PENDING => error.IoPending, - windows.ERROR.BROKEN_PIPE => error.BrokenPipe, + windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, + windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources, + windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted, + windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources, + windows.ERROR.IO_PENDING => WriteError.IoPending, + windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe, else => os.unexpectedErrorWindows(err), }; } @@ -66,12 +75,22 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool { mem.indexOf(u16, name_wide, []u16{'-','p','t','y'}) != null; } +pub const OpenError = error { + SharingViolation, + PathAlreadyExists, + FileNotFound, + AccessDenied, + PipeBusy, + Unexpected, +}; + /// `file_path` may need to be copied in memory to add a null terminating byte. In this case /// a fixed size buffer of size ::max_noalloc_path_len is an attempted solution. If the fixed /// size buffer is too small, and the provided allocator is null, ::error.NameTooLong is returned. /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory. pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_mode: windows.DWORD, - creation_disposition: windows.DWORD, flags_and_attrs: windows.DWORD, allocator: ?&mem.Allocator) %windows.HANDLE + creation_disposition: windows.DWORD, flags_and_attrs: windows.DWORD, allocator: ?&mem.Allocator) + OpenError!windows.HANDLE { var stack_buf: [os.max_noalloc_path_len]u8 = undefined; var path0: []u8 = undefined; @@ -95,11 +114,11 @@ pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_m if (result == windows.INVALID_HANDLE_VALUE) { const err = windows.GetLastError(); return switch (err) { - windows.ERROR.SHARING_VIOLATION => error.SharingViolation, - windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => error.PathAlreadyExists, - windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, - windows.ERROR.ACCESS_DENIED => error.AccessDenied, - windows.ERROR.PIPE_BUSY => error.PipeBusy, + windows.ERROR.SHARING_VIOLATION => OpenError.SharingViolation, + windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => OpenError.PathAlreadyExists, + windows.ERROR.FILE_NOT_FOUND => OpenError.FileNotFound, + windows.ERROR.ACCESS_DENIED => OpenError.AccessDenied, + windows.ERROR.PIPE_BUSY => OpenError.PipeBusy, else => os.unexpectedErrorWindows(err), }; } -- cgit v1.2.3 From ef5e7bb4693ed7e1582fb0b68cab01e00638d615 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 3 Feb 2018 14:06:37 -0500 Subject: *WIP* error sets - an inferred error set can end up being the global one --- TODO | 2 - src/analyze.cpp | 193 ++------------------------ src/analyze.hpp | 53 +------- src/codegen.cpp | 1 + src/ir.cpp | 406 ++++++++++++++++++++++++++++++++++++++++++++----------- std/os/index.zig | 2 +- 6 files changed, 346 insertions(+), 311 deletions(-) (limited to 'src/codegen.cpp') diff --git a/TODO b/TODO index 69521fe204..67e32779d7 100644 --- a/TODO +++ b/TODO @@ -26,6 +26,4 @@ comptime test for err undefined in infer error -change readlink back to inferred error - syntax - ?a!b should be ?(a!b) but it's (?a)!b diff --git a/src/analyze.cpp b/src/analyze.cpp index 073a91aeff..be6057a309 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3372,180 +3372,6 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *so g->tld_ref_source_node_stack.pop(); } -ConstCastOnly types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type) { - ConstCastOnly result = {}; - result.id = ConstCastResultIdOk; - - if (expected_type == actual_type) - return result; - - // pointer const - if (expected_type->id == TypeTableEntryIdPointer && - actual_type->id == TypeTableEntryIdPointer && - (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) && - (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) && - actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset && - actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count && - actual_type->data.pointer.alignment >= expected_type->data.pointer.alignment) - { - ConstCastOnly child = types_match_const_cast_only(g, expected_type->data.pointer.child_type, actual_type->data.pointer.child_type); - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdPointerChild; - result.data.pointer_child = allocate_nonzero(1); - *result.data.pointer_child = child; - } - return result; - } - - // slice const - if (expected_type->id == TypeTableEntryIdStruct && actual_type->id == TypeTableEntryIdStruct && - expected_type->data.structure.is_slice && actual_type->data.structure.is_slice) - { - TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; - TypeTableEntry *expected_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry; - if ((!actual_ptr_type->data.pointer.is_const || expected_ptr_type->data.pointer.is_const) && - (!actual_ptr_type->data.pointer.is_volatile || expected_ptr_type->data.pointer.is_volatile) && - actual_ptr_type->data.pointer.bit_offset == expected_ptr_type->data.pointer.bit_offset && - actual_ptr_type->data.pointer.unaligned_bit_count == expected_ptr_type->data.pointer.unaligned_bit_count && - actual_ptr_type->data.pointer.alignment >= expected_ptr_type->data.pointer.alignment) - { - ConstCastOnly child = types_match_const_cast_only(g, expected_ptr_type->data.pointer.child_type, - actual_ptr_type->data.pointer.child_type); - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdSliceChild; - result.data.slice_child = allocate_nonzero(1); - *result.data.slice_child = child; - } - return result; - } - } - - // maybe - if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { - ConstCastOnly child = types_match_const_cast_only(g, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type); - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdNullableChild; - result.data.nullable_child = allocate_nonzero(1); - *result.data.nullable_child = child; - } - return result; - } - - // error union - if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) { - ConstCastOnly payload_child = types_match_const_cast_only(g, expected_type->data.error_union.payload_type, actual_type->data.error_union.payload_type); - if (payload_child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdErrorUnionPayload; - result.data.error_union_payload = allocate_nonzero(1); - *result.data.error_union_payload = payload_child; - return result; - } - ConstCastOnly error_set_child = types_match_const_cast_only(g, expected_type->data.error_union.err_set_type, actual_type->data.error_union.err_set_type); - if (error_set_child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdErrorUnionErrorSet; - result.data.error_union_error_set = allocate_nonzero(1); - *result.data.error_union_error_set = error_set_child; - return result; - } - return result; - } - - // error set - if (expected_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdErrorSet) { - TypeTableEntry *contained_set = actual_type; - TypeTableEntry *container_set = expected_type; - - if (container_set == g->builtin_types.entry_global_error_set || container_set->data.error_set.infer_fn != nullptr) { - return result; - } - - ErrorTableEntry **errors = allocate(g->errors_by_index.length); - for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; - errors[error_entry->value] = error_entry; - } - for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) { - ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i]; - ErrorTableEntry *error_entry = errors[contained_error_entry->value]; - if (error_entry == nullptr) { - if (result.id == ConstCastResultIdOk) { - result.id = ConstCastResultIdErrSet; - } - result.data.error_set.missing_errors.append(contained_error_entry); - } - } - free(errors); - return result; - } - - // fn - if (expected_type->id == TypeTableEntryIdFn && - actual_type->id == TypeTableEntryIdFn) - { - if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) { - result.id = ConstCastResultIdFnAlign; - return result; - } - if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { - result.id = ConstCastResultIdFnCC; - return result; - } - if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { - result.id = ConstCastResultIdFnVarArgs; - return result; - } - if (expected_type->data.fn.is_generic != actual_type->data.fn.is_generic) { - result.id = ConstCastResultIdFnIsGeneric; - return result; - } - if (!expected_type->data.fn.is_generic && - actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable) - { - ConstCastOnly child = types_match_const_cast_only(g, expected_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type); - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdFnReturnType; - result.data.return_type = allocate_nonzero(1); - *result.data.return_type = child; - } - return result; - } - if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { - result.id = ConstCastResultIdFnArgCount; - return result; - } - if (expected_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) { - result.id = ConstCastResultIdFnGenericArgCount; - return result; - } - assert(expected_type->data.fn.is_generic || - expected_type->data.fn.fn_type_id.next_param_index == expected_type->data.fn.fn_type_id.param_count); - for (size_t i = 0; i < expected_type->data.fn.fn_type_id.next_param_index; i += 1) { - // note it's reversed for parameters - FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; - FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i]; - - ConstCastOnly arg_child = types_match_const_cast_only(g, actual_param_info->type, expected_param_info->type); - if (arg_child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdFnArg; - result.data.fn_arg.arg_index = i; - result.data.fn_arg.child = allocate_nonzero(1); - *result.data.fn_arg.child = arg_child; - return result; - } - - if (expected_param_info->is_noalias != actual_param_info->is_noalias) { - result.id = ConstCastResultIdFnArgNoAlias; - result.data.arg_no_alias.arg_index = i; - return result; - } - } - return result; - } - - result.id = ConstCastResultIdType; - return result; -} - Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) { // we must resolve all the use decls ImportTableEntry *import = get_scope_import(scope); @@ -3906,10 +3732,16 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ } return_err_set_type->data.error_set.infer_fn = nullptr; - return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count; - return_err_set_type->data.error_set.errors = allocate(inferred_err_set_type->data.error_set.err_count); - for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) { - return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i]; + if (type_is_global_error_set(inferred_err_set_type)) { + return_err_set_type->data.error_set.err_count = UINT32_MAX; + } else { + return_err_set_type->data.error_set.err_count = inferred_err_set_type->data.error_set.err_count; + if (inferred_err_set_type->data.error_set.err_count > 0) { + return_err_set_type->data.error_set.errors = allocate(inferred_err_set_type->data.error_set.err_count); + for (uint32_t i = 0; i < inferred_err_set_type->data.error_set.err_count; i += 1) { + return_err_set_type->data.error_set.errors[i] = inferred_err_set_type->data.error_set.errors[i]; + } + } } } } @@ -5833,3 +5665,8 @@ ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { return var_value; } +bool type_is_global_error_set(TypeTableEntry *err_set_type) { + assert(err_set_type->id == TypeTableEntryIdErrorSet); + assert(err_set_type->data.error_set.infer_fn == nullptr); + return err_set_type->data.error_set.err_count == UINT32_MAX; +} diff --git a/src/analyze.hpp b/src/analyze.hpp index b7869d3a79..82ad05972b 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -56,6 +56,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt TypeTableEntry *container_ref_type(TypeTableEntry *type_entry); bool type_is_complete(TypeTableEntry *type_entry); bool type_is_invalid(TypeTableEntry *type_entry); +bool type_is_global_error_set(TypeTableEntry *err_set_type); bool type_has_zero_bits_known(TypeTableEntry *type_entry); void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry); ScopeDecls *get_container_scope(TypeTableEntry *type_entry); @@ -190,56 +191,4 @@ void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry); TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry); -enum ConstCastResultId { - ConstCastResultIdOk, - ConstCastResultIdErrSet, - ConstCastResultIdPointerChild, - ConstCastResultIdSliceChild, - ConstCastResultIdNullableChild, - ConstCastResultIdErrorUnionPayload, - ConstCastResultIdErrorUnionErrorSet, - ConstCastResultIdFnAlign, - ConstCastResultIdFnCC, - ConstCastResultIdFnVarArgs, - ConstCastResultIdFnIsGeneric, - ConstCastResultIdFnReturnType, - ConstCastResultIdFnArgCount, - ConstCastResultIdFnGenericArgCount, - ConstCastResultIdFnArg, - ConstCastResultIdFnArgNoAlias, - ConstCastResultIdType, -}; - -struct ConstCastErrSetMismatch { - ZigList missing_errors; -}; - -struct ConstCastOnly; - -struct ConstCastArg { - size_t arg_index; - ConstCastOnly *child; -}; - -struct ConstCastArgNoAlias { - size_t arg_index; -}; - -struct ConstCastOnly { - ConstCastResultId id; - union { - ConstCastErrSetMismatch error_set; - ConstCastOnly *pointer_child; - ConstCastOnly *slice_child; - ConstCastOnly *nullable_child; - ConstCastOnly *error_union_payload; - ConstCastOnly *error_union_error_set; - ConstCastOnly *return_type; - ConstCastArg fn_arg; - ConstCastArgNoAlias arg_no_alias; - } data; -}; - -ConstCastOnly types_match_const_cast_only(CodeGen *g, TypeTableEntry *expected_type, TypeTableEntry *actual_type); - #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index acf14474ee..e43060f195 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5216,6 +5216,7 @@ static void define_builtin_types(CodeGen *g) { { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorSet); buf_init_from_str(&entry->name, "error"); + entry->data.error_set.err_count = UINT32_MAX; // TODO allow overriding this type and keep track of max value and emit an // error if there are too many errors declared diff --git a/src/ir.cpp b/src/ir.cpp index 721bd70f12..06b46646e8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -45,6 +45,59 @@ static LVal make_lval_addr(bool is_const, bool is_volatile) { return { true, is_const, is_volatile }; } +enum ConstCastResultId { + ConstCastResultIdOk, + ConstCastResultIdErrSet, + ConstCastResultIdErrSetGlobal, + ConstCastResultIdPointerChild, + ConstCastResultIdSliceChild, + ConstCastResultIdNullableChild, + ConstCastResultIdErrorUnionPayload, + ConstCastResultIdErrorUnionErrorSet, + ConstCastResultIdFnAlign, + ConstCastResultIdFnCC, + ConstCastResultIdFnVarArgs, + ConstCastResultIdFnIsGeneric, + ConstCastResultIdFnReturnType, + ConstCastResultIdFnArgCount, + ConstCastResultIdFnGenericArgCount, + ConstCastResultIdFnArg, + ConstCastResultIdFnArgNoAlias, + ConstCastResultIdType, + ConstCastResultIdUnresolvedInferredErrSet, +}; + +struct ConstCastErrSetMismatch { + ZigList missing_errors; +}; + +struct ConstCastOnly; + +struct ConstCastArg { + size_t arg_index; + ConstCastOnly *child; +}; + +struct ConstCastArgNoAlias { + size_t arg_index; +}; + +struct ConstCastOnly { + ConstCastResultId id; + union { + ConstCastErrSetMismatch error_set; + ConstCastOnly *pointer_child; + ConstCastOnly *slice_child; + ConstCastOnly *nullable_child; + ConstCastOnly *error_union_payload; + ConstCastOnly *error_union_error_set; + ConstCastOnly *return_type; + ConstCastArg fn_arg; + ConstCastArgNoAlias arg_no_alias; + } data; +}; + + static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope); static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval); static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction); @@ -6416,6 +6469,220 @@ static bool slice_is_const(TypeTableEntry *type) { return type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const; } +static bool resolve_inferred_error_set(IrAnalyze *ira, TypeTableEntry *err_set_type, AstNode *source_node) { + assert(err_set_type->id == TypeTableEntryIdErrorSet); + FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn; + if (infer_fn != nullptr) { + if (infer_fn->anal_state == FnAnalStateInvalid) { + return false; + } else if (infer_fn->anal_state == FnAnalStateReady) { + analyze_fn_body(ira->codegen, infer_fn); + if (err_set_type->data.error_set.infer_fn != nullptr) { + assert(ira->codegen->errors.length != 0); + return false; + } + } else { + ir_add_error_node(ira, source_node, + buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet", + buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name))); + return false; + } + } + return true; +} + +static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry *expected_type, + TypeTableEntry *actual_type, AstNode *source_node) +{ + CodeGen *g = ira->codegen; + ConstCastOnly result = {}; + result.id = ConstCastResultIdOk; + + if (expected_type == actual_type) + return result; + + // pointer const + if (expected_type->id == TypeTableEntryIdPointer && + actual_type->id == TypeTableEntryIdPointer && + (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) && + (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) && + actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset && + actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count && + actual_type->data.pointer.alignment >= expected_type->data.pointer.alignment) + { + ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.pointer.child_type, actual_type->data.pointer.child_type, source_node); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdPointerChild; + result.data.pointer_child = allocate_nonzero(1); + *result.data.pointer_child = child; + } + return result; + } + + // slice const + if (expected_type->id == TypeTableEntryIdStruct && actual_type->id == TypeTableEntryIdStruct && + expected_type->data.structure.is_slice && actual_type->data.structure.is_slice) + { + TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry; + TypeTableEntry *expected_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry; + if ((!actual_ptr_type->data.pointer.is_const || expected_ptr_type->data.pointer.is_const) && + (!actual_ptr_type->data.pointer.is_volatile || expected_ptr_type->data.pointer.is_volatile) && + actual_ptr_type->data.pointer.bit_offset == expected_ptr_type->data.pointer.bit_offset && + actual_ptr_type->data.pointer.unaligned_bit_count == expected_ptr_type->data.pointer.unaligned_bit_count && + actual_ptr_type->data.pointer.alignment >= expected_ptr_type->data.pointer.alignment) + { + ConstCastOnly child = types_match_const_cast_only(ira, expected_ptr_type->data.pointer.child_type, + actual_ptr_type->data.pointer.child_type, source_node); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdSliceChild; + result.data.slice_child = allocate_nonzero(1); + *result.data.slice_child = child; + } + return result; + } + } + + // maybe + if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) { + ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type, source_node); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdNullableChild; + result.data.nullable_child = allocate_nonzero(1); + *result.data.nullable_child = child; + } + return result; + } + + // error union + if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) { + ConstCastOnly payload_child = types_match_const_cast_only(ira, expected_type->data.error_union.payload_type, actual_type->data.error_union.payload_type, source_node); + if (payload_child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdErrorUnionPayload; + result.data.error_union_payload = allocate_nonzero(1); + *result.data.error_union_payload = payload_child; + return result; + } + ConstCastOnly error_set_child = types_match_const_cast_only(ira, expected_type->data.error_union.err_set_type, actual_type->data.error_union.err_set_type, source_node); + if (error_set_child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdErrorUnionErrorSet; + result.data.error_union_error_set = allocate_nonzero(1); + *result.data.error_union_error_set = error_set_child; + return result; + } + return result; + } + + // error set + if (expected_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdErrorSet) { + TypeTableEntry *contained_set = actual_type; + TypeTableEntry *container_set = expected_type; + + if (!resolve_inferred_error_set(ira, container_set, source_node)) { + result.id = ConstCastResultIdUnresolvedInferredErrSet; + return result; + } + + if (type_is_global_error_set(container_set)) { + return result; + } + + if (!resolve_inferred_error_set(ira, contained_set, source_node)) { + result.id = ConstCastResultIdUnresolvedInferredErrSet; + return result; + } + + if (type_is_global_error_set(contained_set)) { + result.id = ConstCastResultIdErrSetGlobal; + return result; + } + + ErrorTableEntry **errors = allocate(g->errors_by_index.length); + for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; + errors[error_entry->value] = error_entry; + } + for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + if (result.id == ConstCastResultIdOk) { + result.id = ConstCastResultIdErrSet; + } + result.data.error_set.missing_errors.append(contained_error_entry); + } + } + free(errors); + return result; + } + + // fn + if (expected_type->id == TypeTableEntryIdFn && + actual_type->id == TypeTableEntryIdFn) + { + if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) { + result.id = ConstCastResultIdFnAlign; + return result; + } + if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { + result.id = ConstCastResultIdFnCC; + return result; + } + if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { + result.id = ConstCastResultIdFnVarArgs; + return result; + } + if (expected_type->data.fn.is_generic != actual_type->data.fn.is_generic) { + result.id = ConstCastResultIdFnIsGeneric; + return result; + } + if (!expected_type->data.fn.is_generic && + actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable) + { + ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type, source_node); + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdFnReturnType; + result.data.return_type = allocate_nonzero(1); + *result.data.return_type = child; + } + return result; + } + if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { + result.id = ConstCastResultIdFnArgCount; + return result; + } + if (expected_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) { + result.id = ConstCastResultIdFnGenericArgCount; + return result; + } + assert(expected_type->data.fn.is_generic || + expected_type->data.fn.fn_type_id.next_param_index == expected_type->data.fn.fn_type_id.param_count); + for (size_t i = 0; i < expected_type->data.fn.fn_type_id.next_param_index; i += 1) { + // note it's reversed for parameters + FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; + FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i]; + + ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type, expected_param_info->type, source_node); + if (arg_child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdFnArg; + result.data.fn_arg.arg_index = i; + result.data.fn_arg.child = allocate_nonzero(1); + *result.data.fn_arg.child = arg_child; + return result; + } + + if (expected_param_info->is_noalias != actual_param_info->is_noalias) { + result.id = ConstCastResultIdFnArgNoAlias; + result.data.arg_no_alias.arg_index = i; + return result; + } + } + return result; + } + + result.id = ConstCastResultIdType; + return result; +} + enum ImplicitCastMatchResult { ImplicitCastMatchResultNo, ImplicitCastMatchResultYes, @@ -6425,7 +6692,8 @@ enum ImplicitCastMatchResult { static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type, TypeTableEntry *actual_type, IrInstruction *value) { - ConstCastOnly const_cast_result = types_match_const_cast_only(ira->codegen, expected_type, actual_type); + AstNode *source_node = value->source_node; + ConstCastOnly const_cast_result = types_match_const_cast_only(ira, expected_type, actual_type, source_node); if (const_cast_result.id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6520,7 +6788,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6539,7 +6807,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *array_type = actual_type->data.pointer.child_type; if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, array_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6555,7 +6823,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, expected_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6570,7 +6838,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, expected_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } @@ -6650,7 +6918,7 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, // implicitly take a const pointer to something if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (types_match_const_cast_only(ira->codegen, expected_type, const_ptr_actual).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, expected_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) { return ImplicitCastMatchResultYes; } } @@ -6658,27 +6926,6 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, return ImplicitCastMatchResultNo; } -static bool resolve_inferred_error_set(IrAnalyze *ira, TypeTableEntry *err_set_type, AstNode *source_node) { - FnTableEntry *infer_fn = err_set_type->data.error_set.infer_fn; - if (infer_fn != nullptr) { - if (infer_fn->anal_state == FnAnalStateInvalid) { - return false; - } else if (infer_fn->anal_state == FnAnalStateReady) { - analyze_fn_body(ira->codegen, infer_fn); - if (err_set_type->data.error_set.infer_fn != nullptr) { - assert(ira->codegen->errors.length != 0); - return false; - } - } else { - ir_add_error_node(ira, source_node, - buf_sprintf("cannot resolve inferred error set '%s': function '%s' not fully analyzed yet", - buf_ptr(&err_set_type->name), buf_ptr(&err_set_type->data.error_set.infer_fn->symbol_name))); - return false; - } - } - return true; -} - static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) { assert(instruction_count >= 1); IrInstruction *prev_inst = instructions[0]; @@ -6687,17 +6934,19 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } ErrorTableEntry **errors = nullptr; TypeTableEntry *err_set_type = nullptr; - if (prev_inst->value.type == ira->codegen->builtin_types.entry_global_error_set) { - err_set_type = ira->codegen->builtin_types.entry_global_error_set; - } else if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { - err_set_type = prev_inst->value.type; - errors = allocate(ira->codegen->errors_by_index.length); - if (!resolve_inferred_error_set(ira, err_set_type, prev_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } - for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; - errors[error_entry->value] = error_entry; + if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { + if (type_is_global_error_set(prev_inst->value.type)) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + } else { + err_set_type = prev_inst->value.type; + if (!resolve_inferred_error_set(ira, err_set_type, prev_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + errors = allocate(ira->codegen->errors_by_index.length); + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; + errors[error_entry->value] = error_entry; + } } } @@ -6734,18 +6983,18 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (prev_type->id == TypeTableEntryIdErrorSet) { assert(err_set_type != nullptr); if (cur_type->id == TypeTableEntryIdErrorSet) { - if (err_set_type == ira->codegen->builtin_types.entry_global_error_set) { + if (type_is_global_error_set(err_set_type)) { continue; } - if (cur_type == ira->codegen->builtin_types.entry_global_error_set) { + if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; } - if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } // if err_set_type is a superset of cur_type, keep err_set_type. // if cur_type is a superset of err_set_type, switch err_set_type to cur_type bool prev_is_superset = true; @@ -6791,12 +7040,15 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod assert(errors != nullptr); continue; } else if (cur_type->id == TypeTableEntryIdErrorUnion) { - if (err_set_type == ira->codegen->builtin_types.entry_global_error_set) { + if (type_is_global_error_set(err_set_type)) { prev_inst = cur_inst; continue; } TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type; - if (cur_err_set_type == ira->codegen->builtin_types.entry_global_error_set) { + if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (type_is_global_error_set(cur_err_set_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; prev_inst = cur_inst; continue; @@ -6807,9 +7059,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = nullptr; } - if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; errors[error_entry->value] = error_entry; @@ -6845,11 +7094,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (prev_type->id == TypeTableEntryIdArray) { convert_to_const_slice = true; } - if (cur_type == ira->codegen->builtin_types.entry_global_error_set) { + if (type_is_global_error_set(cur_type)) { err_set_type = ira->codegen->builtin_types.entry_global_error_set; continue; } - if (err_set_type == ira->codegen->builtin_types.entry_global_error_set) { + if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) { continue; } if (!resolve_inferred_error_set(ira, cur_type, cur_inst->source_node)) { @@ -6884,11 +7133,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } - if (types_match_const_cast_only(ira->codegen, prev_type, cur_type).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) { continue; } - if (types_match_const_cast_only(ira->codegen, cur_type, prev_type).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, cur_type, prev_type, source_node).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } @@ -6911,26 +7160,26 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } if (prev_type->id == TypeTableEntryIdErrorUnion && - types_match_const_cast_only(ira->codegen, prev_type->data.error_union.payload_type, cur_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type, source_node).id == ConstCastResultIdOk) { continue; } if (cur_type->id == TypeTableEntryIdErrorUnion && - types_match_const_cast_only(ira->codegen, cur_type->data.error_union.payload_type, prev_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, source_node).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; } if (prev_type->id == TypeTableEntryIdMaybe && - types_match_const_cast_only(ira->codegen, prev_type->data.maybe.child_type, cur_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, source_node).id == ConstCastResultIdOk) { continue; } if (cur_type->id == TypeTableEntryIdMaybe && - types_match_const_cast_only(ira->codegen, cur_type->data.maybe.child_type, prev_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, source_node).id == ConstCastResultIdOk) { prev_inst = cur_inst; continue; @@ -6968,7 +7217,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && cur_type->data.array.len != prev_type->data.array.len && - types_match_const_cast_only(ira->codegen, cur_type->data.array.child_type, prev_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, cur_type->data.array.child_type, prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { convert_to_const_slice = true; prev_inst = cur_inst; @@ -6977,7 +7226,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray && cur_type->data.array.len != prev_type->data.array.len && - types_match_const_cast_only(ira->codegen, prev_type->data.array.child_type, cur_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, prev_type->data.array.child_type, cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { convert_to_const_slice = true; continue; @@ -6986,8 +7235,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) && (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || cur_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, - cur_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, + cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { convert_to_const_slice = false; continue; @@ -6996,8 +7245,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) && (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const || prev_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, - prev_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type, + prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { prev_inst = cur_inst; convert_to_const_slice = false; @@ -7581,7 +7830,7 @@ static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *sou zig_panic("TODO explicit error set cast"); if (container_set->data.error_set.infer_fn == nullptr && - container_set != ira->codegen->builtin_types.entry_global_error_set) + !type_is_global_error_set(container_set)) { ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length); for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { @@ -8103,7 +8352,7 @@ static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *sourc } else { zig_unreachable(); } - if (err_set_type != ira->codegen->builtin_types.entry_global_error_set) { + if (!type_is_global_error_set(err_set_type)) { if (!resolve_inferred_error_set(ira, err_set_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; } @@ -8140,6 +8389,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *wanted_type, IrInstruction *value) { TypeTableEntry *actual_type = value->value.type; + AstNode *source_node = source_instr->source_node; if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) { return ira->codegen->invalid_instruction; @@ -8149,7 +8399,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return value; // explicit match or non-const to const - if (types_match_const_cast_only(ira->codegen, wanted_type, actual_type).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); } @@ -8195,7 +8445,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); } @@ -8213,7 +8463,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst TypeTableEntry *array_type = actual_type->data.pointer.child_type; if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, array_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type); } @@ -8229,7 +8479,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value); if (type_is_invalid(cast1->value.type)) @@ -8252,7 +8502,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value); if (type_is_invalid(cast1->value.type)) @@ -8314,7 +8564,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from child type of maybe type to maybe type if (wanted_type->id == TypeTableEntryIdMaybe) { - if (types_match_const_cast_only(ira->codegen, wanted_type->data.maybe.child_type, actual_type).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type); } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) @@ -8336,7 +8586,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from child type of error type to error type if (wanted_type->id == TypeTableEntryIdErrorUnion) { - if (types_match_const_cast_only(ira->codegen, wanted_type->data.error_union.payload_type, actual_type).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type); } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) @@ -8358,7 +8608,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry; assert(ptr_type->id == TypeTableEntryIdPointer); if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) && - types_match_const_cast_only(ira->codegen, ptr_type->data.pointer.child_type, actual_type->data.array.child_type).id == ConstCastResultIdOk) + types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk) { IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); if (type_is_invalid(cast1->value.type)) @@ -8385,7 +8635,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst actual_type->id != TypeTableEntryIdMaybe) { TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; - if (types_match_const_cast_only(ira->codegen, wanted_child_type, actual_type).id == ConstCastResultIdOk || + if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk || actual_type->id == TypeTableEntryIdNullLit || actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) @@ -8535,7 +8785,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst // explicit cast from something to const pointer of it if (!type_requires_comptime(actual_type)) { TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true); - if (types_match_const_cast_only(ira->codegen, wanted_type, const_ptr_actual).id == ConstCastResultIdOk) { + if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) { return ir_analyze_cast_ref(ira, source_instr, value, wanted_type); } } @@ -9700,8 +9950,8 @@ static TypeTableEntry *ir_analyze_merge_error_sets(IrAnalyze *ira, IrInstruction if (type_is_invalid(op2_type)) return ira->codegen->builtin_types.entry_invalid; - if (op1_type == ira->codegen->builtin_types.entry_global_error_set || - op2_type == ira->codegen->builtin_types.entry_global_error_set) + if (type_is_global_error_set(op1_type) || + type_is_global_error_set(op2_type)) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = ira->codegen->builtin_types.entry_global_error_set; @@ -11716,7 +11966,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru } else if (child_type->id == TypeTableEntryIdErrorSet) { ErrorTableEntry *err_entry; TypeTableEntry *err_set_type; - if (child_type == ira->codegen->builtin_types.entry_global_error_set) { + if (type_is_global_error_set(child_type)) { auto existing_entry = ira->codegen->error_table.maybe_get(field_name); if (existing_entry) { err_entry = existing_entry->value; @@ -14764,7 +15014,7 @@ static TypeTableEntry *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstruc if (!resolve_inferred_error_set(ira, err_set_type, instruction->base.source_node)) { return ira->codegen->builtin_types.entry_invalid; } - if (err_set_type != ira->codegen->builtin_types.entry_global_error_set && + if (!type_is_global_error_set(err_set_type) && err_set_type->data.error_set.err_count == 0) { assert(err_set_type->data.error_set.infer_fn == nullptr); diff --git a/std/os/index.zig b/std/os/index.zig index aad24cf996..e57968dcad 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1072,7 +1072,7 @@ pub fn changeCurDir(allocator: &Allocator, dir_path: []const u8) !void { } /// Read value of a symbolic link. -pub fn readLink(allocator: &Allocator, pathname: []const u8) error![]u8 { +pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 { const path_buf = try allocator.alloc(u8, pathname.len + 1); defer allocator.free(path_buf); -- cgit v1.2.3 From 21ce559c9c923e62a76ffda6c26d31ac2e062acb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 4 Feb 2018 22:06:03 -0500 Subject: add --forbid-library to help track down accidentally linking against a library --- src/all_types.hpp | 2 ++ src/analyze.cpp | 11 ----------- src/analyze.hpp | 1 - src/codegen.cpp | 5 +++++ src/codegen.hpp | 1 + src/ir.cpp | 27 +++++++++++++++++++++++---- src/main.cpp | 8 ++++++++ 7 files changed, 39 insertions(+), 16 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index c94c158046..869d17f0b2 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1631,6 +1631,8 @@ struct CodeGen { TypeTableEntry *ptr_to_stack_trace_type; ZigList error_di_types; + + ZigList forbidden_libs; }; enum VarLinkage { diff --git a/src/analyze.cpp b/src/analyze.cpp index be6057a309..a7adbf4a3a 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5609,17 +5609,6 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) { return link_lib; } -void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name) { - LinkLib *link_lib = add_link_lib(g, lib_name); - for (size_t i = 0; i < link_lib->symbols.length; i += 1) { - Buf *existing_symbol_name = link_lib->symbols.at(i); - if (buf_eql_buf(existing_symbol_name, symbol_name)) { - return; - } - } - link_lib->symbols.append(symbol_name); -} - uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) { type_ensure_zero_bits_known(g, type_entry); if (type_entry->zero_bits) return 0; diff --git a/src/analyze.hpp b/src/analyze.hpp index 82ad05972b..c0c89cf36b 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -175,7 +175,6 @@ bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry); LinkLib *create_link_lib(Buf *name); bool calling_convention_does_first_arg_return(CallingConvention cc); LinkLib *add_link_lib(CodeGen *codegen, Buf *lib); -void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name); uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry); TypeTableEntry *get_align_amt_type(CodeGen *g); diff --git a/src/codegen.cpp b/src/codegen.cpp index e43060f195..c533e66d61 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -253,6 +253,10 @@ LinkLib *codegen_add_link_lib(CodeGen *g, Buf *name) { return add_link_lib(g, name); } +void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib) { + codegen->forbidden_libs.append(lib); +} + void codegen_add_framework(CodeGen *g, const char *framework) { g->darwin_frameworks.append(buf_create_from_str(framework)); } @@ -6311,3 +6315,4 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir, } return pkg; } + diff --git a/src/codegen.hpp b/src/codegen.hpp index b29cadee55..a7a4b748c4 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -36,6 +36,7 @@ void codegen_set_kernel32_lib_dir(CodeGen *codegen, Buf *kernel32_lib_dir); void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker); void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole); void codegen_add_lib_dir(CodeGen *codegen, const char *dir); +void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib); LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib); void codegen_add_framework(CodeGen *codegen, const char *name); void codegen_add_rpath(CodeGen *codegen, const char *name); diff --git a/src/ir.cpp b/src/ir.cpp index 56ead2b906..27b39b868f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -11762,6 +11762,25 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field } } +static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) { + LinkLib *link_lib = add_link_lib(ira->codegen, lib_name); + for (size_t i = 0; i < link_lib->symbols.length; i += 1) { + Buf *existing_symbol_name = link_lib->symbols.at(i); + if (buf_eql_buf(existing_symbol_name, symbol_name)) { + return; + } + } + for (size_t i = 0; i < ira->codegen->forbidden_libs.length; i += 1) { + Buf *forbidden_lib_name = ira->codegen->forbidden_libs.at(i); + if (buf_eql_buf(lib_name, forbidden_lib_name)) { + ir_add_error_node(ira, source_node, + buf_sprintf("linking against forbidden library '%s'", buf_ptr(symbol_name))); + } + } + link_lib->symbols.append(symbol_name); +} + + static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld) { bool pointer_only = false; resolve_top_level_decl(ira->codegen, tld, pointer_only, source_instruction->source_node); @@ -11777,7 +11796,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source TldVar *tld_var = (TldVar *)tld; VariableTableEntry *var = tld_var->var; if (tld_var->extern_lib_name != nullptr) { - add_link_lib_symbol(ira->codegen, tld_var->extern_lib_name, &var->name); + add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, source_instruction->source_node); } return ir_analyze_var_ptr(ira, source_instruction, var, false, false); @@ -11799,7 +11818,7 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source const_val->data.x_fn.fn_entry = fn_entry; if (tld_fn->extern_lib_name != nullptr) { - add_link_lib_symbol(ira->codegen, tld_fn->extern_lib_name, &fn_entry->symbol_name); + add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, source_instruction->source_node); } bool ptr_is_const = true; @@ -15839,7 +15858,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; if (tld_var->extern_lib_name != nullptr) { - add_link_lib_symbol(ira->codegen, tld_var->extern_lib_name, &var->name); + add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node); } if (lval.is_ptr) { @@ -15858,7 +15877,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, assert(fn_entry->type_entry); if (tld_fn->extern_lib_name != nullptr) { - add_link_lib_symbol(ira->codegen, tld_fn->extern_lib_name, &fn_entry->symbol_name); + add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, instruction->base.source_node); } IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope, diff --git a/src/main.cpp b/src/main.cpp index e3ef62be07..a533973de9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -66,6 +66,7 @@ static int usage(const char *arg0) { " --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides\n" " --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides\n" " --library [lib] link against lib\n" + " --forbid-library [lib] make it an error to link against lib\n" " --library-path [dir] add a directory to the library search path\n" " --linker-script [path] use a custom linker script\n" " --object [obj] add object file to build\n" @@ -309,6 +310,7 @@ int main(int argc, char **argv) { ZigList llvm_argv = {0}; ZigList lib_dirs = {0}; ZigList link_libs = {0}; + ZigList forbidden_link_libs = {0}; ZigList frameworks = {0}; int err; const char *target_arch = nullptr; @@ -592,6 +594,8 @@ int main(int argc, char **argv) { lib_dirs.append(argv[i]); } else if (strcmp(arg, "--library") == 0) { link_libs.append(argv[i]); + } else if (strcmp(arg, "--forbid-library") == 0) { + forbidden_link_libs.append(argv[i]); } else if (strcmp(arg, "--object") == 0) { objects.append(argv[i]); } else if (strcmp(arg, "--assembly") == 0) { @@ -804,6 +808,10 @@ int main(int argc, char **argv) { LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i))); link_lib->provided_explicitly = true; } + for (size_t i = 0; i < forbidden_link_libs.length; i += 1) { + Buf *forbidden_link_lib = buf_create_from_str(forbidden_link_libs.at(i)); + codegen_add_forbidden_lib(g, forbidden_link_lib); + } for (size_t i = 0; i < frameworks.length; i += 1) { codegen_add_framework(g, frameworks.at(i)); } -- cgit v1.2.3 From 6940212ecbef349e449441e1fd813116865d3a5f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 5 Feb 2018 17:42:13 -0500 Subject: error sets: fix peer resolution of error unions --- src/codegen.cpp | 3 ++- src/ir.cpp | 17 +++++++++++++++-- std/debug/index.zig | 4 ++-- 3 files changed, 19 insertions(+), 5 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index c533e66d61..e3f182e17c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1790,7 +1790,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, assert(op1->value.type == op2->value.type || op_id == IrBinOpBitShiftLeftLossy || op_id == IrBinOpBitShiftLeftExact || op_id == IrBinOpBitShiftRightLossy || - op_id == IrBinOpBitShiftRightExact); + op_id == IrBinOpBitShiftRightExact || + (op1->value.type->id == TypeTableEntryIdErrorSet && op2->value.type->id == TypeTableEntryIdErrorSet)); TypeTableEntry *type_entry = op1->value.type; bool want_runtime_safety = bin_op_instruction->safety_check_on && diff --git a/src/ir.cpp b/src/ir.cpp index 3f3dfb1ad7..0f9020c9dc 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7258,8 +7258,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } // unset all the errors - for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i]; + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; errors[error_entry->value] = nullptr; } for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { @@ -7320,6 +7320,19 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (cur_type->id == TypeTableEntryIdErrorUnion && types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, source_node).id == ConstCastResultIdOk) { + if (err_set_type != nullptr) { + TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type; + if (!resolve_inferred_error_set(ira, cur_err_set_type, cur_inst->source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (type_is_global_error_set(cur_err_set_type) || type_is_global_error_set(err_set_type)) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + prev_inst = cur_inst; + continue; + } + + err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type); + } prev_inst = cur_inst; continue; } diff --git a/std/debug/index.zig b/std/debug/index.zig index a759a2af16..2bb03a6706 100644 --- a/std/debug/index.zig +++ b/std/debug/index.zig @@ -264,7 +264,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace { } } -fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_info: &const LineInfo) !void { +fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &const LineInfo) !void { var f = try io.File.openRead(line_info.file_name, allocator); defer f.close(); // TODO fstat and make sure that the file has the correct size @@ -1054,7 +1054,7 @@ fn readULeb128(in_stream: var) !u64 { } } -fn readILeb128(in_stream: &io.InStream) !i64 { +fn readILeb128(in_stream: var) !i64 { var result: i64 = 0; var shift: usize = 0; -- cgit v1.2.3 From fee875770cb8c9363219b736f6c03e15cff39b92 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Feb 2018 11:09:18 -0500 Subject: error set casting building --- TODO | 1 + src/all_types.hpp | 1 + src/codegen.cpp | 3 +++ src/ir.cpp | 61 +++++++++++++++++++++---------------------------------- 4 files changed, 28 insertions(+), 38 deletions(-) (limited to 'src/codegen.cpp') diff --git a/TODO b/TODO index f470ebaafe..567d31e39d 100644 --- a/TODO +++ b/TODO @@ -19,6 +19,7 @@ foo() catch |err| switch (err) {}; // TODO this is an explicit cast and should actually coerce the type erorr set casting + // add a runtime safety check test err should be comptime if error set has 0 members diff --git a/src/all_types.hpp b/src/all_types.hpp index 869d17f0b2..04b781a598 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -560,6 +560,7 @@ enum CastOp { CastOpResizeSlice, CastOpBytesToSlice, CastOpNumLitToConcrete, + CastOpErrSet, }; struct AstNodeFnCallExpr { diff --git a/src/codegen.cpp b/src/codegen.cpp index e3f182e17c..69460d723a 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2081,6 +2081,9 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, assert(wanted_type->id == TypeTableEntryIdInt); assert(actual_type->id == TypeTableEntryIdBool); return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, ""); + case CastOpErrSet: + // TODO runtime safety for error casting + return expr_val; } zig_unreachable(); } diff --git a/src/ir.cpp b/src/ir.cpp index 0c30f49d36..25723535dd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7584,6 +7584,8 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, switch (cast_op) { case CastOpNoCast: zig_unreachable(); + case CastOpErrSet: + zig_panic("TODO"); case CastOpNoop: { copy_const_val(const_val, other_val, other_val->special == ConstValSpecialStatic); @@ -8039,52 +8041,35 @@ static IrInstruction *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInstruction return result; } -// TODO this is an explicit cast and should actually coerce the type static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) { - TypeTableEntry *contained_set = value->value.type; - TypeTableEntry *container_set = wanted_type; - - assert(contained_set->id == TypeTableEntryIdErrorSet); - assert(container_set->id == TypeTableEntryIdErrorSet); + assert(value->value.type->id == TypeTableEntryIdErrorSet); + assert(wanted_type->id == TypeTableEntryIdErrorSet); - zig_panic("TODO explicit error set cast"); + if (instr_is_comptime(value)) { + ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; - if (container_set->data.error_set.infer_fn == nullptr && - !type_is_global_error_set(container_set)) - { - ErrorTableEntry **errors = allocate(ira->codegen->errors_by_index.length); - for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; - assert(errors[error_entry->value] == nullptr); - errors[error_entry->value] = error_entry; + if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) { + return ira->codegen->invalid_instruction; } - ErrorMsg *err_msg = nullptr; - for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) { - ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i]; - ErrorTableEntry *error_entry = errors[contained_error_entry->value]; - if (error_entry == nullptr) { - if (err_msg == nullptr) { - err_msg = ir_add_error(ira, source_instr, - buf_sprintf("invalid cast of error set '%s' to error set '%s'", - buf_ptr(&contained_set->name), buf_ptr(&container_set->name))); + if (!type_is_global_error_set(wanted_type)) { + bool subset = false; + for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) { + if (wanted_type->data.error_set.errors[i]->value == val->data.x_err_set->value) { + subset = true; + break; } - add_error_note(ira->codegen, err_msg, contained_error_entry->decl_node, - buf_sprintf("'%s.%s' not present in '%s'", buf_ptr(&contained_set->name), - buf_ptr(&contained_error_entry->name), buf_ptr(&container_set->name))); + } + if (!subset) { + ir_add_error(ira, source_instr, + buf_sprintf("error.%s not a member of error set '%s'", + buf_ptr(&val->data.x_err_set->name), buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_instruction; } } - free(errors); - if (err_msg != nullptr) { - return ira->codegen->invalid_instruction; - } - } - - if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); - if (!val) - return ira->codegen->invalid_instruction; IrInstructionConst *const_instruction = ir_create_instruction(&ira->new_irb, source_instr->scope, source_instr->source_node); @@ -8094,7 +8079,7 @@ static IrInstruction *ir_analyze_err_set_cast(IrAnalyze *ira, IrInstruction *sou return &const_instruction->base; } - IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpNoop); + IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, CastOpErrSet); result->value.type = wanted_type; return result; } -- cgit v1.2.3 From 54c06bf7158ce52c5de8d09f109215c467a3bf6a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 8 Feb 2018 21:54:44 -0500 Subject: error sets: runtime safety for int-to-err and err set cast --- TODO | 4 --- src/codegen.cpp | 79 ++++++++++++++++++++++++++++++++----------------- src/ir.cpp | 50 ++++++++++++++++++++++++------- test/compile_errors.zig | 32 ++++++++++++++++++++ test/runtime_safety.zig | 16 +++++++++- 5 files changed, 139 insertions(+), 42 deletions(-) (limited to 'src/codegen.cpp') diff --git a/TODO b/TODO index 567d31e39d..2f3f25c3d3 100644 --- a/TODO +++ b/TODO @@ -17,10 +17,6 @@ you can get the compiler to tell you the possible errors for an inferred error s foo() catch |err| switch (err) {}; -// TODO this is an explicit cast and should actually coerce the type - erorr set casting - // add a runtime safety check - test err should be comptime if error set has 0 members diff --git a/src/codegen.cpp b/src/codegen.cpp index 69460d723a..2f8ebf768d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1958,6 +1958,54 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable, zig_unreachable(); } +static void add_error_range_check(CodeGen *g, TypeTableEntry *err_set_type, TypeTableEntry *int_type, LLVMValueRef target_val) { + assert(err_set_type->id == TypeTableEntryIdErrorSet); + + if (type_is_global_error_set(err_set_type)) { + LLVMValueRef zero = LLVMConstNull(int_type->type_ref); + LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, ""); + LLVMValueRef ok_bit; + + BigInt biggest_possible_err_val = {0}; + eval_min_max_value_int(g, int_type, &biggest_possible_err_val, true); + + if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) && + bigint_as_unsigned(&biggest_possible_err_val) < g->errors_by_index.length) + { + ok_bit = neq_zero_bit; + } else { + LLVMValueRef error_value_count = LLVMConstInt(int_type->type_ref, g->errors_by_index.length, false); + LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, ""); + ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, ""); + } + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail"); + + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdInvalidErrorCode); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } else { + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail"); + + uint32_t err_count = err_set_type->data.error_set.err_count; + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_val, fail_block, err_count); + for (uint32_t i = 0; i < err_count; i += 1) { + LLVMValueRef case_value = LLVMConstInt(g->err_tag_type->type_ref, err_set_type->data.error_set.errors[i]->value, false); + LLVMAddCase(switch_instr, case_value, ok_block); + } + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdInvalidErrorCode); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } +} + static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, IrInstructionCast *cast_instruction) { @@ -2082,7 +2130,9 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, assert(actual_type->id == TypeTableEntryIdBool); return LLVMBuildZExt(g->builder, expr_val, wanted_type->type_ref, ""); case CastOpErrSet: - // TODO runtime safety for error casting + if (ir_want_runtime_safety(g, &cast_instruction->base)) { + add_error_range_check(g, wanted_type, g->err_tag_type, expr_val); + } return expr_val; } zig_unreachable(); @@ -2154,32 +2204,7 @@ static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, I LLVMValueRef target_val = ir_llvm_value(g, instruction->target); if (ir_want_runtime_safety(g, &instruction->base)) { - LLVMValueRef zero = LLVMConstNull(actual_type->type_ref); - LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, ""); - LLVMValueRef ok_bit; - - BigInt biggest_possible_err_val = {0}; - eval_min_max_value_int(g, actual_type, &biggest_possible_err_val, true); - - if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) && - bigint_as_unsigned(&biggest_possible_err_val) < g->errors_by_index.length) - { - ok_bit = neq_zero_bit; - } else { - LLVMValueRef error_value_count = LLVMConstInt(actual_type->type_ref, g->errors_by_index.length, false); - LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, ""); - ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, ""); - } - - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail"); - - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdInvalidErrorCode); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); + add_error_range_check(g, wanted_type, actual_type, target_val); } return gen_widen_or_shorten(g, false, actual_type, g->err_tag_type, target_val); diff --git a/src/ir.cpp b/src/ir.cpp index 9cc05a2bc2..fa2815d6dd 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8505,19 +8505,49 @@ static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *sourc IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type); - BigInt err_count; - bigint_init_unsigned(&err_count, ira->codegen->errors_by_index.length); - if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) { - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &val->data.x_bigint, 10); - ir_add_error(ira, source_instr, - buf_sprintf("integer value %s represents no error", buf_ptr(val_buf))); + if (!resolve_inferred_error_set(ira, wanted_type, source_instr->source_node)) { return ira->codegen->invalid_instruction; } - size_t index = bigint_as_unsigned(&val->data.x_bigint); - result->value.data.x_err_set = ira->codegen->errors_by_index.at(index); - return result; + if (type_is_global_error_set(wanted_type)) { + BigInt err_count; + bigint_init_unsigned(&err_count, ira->codegen->errors_by_index.length); + + if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &val->data.x_bigint, 10); + ir_add_error(ira, source_instr, + buf_sprintf("integer value %s represents no error", buf_ptr(val_buf))); + return ira->codegen->invalid_instruction; + } + + size_t index = bigint_as_unsigned(&val->data.x_bigint); + result->value.data.x_err_set = ira->codegen->errors_by_index.at(index); + return result; + } else { + ErrorTableEntry *err = nullptr; + BigInt err_int; + + for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) { + ErrorTableEntry *this_err = wanted_type->data.error_set.errors[i]; + bigint_init_unsigned(&err_int, this_err->value); + if (bigint_cmp(&val->data.x_bigint, &err_int) == CmpEQ) { + err = this_err; + break; + } + } + + if (err == nullptr) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &val->data.x_bigint, 10); + ir_add_error(ira, source_instr, + buf_sprintf("integer value %s represents no error in '%s'", buf_ptr(val_buf), buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_instruction; + } + + result->value.data.x_err_set = err; + return result; + } } IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f497f938ed..0378d9366d 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,38 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("implicit cast of error set not a subset", + \\const Set1 = error{A, B}; + \\const Set2 = error{A, C}; + \\export fn entry() void { + \\ foo(Set1.B); + \\} + \\fn foo(set1: Set1) void { + \\ var x: Set2 = set1; + \\} + , + ".tmp_source.zig:7:19: error: expected 'Set2', found 'Set1'", + ".tmp_source.zig:1:23: note: 'error.B' not a member of destination error set"); + + cases.add("int to err global invalid number", + \\const Set1 = error{A, B}; + \\comptime { + \\ var x: usize = 3; + \\ var y = error(x); + \\} + , + ".tmp_source.zig:4:18: error: integer value 3 represents no error"); + + cases.add("int to err non global invalid number", + \\const Set1 = error{A, B}; + \\const Set2 = error{A, C}; + \\comptime { + \\ var x = usize(Set1.B); + \\ var y = Set2(x); + \\} + , + ".tmp_source.zig:5:17: error: integer value 2 represents no error in 'Set2'"); + cases.add("@memberCount of error", \\comptime { \\ _ = @memberCount(error); diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index def6430961..8b8f612056 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -220,7 +220,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\} ); - cases.addRuntimeSafety("cast integer to error and no code matches", + cases.addRuntimeSafety("cast integer to global error and no code matches", \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); \\} @@ -232,6 +232,20 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\} ); + cases.addRuntimeSafety("cast integer to non-global error set and no match", + \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\const Set1 = error{A, B}; + \\const Set2 = error{A, C}; + \\pub fn main() void { + \\ _ = foo(Set1.B); + \\} + \\fn foo(set1: Set1) Set2 { + \\ return Set2(set1); + \\} + ); + cases.addRuntimeSafety("@alignCast misaligned", \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn { \\ @import("std").os.exit(126); -- cgit v1.2.3 From 32c988a2d7920fcd3da50a13a1ae9abcd57daf50 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 9 Feb 2018 00:24:23 -0500 Subject: fix build runner on windows --- src/analyze.cpp | 1 + src/codegen.cpp | 4 ++-- std/os/child_process.zig | 2 +- std/os/index.zig | 30 +++++++++++++++++++++++++++--- std/os/windows/util.zig | 8 +++++++- 5 files changed, 38 insertions(+), 7 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/analyze.cpp b/src/analyze.cpp index b6f08b7aec..bf7b6e363f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -543,6 +543,7 @@ TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, T if (type_has_bits(err_set_type)) { entry->type_ref = err_set_type->type_ref; entry->di_type = err_set_type->di_type; + g->error_di_types.append(&entry->di_type); } else { entry->zero_bits = true; entry->di_type = g->builtin_types.entry_void->di_type; diff --git a/src/codegen.cpp b/src/codegen.cpp index 2f8ebf768d..25c115b6f1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4622,6 +4622,8 @@ static void validate_inline_fns(CodeGen *g) { static void do_code_gen(CodeGen *g) { assert(!g->errors.length); + codegen_add_time_event(g, "Code Generation"); + { // create debug type for error sets assert(g->err_enumerators.length == g->errors_by_index.length); @@ -4644,8 +4646,6 @@ static void do_code_gen(CodeGen *g) { } } - codegen_add_time_event(g, "Code Generation"); - generate_error_name_table(g); generate_enum_name_tables(g); diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 1b7639fb71..0b3040cdc9 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -160,7 +160,7 @@ pub const ChildProcess = struct { else => os.unexpectedErrorWindows(err), }; } - self.waitUnwrappedWindows(); + try self.waitUnwrappedWindows(); return ??self.term; } diff --git a/std/os/index.zig b/std/os/index.zig index d8b2303981..a2da2ec673 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -38,6 +38,7 @@ pub const windowsLoadDll = windows_util.windowsLoadDll; pub const windowsUnloadDll = windows_util.windowsUnloadDll; pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; +pub const WindowsWaitError = windows_util.WaitError; pub const WindowsOpenError = windows_util.OpenError; pub const WindowsWriteError = windows_util.WriteError; @@ -605,7 +606,9 @@ test "os.getCwd" { _ = getCwd(debug.global_allocator); } -pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { +pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; + +pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) SymLinkError!void { if (is_windows) { return symLinkWindows(allocator, existing_path, new_path); } else { @@ -613,7 +616,12 @@ pub fn symLink(allocator: &Allocator, existing_path: []const u8, new_path: []con } } -pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { +pub const WindowsSymLinkError = error { + OutOfMemory, + Unexpected, +}; + +pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { const existing_with_null = try cstr.addNullByte(allocator, existing_path); defer allocator.free(existing_with_null); const new_with_null = try cstr.addNullByte(allocator, new_path); @@ -627,7 +635,23 @@ pub fn symLinkWindows(allocator: &Allocator, existing_path: []const u8, new_path } } -pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void { +pub const PosixSymLinkError = error { + OutOfMemory, + AccessDenied, + DiskQuota, + PathAlreadyExists, + FileSystem, + SymLinkLoop, + NameTooLong, + FileNotFound, + SystemResources, + NoSpaceLeft, + ReadOnlyFileSystem, + NotDir, + Unexpected, +}; + +pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { const full_buf = try allocator.alloc(u8, existing_path.len + new_path.len + 2); defer allocator.free(full_buf); diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index e2d7c14149..c19a5789e7 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -7,7 +7,13 @@ const mem = std.mem; const BufMap = std.BufMap; const cstr = std.cstr; -pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) !void { +pub const WaitError = error { + WaitAbandoned, + WaitTimeOut, + Unexpected, +}; + +pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) WaitError!void { const result = windows.WaitForSingleObject(handle, milliseconds); return switch (result) { windows.WAIT_ABANDONED => error.WaitAbandoned, -- cgit v1.2.3 From f2d601661d286b135293373a83ce1a8628272379 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 11 Feb 2018 16:45:33 -0500 Subject: fix exported variable not named in the object file closes #771 --- example/mix_o_files/base64.zig | 3 +++ example/mix_o_files/test.c | 4 ++++ src/codegen.cpp | 51 +++++++++++++++++++++--------------------- 3 files changed, 33 insertions(+), 25 deletions(-) (limited to 'src/codegen.cpp') diff --git a/example/mix_o_files/base64.zig b/example/mix_o_files/base64.zig index 553c780b98..e682a97055 100644 --- a/example/mix_o_files/base64.zig +++ b/example/mix_o_files/base64.zig @@ -8,3 +8,6 @@ export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8, base64_decoder.decode(dest[0..decoded_size], src); return decoded_size; } + +var x: c_int = 1234; +export var x_ptr = &x; diff --git a/example/mix_o_files/test.c b/example/mix_o_files/test.c index 8a4758acc4..d821bbe108 100644 --- a/example/mix_o_files/test.c +++ b/example/mix_o_files/test.c @@ -4,6 +4,8 @@ #include #include +extern int *x_ptr; + int main(int argc, char **argv) { const char *encoded = "YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz"; char buf[200]; @@ -12,5 +14,7 @@ int main(int argc, char **argv) { buf[len] = 0; assert(strcmp(buf, "all your base are belong to us") == 0); + assert(*x_ptr == 1234); + return 0; } diff --git a/src/codegen.cpp b/src/codegen.cpp index 25c115b6f1..4f100d75ad 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -283,9 +283,9 @@ void codegen_set_linker_script(CodeGen *g, const char *linker_script) { } -static void render_const_val(CodeGen *g, ConstExprValue *const_val); +static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char *name); static void render_const_val_global(CodeGen *g, ConstExprValue *const_val, const char *name); -static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val); +static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name); static void generate_error_name_table(CodeGen *g); static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) { @@ -874,7 +874,7 @@ static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) { ConstExprValue *array_val = create_const_str_lit(g, buf_msg); init_const_slice(g, val, array_val, 0, buf_len(buf_msg), true); - render_const_val(g, val); + render_const_val(g, val, ""); render_const_val_global(g, val, ""); assert(val->global_refs->llvm_global); @@ -1413,7 +1413,7 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { if (!instruction->llvm_value) { assert(instruction->value.special != ConstValSpecialRuntime); assert(instruction->value.type); - render_const_val(g, &instruction->value); + render_const_val(g, &instruction->value, ""); // we might have to do some pointer casting here due to the way union // values are rendered with a type other than the one we expect if (handle_is_ptr(instruction->value.type)) { @@ -3892,7 +3892,7 @@ static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *ar static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) { switch (parent->id) { case ConstParentIdNone: - render_const_val(g, val); + render_const_val(g, val, ""); render_const_val_global(g, val, ""); return val->global_refs->llvm_global; case ConstParentIdStruct: @@ -3991,17 +3991,17 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdEnum: { assert(type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr); - LLVMValueRef int_val = gen_const_val(g, const_val); + LLVMValueRef int_val = gen_const_val(g, const_val, ""); return LLVMConstZExt(int_val, big_int_type_ref); } case TypeTableEntryIdInt: { - LLVMValueRef int_val = gen_const_val(g, const_val); + LLVMValueRef int_val = gen_const_val(g, const_val, ""); return LLVMConstZExt(int_val, big_int_type_ref); } case TypeTableEntryIdFloat: { - LLVMValueRef float_val = gen_const_val(g, const_val); + LLVMValueRef float_val = gen_const_val(g, const_val, ""); LLVMValueRef int_val = LLVMConstFPToUI(float_val, LLVMIntType((unsigned)type_entry->data.floating.bit_count)); return LLVMConstZExt(int_val, big_int_type_ref); @@ -4010,7 +4010,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con case TypeTableEntryIdFn: case TypeTableEntryIdMaybe: { - LLVMValueRef ptr_val = gen_const_val(g, const_val); + LLVMValueRef ptr_val = gen_const_val(g, const_val, ""); LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->type_ref); return LLVMConstZExt(ptr_size_int_val, big_int_type_ref); } @@ -4055,7 +4055,7 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef return LLVMTypeOf(val) != type_entry->type_ref; } -static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { +static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) { TypeTableEntry *type_entry = const_val->type; assert(!type_entry->zero_bits); @@ -4108,7 +4108,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { child_type->id == TypeTableEntryIdFn) { if (const_val->data.x_maybe) { - return gen_const_val(g, const_val->data.x_maybe); + return gen_const_val(g, const_val->data.x_maybe, ""); } else { return LLVMConstNull(child_type->type_ref); } @@ -4117,7 +4117,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { LLVMValueRef maybe_val; bool make_unnamed_struct; if (const_val->data.x_maybe) { - child_val = gen_const_val(g, const_val->data.x_maybe); + child_val = gen_const_val(g, const_val->data.x_maybe, ""); maybe_val = LLVMConstAllOnes(LLVMInt1Type()); make_unnamed_struct = is_llvm_value_unnamed_type(const_val->type, child_val); @@ -4161,7 +4161,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { if (src_field_index + 1 == src_field_index_end) { ConstExprValue *field_val = &const_val->data.x_struct.fields[src_field_index]; - LLVMValueRef val = gen_const_val(g, field_val); + LLVMValueRef val = gen_const_val(g, field_val, ""); fields[type_struct_field->gen_index] = val; make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val); } else { @@ -4201,7 +4201,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { continue; } ConstExprValue *field_val = &const_val->data.x_struct.fields[i]; - LLVMValueRef val = gen_const_val(g, field_val); + LLVMValueRef val = gen_const_val(g, field_val, ""); fields[type_struct_field->gen_index] = val; make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val); } @@ -4225,7 +4225,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { bool make_unnamed_struct = false; for (uint64_t i = 0; i < len; i += 1) { ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i]; - LLVMValueRef val = gen_const_val(g, elem_value); + LLVMValueRef val = gen_const_val(g, elem_value, ""); values[i] = val; make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val); } @@ -4260,7 +4260,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { } else { uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref); uint64_t pad_bytes = type_entry->data.unionation.union_size_bytes - field_type_bytes; - LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value); + LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value, ""); make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) || payload_value->type != type_entry->data.unionation.most_aligned_union_member; @@ -4305,7 +4305,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { return fn_llvm_value(g, const_val->data.x_fn.fn_entry); case TypeTableEntryIdPointer: { - render_const_val_global(g, const_val, ""); + render_const_val_global(g, const_val, name); switch (const_val->data.x_ptr.special) { case ConstPtrSpecialInvalid: case ConstPtrSpecialDiscard: @@ -4313,7 +4313,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { case ConstPtrSpecialRef: { ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee; - render_const_val(g, pointee); + render_const_val(g, pointee, ""); render_const_val_global(g, pointee, ""); ConstExprValue *other_val = pointee; const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref); @@ -4383,7 +4383,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { return LLVMConstInt(g->err_tag_type->type_ref, value, false); } else if (!type_has_bits(err_set_type)) { assert(type_has_bits(payload_type)); - return gen_const_val(g, const_val->data.x_err_union.payload); + return gen_const_val(g, const_val->data.x_err_union.payload, ""); } else { LLVMValueRef err_tag_value; LLVMValueRef err_payload_value; @@ -4395,7 +4395,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { } else { err_tag_value = LLVMConstNull(g->err_tag_type->type_ref); ConstExprValue *payload_val = const_val->data.x_err_union.payload; - err_payload_value = gen_const_val(g, payload_val); + err_payload_value = gen_const_val(g, payload_val, ""); make_unnamed_struct = is_llvm_value_unnamed_type(payload_val->type, err_payload_value); } LLVMValueRef fields[] = { @@ -4430,11 +4430,11 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) { zig_unreachable(); } -static void render_const_val(CodeGen *g, ConstExprValue *const_val) { +static void render_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) { if (!const_val->global_refs) const_val->global_refs = allocate(1); if (!const_val->global_refs->llvm_value) - const_val->global_refs->llvm_value = gen_const_val(g, const_val); + const_val->global_refs->llvm_value = gen_const_val(g, const_val, name); if (const_val->global_refs->llvm_global) LLVMSetInitializer(const_val->global_refs->llvm_global, const_val->global_refs->llvm_value); @@ -4663,7 +4663,7 @@ static void do_code_gen(CodeGen *g) { coerced_value.special = ConstValSpecialStatic; coerced_value.type = var_type; coerced_value.data.x_f128 = bigfloat_to_f128(&const_val->data.x_bigfloat); - LLVMValueRef init_val = gen_const_val(g, &coerced_value); + LLVMValueRef init_val = gen_const_val(g, &coerced_value, ""); gen_global_var(g, var, init_val, var_type); continue; } @@ -4697,8 +4697,9 @@ static void do_code_gen(CodeGen *g) { LLVMSetAlignment(global_value, var->align_bytes); } else { bool exported = (var->linkage == VarLinkageExport); - render_const_val(g, var->value); - render_const_val_global(g, var->value, buf_ptr(get_mangled_name(g, &var->name, exported))); + const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported)); + render_const_val(g, var->value, mangled_name); + render_const_val_global(g, var->value, mangled_name); global_value = var->value->global_refs->llvm_global; if (exported) { -- cgit v1.2.3