*WIP* error sets - correctly resolve inferred error sets

master
Andrew Kelley 2018-02-02 18:13:32 -05:00
parent 39d5f44863
commit b8f59e14cd
16 changed files with 351 additions and 90 deletions

12
TODO
View File

@ -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 // TODO this is an explicit cast and should actually coerce the type
erorr set casting 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

View File

@ -5682,7 +5682,7 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression |
CurlySuffixExpression = TypeExpr option(ContainerInitExpression) CurlySuffixExpression = TypeExpr option(ContainerInitExpression)
MultiplyOperator = "*" | "/" | "%" | "**" | "*%" MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression

View File

@ -510,8 +510,7 @@ enum BinOpType {
BinOpTypeAssignBitAnd, BinOpTypeAssignBitAnd,
BinOpTypeAssignBitXor, BinOpTypeAssignBitXor,
BinOpTypeAssignBitOr, BinOpTypeAssignBitOr,
BinOpTypeAssignBoolAnd, BinOpTypeAssignMergeErrorSets,
BinOpTypeAssignBoolOr,
BinOpTypeBoolOr, BinOpTypeBoolOr,
BinOpTypeBoolAnd, BinOpTypeBoolAnd,
BinOpTypeCmpEq, BinOpTypeCmpEq,
@ -537,6 +536,7 @@ enum BinOpType {
BinOpTypeArrayCat, BinOpTypeArrayCat,
BinOpTypeArrayMult, BinOpTypeArrayMult,
BinOpTypeErrorUnion, BinOpTypeErrorUnion,
BinOpTypeMergeErrorSets,
}; };
struct AstNodeBinOpExpr { struct AstNodeBinOpExpr {
@ -2054,6 +2054,7 @@ enum IrBinOp {
IrBinOpRemMod, IrBinOpRemMod,
IrBinOpArrayCat, IrBinOpArrayCat,
IrBinOpArrayMult, IrBinOpArrayMult,
IrBinOpMergeErrorSets,
}; };
struct IrInstructionBinOp { struct IrInstructionBinOp {

View File

@ -530,7 +530,6 @@ TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, T
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion); TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion);
entry->is_copyable = true; entry->is_copyable = true;
assert(payload_type->type_ref);
assert(payload_type->di_type); assert(payload_type->di_type);
ensure_complete_type(g, payload_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; entry->data.error_union.payload_type = payload_type;
if (!type_has_bits(payload_type)) { if (!type_has_bits(payload_type)) {
entry->type_ref = err_set_type->type_ref; if (type_has_bits(err_set_type)) {
entry->di_type = err_set_type->di_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 { } else {
LLVMTypeRef elem_types[] = { LLVMTypeRef elem_types[] = {
err_set_type->type_ref, 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) { void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_type_node) {
TypeTableEntry *fn_type = fn_table_entry->type_entry; TypeTableEntry *fn_type = fn_table_entry->type_entry;
assert(!fn_type->data.fn.is_generic); 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; 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.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.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); return_err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(inferred_err_set_type->data.error_set.err_count);

View File

@ -49,12 +49,12 @@ static const char *bin_op_str(BinOpType bin_op) {
case BinOpTypeAssignBitAnd: return "&="; case BinOpTypeAssignBitAnd: return "&=";
case BinOpTypeAssignBitXor: return "^="; case BinOpTypeAssignBitXor: return "^=";
case BinOpTypeAssignBitOr: return "|="; case BinOpTypeAssignBitOr: return "|=";
case BinOpTypeAssignBoolAnd: return "&&="; case BinOpTypeAssignMergeErrorSets: return "||=";
case BinOpTypeAssignBoolOr: return "||=";
case BinOpTypeUnwrapMaybe: return "??"; case BinOpTypeUnwrapMaybe: return "??";
case BinOpTypeArrayCat: return "++"; case BinOpTypeArrayCat: return "++";
case BinOpTypeArrayMult: return "**"; case BinOpTypeArrayMult: return "**";
case BinOpTypeErrorUnion: return "!"; case BinOpTypeErrorUnion: return "!";
case BinOpTypeMergeErrorSets: return "||";
} }
zig_unreachable(); zig_unreachable();
} }

View File

@ -1799,6 +1799,7 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
case IrBinOpArrayCat: case IrBinOpArrayCat:
case IrBinOpArrayMult: case IrBinOpArrayMult:
case IrBinOpRemUnspecified: case IrBinOpRemUnspecified:
case IrBinOpMergeErrorSets:
zig_unreachable(); zig_unreachable();
case IrBinOpBoolOr: case IrBinOpBoolOr:
return LLVMBuildOr(g->builder, op1_value, op2_value, ""); 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), return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
g->err_tag_type, wanted_type, target_val); g->err_tag_type, wanted_type, target_val);
} else if (actual_type->id == TypeTableEntryIdErrorUnion) { } 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)) { if (!type_has_bits(actual_type->data.error_union.payload_type)) {
return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base),
g->err_tag_type, wanted_type, target_val); 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_ptr = ir_llvm_value(g, instruction->value);
LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); 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) { if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on && g->errors_by_index.length > 1) {
LLVMValueRef err_val; LLVMValueRef err_val;
if (type_has_bits(payload_type)) { 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); assert(wanted_type->id == TypeTableEntryIdErrorUnion);
TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type; 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); 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; return err_val;
assert(instruction->tmp_ptr); assert(instruction->tmp_ptr);
@ -3509,6 +3519,11 @@ static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutable *executa
assert(wanted_type->id == TypeTableEntryIdErrorUnion); assert(wanted_type->id == TypeTableEntryIdErrorUnion);
TypeTableEntry *payload_type = wanted_type->data.error_union.payload_type; 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); 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: case TypeTableEntryIdErrorUnion:
{ {
TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; 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)) { 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; 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); 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 { } else {
LLVMValueRef err_tag_value; LLVMValueRef err_tag_value;
LLVMValueRef err_payload_value; LLVMValueRef err_payload_value;

View File

@ -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); return ir_gen_assign_op(irb, scope, node, IrBinOpBinXor);
case BinOpTypeAssignBitOr: case BinOpTypeAssignBitOr:
return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr); return ir_gen_assign_op(irb, scope, node, IrBinOpBinOr);
case BinOpTypeAssignBoolAnd: case BinOpTypeAssignMergeErrorSets:
return ir_gen_assign_op(irb, scope, node, IrBinOpBoolAnd); return ir_gen_assign_op(irb, scope, node, IrBinOpMergeErrorSets);
case BinOpTypeAssignBoolOr:
return ir_gen_assign_op(irb, scope, node, IrBinOpBoolOr);
case BinOpTypeBoolOr: case BinOpTypeBoolOr:
return ir_gen_bool_or(irb, scope, node); return ir_gen_bool_or(irb, scope, node);
case BinOpTypeBoolAnd: 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); return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat);
case BinOpTypeArrayMult: case BinOpTypeArrayMult:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult); return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult);
case BinOpTypeMergeErrorSets:
return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets);
case BinOpTypeUnwrapMaybe: case BinOpTypeUnwrapMaybe:
return ir_gen_maybe_ok_or(irb, scope, node); return ir_gen_maybe_ok_or(irb, scope, node);
case BinOpTypeErrorUnion: case BinOpTypeErrorUnion:
@ -5420,6 +5420,7 @@ static TypeTableEntry *get_error_set_union(CodeGen *g, ErrorTableEntry **errors,
} }
} }
assert(index == count); assert(index == count);
assert(count != 0);
buf_appendf(&err_set_type->name, "}"); 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; 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); Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error set", node);
TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet);
buf_init_from_buf(&err_set_type->name, type_name); buf_init_from_buf(&err_set_type->name, type_name);
err_set_type->is_copyable = true; 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.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) { for (uint32_t i = 0; i < err_count; i += 1) {
AstNode *symbol_node = node->data.err_set_decl.decls.at(i); 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; 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) { static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) {
assert(instruction_count >= 1); assert(instruction_count >= 1);
IrInstruction *prev_inst = instructions[0]; 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) { } else if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) {
err_set_type = prev_inst->value.type; err_set_type = prev_inst->value.type;
errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length); 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) { 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]; ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
errors[error_entry->value] = error_entry; 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; prev_inst = cur_inst;
continue; 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 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 // if cur_type is a superset of err_set_type, switch err_set_type to cur_type
bool prev_is_superset = true; 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]; ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i];
errors[error_entry->value] = nullptr; 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) { 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]; ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i];
errors[error_entry->value] = error_entry; 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) { if (err_set_type == ira->codegen->builtin_types.entry_global_error_set) {
continue; 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) { if (err_set_type == nullptr) {
err_set_type = cur_type; err_set_type = cur_type;
errors = allocate<ErrorTableEntry *>(ira->codegen->errors_by_index.length); 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(contained_set->id == TypeTableEntryIdErrorSet);
assert(container_set->id == TypeTableEntryIdErrorSet); assert(container_set->id == TypeTableEntryIdErrorSet);
zig_panic("TODO explicit error set cast");
if (container_set->data.error_set.infer_fn == nullptr && if (container_set->data.error_set.infer_fn == nullptr &&
container_set != ira->codegen->builtin_types.entry_global_error_set) 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; 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 bn;
bigint_init_unsigned(&bn, ira->codegen->errors_by_index.length); bigint_init_unsigned(&bn, ira->codegen->errors_by_index.length);
if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { 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 IrBinOpArrayCat:
case IrBinOpArrayMult: case IrBinOpArrayMult:
case IrBinOpRemUnspecified: case IrBinOpRemUnspecified:
case IrBinOpMergeErrorSets:
zig_unreachable(); zig_unreachable();
case IrBinOpBinOr: case IrBinOpBinOr:
assert(is_int); 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); 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) { static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
IrBinOp op_id = bin_op_instruction->op_id; IrBinOp op_id = bin_op_instruction->op_id;
switch (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); return ir_analyze_array_cat(ira, bin_op_instruction);
case IrBinOpArrayMult: case IrBinOpArrayMult:
return ir_analyze_array_mult(ira, bin_op_instruction); return ir_analyze_array_mult(ira, bin_op_instruction);
case IrBinOpMergeErrorSets:
return ir_analyze_merge_error_sets(ira, bin_op_instruction);
} }
zig_unreachable(); 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; err_set_type = err_entry->set_with_only_this_in_it;
} else { } 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); ErrorTableEntry *err_entry = find_err_table_entry(child_type, field_name);
if (err_entry == nullptr) { if (err_entry == nullptr) {
ir_add_error(ira, &field_ptr_instruction->base, 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); ir_build_test_err_from(&ira->new_irb, &instruction->base, value);
return ira->codegen->builtin_types.entry_bool; return ira->codegen->builtin_types.entry_bool;
} else if (type_entry->id == TypeTableEntryIdErrorSet) { } 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) { } else if (switch_type->id == TypeTableEntryIdErrorSet) {
FnTableEntry *infer_fn = switch_type->data.error_set.infer_fn; if (!resolve_inferred_error_set(ira, switch_type, target_value->source_node)) {
if (infer_fn != nullptr) { return ira->codegen->builtin_types.entry_invalid;
if (infer_fn->anal_state == FnAnalStateInvalid) {
return ira->codegen->builtin_types.entry_invalid;
} else if (infer_fn->anal_state == FnAnalStateReady) {
analyze_fn_body(ira->codegen, infer_fn);
if (switch_type->data.error_set.infer_fn != nullptr) {
assert(ira->codegen->errors.length != 0);
return ira->codegen->builtin_types.entry_invalid;
}
} else {
ir_add_error(ira, &instruction->base,
buf_sprintf("cannot switch on inferred error set '%s': function '%s' not fully analyzed yet",
buf_ptr(&switch_type->name), buf_ptr(&switch_type->data.error_set.infer_fn->symbol_name)));
return ira->codegen->builtin_types.entry_invalid;
}
} }
AstNode **field_prev_uses = allocate<AstNode *>(ira->codegen->errors_by_index.length); AstNode **field_prev_uses = allocate<AstNode *>(ira->codegen->errors_by_index.length);

View File

@ -130,6 +130,8 @@ static const char *ir_bin_op_id_str(IrBinOp op_id) {
return "++"; return "++";
case IrBinOpArrayMult: case IrBinOpArrayMult:
return "**"; return "**";
case IrBinOpMergeErrorSets:
return "||";
} }
zig_unreachable(); zig_unreachable();
} }

