diff --git a/TODO b/TODO index 963169d5a..c6fcfa939 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 72d1f946a..24c4b324b 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 304fa5651..c94c15804 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 8ed5706a2..073a91aef 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 88895d2d3..aed4b3e6d 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 ea82b3fcd..acf14474e 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 2e4a13927..12dc76e97 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 781cd4a1e..f2c0d6a5b 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 5034a9556..1a3b568a2 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 f98c0c834..44d838a72 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,20 +889,37 @@ 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); + t.state = TokenizeStateStart; + 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; } - break; 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 749f72f41..92a3b8de0 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 4778d63dd..50ab1a221 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 e110d4ddf..dbca37745 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 1c75d9254..25c064888 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 224c4ba32..a303ee4be 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 08da342e0..d03092ff1 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), }; }