| author | |
| committer | |
| log | 8fd0fddce5d44344dd7914ae86a4d976b99f9cc3 |
| tree | 69d6a1b6e32e8c8a622eb149c0e60f33f426996d |
| parent | 0594487a2e98e18a18c0c6bdb2532c5e36fc6ea7 |
* In-progress os.ChildProcess.spawn implementation. See #204
* Add explicit cast from integer to error. Closes #294
* fix casting from error to integer
* fix compiler crash when initializing variable to undefined
with no type11 files changed, 742 insertions(+), 51 deletions(-)
src/all_types.hpp+15-1| ... | @@ -520,7 +520,6 @@ struct AstNodeUnwrapErrorExpr { | ... | @@ -520,7 +520,6 @@ struct AstNodeUnwrapErrorExpr { |
| 520 | enum CastOp { | 520 | enum CastOp { |
| 521 | CastOpNoCast, // signifies the function call expression is not a cast | 521 | CastOpNoCast, // signifies the function call expression is not a cast |
| 522 | CastOpNoop, // fn call expr is a cast, but does nothing | 522 | CastOpNoop, // fn call expr is a cast, but does nothing |
| 523 | CastOpErrToInt, | ||
| 524 | CastOpIntToFloat, | 523 | CastOpIntToFloat, |
| 525 | CastOpFloatToInt, | 524 | CastOpFloatToInt, |
| 526 | CastOpBoolToInt, | 525 | CastOpBoolToInt, |
| ... | @@ -1223,6 +1222,7 @@ enum PanicMsgId { | ... | @@ -1223,6 +1222,7 @@ enum PanicMsgId { |
| 1223 | PanicMsgIdSliceWidenRemainder, | 1222 | PanicMsgIdSliceWidenRemainder, |
| 1224 | PanicMsgIdUnwrapMaybeFail, | 1223 | PanicMsgIdUnwrapMaybeFail, |
| 1225 | PanicMsgIdUnwrapErrFail, | 1224 | PanicMsgIdUnwrapErrFail, |
| 1225 | PanicMsgIdInvalidErrorCode, | ||
| 1226 | 1226 | ||
| 1227 | PanicMsgIdCount, | 1227 | PanicMsgIdCount, |
| 1228 | }; | 1228 | }; |
| ... | @@ -1728,6 +1728,8 @@ enum IrInstructionId { | ... | @@ -1728,6 +1728,8 @@ enum IrInstructionId { |
| 1728 | IrInstructionIdIntToPtr, | 1728 | IrInstructionIdIntToPtr, |
| 1729 | IrInstructionIdPtrToInt, | 1729 | IrInstructionIdPtrToInt, |
| 1730 | IrInstructionIdIntToEnum, | 1730 | IrInstructionIdIntToEnum, |
| 1731 | IrInstructionIdIntToErr, | ||
| 1732 | IrInstructionIdErrToInt, | ||
| 1731 | IrInstructionIdCheckSwitchProngs, | 1733 | IrInstructionIdCheckSwitchProngs, |
| 1732 | IrInstructionIdTestType, | 1734 | IrInstructionIdTestType, |
| 1733 | IrInstructionIdTypeName, | 1735 | IrInstructionIdTypeName, |
| ... | @@ -2404,6 +2406,18 @@ struct IrInstructionIntToEnum { | ... | @@ -2404,6 +2406,18 @@ struct IrInstructionIntToEnum { |
| 2404 | IrInstruction *target; | 2406 | IrInstruction *target; |
| 2405 | }; | 2407 | }; |
| 2406 | 2408 | ||
| 2409 | struct IrInstructionIntToErr { | ||
| 2410 | IrInstruction base; | ||
| 2411 | |||
| 2412 | IrInstruction *target; | ||
| 2413 | }; | ||
| 2414 | |||
| 2415 | struct IrInstructionErrToInt { | ||
| 2416 | IrInstruction base; | ||
| 2417 | |||
| 2418 | IrInstruction *target; | ||
| 2419 | }; | ||
| 2420 | |||
| 2407 | struct IrInstructionCheckSwitchProngsRange { | 2421 | struct IrInstructionCheckSwitchProngsRange { |
| 2408 | IrInstruction *start; | 2422 | IrInstruction *start; |
| 2409 | IrInstruction *end; | 2423 | IrInstruction *end; |
src/codegen.cpp+66-8| ... | @@ -570,6 +570,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { | ... | @@ -570,6 +570,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 570 | return buf_create_from_str("attempt to unwrap error"); | 570 | return buf_create_from_str("attempt to unwrap error"); |
| 571 | case PanicMsgIdUnreachable: | 571 | case PanicMsgIdUnreachable: |
| 572 | return buf_create_from_str("reached unreachable code"); | 572 | return buf_create_from_str("reached unreachable code"); |
| 573 | case PanicMsgIdInvalidErrorCode: | ||
| 574 | return buf_create_from_str("invalid error code"); | ||
| 573 | } | 575 | } |
| 574 | zig_unreachable(); | 576 | zig_unreachable(); |
| 575 | } | 577 | } |
| ... | @@ -1227,14 +1229,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, | ... | @@ -1227,14 +1229,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable, |
| 1227 | zig_unreachable(); | 1229 | zig_unreachable(); |
| 1228 | case CastOpNoop: | 1230 | case CastOpNoop: |
| 1229 | return expr_val; | 1231 | return expr_val; |
| 1230 | case CastOpErrToInt: | ||
| 1231 | assert(actual_type->id == TypeTableEntryIdErrorUnion); | ||
| 1232 | if (!type_has_bits(actual_type->data.error.child_type)) { | ||
| 1233 | return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base), | ||
| 1234 | g->err_tag_type, wanted_type, expr_val); | ||
| 1235 | } else { | ||
| 1236 | zig_panic("TODO"); | ||
| 1237 | } | ||
| 1238 | case CastOpResizeSlice: | 1232 | case CastOpResizeSlice: |
| 1239 | { | 1233 | { |
| 1240 | assert(cast_instruction->tmp_ptr); | 1234 | assert(cast_instruction->tmp_ptr); |
| ... | @@ -1402,6 +1396,66 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, | ... | @@ -1402,6 +1396,66 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable, |
| 1402 | instruction->target->value.type, wanted_int_type, target_val); | 1396 | instruction->target->value.type, wanted_int_type, target_val); |
| 1403 | } | 1397 | } |
| 1404 | 1398 | ||
| 1399 | static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) { | ||
| 1400 | TypeTableEntry *wanted_type = instruction->base.value.type; | ||
| 1401 | assert(wanted_type->id == TypeTableEntryIdPureError); | ||
| 1402 | |||
| 1403 | TypeTableEntry *actual_type = instruction->target->value.type; | ||
| 1404 | assert(actual_type->id == TypeTableEntryIdInt); | ||
| 1405 | assert(!actual_type->data.integral.is_signed); | ||
| 1406 | |||
| 1407 | LLVMValueRef target_val = ir_llvm_value(g, instruction->target); | ||
| 1408 | |||
| 1409 | if (ir_want_debug_safety(g, &instruction->base)) { | ||
| 1410 | LLVMValueRef zero = LLVMConstNull(actual_type->type_ref); | ||
| 1411 | LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, ""); | ||
| 1412 | LLVMValueRef ok_bit; | ||
| 1413 | uint64_t biggest_possible_err_val = max_unsigned_val(actual_type); | ||
| 1414 | if (biggest_possible_err_val < g->error_decls.length) { | ||
| 1415 | ok_bit = neq_zero_bit; | ||
| 1416 | } else { | ||
| 1417 | LLVMValueRef error_value_count = LLVMConstInt(actual_type->type_ref, g->error_decls.length, false); | ||
| 1418 | LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, ""); | ||
| 1419 | ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, ""); | ||
| 1420 | } | ||
| 1421 | |||
| 1422 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk"); | ||
| 1423 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail"); | ||
| 1424 | |||
| 1425 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | ||
| 1426 | |||
| 1427 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | ||
| 1428 | gen_debug_safety_crash(g, PanicMsgIdInvalidErrorCode); | ||
| 1429 | |||
| 1430 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | ||
| 1431 | } | ||
| 1432 | |||
| 1433 | return gen_widen_or_shorten(g, false, actual_type, g->err_tag_type, target_val); | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | static LLVMValueRef ir_render_err_to_int(CodeGen *g, IrExecutable *executable, IrInstructionErrToInt *instruction) { | ||
| 1437 | TypeTableEntry *wanted_type = instruction->base.value.type; | ||
| 1438 | assert(wanted_type->id == TypeTableEntryIdInt); | ||
| 1439 | assert(!wanted_type->data.integral.is_signed); | ||
| 1440 | |||
| 1441 | TypeTableEntry *actual_type = instruction->target->value.type; | ||
| 1442 | LLVMValueRef target_val = ir_llvm_value(g, instruction->target); | ||
| 1443 | |||
| 1444 | if (actual_type->id == TypeTableEntryIdPureError) { | ||
| 1445 | return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base), | ||
| 1446 | g->err_tag_type, wanted_type, target_val); | ||
| 1447 | } else if (actual_type->id == TypeTableEntryIdErrorUnion) { | ||
| 1448 | if (!type_has_bits(actual_type->data.error.child_type)) { | ||
| 1449 | return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base), | ||
| 1450 | g->err_tag_type, wanted_type, target_val); | ||
| 1451 | } else { | ||
| 1452 | zig_panic("TODO"); | ||
| 1453 | } | ||
| 1454 | } else { | ||
| 1455 | zig_unreachable(); | ||
| 1456 | } | ||
| 1457 | } | ||
| 1458 | |||
| 1405 | static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable, | 1459 | static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable, |
| 1406 | IrInstructionUnreachable *unreachable_instruction) | 1460 | IrInstructionUnreachable *unreachable_instruction) |
| 1407 | { | 1461 | { |
| ... | @@ -2786,6 +2840,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, | ... | @@ -2786,6 +2840,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 2786 | return ir_render_int_to_ptr(g, executable, (IrInstructionIntToPtr *)instruction); | 2840 | return ir_render_int_to_ptr(g, executable, (IrInstructionIntToPtr *)instruction); |
| 2787 | case IrInstructionIdIntToEnum: | 2841 | case IrInstructionIdIntToEnum: |
| 2788 | return ir_render_int_to_enum(g, executable, (IrInstructionIntToEnum *)instruction); | 2842 | return ir_render_int_to_enum(g, executable, (IrInstructionIntToEnum *)instruction); |
| 2843 | case IrInstructionIdIntToErr: | ||
| 2844 | return ir_render_int_to_err(g, executable, (IrInstructionIntToErr *)instruction); | ||
| 2845 | case IrInstructionIdErrToInt: | ||
| 2846 | return ir_render_err_to_int(g, executable, (IrInstructionErrToInt *)instruction); | ||
| 2789 | case IrInstructionIdContainerInitList: | 2847 | case IrInstructionIdContainerInitList: |
| 2790 | return ir_render_container_init_list(g, executable, (IrInstructionContainerInitList *)instruction); | 2848 | return ir_render_container_init_list(g, executable, (IrInstructionContainerInitList *)instruction); |
| 2791 | case IrInstructionIdPanic: | 2849 | case IrInstructionIdPanic: |
src/ir.cpp+148-28| ... | @@ -500,6 +500,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToEnum *) { | ... | @@ -500,6 +500,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToEnum *) { |
| 500 | return IrInstructionIdIntToEnum; | 500 | return IrInstructionIdIntToEnum; |
| 501 | } | 501 | } |
| 502 | 502 | ||
| 503 | static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToErr *) { | ||
| 504 | return IrInstructionIdIntToErr; | ||
| 505 | } | ||
| 506 | |||
| 507 | static constexpr IrInstructionId ir_instruction_id(IrInstructionErrToInt *) { | ||
| 508 | return IrInstructionIdErrToInt; | ||
| 509 | } | ||
| 510 | |||
| 503 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProngs *) { | 511 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProngs *) { |
| 504 | return IrInstructionIdCheckSwitchProngs; | 512 | return IrInstructionIdCheckSwitchProngs; |
| 505 | } | 513 | } |
| ... | @@ -2002,6 +2010,30 @@ static IrInstruction *ir_build_int_to_enum(IrBuilder *irb, Scope *scope, AstNode | ... | @@ -2002,6 +2010,30 @@ static IrInstruction *ir_build_int_to_enum(IrBuilder *irb, Scope *scope, AstNode |
| 2002 | return &instruction->base; | 2010 | return &instruction->base; |
| 2003 | } | 2011 | } |
| 2004 | 2012 | ||
| 2013 | static IrInstruction *ir_build_int_to_err(IrBuilder *irb, Scope *scope, AstNode *source_node, | ||
| 2014 | IrInstruction *target) | ||
| 2015 | { | ||
| 2016 | IrInstructionIntToErr *instruction = ir_build_instruction<IrInstructionIntToErr>( | ||
| 2017 | irb, scope, source_node); | ||
| 2018 | instruction->target = target; | ||
| 2019 | |||
| 2020 | ir_ref_instruction(target, irb->current_basic_block); | ||
| 2021 | |||
| 2022 | return &instruction->base; | ||
| 2023 | } | ||
| 2024 | |||
| 2025 | static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node, | ||
| 2026 | IrInstruction *target) | ||
| 2027 | { | ||
| 2028 | IrInstructionErrToInt *instruction = ir_build_instruction<IrInstructionErrToInt>( | ||
| 2029 | irb, scope, source_node); | ||
| 2030 | instruction->target = target; | ||
| 2031 | |||
| 2032 | ir_ref_instruction(target, irb->current_basic_block); | ||
| 2033 | |||
| 2034 | return &instruction->base; | ||
| 2035 | } | ||
| 2036 | |||
| 2005 | static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node, | 2037 | static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 2006 | IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count) | 2038 | IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count) |
| 2007 | { | 2039 | { |
| ... | @@ -2704,6 +2736,20 @@ static IrInstruction *ir_instruction_inttoenum_get_dep(IrInstructionIntToEnum *i | ... | @@ -2704,6 +2736,20 @@ static IrInstruction *ir_instruction_inttoenum_get_dep(IrInstructionIntToEnum *i |
| 2704 | } | 2736 | } |
| 2705 | } | 2737 | } |
| 2706 | 2738 | ||
| 2739 | static IrInstruction *ir_instruction_inttoerr_get_dep(IrInstructionIntToErr *instruction, size_t index) { | ||
| 2740 | switch (index) { | ||
| 2741 | case 0: return instruction->target; | ||
| 2742 | default: return nullptr; | ||
| 2743 | } | ||
| 2744 | } | ||
| 2745 | |||
| 2746 | static IrInstruction *ir_instruction_errtoint_get_dep(IrInstructionErrToInt *instruction, size_t index) { | ||
| 2747 | switch (index) { | ||
| 2748 | case 0: return instruction->target; | ||
| 2749 | default: return nullptr; | ||
| 2750 | } | ||
| 2751 | } | ||
| 2752 | |||
| 2707 | static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionCheckSwitchProngs *instruction, | 2753 | static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionCheckSwitchProngs *instruction, |
| 2708 | size_t index) | 2754 | size_t index) |
| 2709 | { | 2755 | { |
| ... | @@ -2938,6 +2984,10 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t | ... | @@ -2938,6 +2984,10 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 2938 | return ir_instruction_ptrtoint_get_dep((IrInstructionPtrToInt *) instruction, index); | 2984 | return ir_instruction_ptrtoint_get_dep((IrInstructionPtrToInt *) instruction, index); |
| 2939 | case IrInstructionIdIntToEnum: | 2985 | case IrInstructionIdIntToEnum: |
| 2940 | return ir_instruction_inttoenum_get_dep((IrInstructionIntToEnum *) instruction, index); | 2986 | return ir_instruction_inttoenum_get_dep((IrInstructionIntToEnum *) instruction, index); |
| 2987 | case IrInstructionIdIntToErr: | ||
| 2988 | return ir_instruction_inttoerr_get_dep((IrInstructionIntToErr *) instruction, index); | ||
| 2989 | case IrInstructionIdErrToInt: | ||
| 2990 | return ir_instruction_errtoint_get_dep((IrInstructionErrToInt *) instruction, index); | ||
| 2941 | case IrInstructionIdCheckSwitchProngs: | 2991 | case IrInstructionIdCheckSwitchProngs: |
| 2942 | return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index); | 2992 | return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index); |
| 2943 | case IrInstructionIdTestType: | 2993 | case IrInstructionIdTestType: |
| ... | @@ -6001,20 +6051,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, | ... | @@ -6001,20 +6051,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op, |
| 6001 | case CastOpBytesToSlice: | 6051 | case CastOpBytesToSlice: |
| 6002 | // can't do it | 6052 | // can't do it |
| 6003 | break; | 6053 | break; |
| 6004 | case CastOpErrToInt: | ||
| 6005 | { | ||
| 6006 | uint64_t value; | ||
| 6007 | if (other_type->id == TypeTableEntryIdErrorUnion) { | ||
| 6008 | value = other_val->data.x_err_union.err ? other_val->data.x_err_union.err->value : 0; | ||
| 6009 | } else if (other_type->id == TypeTableEntryIdPureError) { | ||
| 6010 | value = other_val->data.x_pure_err->value; | ||
| 6011 | } else { | ||
| 6012 | zig_unreachable(); | ||
| 6013 | } | ||
| 6014 | bignum_init_unsigned(&const_val->data.x_bignum, value); | ||
| 6015 | const_val->special = ConstValSpecialStatic; | ||
| 6016 | break; | ||
| 6017 | } | ||
| 6018 | case CastOpIntToFloat: | 6054 | case CastOpIntToFloat: |
| 6019 | bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum); | 6055 | bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum); |
| 6020 | const_val->special = ConstValSpecialStatic; | 6056 | const_val->special = ConstValSpecialStatic; |
| ... | @@ -6707,6 +6743,87 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction | ... | @@ -6707,6 +6743,87 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction |
| 6707 | return result; | 6743 | return result; |
| 6708 | } | 6744 | } |
| 6709 | 6745 | ||
| 6746 | static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target) { | ||
| 6747 | assert(target->value.type->id == TypeTableEntryIdInt); | ||
| 6748 | assert(!target->value.type->data.integral.is_signed); | ||
| 6749 | |||
| 6750 | if (instr_is_comptime(target)) { | ||
| 6751 | ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); | ||
| 6752 | if (!val) | ||
| 6753 | return ira->codegen->invalid_instruction; | ||
| 6754 | |||
| 6755 | IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, | ||
| 6756 | source_instr->source_node, ira->codegen->builtin_types.entry_pure_error); | ||
| 6757 | |||
| 6758 | uint64_t index = val->data.x_bignum.data.x_uint; | ||
| 6759 | if (index == 0 || index >= ira->codegen->error_decls.length) { | ||
| 6760 | ir_add_error(ira, source_instr, | ||
| 6761 | buf_sprintf("integer value %" PRIu64 " represents no error", index)); | ||
| 6762 | return ira->codegen->invalid_instruction; | ||
| 6763 | } | ||
| 6764 | |||
| 6765 | AstNode *error_decl_node = ira->codegen->error_decls.at(index); | ||
| 6766 | result->value.data.x_pure_err = error_decl_node->data.error_value_decl.err; | ||
| 6767 | return result; | ||
| 6768 | } | ||
| 6769 | |||
| 6770 | IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target); | ||
| 6771 | result->value.type = ira->codegen->builtin_types.entry_pure_error; | ||
| 6772 | return result; | ||
| 6773 | } | ||
| 6774 | |||
| 6775 | static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target, | ||
| 6776 | TypeTableEntry *wanted_type) | ||
| 6777 | { | ||
| 6778 | assert(wanted_type->id == TypeTableEntryIdInt); | ||
| 6779 | |||
| 6780 | TypeTableEntry *err_type = target->value.type; | ||
| 6781 | |||
| 6782 | if (instr_is_comptime(target)) { | ||
| 6783 | ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); | ||
| 6784 | if (!val) | ||
| 6785 | return ira->codegen->invalid_instruction; | ||
| 6786 | |||
| 6787 | IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, | ||
| 6788 | source_instr->source_node, wanted_type); | ||
| 6789 | |||
| 6790 | ErrorTableEntry *err; | ||
| 6791 | if (err_type->id == TypeTableEntryIdErrorUnion) { | ||
| 6792 | err = val->data.x_err_union.err; | ||
| 6793 | } else if (err_type->id == TypeTableEntryIdPureError) { | ||
| 6794 | err = val->data.x_pure_err; | ||
| 6795 | } else { | ||
| 6796 | zig_unreachable(); | ||
| 6797 | } | ||
| 6798 | result->value.type = wanted_type; | ||
| 6799 | uint64_t err_value = err ? err->value : 0; | ||
| 6800 | bignum_init_unsigned(&result->value.data.x_bignum, err_value); | ||
| 6801 | |||
| 6802 | if (!bignum_fits_in_bits(&result->value.data.x_bignum, | ||
| 6803 | wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) | ||
| 6804 | { | ||
| 6805 | ir_add_error_node(ira, source_instr->source_node, | ||
| 6806 | buf_sprintf("error code '%s' does not fit in '%s'", | ||
| 6807 | buf_ptr(&err->name), buf_ptr(&wanted_type->name))); | ||
| 6808 | return ira->codegen->invalid_instruction; | ||
| 6809 | } | ||
| 6810 | |||
| 6811 | return result; | ||
| 6812 | } | ||
| 6813 | |||
| 6814 | BigNum bn; | ||
| 6815 | bignum_init_unsigned(&bn, ira->codegen->error_decls.length); | ||
| 6816 | if (!bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { | ||
| 6817 | ir_add_error_node(ira, source_instr->source_node, | ||
| 6818 | buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); | ||
| 6819 | return ira->codegen->invalid_instruction; | ||
| 6820 | } | ||
| 6821 | |||
| 6822 | IrInstruction *result = ir_build_err_to_int(&ira->new_irb, source_instr->scope, source_instr->source_node, target); | ||
| 6823 | result->value.type = wanted_type; | ||
| 6824 | return result; | ||
| 6825 | } | ||
| 6826 | |||
| 6710 | static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, | 6827 | static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, |
| 6711 | TypeTableEntry *wanted_type, IrInstruction *value) | 6828 | TypeTableEntry *wanted_type, IrInstruction *value) |
| 6712 | { | 6829 | { |
| ... | @@ -6781,7 +6898,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst | ... | @@ -6781,7 +6898,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 6781 | return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpFloatToInt, false); | 6898 | return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpFloatToInt, false); |
| 6782 | } | 6899 | } |
| 6783 | 6900 | ||
| 6784 | // explicit cast from array to slice | 6901 | // explicit cast from [N]T to []const T |
| 6785 | if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) { | 6902 | if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) { |
| 6786 | TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; | 6903 | TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry; |
| 6787 | assert(ptr_type->id == TypeTableEntryIdPointer); | 6904 | assert(ptr_type->id == TypeTableEntryIdPointer); |
| ... | @@ -6909,17 +7026,14 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst | ... | @@ -6909,17 +7026,14 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 6909 | if ((actual_type_is_void_err || actual_type_is_pure_err) && | 7026 | if ((actual_type_is_void_err || actual_type_is_pure_err) && |
| 6910 | wanted_type->id == TypeTableEntryIdInt) | 7027 | wanted_type->id == TypeTableEntryIdInt) |
| 6911 | { | 7028 | { |
| 6912 | BigNum bn; | 7029 | return ir_analyze_err_to_int(ira, source_instr, value, wanted_type); |
| 6913 | bignum_init_unsigned(&bn, ira->codegen->error_decls.length); | 7030 | } |
| 6914 | if (bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count, | 7031 | |
| 6915 | wanted_type->data.integral.is_signed)) | 7032 | // explicit cast from integer to pure error |
| 6916 | { | 7033 | if (wanted_type->id == TypeTableEntryIdPureError && actual_type->id == TypeTableEntryIdInt && |
| 6917 | return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpErrToInt, false); | 7034 | !actual_type->data.integral.is_signed) |
| 6918 | } else { | 7035 | { |
| 6919 | ir_add_error_node(ira, source_instr->source_node, | 7036 | return ir_analyze_int_to_err(ira, source_instr, value); |
| 6920 | buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); | ||
| 6921 | return ira->codegen->invalid_instruction; | ||
| 6922 | } | ||
| 6923 | } | 7037 | } |
| 6924 | 7038 | ||
| 6925 | // explicit cast from integer to enum type with no payload | 7039 | // explicit cast from integer to enum type with no payload |
| ... | @@ -7843,7 +7957,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc | ... | @@ -7843,7 +7957,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc |
| 7843 | result_type = ira->codegen->builtin_types.entry_invalid; | 7957 | result_type = ira->codegen->builtin_types.entry_invalid; |
| 7844 | } | 7958 | } |
| 7845 | 7959 | ||
| 7846 | bool is_comptime_var = ir_get_var_is_comptime(var); | 7960 | bool is_comptime_var = ir_get_var_is_comptime(var); |
| 7847 | 7961 | ||
| 7848 | switch (result_type->id) { | 7962 | switch (result_type->id) { |
| 7849 | case TypeTableEntryIdTypeDecl: | 7963 | case TypeTableEntryIdTypeDecl: |
| ... | @@ -7852,6 +7966,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc | ... | @@ -7852,6 +7966,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc |
| 7852 | break; // handled above | 7966 | break; // handled above |
| 7853 | case TypeTableEntryIdNumLitFloat: | 7967 | case TypeTableEntryIdNumLitFloat: |
| 7854 | case TypeTableEntryIdNumLitInt: | 7968 | case TypeTableEntryIdNumLitInt: |
| 7969 | case TypeTableEntryIdUndefLit: | ||
| 7855 | if (is_export || is_extern || (!var->src_is_const && !is_comptime_var)) { | 7970 | if (is_export || is_extern || (!var->src_is_const && !is_comptime_var)) { |
| 7856 | ir_add_error_node(ira, source_node, buf_sprintf("unable to infer variable type")); | 7971 | ir_add_error_node(ira, source_node, buf_sprintf("unable to infer variable type")); |
| 7857 | result_type = ira->codegen->builtin_types.entry_invalid; | 7972 | result_type = ira->codegen->builtin_types.entry_invalid; |
| ... | @@ -7873,7 +7988,6 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc | ... | @@ -7873,7 +7988,6 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc |
| 7873 | result_type = ira->codegen->builtin_types.entry_invalid; | 7988 | result_type = ira->codegen->builtin_types.entry_invalid; |
| 7874 | } | 7989 | } |
| 7875 | break; | 7990 | break; |
| 7876 | case TypeTableEntryIdUndefLit: | ||
| 7877 | case TypeTableEntryIdVoid: | 7991 | case TypeTableEntryIdVoid: |
| 7878 | case TypeTableEntryIdBool: | 7992 | case TypeTableEntryIdBool: |
| 7879 | case TypeTableEntryIdInt: | 7993 | case TypeTableEntryIdInt: |
| ... | @@ -10765,6 +10879,8 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira | ... | @@ -10765,6 +10879,8 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira |
| 10765 | TypeTableEntry *this_field_type = field->type_entry; | 10879 | TypeTableEntry *this_field_type = field->type_entry; |
| 10766 | 10880 | ||
| 10767 | IrInstruction *init_value = instruction->items[0]->other; | 10881 | IrInstruction *init_value = instruction->items[0]->other; |
| 10882 | if (type_is_invalid(init_value->value.type)) | ||
| 10883 | return ira->codegen->builtin_types.entry_invalid; | ||
| 10768 | 10884 | ||
| 10769 | IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, this_field_type); | 10885 | IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, this_field_type); |
| 10770 | if (casted_init_value == ira->codegen->invalid_instruction) | 10886 | if (casted_init_value == ira->codegen->invalid_instruction) |
| ... | @@ -12311,6 +12427,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi | ... | @@ -12311,6 +12427,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 12311 | case IrInstructionIdIntToPtr: | 12427 | case IrInstructionIdIntToPtr: |
| 12312 | case IrInstructionIdPtrToInt: | 12428 | case IrInstructionIdPtrToInt: |
| 12313 | case IrInstructionIdIntToEnum: | 12429 | case IrInstructionIdIntToEnum: |
| 12430 | case IrInstructionIdIntToErr: | ||
| 12431 | case IrInstructionIdErrToInt: | ||
| 12314 | case IrInstructionIdStructInit: | 12432 | case IrInstructionIdStructInit: |
| 12315 | case IrInstructionIdStructFieldPtr: | 12433 | case IrInstructionIdStructFieldPtr: |
| 12316 | case IrInstructionIdEnumFieldPtr: | 12434 | case IrInstructionIdEnumFieldPtr: |
| ... | @@ -12650,6 +12768,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { | ... | @@ -12650,6 +12768,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 12650 | case IrInstructionIdPtrToInt: | 12768 | case IrInstructionIdPtrToInt: |
| 12651 | case IrInstructionIdIntToPtr: | 12769 | case IrInstructionIdIntToPtr: |
| 12652 | case IrInstructionIdIntToEnum: | 12770 | case IrInstructionIdIntToEnum: |
| 12771 | case IrInstructionIdIntToErr: | ||
| 12772 | case IrInstructionIdErrToInt: | ||
| 12653 | case IrInstructionIdTestType: | 12773 | case IrInstructionIdTestType: |
| 12654 | case IrInstructionIdTypeName: | 12774 | case IrInstructionIdTypeName: |
| 12655 | case IrInstructionIdCanImplicitCast: | 12775 | case IrInstructionIdCanImplicitCast: |
src/ir_print.cpp+16| ... | @@ -799,6 +799,16 @@ static void ir_print_int_to_enum(IrPrint *irp, IrInstructionIntToEnum *instructi | ... | @@ -799,6 +799,16 @@ static void ir_print_int_to_enum(IrPrint *irp, IrInstructionIntToEnum *instructi |
| 799 | fprintf(irp->f, ")"); | 799 | fprintf(irp->f, ")"); |
| 800 | } | 800 | } |
| 801 | 801 | ||
| 802 | static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) { | ||
| 803 | fprintf(irp->f, "inttoerr "); | ||
| 804 | ir_print_other_instruction(irp, instruction->target); | ||
| 805 | } | ||
| 806 | |||
| 807 | static void ir_print_err_to_int(IrPrint *irp, IrInstructionErrToInt *instruction) { | ||
| 808 | fprintf(irp->f, "errtoint "); | ||
| 809 | ir_print_other_instruction(irp, instruction->target); | ||
| 810 | } | ||
| 811 | |||
| 802 | static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchProngs *instruction) { | 812 | static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchProngs *instruction) { |
| 803 | fprintf(irp->f, "@checkSwitchProngs("); | 813 | fprintf(irp->f, "@checkSwitchProngs("); |
| 804 | ir_print_other_instruction(irp, instruction->target_value); | 814 | ir_print_other_instruction(irp, instruction->target_value); |
| ... | @@ -1117,6 +1127,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | ... | @@ -1117,6 +1127,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1117 | case IrInstructionIdIntToEnum: | 1127 | case IrInstructionIdIntToEnum: |
| 1118 | ir_print_int_to_enum(irp, (IrInstructionIntToEnum *)instruction); | 1128 | ir_print_int_to_enum(irp, (IrInstructionIntToEnum *)instruction); |
| 1119 | break; | 1129 | break; |
| 1130 | case IrInstructionIdIntToErr: | ||
| 1131 | ir_print_int_to_err(irp, (IrInstructionIntToErr *)instruction); | ||
| 1132 | break; | ||
| 1133 | case IrInstructionIdErrToInt: | ||
| 1134 | ir_print_err_to_int(irp, (IrInstructionErrToInt *)instruction); | ||
| 1135 | break; | ||
| 1120 | case IrInstructionIdCheckSwitchProngs: | 1136 | case IrInstructionIdCheckSwitchProngs: |
| 1121 | ir_print_check_switch_prongs(irp, (IrInstructionCheckSwitchProngs *)instruction); | 1137 | ir_print_check_switch_prongs(irp, (IrInstructionCheckSwitchProngs *)instruction); |
| 1122 | break; | 1138 | break; |
std/build.zig+35-3| ... | @@ -3,8 +3,12 @@ const mem = @import("mem.zig"); | ... | @@ -3,8 +3,12 @@ const mem = @import("mem.zig"); |
| 3 | const debug = @import("debug.zig"); | 3 | const debug = @import("debug.zig"); |
| 4 | const List = @import("list.zig").List; | 4 | const List = @import("list.zig").List; |
| 5 | const Allocator = @import("mem.zig").Allocator; | 5 | const Allocator = @import("mem.zig").Allocator; |
| 6 | const os = @import("os/index.zig"); | ||
| 7 | const StdIo = os.ChildProcess.StdIo; | ||
| 8 | const Term = os.ChildProcess.Term; | ||
| 6 | 9 | ||
| 7 | error ExtraArg; | 10 | error ExtraArg; |
| 11 | error UncleanExit; | ||
| 8 | 12 | ||
| 9 | pub const Builder = struct { | 13 | pub const Builder = struct { |
| 10 | zig_exe: []const u8, | 14 | zig_exe: []const u8, |
| ... | @@ -33,9 +37,9 @@ pub const Builder = struct { | ... | @@ -33,9 +37,9 @@ pub const Builder = struct { |
| 33 | return exe; | 37 | return exe; |
| 34 | } | 38 | } |
| 35 | 39 | ||
| 36 | pub fn make(self: &Builder, args: []const []const u8) -> %void { | 40 | pub fn make(self: &Builder, cli_args: []const []const u8) -> %void { |
| 37 | var verbose = false; | 41 | var verbose = false; |
| 38 | for (args) |arg| { | 42 | for (cli_args) |arg| { |
| 39 | if (mem.eql(u8, arg, "--verbose")) { | 43 | if (mem.eql(u8, arg, "--verbose")) { |
| 40 | verbose = true; | 44 | verbose = true; |
| 41 | } else { | 45 | } else { |
| ... | @@ -44,7 +48,27 @@ pub const Builder = struct { | ... | @@ -44,7 +48,27 @@ pub const Builder = struct { |
| 44 | } | 48 | } |
| 45 | } | 49 | } |
| 46 | for (self.exe_list.toSlice()) |exe| { | 50 | for (self.exe_list.toSlice()) |exe| { |
| 47 | %%io.stderr.printf("TODO: invoke this command:\nzig build_exe {} --name {}\n", exe.root_src, exe.name); | 51 | var zig_args = List([]const u8).init(self.allocator); |
| 52 | defer zig_args.deinit(); | ||
| 53 | |||
| 54 | %return zig_args.append("build_exe"[0...]); // TODO issue #296 | ||
| 55 | %return zig_args.append(exe.root_src); | ||
| 56 | %return zig_args.append("--name"[0...]); // TODO issue #296 | ||
| 57 | %return zig_args.append(exe.name); | ||
| 58 | |||
| 59 | printInvocation(self.zig_exe, zig_args); | ||
| 60 | const TODO_env: []const []const u8 = undefined; // TODO | ||
| 61 | var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), TODO_env, | ||
| 62 | StdIo.Ignore, StdIo.Inherit, StdIo.Inherit); | ||
| 63 | const term = %return child.wait(); | ||
| 64 | switch (term) { | ||
| 65 | Term.Clean => |code| { | ||
| 66 | if (code != 0) { | ||
| 67 | return error.UncleanExit; | ||
| 68 | } | ||
| 69 | }, | ||
| 70 | else => return error.UncleanExit, | ||
| 71 | } | ||
| 48 | } | 72 | } |
| 49 | } | 73 | } |
| 50 | }; | 74 | }; |
| ... | @@ -57,3 +81,11 @@ const Exe = struct { | ... | @@ -57,3 +81,11 @@ const Exe = struct { |
| 57 | fn handleErr(err: error) -> noreturn { | 81 | fn handleErr(err: error) -> noreturn { |
| 58 | debug.panic("error: {}\n", @errorName(err)); | 82 | debug.panic("error: {}\n", @errorName(err)); |
| 59 | } | 83 | } |
| 84 | |||
| 85 | fn printInvocation(exe_name: []const u8, args: &const List([]const u8)) { | ||
| 86 | %%io.stderr.printf("{}", exe_name); | ||
| 87 | for (args.toSliceConst()) |arg| { | ||
| 88 | %%io.stderr.printf(" {}", arg); | ||
| 89 | } | ||
| 90 | %%io.stderr.printf("\n"); | ||
| 91 | } |
std/io.zig+4-9| ... | @@ -13,22 +13,18 @@ const mem = @import("mem.zig"); | ... | @@ -13,22 +13,18 @@ const mem = @import("mem.zig"); |
| 13 | const Buffer0 = @import("cstr.zig").Buffer0; | 13 | const Buffer0 = @import("cstr.zig").Buffer0; |
| 14 | const fmt = @import("fmt.zig"); | 14 | const fmt = @import("fmt.zig"); |
| 15 | 15 | ||
| 16 | pub const stdin_fileno = 0; | ||
| 17 | pub const stdout_fileno = 1; | ||
| 18 | pub const stderr_fileno = 2; | ||
| 19 | |||
| 20 | pub var stdin = InStream { | 16 | pub var stdin = InStream { |
| 21 | .fd = stdin_fileno, | 17 | .fd = system.STDIN_FILENO, |
| 22 | }; | 18 | }; |
| 23 | 19 | ||
| 24 | pub var stdout = OutStream { | 20 | pub var stdout = OutStream { |
| 25 | .fd = stdout_fileno, | 21 | .fd = system.STDOUT_FILENO, |
| 26 | .buffer = undefined, | 22 | .buffer = undefined, |
| 27 | .index = 0, | 23 | .index = 0, |
| 28 | }; | 24 | }; |
| 29 | 25 | ||
| 30 | pub var stderr = OutStream { | 26 | pub var stderr = OutStream { |
| 31 | .fd = stderr_fileno, | 27 | .fd = system.STDERR_FILENO, |
| 32 | .buffer = undefined, | 28 | .buffer = undefined, |
| 33 | .index = 0, | 29 | .index = 0, |
| 34 | }; | 30 | }; |
| ... | @@ -234,7 +230,6 @@ pub const InStream = struct { | ... | @@ -234,7 +230,6 @@ pub const InStream = struct { |
| 234 | if (read_err > 0) { | 230 | if (read_err > 0) { |
| 235 | switch (read_err) { | 231 | switch (read_err) { |
| 236 | errno.EINTR => continue, | 232 | errno.EINTR => continue, |
| 237 | |||
| 238 | errno.EINVAL => unreachable, | 233 | errno.EINVAL => unreachable, |
| 239 | errno.EFAULT => unreachable, | 234 | errno.EFAULT => unreachable, |
| 240 | errno.EBADF => return error.BadFd, | 235 | errno.EBADF => return error.BadFd, |
| ... | @@ -247,7 +242,7 @@ pub const InStream = struct { | ... | @@ -247,7 +242,7 @@ pub const InStream = struct { |
| 247 | } | 242 | } |
| 248 | return index; | 243 | return index; |
| 249 | }, | 244 | }, |
| 250 | else => @compileError("unsupported OS"), | 245 | else => @compileError("Unsupported OS"), |
| 251 | } | 246 | } |
| 252 | } | 247 | } |
| 253 | 248 |
std/os/darwin.zig+4| ... | @@ -6,6 +6,10 @@ const arch = switch (@compileVar("arch")) { | ... | @@ -6,6 +6,10 @@ const arch = switch (@compileVar("arch")) { |
| 6 | 6 | ||
| 7 | const errno = @import("errno.zig"); | 7 | const errno = @import("errno.zig"); |
| 8 | 8 | ||
| 9 | pub const STDIN_FILENO = 0; | ||
| 10 | pub const STDOUT_FILENO = 1; | ||
| 11 | pub const STDERR_FILENO = 2; | ||
| 12 | |||
| 9 | pub const O_LARGEFILE = 0x0000; | 13 | pub const O_LARGEFILE = 0x0000; |
| 10 | pub const O_RDONLY = 0x0000; | 14 | pub const O_RDONLY = 0x0000; |
| 11 | 15 |
std/os/index.zig+343| ... | @@ -7,12 +7,26 @@ pub const posix = switch(@compileVar("os")) { | ... | @@ -7,12 +7,26 @@ pub const posix = switch(@compileVar("os")) { |
| 7 | Os.windows => windows, | 7 | Os.windows => windows, |
| 8 | else => @compileError("Unsupported OS"), | 8 | else => @compileError("Unsupported OS"), |
| 9 | }; | 9 | }; |
| 10 | const debug = @import("../debug.zig"); | ||
| 11 | const assert = debug.assert; | ||
| 10 | 12 | ||
| 11 | const errno = @import("errno.zig"); | 13 | const errno = @import("errno.zig"); |
| 12 | const linking_libc = @import("../target.zig").linking_libc; | 14 | const linking_libc = @import("../target.zig").linking_libc; |
| 13 | const c = @import("../c/index.zig"); | 15 | const c = @import("../c/index.zig"); |
| 14 | 16 | ||
| 17 | const mem = @import("../mem.zig"); | ||
| 18 | const Allocator = mem.Allocator; | ||
| 19 | |||
| 20 | const io = @import("../io.zig"); | ||
| 21 | |||
| 15 | error Unexpected; | 22 | error Unexpected; |
| 23 | error SysResources; | ||
| 24 | error AccessDenied; | ||
| 25 | error InvalidExe; | ||
| 26 | error FileSystem; | ||
| 27 | error IsDir; | ||
| 28 | error FileNotFound; | ||
| 29 | error FileBusy; | ||
| 16 | 30 | ||
| 17 | /// Fills `buf` with random bytes. If linking against libc, this calls the | 31 | /// Fills `buf` with random bytes. If linking against libc, this calls the |
| 18 | /// appropriate OS-specific library call. Otherwise it uses the zig standard | 32 | /// appropriate OS-specific library call. Otherwise it uses the zig standard |
| ... | @@ -76,3 +90,332 @@ pub coldcc fn abort() -> noreturn { | ... | @@ -76,3 +90,332 @@ pub coldcc fn abort() -> noreturn { |
| 76 | else => @compileError("Unsupported OS"), | 90 | else => @compileError("Unsupported OS"), |
| 77 | } | 91 | } |
| 78 | } | 92 | } |
| 93 | |||
| 94 | fn makePipe() -> %[2]i32 { | ||
| 95 | var fds: [2]i32 = undefined; | ||
| 96 | const err = posix.getErrno(posix.pipe(&fds)); | ||
| 97 | if (err > 0) { | ||
| 98 | return switch (err) { | ||
| 99 | errno.EMFILE, errno.ENFILE => error.SysResources, | ||
| 100 | else => error.Unexpected, | ||
| 101 | } | ||
| 102 | } | ||
| 103 | return fds; | ||
| 104 | } | ||
| 105 | |||
| 106 | fn destroyPipe(pipe: &const [2]i32) { | ||
| 107 | closeNoIntr((*pipe)[0]); | ||
| 108 | closeNoIntr((*pipe)[1]); | ||
| 109 | } | ||
| 110 | |||
| 111 | fn closeNoIntr(fd: i32) { | ||
| 112 | while (true) { | ||
| 113 | const err = posix.getErrno(posix.close(fd)); | ||
| 114 | if (err == errno.EINTR) { | ||
| 115 | continue; | ||
| 116 | } else { | ||
| 117 | return; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | fn openNoIntr(path: []const u8, flags: usize, perm: usize) -> %i32 { | ||
| 123 | while (true) { | ||
| 124 | const result = posix.open(path, flags, perm); | ||
| 125 | const err = posix.getErrno(result); | ||
| 126 | if (err > 0) { | ||
| 127 | return switch (err) { | ||
| 128 | errno.EINTR => continue, | ||
| 129 | |||
| 130 | errno.EFAULT => unreachable, | ||
| 131 | errno.EINVAL => unreachable, | ||
| 132 | errno.EACCES => error.BadPerm, | ||
| 133 | errno.EFBIG, errno.EOVERFLOW => error.FileTooBig, | ||
| 134 | errno.EISDIR => error.IsDir, | ||
| 135 | errno.ELOOP => error.SymLinkLoop, | ||
| 136 | errno.EMFILE => error.ProcessFdQuotaExceeded, | ||
| 137 | errno.ENAMETOOLONG => error.NameTooLong, | ||
| 138 | errno.ENFILE => error.SystemFdQuotaExceeded, | ||
| 139 | errno.ENODEV => error.NoDevice, | ||
| 140 | errno.ENOENT => error.PathNotFound, | ||
| 141 | errno.ENOMEM => error.NoMem, | ||
| 142 | errno.ENOSPC => error.NoSpaceLeft, | ||
| 143 | errno.ENOTDIR => error.NotDir, | ||
| 144 | errno.EPERM => error.BadPerm, | ||
| 145 | else => error.Unexpected, | ||
| 146 | } | ||
| 147 | } | ||
| 148 | return i32(result); | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | const ErrInt = @intType(false, @sizeOf(error) * 8); | ||
| 153 | fn writeIntFd(fd: i32, value: ErrInt) -> %void { | ||
| 154 | var bytes: [@sizeOf(ErrInt)]u8 = undefined; | ||
| 155 | mem.writeInt(bytes[0...], value, true); | ||
| 156 | |||
| 157 | var index: usize = 0; | ||
| 158 | while (index < bytes.len) { | ||
| 159 | const amt_written = posix.write(fd, &bytes[index], bytes.len - index); | ||
| 160 | const err = posix.getErrno(amt_written); | ||
| 161 | if (err > 0) { | ||
| 162 | switch (err) { | ||
| 163 | errno.EINTR => continue, | ||
| 164 | errno.EINVAL => unreachable, | ||
| 165 | else => return error.SysResources, | ||
| 166 | } | ||
| 167 | } | ||
| 168 | index += amt_written; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | fn readIntFd(fd: i32) -> %ErrInt { | ||
| 173 | var bytes: [@sizeOf(ErrInt)]u8 = undefined; | ||
| 174 | |||
| 175 | var index: usize = 0; | ||
| 176 | while (index < bytes.len) { | ||
| 177 | const amt_written = posix.read(fd, &bytes[index], bytes.len - index); | ||
| 178 | const err = posix.getErrno(amt_written); | ||
| 179 | if (err > 0) { | ||
| 180 | switch (err) { | ||
| 181 | errno.EINTR => continue, | ||
| 182 | errno.EINVAL => unreachable, | ||
| 183 | else => return error.SysResources, | ||
| 184 | } | ||
| 185 | } | ||
| 186 | index += amt_written; | ||
| 187 | } | ||
| 188 | |||
| 189 | return mem.readInt(bytes[0...], ErrInt, true); | ||
| 190 | } | ||
| 191 | |||
| 192 | // Child of fork calls this to report an error to the fork parent. | ||
| 193 | // Then the child exits. | ||
| 194 | fn forkChildErrReport(fd: i32, err: error) -> noreturn { | ||
| 195 | _ = writeIntFd(fd, ErrInt(err)); | ||
| 196 | posix.exit(1); | ||
| 197 | } | ||
| 198 | |||
| 199 | fn dup2NoIntr(old_fd: i32, new_fd: i32) -> %void { | ||
| 200 | while (true) { | ||
| 201 | const err = posix.getErrno(posix.dup2(old_fd, new_fd)); | ||
| 202 | if (err > 0) { | ||
| 203 | return switch (err) { | ||
| 204 | errno.EBUSY, errno.EINTR => continue, | ||
| 205 | errno.EMFILE => error.SysResources, | ||
| 206 | errno.EINVAL => unreachable, | ||
| 207 | else => error.Unexpected, | ||
| 208 | }; | ||
| 209 | } | ||
| 210 | return; | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | pub const ChildProcess = struct { | ||
| 215 | pid: i32, | ||
| 216 | err_pipe: [2]i32, | ||
| 217 | |||
| 218 | stdin: ?io.OutStream, | ||
| 219 | stdout: ?io.InStream, | ||
| 220 | stderr: ?io.InStream, | ||
| 221 | |||
| 222 | pub const Term = enum { | ||
| 223 | Clean: i32, | ||
| 224 | Signal: i32, | ||
| 225 | Stopped: i32, | ||
| 226 | Unknown: i32, | ||
| 227 | }; | ||
| 228 | |||
| 229 | pub const StdIo = enum { | ||
| 230 | Inherit, | ||
| 231 | Ignore, | ||
| 232 | Pipe, | ||
| 233 | Close, | ||
| 234 | }; | ||
| 235 | |||
| 236 | pub fn spawn(exe_path: []const u8, args: []const []const u8, env: []const []const u8, | ||
| 237 | stdin: StdIo, stdout: StdIo, stderr: StdIo) -> %ChildProcess | ||
| 238 | { | ||
| 239 | switch (@compileVar("os")) { | ||
| 240 | Os.linux, Os.macosx, Os.ios, Os.darwin => { | ||
| 241 | return spawnPosix(exe_path, args, env, stdin, stdout, stderr); | ||
| 242 | }, | ||
| 243 | else => @compileError("Unsupported OS"), | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | pub fn wait(self: &ChildProcess) -> %Term { | ||
| 248 | defer { | ||
| 249 | closeNoIntr(self.err_pipe[0]); | ||
| 250 | closeNoIntr(self.err_pipe[1]); | ||
| 251 | }; | ||
| 252 | |||
| 253 | var status: i32 = undefined; | ||
| 254 | while (true) { | ||
| 255 | const err = posix.getErrno(posix.waitpid(self.pid, &status, 0)); | ||
| 256 | if (err > 0) { | ||
| 257 | switch (err) { | ||
| 258 | errno.EINVAL, errno.ECHILD => unreachable, | ||
| 259 | errno.EINTR => continue, | ||
| 260 | else => { | ||
| 261 | if (const *stdin ?= self.stdin) { stdin.close(); } | ||
| 262 | if (const *stdout ?= self.stdin) { stdout.close(); } | ||
| 263 | if (const *stderr ?= self.stdin) { stderr.close(); } | ||
| 264 | return error.Unexpected; | ||
| 265 | }, | ||
| 266 | } | ||
| 267 | } | ||
| 268 | break; | ||
| 269 | } | ||
| 270 | |||
| 271 | if (const *stdin ?= self.stdin) { stdin.close(); } | ||
| 272 | if (const *stdout ?= self.stdin) { stdout.close(); } | ||
| 273 | if (const *stderr ?= self.stdin) { stderr.close(); } | ||
| 274 | |||
| 275 | // Write @maxValue(ErrInt) to the write end of the err_pipe. This is after | ||
| 276 | // waitpid, so this write is guaranteed to be after the child | ||
| 277 | // pid potentially wrote an error. This way we can do a blocking | ||
| 278 | // read on the error pipe and either get @maxValue(ErrInt) (no error) or | ||
| 279 | // an error code. | ||
| 280 | %return writeIntFd(self.err_pipe[1], @maxValue(ErrInt)); | ||
| 281 | const err_int = %return readIntFd(self.err_pipe[0]); | ||
| 282 | // Here we potentially return the fork child's error | ||
| 283 | // from the parent pid. | ||
| 284 | if (err_int != @maxValue(ErrInt)) { | ||
| 285 | return error(err_int); | ||
| 286 | } | ||
| 287 | |||
| 288 | return statusToTerm(status); | ||
| 289 | } | ||
| 290 | |||
| 291 | fn statusToTerm(status: i32) -> Term { | ||
| 292 | return if (posix.WIFEXITED(status)) { | ||
| 293 | Term.Clean { posix.WEXITSTATUS(status) } | ||
| 294 | } else if (posix.WIFSIGNALED(status)) { | ||
| 295 | Term.Signal { posix.WTERMSIG(status) } | ||
| 296 | } else if (posix.WIFSTOPPED(status)) { | ||
| 297 | Term.Stopped { posix.WSTOPSIG(status) } | ||
| 298 | } else { | ||
| 299 | Term.Unknown { status } | ||
| 300 | }; | ||
| 301 | } | ||
| 302 | |||
| 303 | fn spawnPosix(exe_path: []const u8, args: []const []const u8, env: []const []const u8, | ||
| 304 | stdin: StdIo, stdout: StdIo, stderr: StdIo) -> %ChildProcess | ||
| 305 | { | ||
| 306 | // TODO issue #295 | ||
| 307 | //const stdin_pipe = if (stdin == StdIo.Pipe) %return makePipe() else undefined; | ||
| 308 | var stdin_pipe: [2]i32 = undefined; | ||
| 309 | if (stdin == StdIo.Pipe) | ||
| 310 | stdin_pipe = %return makePipe(); | ||
| 311 | %defer if (stdin == StdIo.Pipe) { destroyPipe(stdin_pipe); }; | ||
| 312 | |||
| 313 | // TODO issue #295 | ||
| 314 | //const stdout_pipe = if (stdout == StdIo.Pipe) %return makePipe() else undefined; | ||
| 315 | var stdout_pipe: [2]i32 = undefined; | ||
| 316 | if (stdout == StdIo.Pipe) | ||
| 317 | stdout_pipe = %return makePipe(); | ||
| 318 | %defer if (stdout == StdIo.Pipe) { destroyPipe(stdout_pipe); }; | ||
| 319 | |||
| 320 | // TODO issue #295 | ||
| 321 | //const stderr_pipe = if (stderr == StdIo.Pipe) %return makePipe() else undefined; | ||
| 322 | var stderr_pipe: [2]i32 = undefined; | ||
| 323 | if (stderr == StdIo.Pipe) | ||
| 324 | stderr_pipe = %return makePipe(); | ||
| 325 | %defer if (stderr == StdIo.Pipe) { destroyPipe(stderr_pipe); }; | ||
| 326 | |||
| 327 | const any_ignore = (stdin == StdIo.Ignore or stdout == StdIo.Ignore or stderr == StdIo.Ignore); | ||
| 328 | // TODO issue #295 | ||
| 329 | //const dev_null_fd = if (any_ignore) { | ||
| 330 | // %return openNoIntr("/dev/null", posix.O_RDWR, 0) | ||
| 331 | //} else { | ||
| 332 | // undefined | ||
| 333 | //}; | ||
| 334 | var dev_null_fd: i32 = undefined; | ||
| 335 | if (any_ignore) | ||
| 336 | dev_null_fd = %return openNoIntr("/dev/null", posix.O_RDWR, 0); | ||
| 337 | |||
| 338 | // This pipe is used to communicate errors between the time of fork | ||
| 339 | // and execve from the child process to the parent process. | ||
| 340 | const err_pipe = %return makePipe(); | ||
| 341 | %defer destroyPipe(err_pipe); | ||
| 342 | |||
| 343 | const pid = posix.fork(); | ||
| 344 | const pid_err = linux.getErrno(pid); | ||
| 345 | if (pid_err > 0) { | ||
| 346 | return switch (pid_err) { | ||
| 347 | errno.EAGAIN, errno.ENOMEM, errno.ENOSYS => error.SysResources, | ||
| 348 | else => error.Unexpected, | ||
| 349 | }; | ||
| 350 | } | ||
| 351 | if (pid == 0) { | ||
| 352 | // we are the child | ||
| 353 | setUpChildIo(stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) %% | ||
| 354 | |err| forkChildErrReport(err_pipe[1], err); | ||
| 355 | setUpChildIo(stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) %% | ||
| 356 | |err| forkChildErrReport(err_pipe[1], err); | ||
| 357 | setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %% | ||
| 358 | |err| forkChildErrReport(err_pipe[1], err); | ||
| 359 | |||
| 360 | const err = posix.getErrno(posix.execve(exe_path, args, env)); | ||
| 361 | assert(err > 0); | ||
| 362 | forkChildErrReport(err_pipe[1], switch (err) { | ||
| 363 | errno.EFAULT => unreachable, | ||
| 364 | errno.E2BIG, errno.EMFILE, errno.ENAMETOOLONG, errno.ENFILE, errno.ENOMEM => error.SysResources, | ||
| 365 | errno.EACCES, errno.EPERM => error.AccessDenied, | ||
| 366 | errno.EINVAL, errno.ENOEXEC => error.InvalidExe, | ||
| 367 | errno.EIO, errno.ELOOP => error.FileSystem, | ||
| 368 | errno.EISDIR => error.IsDir, | ||
| 369 | errno.ENOENT, errno.ENOTDIR => error.FileNotFound, | ||
| 370 | errno.ETXTBSY => error.FileBusy, | ||
| 371 | else => error.Unexpected, | ||
| 372 | }); | ||
| 373 | } | ||
| 374 | |||
| 375 | // we are the parent | ||
| 376 | if (stdin == StdIo.Pipe) { closeNoIntr(stdin_pipe[0]); } | ||
| 377 | if (stdout == StdIo.Pipe) { closeNoIntr(stdout_pipe[1]); } | ||
| 378 | if (stderr == StdIo.Pipe) { closeNoIntr(stderr_pipe[1]); } | ||
| 379 | if (any_ignore) { closeNoIntr(dev_null_fd); } | ||
| 380 | |||
| 381 | return ChildProcess { | ||
| 382 | .pid = i32(pid), | ||
| 383 | .err_pipe = err_pipe, | ||
| 384 | |||
| 385 | .stdin = if (stdin == StdIo.Pipe) { | ||
| 386 | io.OutStream { | ||
| 387 | .fd = stdin_pipe[1], | ||
| 388 | } | ||
| 389 | } else { | ||
| 390 | null | ||
| 391 | }, | ||
| 392 | .stdout = if (stdout == StdIo.Pipe) { | ||
| 393 | io.InStream { | ||
| 394 | .fd = stdout_pipe[0], | ||
| 395 | .buffer = undefined, | ||
| 396 | .index = 0, | ||
| 397 | } | ||
| 398 | } else { | ||
| 399 | null | ||
| 400 | }, | ||
| 401 | .stderr = if (stderr == StdIo.Pipe) { | ||
| 402 | io.InStream { | ||
| 403 | .fd = stderr_pipe[0], | ||
| 404 | .buffer = undefined, | ||
| 405 | .index = 0, | ||
| 406 | } | ||
| 407 | } else { | ||
| 408 | null | ||
| 409 | }, | ||
| 410 | }; | ||
| 411 | } | ||
| 412 | |||
| 413 | fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void { | ||
| 414 | switch (stdio) { | ||
| 415 | StdIo.Pipe => %return dup2NoIntr(pipe_fd, std_fileno), | ||
| 416 | StdIo.Close => closeNoIntr(std_fileno), | ||
| 417 | StdIo.Inherit => {}, | ||
| 418 | StdIo.Ignore => %return dup2NoIntr(dev_null_fd, std_fileno), | ||
| 419 | } | ||
| 420 | } | ||
| 421 | }; |
std/os/linux.zig+72-2| ... | @@ -5,6 +5,10 @@ const arch = switch (@compileVar("arch")) { | ... | @@ -5,6 +5,10 @@ const arch = switch (@compileVar("arch")) { |
| 5 | }; | 5 | }; |
| 6 | const errno = @import("errno.zig"); | 6 | const errno = @import("errno.zig"); |
| 7 | 7 | ||
| 8 | pub const STDIN_FILENO = 0; | ||
| 9 | pub const STDOUT_FILENO = 1; | ||
| 10 | pub const STDERR_FILENO = 2; | ||
| 11 | |||
| 8 | pub const PROT_NONE = 0; | 12 | pub const PROT_NONE = 0; |
| 9 | pub const PROT_READ = 1; | 13 | pub const PROT_READ = 1; |
| 10 | pub const PROT_WRITE = 2; | 14 | pub const PROT_WRITE = 2; |
| ... | @@ -237,12 +241,66 @@ pub const AF_NFC = PF_NFC; | ... | @@ -237,12 +241,66 @@ pub const AF_NFC = PF_NFC; |
| 237 | pub const AF_VSOCK = PF_VSOCK; | 241 | pub const AF_VSOCK = PF_VSOCK; |
| 238 | pub const AF_MAX = PF_MAX; | 242 | pub const AF_MAX = PF_MAX; |
| 239 | 243 | ||
| 244 | |||
| 245 | fn unsigned(s: i32) -> u32 { *@ptrcast(&u32, &s) } | ||
| 246 | fn signed(s: u32) -> i32 { *@ptrcast(&i32, &s) } | ||
| 247 | pub fn WEXITSTATUS(s: i32) -> i32 { signed((unsigned(s) & 0xff00) >> 8) } | ||
| 248 | pub fn WTERMSIG(s: i32) -> i32 { signed(unsigned(s) & 0x7f) } | ||
| 249 | pub fn WSTOPSIG(s: i32) -> i32 { WEXITSTATUS(s) } | ||
| 250 | pub fn WIFEXITED(s: i32) -> bool { WTERMSIG(s) == 0 } | ||
| 251 | pub fn WIFSTOPPED(s: i32) -> bool { (u16)(((unsigned(s)&0xffff)*%0x10001)>>8) > 0x7f00 } | ||
| 252 | pub fn WIFSIGNALED(s: i32) -> bool { (unsigned(s)&0xffff)-%1 < 0xff } | ||
| 253 | |||
| 240 | /// Get the errno from a syscall return value, or 0 for no error. | 254 | /// Get the errno from a syscall return value, or 0 for no error. |
| 241 | pub fn getErrno(r: usize) -> usize { | 255 | pub fn getErrno(r: usize) -> usize { |
| 242 | const signed_r = *@ptrcast(&isize, &r); | 256 | const signed_r = *@ptrcast(&isize, &r); |
| 243 | if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0 | 257 | if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0 |
| 244 | } | 258 | } |
| 245 | 259 | ||
| 260 | pub fn dup2(old: i32, new: i32) -> usize { | ||
| 261 | arch.syscall2(arch.SYS_dup2, usize(old), usize(new)) | ||
| 262 | } | ||
| 263 | |||
| 264 | pub fn execve_c(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) -> usize { | ||
| 265 | arch.syscall3(arch.SYS_execve, path, argv, envp) | ||
| 266 | } | ||
| 267 | |||
| 268 | /// This function must allocate memory to add a null terminating bytes on path, each arg, | ||
| 269 | /// and each environment variable line, as well as a null pointer after the arg list and | ||
| 270 | /// environment variable list. We allocate stack memory since the process is about to get | ||
| 271 | /// wiped anyway. | ||
| 272 | pub fn execve(path: []const u8, argv: []const []const u8, envp: []const []const u8) -> usize { | ||
| 273 | const path_buf = @alloca(u8, path.len + 1); | ||
| 274 | @memcpy(&path_buf[0], &path[0], path.len); | ||
| 275 | path_buf[path.len] = 0; | ||
| 276 | |||
| 277 | const argv_buf = @alloca([]const ?&const u8, argv.len + 1); | ||
| 278 | for (argv) |arg, i| { | ||
| 279 | const arg_buf = @alloca(u8, arg.len + 1); | ||
| 280 | @memcpy(&arg_buf[0], &arg[0], arg.len); | ||
| 281 | arg_buf[arg.len] = 0; | ||
| 282 | |||
| 283 | argv[i] = arg_buf; | ||
| 284 | } | ||
| 285 | argv_buf[argv.len] = null; | ||
| 286 | |||
| 287 | const envp_buf = @alloca([]const ?&const u8, envp.len + 1); | ||
| 288 | for (envp) |env, i| { | ||
| 289 | const env_buf = @alloca(u8, env.len + 1); | ||
| 290 | @memcpy(&env_buf[0], &env[0], env.len); | ||
| 291 | env_buf[env.len] = 0; | ||
| 292 | |||
| 293 | envp[i] = env_buf; | ||
| 294 | } | ||
| 295 | envp_buf[envp.len] = null; | ||
| 296 | |||
| 297 | return execve_c(path_buf.ptr, argv_buf.ptr, envp_buf.ptr); | ||
| 298 | } | ||
| 299 | |||
| 300 | pub fn fork() -> usize { | ||
| 301 | arch.syscall0(arch.SYS_fork) | ||
| 302 | } | ||
| 303 | |||
| 246 | pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: usize) | 304 | pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: usize) |
| 247 | -> usize | 305 | -> usize |
| 248 | { | 306 | { |
| ... | @@ -261,6 +319,14 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) -> usize { | ... | @@ -261,6 +319,14 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) -> usize { |
| 261 | arch.syscall4(arch.SYS_pread, usize(fd), usize(buf), count, offset) | 319 | arch.syscall4(arch.SYS_pread, usize(fd), usize(buf), count, offset) |
| 262 | } | 320 | } |
| 263 | 321 | ||
| 322 | pub fn pipe(fd: &[2]i32) -> usize { | ||
| 323 | pipe2(fd, 0) | ||
| 324 | } | ||
| 325 | |||
| 326 | pub fn pipe2(fd: &[2]i32, flags: usize) -> usize { | ||
| 327 | arch.syscall2(arch.SYS_pipe2, usize(fd), flags) | ||
| 328 | } | ||
| 329 | |||
| 264 | pub fn write(fd: i32, buf: &const u8, count: usize) -> usize { | 330 | pub fn write(fd: i32, buf: &const u8, count: usize) -> usize { |
| 265 | arch.syscall3(arch.SYS_write, usize(fd), usize(buf), count) | 331 | arch.syscall3(arch.SYS_write, usize(fd), usize(buf), count) |
| 266 | } | 332 | } |
| ... | @@ -319,8 +385,12 @@ pub fn getrandom(buf: &u8, count: usize, flags: u32) -> usize { | ... | @@ -319,8 +385,12 @@ pub fn getrandom(buf: &u8, count: usize, flags: u32) -> usize { |
| 319 | arch.syscall3(arch.SYS_getrandom, usize(buf), count, usize(flags)) | 385 | arch.syscall3(arch.SYS_getrandom, usize(buf), count, usize(flags)) |
| 320 | } | 386 | } |
| 321 | 387 | ||
| 322 | pub fn kill(pid: i32, sig: i32) -> i32 { | 388 | pub fn kill(pid: i32, sig: i32) -> usize { |
| 323 | i32(arch.syscall2(arch.SYS_kill, usize(pid), usize(sig))) | 389 | arch.syscall2(arch.SYS_kill, usize(pid), usize(sig)) |
| 390 | } | ||
| 391 | |||
| 392 | pub fn waitpid(pid: i32, status: &i32, options: i32) -> usize { | ||
| 393 | arch.syscall4(arch.SYS_wait4, usize(pid), usize(status), usize(options), 0) | ||
| 324 | } | 394 | } |
| 325 | 395 | ||
| 326 | const NSIG = 65; | 396 | const NSIG = 65; |
test/cases/cast.zig+11| ... | @@ -30,3 +30,14 @@ test "implicitly cast a pointer to a const pointer of it" { | ... | @@ -30,3 +30,14 @@ test "implicitly cast a pointer to a const pointer of it" { |
| 30 | fn funcWithConstPtrPtr(x: &const &i32) { | 30 | fn funcWithConstPtrPtr(x: &const &i32) { |
| 31 | **x += 1; | 31 | **x += 1; |
| 32 | } | 32 | } |
| 33 | |||
| 34 | error ItBroke; | ||
| 35 | test "explicit cast from integer to error type" { | ||
| 36 | testCastIntToErr(error.ItBroke); | ||
| 37 | comptime testCastIntToErr(error.ItBroke); | ||
| 38 | } | ||
| 39 | fn testCastIntToErr(err: error) { | ||
| 40 | const x = usize(err); | ||
| 41 | const y = error(x); | ||
| 42 | assert(error.ItBroke == y); | ||
| 43 | } |
test/run_tests.cpp+28| ... | @@ -1758,6 +1758,13 @@ export fn foo() { | ... | @@ -1758,6 +1758,13 @@ export fn foo() { |
| 1758 | } | 1758 | } |
| 1759 | )SOURCE", 1, ".tmp_source.zig:3:5: error: unable to infer variable type"); | 1759 | )SOURCE", 1, ".tmp_source.zig:3:5: error: unable to infer variable type"); |
| 1760 | 1760 | ||
| 1761 | add_compile_fail_case("undefined literal on a non-comptime var", R"SOURCE( | ||
| 1762 | export fn foo() { | ||
| 1763 | var i = undefined; | ||
| 1764 | i = i32(1); | ||
| 1765 | } | ||
| 1766 | )SOURCE", 1, ".tmp_source.zig:3:5: error: unable to infer variable type"); | ||
| 1767 | |||
| 1761 | add_compile_fail_case("dereference an array", R"SOURCE( | 1768 | add_compile_fail_case("dereference an array", R"SOURCE( |
| 1762 | var s_buffer: [10]u8 = undefined; | 1769 | var s_buffer: [10]u8 = undefined; |
| 1763 | pub fn pass(in: []u8) -> []u8 { | 1770 | pub fn pass(in: []u8) -> []u8 { |
| ... | @@ -1818,6 +1825,15 @@ export fn entry(a: &i32) -> usize { | ... | @@ -1818,6 +1825,15 @@ export fn entry(a: &i32) -> usize { |
| 1818 | return @ptrcast(usize, a); | 1825 | return @ptrcast(usize, a); |
| 1819 | } | 1826 | } |
| 1820 | )SOURCE", 1, ".tmp_source.zig:3:21: error: expected pointer, found 'usize'"); | 1827 | )SOURCE", 1, ".tmp_source.zig:3:21: error: expected pointer, found 'usize'"); |
| 1828 | |||
| 1829 | add_compile_fail_case("too many error values to cast to small integer", R"SOURCE( | ||
| 1830 | error A; error B; error C; error D; error E; error F; error G; error H; | ||
| 1831 | const u2 = @intType(false, 2); | ||
| 1832 | fn foo(e: error) -> u2 { | ||
| 1833 | return u2(e); | ||
| 1834 | } | ||
| 1835 | export fn entry() -> usize { @sizeOf(@typeOf(foo)) } | ||
| 1836 | )SOURCE", 1, ".tmp_source.zig:5:14: error: too many error values to fit in 'u2'"); | ||
| 1821 | } | 1837 | } |
| 1822 | 1838 | ||
| 1823 | ////////////////////////////////////////////////////////////////////////////// | 1839 | ////////////////////////////////////////////////////////////////////////////// |
| ... | @@ -2043,6 +2059,18 @@ fn bar() -> %void { | ... | @@ -2043,6 +2059,18 @@ fn bar() -> %void { |
| 2043 | } | 2059 | } |
| 2044 | )SOURCE"); | 2060 | )SOURCE"); |
| 2045 | 2061 | ||
| 2062 | add_debug_safety_case("cast integer to error and no code matches", R"SOURCE( | ||
| 2063 | pub fn panic(message: []const u8) -> noreturn { | ||
| 2064 | @breakpoint(); | ||
| 2065 | while (true) {} | ||
| 2066 | } | ||
| 2067 | pub fn main(args: [][]u8) -> %void { | ||
| 2068 | _ = bar(9999); | ||
| 2069 | } | ||
| 2070 | fn bar(x: u32) -> error { | ||
| 2071 | return error(x); | ||
| 2072 | } | ||
| 2073 | )SOURCE"); | ||
| 2046 | } | 2074 | } |
| 2047 | 2075 | ||
| 2048 | ////////////////////////////////////////////////////////////////////////////// | 2076 | ////////////////////////////////////////////////////////////////////////////// |