View File

@ -1088,12 +1088,13 @@ static BinOpType tok_to_mult_op(Token *token) {
case TokenIdSlash: return BinOpTypeDiv; case TokenIdSlash: return BinOpTypeDiv;
case TokenIdPercent: return BinOpTypeMod; case TokenIdPercent: return BinOpTypeMod;
case TokenIdBang: return BinOpTypeErrorUnion; case TokenIdBang: return BinOpTypeErrorUnion;
case TokenIdBarBar: return BinOpTypeMergeErrorSets;
default: return BinOpTypeInvalid; default: return BinOpTypeInvalid;
} }
} }
/* /*
MultiplyOperator = "!" | "*" | "/" | "%" | "**" | "*%" MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
*/ */
static BinOpType ast_parse_mult_op(ParseContext *pc, size_t *token_index, bool mandatory) { static BinOpType ast_parse_mult_op(ParseContext *pc, size_t *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index); Token *token = &pc->tokens->at(*token_index);

View File

@ -195,7 +195,8 @@ enum TokenizeState {
TokenizeStateSawMinusPercent, TokenizeStateSawMinusPercent,
TokenizeStateSawAmpersand, TokenizeStateSawAmpersand,
TokenizeStateSawCaret, TokenizeStateSawCaret,
TokenizeStateSawPipe, TokenizeStateSawBar,
TokenizeStateSawBarBar,
TokenizeStateLineComment, TokenizeStateLineComment,
TokenizeStateLineString, TokenizeStateLineString,
TokenizeStateLineStringEnd, TokenizeStateLineStringEnd,
@ -594,7 +595,7 @@ void tokenize(Buf *buf, Tokenization *out) {
break; break;
case '|': case '|':
begin_token(&t, TokenIdBinOr); begin_token(&t, TokenIdBinOr);
t.state = TokenizeStateSawPipe; t.state = TokenizeStateSawBar;
break; break;
case '=': case '=':
begin_token(&t, TokenIdEq); begin_token(&t, TokenIdEq);
@ -888,20 +889,37 @@ void tokenize(Buf *buf, Tokenization *out) {
continue; continue;
} }
break; break;
case TokenizeStateSawPipe: case TokenizeStateSawBar:
switch (c) { switch (c) {
case '=': case '=':
set_token_id(&t, t.cur_tok, TokenIdBitOrEq); set_token_id(&t, t.cur_tok, TokenIdBitOrEq);
end_token(&t); end_token(&t);
t.state = TokenizeStateStart; t.state = TokenizeStateStart;
break; 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: default:
t.pos -= 1; t.pos -= 1;
end_token(&t); end_token(&t);
t.state = TokenizeStateStart; t.state = TokenizeStateStart;
continue; continue;
} }
break;
case TokenizeStateSawSlash: case TokenizeStateSawSlash:
switch (c) { switch (c) {
case '/': case '/':
@ -1428,7 +1446,7 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateSawDash: case TokenizeStateSawDash:
case TokenizeStateSawAmpersand: case TokenizeStateSawAmpersand:
case TokenizeStateSawCaret: case TokenizeStateSawCaret:
case TokenizeStateSawPipe: case TokenizeStateSawBar:
case TokenizeStateSawEq: case TokenizeStateSawEq:
case TokenizeStateSawBang: case TokenizeStateSawBang:
case TokenizeStateSawLessThan: case TokenizeStateSawLessThan:
@ -1443,6 +1461,7 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateSawMinusPercent: case TokenizeStateSawMinusPercent:
case TokenizeStateLineString: case TokenizeStateLineString:
case TokenizeStateLineStringEnd: case TokenizeStateLineStringEnd:
case TokenizeStateSawBarBar:
end_token(&t); end_token(&t);
break; break;
case TokenizeStateSawDotDot: case TokenizeStateSawDotDot:
@ -1475,6 +1494,7 @@ const char * token_name(TokenId id) {
case TokenIdArrow: return "->"; case TokenIdArrow: return "->";
case TokenIdAtSign: return "@"; case TokenIdAtSign: return "@";
case TokenIdBang: return "!"; case TokenIdBang: return "!";
case TokenIdBarBar: return "||";
case TokenIdBinOr: return "|"; case TokenIdBinOr: return "|";
case TokenIdBinXor: return "^"; case TokenIdBinXor: return "^";
case TokenIdBitAndEq: return "&="; case TokenIdBitAndEq: return "&=";
@ -1577,6 +1597,7 @@ const char * token_name(TokenId id) {
case TokenIdTimesEq: return "*="; case TokenIdTimesEq: return "*=";
case TokenIdTimesPercent: return "*%"; case TokenIdTimesPercent: return "*%";
case TokenIdTimesPercentEq: return "*%="; case TokenIdTimesPercentEq: return "*%=";
case TokenIdBarBarEq: return "||=";
} }
return "(invalid token)"; return "(invalid token)";
} }

View File

@ -17,6 +17,8 @@ enum TokenId {
TokenIdArrow, TokenIdArrow,
TokenIdAtSign, TokenIdAtSign,
TokenIdBang, TokenIdBang,
TokenIdBarBar,
TokenIdBarBarEq,
TokenIdBinOr, TokenIdBinOr,
TokenIdBinXor, TokenIdBinXor,
TokenIdBitAndEq, TokenIdBitAndEq,

View File

@ -210,6 +210,7 @@ fn printSourceAtAddress(debug_info: &ElfStackTrace, out_stream: &io.OutStream, a
} }
} else |err| switch (err) { } else |err| switch (err) {
error.EndOfFile => {}, error.EndOfFile => {},
else => return err,
} }
} else |err| switch (err) { } else |err| switch (err) {
error.MissingDebugInfo, error.InvalidDebugInfo => { error.MissingDebugInfo, error.InvalidDebugInfo => {

View File

@ -102,12 +102,14 @@ pub const File = struct {
/// The OS-specific file descriptor or file handle. /// The OS-specific file descriptor or file handle.
handle: os.FileHandle, 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 /// `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 /// 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. /// 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. /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
/// Call close to clean up. /// 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) { if (is_posix) {
const flags = system.O_LARGEFILE|system.O_RDONLY; const flags = system.O_LARGEFILE|system.O_RDONLY;
const fd = try os.posixOpen(path, flags, 0, allocator); 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) { if (is_posix) {
try os.posixWrite(self.handle, bytes); try os.posixWrite(self.handle, bytes);
} else if (is_windows) { } else if (is_windows) {

View File

@ -5,12 +5,12 @@ const math = std.math;
const builtin = @import("builtin"); const builtin = @import("builtin");
pub const Allocator = struct { pub const Allocator = struct {
const Errors = error {OutOfMemory}; const Error = error {OutOfMemory};
/// Allocate byte_count bytes and return them in a slice, with the /// Allocate byte_count bytes and return them in a slice, with the
/// slice's pointer aligned at least to alignment bytes. /// slice's pointer aligned at least to alignment bytes.
/// The returned newly allocated memory is undefined. /// 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`: /// If `new_byte_count > old_mem.len`:
/// * `old_mem.len` is the same as what was returned from allocFn or reallocFn. /// * `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 /// * alignment <= alignment of old_mem.ptr
/// ///
/// The returned newly allocated memory is undefined. /// 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` /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
freeFn: fn (self: &Allocator, old_mem: []u8) void, 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, fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29,
n: usize) ![]align(alignment) T n: usize) ![]align(alignment) T
{ {
const byte_count = try math.mul(usize, @sizeOf(T), n); const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
const byte_slice = try self.allocFn(self, byte_count, alignment); const byte_slice = try self.allocFn(self, byte_count, alignment);
// This loop should get optimized out in ReleaseFast mode // This loop should get optimized out in ReleaseFast mode
for (byte_slice) |*byte| { for (byte_slice) |*byte| {
@ -63,7 +63,7 @@ pub const Allocator = struct {
} }
const old_byte_slice = ([]u8)(old_mem); 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); const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);
// This loop should get optimized out in ReleaseFast mode // This loop should get optimized out in ReleaseFast mode
for (byte_slice[old_byte_slice.len..]) |*byte| { for (byte_slice[old_byte_slice.len..]) |*byte| {

View File

@ -38,6 +38,9 @@ pub const windowsLoadDll = windows_util.windowsLoadDll;
pub const windowsUnloadDll = windows_util.windowsUnloadDll; pub const windowsUnloadDll = windows_util.windowsUnloadDll;
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; 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; pub const FileHandle = if (is_windows) windows.HANDLE else i32;
const debug = std.debug; 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. /// 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) { while (true) {
const write_ret = posix.write(fd, bytes.ptr, bytes.len); const write_ret = posix.write(fd, bytes.ptr, bytes.len);
const write_err = posix.getErrno(write_ret); const write_err = posix.getErrno(write_ret);
@ -197,15 +213,15 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
return switch (write_err) { return switch (write_err) {
posix.EINTR => continue, posix.EINTR => continue,
posix.EINVAL, posix.EFAULT => unreachable, posix.EINVAL, posix.EFAULT => unreachable,
posix.EAGAIN => error.WouldBlock, posix.EAGAIN => PosixWriteError.WouldBlock,
posix.EBADF => error.FileClosed, posix.EBADF => PosixWriteError.FileClosed,
posix.EDESTADDRREQ => error.DestinationAddressRequired, posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired,
posix.EDQUOT => error.DiskQuota, posix.EDQUOT => PosixWriteError.DiskQuota,
posix.EFBIG => error.FileTooBig, posix.EFBIG => PosixWriteError.FileTooBig,
posix.EIO => error.InputOutput, posix.EIO => PosixWriteError.InputOutput,
posix.ENOSPC => error.NoSpaceLeft, posix.ENOSPC => PosixWriteError.NoSpaceLeft,
posix.EPERM => error.AccessDenied, posix.EPERM => PosixWriteError.AccessDenied,
posix.EPIPE => error.BrokenPipe, posix.EPIPE => PosixWriteError.BrokenPipe,
else => unexpectedErrorPosix(write_err), 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 /// ::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 /// 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. /// 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. /// 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 /// Calls POSIX open, keeps trying if it gets interrupted, and translates
/// the return value into zig errors. /// 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 stack_buf: [max_noalloc_path_len]u8 = undefined;
var path0: []u8 = undefined; var path0: []u8 = undefined;
var need_free = false; 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.EFAULT => unreachable,
posix.EINVAL => unreachable, posix.EINVAL => unreachable,
posix.EACCES => error.AccessDenied, posix.EACCES => PosixOpenError.AccessDenied,
posix.EFBIG, posix.EOVERFLOW => error.FileTooBig, posix.EFBIG, posix.EOVERFLOW => PosixOpenError.FileTooBig,
posix.EISDIR => error.IsDir, posix.EISDIR => PosixOpenError.IsDir,
posix.ELOOP => error.SymLinkLoop, posix.ELOOP => PosixOpenError.SymLinkLoop,
posix.EMFILE => error.ProcessFdQuotaExceeded, posix.EMFILE => PosixOpenError.ProcessFdQuotaExceeded,
posix.ENAMETOOLONG => error.NameTooLong, posix.ENAMETOOLONG => PosixOpenError.NameTooLong,
posix.ENFILE => error.SystemFdQuotaExceeded, posix.ENFILE => PosixOpenError.SystemFdQuotaExceeded,
posix.ENODEV => error.NoDevice, posix.ENODEV => PosixOpenError.NoDevice,
posix.ENOENT => error.PathNotFound, posix.ENOENT => PosixOpenError.PathNotFound,
posix.ENOMEM => error.SystemResources, posix.ENOMEM => PosixOpenError.SystemResources,
posix.ENOSPC => error.NoSpaceLeft, posix.ENOSPC => PosixOpenError.NoSpaceLeft,
posix.ENOTDIR => error.NotDir, posix.ENOTDIR => PosixOpenError.NotDir,
posix.EPERM => error.AccessDenied, posix.EPERM => PosixOpenError.AccessDenied,
posix.EEXIST => error.PathAlreadyExists, posix.EEXIST => PosixOpenError.PathAlreadyExists,
else => unexpectedErrorPosix(err), else => unexpectedErrorPosix(err),
}; };
} }

View File

@ -26,16 +26,25 @@ pub fn windowsClose(handle: windows.HANDLE) void {
assert(windows.CloseHandle(handle) != 0); 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) { if (windows.WriteFile(handle, @ptrCast(&const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) {
const err = windows.GetLastError(); const err = windows.GetLastError();
return switch (err) { return switch (err) {
windows.ERROR.INVALID_USER_BUFFER => error.SystemResources, windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,
windows.ERROR.NOT_ENOUGH_MEMORY => error.SystemResources, windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,
windows.ERROR.OPERATION_ABORTED => error.OperationAborted, windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,
windows.ERROR.NOT_ENOUGH_QUOTA => error.SystemResources, windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,
windows.ERROR.IO_PENDING => error.IoPending, windows.ERROR.IO_PENDING => WriteError.IoPending,
windows.ERROR.BROKEN_PIPE => error.BrokenPipe, windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,
else => os.unexpectedErrorWindows(err), 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; 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 /// `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 /// 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. /// 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. /// 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, 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 stack_buf: [os.max_noalloc_path_len]u8 = undefined;
var path0: []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) { if (result == windows.INVALID_HANDLE_VALUE) {
const err = windows.GetLastError(); const err = windows.GetLastError();
return switch (err) { return switch (err) {
windows.ERROR.SHARING_VIOLATION => error.SharingViolation, windows.ERROR.SHARING_VIOLATION => OpenError.SharingViolation,
windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => error.PathAlreadyExists, windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => OpenError.PathAlreadyExists,
windows.ERROR.FILE_NOT_FOUND => error.FileNotFound, windows.ERROR.FILE_NOT_FOUND => OpenError.FileNotFound,
windows.ERROR.ACCESS_DENIED => error.AccessDenied, windows.ERROR.ACCESS_DENIED => OpenError.AccessDenied,
windows.ERROR.PIPE_BUSY => error.PipeBusy, windows.ERROR.PIPE_BUSY => OpenError.PipeBusy,
else => os.unexpectedErrorWindows(err), else => os.unexpectedErrorWindows(err),
}; };
} }