Commit b8f59e14cd
Changed files (16)
doc/langref.html.in
@@ -5682,7 +5682,7 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression |
CurlySuffixExpression = TypeExpr option(ContainerInitExpression)
-MultiplyOperator = "*" | "/" | "%" | "**" | "*%"
+MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression
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 {
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<ErrorTableEntry *>(inferred_err_set_type->data.error_set.err_count);
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();
}
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;
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<ErrorTableEntry *>(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<ErrorTableEntry *>(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<ErrorTableEntry *>(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<ErrorTableEntry *>(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<ErrorTableEntry *>(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<AstNode *>(ira->codegen->errors_by_index.length);
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();
}
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);
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)";
}
src/tokenizer.hpp
@@ -17,6 +17,8 @@ enum TokenId {
TokenIdArrow,
TokenIdAtSign,
TokenIdBang,
+ TokenIdBarBar,
+ TokenIdBarBarEq,
TokenIdBinOr,
TokenIdBinXor,
TokenIdBitAndEq,
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 => {
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),
};
}
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),
};
}
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) {
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| {
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