zig build system progress
* 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 type
This commit is contained in:
parent
0594487a2e
commit
8fd0fddce5
@ -520,7 +520,6 @@ struct AstNodeUnwrapErrorExpr {
|
||||
enum CastOp {
|
||||
CastOpNoCast, // signifies the function call expression is not a cast
|
||||
CastOpNoop, // fn call expr is a cast, but does nothing
|
||||
CastOpErrToInt,
|
||||
CastOpIntToFloat,
|
||||
CastOpFloatToInt,
|
||||
CastOpBoolToInt,
|
||||
@ -1223,6 +1222,7 @@ enum PanicMsgId {
|
||||
PanicMsgIdSliceWidenRemainder,
|
||||
PanicMsgIdUnwrapMaybeFail,
|
||||
PanicMsgIdUnwrapErrFail,
|
||||
PanicMsgIdInvalidErrorCode,
|
||||
|
||||
PanicMsgIdCount,
|
||||
};
|
||||
@ -1728,6 +1728,8 @@ enum IrInstructionId {
|
||||
IrInstructionIdIntToPtr,
|
||||
IrInstructionIdPtrToInt,
|
||||
IrInstructionIdIntToEnum,
|
||||
IrInstructionIdIntToErr,
|
||||
IrInstructionIdErrToInt,
|
||||
IrInstructionIdCheckSwitchProngs,
|
||||
IrInstructionIdTestType,
|
||||
IrInstructionIdTypeName,
|
||||
@ -2404,6 +2406,18 @@ struct IrInstructionIntToEnum {
|
||||
IrInstruction *target;
|
||||
};
|
||||
|
||||
struct IrInstructionIntToErr {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *target;
|
||||
};
|
||||
|
||||
struct IrInstructionErrToInt {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *target;
|
||||
};
|
||||
|
||||
struct IrInstructionCheckSwitchProngsRange {
|
||||
IrInstruction *start;
|
||||
IrInstruction *end;
|
||||
|
@ -570,6 +570,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
|
||||
return buf_create_from_str("attempt to unwrap error");
|
||||
case PanicMsgIdUnreachable:
|
||||
return buf_create_from_str("reached unreachable code");
|
||||
case PanicMsgIdInvalidErrorCode:
|
||||
return buf_create_from_str("invalid error code");
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -1227,14 +1229,6 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
|
||||
zig_unreachable();
|
||||
case CastOpNoop:
|
||||
return expr_val;
|
||||
case CastOpErrToInt:
|
||||
assert(actual_type->id == TypeTableEntryIdErrorUnion);
|
||||
if (!type_has_bits(actual_type->data.error.child_type)) {
|
||||
return gen_widen_or_shorten(g, ir_want_debug_safety(g, &cast_instruction->base),
|
||||
g->err_tag_type, wanted_type, expr_val);
|
||||
} else {
|
||||
zig_panic("TODO");
|
||||
}
|
||||
case CastOpResizeSlice:
|
||||
{
|
||||
assert(cast_instruction->tmp_ptr);
|
||||
@ -1402,6 +1396,66 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutable *executable,
|
||||
instruction->target->value.type, wanted_int_type, target_val);
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutable *executable, IrInstructionIntToErr *instruction) {
|
||||
TypeTableEntry *wanted_type = instruction->base.value.type;
|
||||
assert(wanted_type->id == TypeTableEntryIdPureError);
|
||||
|
||||
TypeTableEntry *actual_type = instruction->target->value.type;
|
||||
assert(actual_type->id == TypeTableEntryIdInt);
|
||||
assert(!actual_type->data.integral.is_signed);
|
||||
|
||||
LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
|
||||
|
||||
if (ir_want_debug_safety(g, &instruction->base)) {
|
||||
LLVMValueRef zero = LLVMConstNull(actual_type->type_ref);
|
||||
LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, "");
|
||||
LLVMValueRef ok_bit;
|
||||
uint64_t biggest_possible_err_val = max_unsigned_val(actual_type);
|
||||
if (biggest_possible_err_val < g->error_decls.length) {
|
||||
ok_bit = neq_zero_bit;
|
||||
} else {
|
||||
LLVMValueRef error_value_count = LLVMConstInt(actual_type->type_ref, g->error_decls.length, false);
|
||||
LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, "");
|
||||
ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, "");
|
||||
}
|
||||
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk");
|
||||
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail");
|
||||
|
||||
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, fail_block);
|
||||
gen_debug_safety_crash(g, PanicMsgIdInvalidErrorCode);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
}
|
||||
|
||||
return gen_widen_or_shorten(g, false, actual_type, g->err_tag_type, target_val);
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_err_to_int(CodeGen *g, IrExecutable *executable, IrInstructionErrToInt *instruction) {
|
||||
TypeTableEntry *wanted_type = instruction->base.value.type;
|
||||
assert(wanted_type->id == TypeTableEntryIdInt);
|
||||
assert(!wanted_type->data.integral.is_signed);
|
||||
|
||||
TypeTableEntry *actual_type = instruction->target->value.type;
|
||||
LLVMValueRef target_val = ir_llvm_value(g, instruction->target);
|
||||
|
||||
if (actual_type->id == TypeTableEntryIdPureError) {
|
||||
return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base),
|
||||
g->err_tag_type, wanted_type, target_val);
|
||||
} else if (actual_type->id == TypeTableEntryIdErrorUnion) {
|
||||
if (!type_has_bits(actual_type->data.error.child_type)) {
|
||||
return gen_widen_or_shorten(g, ir_want_debug_safety(g, &instruction->base),
|
||||
g->err_tag_type, wanted_type, target_val);
|
||||
} else {
|
||||
zig_panic("TODO");
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutable *executable,
|
||||
IrInstructionUnreachable *unreachable_instruction)
|
||||
{
|
||||
@ -2786,6 +2840,10 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
return ir_render_int_to_ptr(g, executable, (IrInstructionIntToPtr *)instruction);
|
||||
case IrInstructionIdIntToEnum:
|
||||
return ir_render_int_to_enum(g, executable, (IrInstructionIntToEnum *)instruction);
|
||||
case IrInstructionIdIntToErr:
|
||||
return ir_render_int_to_err(g, executable, (IrInstructionIntToErr *)instruction);
|
||||
case IrInstructionIdErrToInt:
|
||||
return ir_render_err_to_int(g, executable, (IrInstructionErrToInt *)instruction);
|
||||
case IrInstructionIdContainerInitList:
|
||||
return ir_render_container_init_list(g, executable, (IrInstructionContainerInitList *)instruction);
|
||||
case IrInstructionIdPanic:
|
||||
|
176
src/ir.cpp
176
src/ir.cpp
@ -500,6 +500,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToEnum *) {
|
||||
return IrInstructionIdIntToEnum;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToErr *) {
|
||||
return IrInstructionIdIntToErr;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrToInt *) {
|
||||
return IrInstructionIdErrToInt;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProngs *) {
|
||||
return IrInstructionIdCheckSwitchProngs;
|
||||
}
|
||||
@ -2002,6 +2010,30 @@ static IrInstruction *ir_build_int_to_enum(IrBuilder *irb, Scope *scope, AstNode
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_int_to_err(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *target)
|
||||
{
|
||||
IrInstructionIntToErr *instruction = ir_build_instruction<IrInstructionIntToErr>(
|
||||
irb, scope, source_node);
|
||||
instruction->target = target;
|
||||
|
||||
ir_ref_instruction(target, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_err_to_int(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *target)
|
||||
{
|
||||
IrInstructionErrToInt *instruction = ir_build_instruction<IrInstructionErrToInt>(
|
||||
irb, scope, source_node);
|
||||
instruction->target = target;
|
||||
|
||||
ir_ref_instruction(target, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count)
|
||||
{
|
||||
@ -2704,6 +2736,20 @@ static IrInstruction *ir_instruction_inttoenum_get_dep(IrInstructionIntToEnum *i
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_inttoerr_get_dep(IrInstructionIntToErr *instruction, size_t index) {
|
||||
switch (index) {
|
||||
case 0: return instruction->target;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_errtoint_get_dep(IrInstructionErrToInt *instruction, size_t index) {
|
||||
switch (index) {
|
||||
case 0: return instruction->target;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionCheckSwitchProngs *instruction,
|
||||
size_t index)
|
||||
{
|
||||
@ -2938,6 +2984,10 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
|
||||
return ir_instruction_ptrtoint_get_dep((IrInstructionPtrToInt *) instruction, index);
|
||||
case IrInstructionIdIntToEnum:
|
||||
return ir_instruction_inttoenum_get_dep((IrInstructionIntToEnum *) instruction, index);
|
||||
case IrInstructionIdIntToErr:
|
||||
return ir_instruction_inttoerr_get_dep((IrInstructionIntToErr *) instruction, index);
|
||||
case IrInstructionIdErrToInt:
|
||||
return ir_instruction_errtoint_get_dep((IrInstructionErrToInt *) instruction, index);
|
||||
case IrInstructionIdCheckSwitchProngs:
|
||||
return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index);
|
||||
case IrInstructionIdTestType:
|
||||
@ -6001,20 +6051,6 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
|
||||
case CastOpBytesToSlice:
|
||||
// can't do it
|
||||
break;
|
||||
case CastOpErrToInt:
|
||||
{
|
||||
uint64_t value;
|
||||
if (other_type->id == TypeTableEntryIdErrorUnion) {
|
||||
value = other_val->data.x_err_union.err ? other_val->data.x_err_union.err->value : 0;
|
||||
} else if (other_type->id == TypeTableEntryIdPureError) {
|
||||
value = other_val->data.x_pure_err->value;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
bignum_init_unsigned(&const_val->data.x_bignum, value);
|
||||
const_val->special = ConstValSpecialStatic;
|
||||
break;
|
||||
}
|
||||
case CastOpIntToFloat:
|
||||
bignum_cast_to_float(&const_val->data.x_bignum, &other_val->data.x_bignum);
|
||||
const_val->special = ConstValSpecialStatic;
|
||||
@ -6707,6 +6743,87 @@ static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction
|
||||
return result;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_int_to_err(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target) {
|
||||
assert(target->value.type->id == TypeTableEntryIdInt);
|
||||
assert(!target->value.type->data.integral.is_signed);
|
||||
|
||||
if (instr_is_comptime(target)) {
|
||||
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
||||
if (!val)
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
|
||||
source_instr->source_node, ira->codegen->builtin_types.entry_pure_error);
|
||||
|
||||
uint64_t index = val->data.x_bignum.data.x_uint;
|
||||
if (index == 0 || index >= ira->codegen->error_decls.length) {
|
||||
ir_add_error(ira, source_instr,
|
||||
buf_sprintf("integer value %" PRIu64 " represents no error", index));
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
|
||||
AstNode *error_decl_node = ira->codegen->error_decls.at(index);
|
||||
result->value.data.x_pure_err = error_decl_node->data.error_value_decl.err;
|
||||
return result;
|
||||
}
|
||||
|
||||
IrInstruction *result = ir_build_int_to_err(&ira->new_irb, source_instr->scope, source_instr->source_node, target);
|
||||
result->value.type = ira->codegen->builtin_types.entry_pure_error;
|
||||
return result;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_err_to_int(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *target,
|
||||
TypeTableEntry *wanted_type)
|
||||
{
|
||||
assert(wanted_type->id == TypeTableEntryIdInt);
|
||||
|
||||
TypeTableEntry *err_type = target->value.type;
|
||||
|
||||
if (instr_is_comptime(target)) {
|
||||
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
|
||||
if (!val)
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
|
||||
source_instr->source_node, wanted_type);
|
||||
|
||||
ErrorTableEntry *err;
|
||||
if (err_type->id == TypeTableEntryIdErrorUnion) {
|
||||
err = val->data.x_err_union.err;
|
||||
} else if (err_type->id == TypeTableEntryIdPureError) {
|
||||
err = val->data.x_pure_err;
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
result->value.type = wanted_type;
|
||||
uint64_t err_value = err ? err->value : 0;
|
||||
bignum_init_unsigned(&result->value.data.x_bignum, err_value);
|
||||
|
||||
if (!bignum_fits_in_bits(&result->value.data.x_bignum,
|
||||
wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed))
|
||||
{
|
||||
ir_add_error_node(ira, source_instr->source_node,
|
||||
buf_sprintf("error code '%s' does not fit in '%s'",
|
||||
buf_ptr(&err->name), buf_ptr(&wanted_type->name)));
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BigNum bn;
|
||||
bignum_init_unsigned(&bn, ira->codegen->error_decls.length);
|
||||
if (!bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) {
|
||||
ir_add_error_node(ira, source_instr->source_node,
|
||||
buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name)));
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
|
||||
IrInstruction *result = ir_build_err_to_int(&ira->new_irb, source_instr->scope, source_instr->source_node, target);
|
||||
result->value.type = wanted_type;
|
||||
return result;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
|
||||
TypeTableEntry *wanted_type, IrInstruction *value)
|
||||
{
|
||||
@ -6781,7 +6898,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
|
||||
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpFloatToInt, false);
|
||||
}
|
||||
|
||||
// explicit cast from array to slice
|
||||
// explicit cast from [N]T to []const T
|
||||
if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) {
|
||||
TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
|
||||
assert(ptr_type->id == TypeTableEntryIdPointer);
|
||||
@ -6909,17 +7026,14 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
|
||||
if ((actual_type_is_void_err || actual_type_is_pure_err) &&
|
||||
wanted_type->id == TypeTableEntryIdInt)
|
||||
{
|
||||
BigNum bn;
|
||||
bignum_init_unsigned(&bn, ira->codegen->error_decls.length);
|
||||
if (bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count,
|
||||
wanted_type->data.integral.is_signed))
|
||||
{
|
||||
return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpErrToInt, false);
|
||||
} else {
|
||||
ir_add_error_node(ira, source_instr->source_node,
|
||||
buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name)));
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
return ir_analyze_err_to_int(ira, source_instr, value, wanted_type);
|
||||
}
|
||||
|
||||
// explicit cast from integer to pure error
|
||||
if (wanted_type->id == TypeTableEntryIdPureError && actual_type->id == TypeTableEntryIdInt &&
|
||||
!actual_type->data.integral.is_signed)
|
||||
{
|
||||
return ir_analyze_int_to_err(ira, source_instr, value);
|
||||
}
|
||||
|
||||
// explicit cast from integer to enum type with no payload
|
||||
@ -7843,7 +7957,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
|
||||
result_type = ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
bool is_comptime_var = ir_get_var_is_comptime(var);
|
||||
bool is_comptime_var = ir_get_var_is_comptime(var);
|
||||
|
||||
switch (result_type->id) {
|
||||
case TypeTableEntryIdTypeDecl:
|
||||
@ -7852,6 +7966,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
|
||||
break; // handled above
|
||||
case TypeTableEntryIdNumLitFloat:
|
||||
case TypeTableEntryIdNumLitInt:
|
||||
case TypeTableEntryIdUndefLit:
|
||||
if (is_export || is_extern || (!var->src_is_const && !is_comptime_var)) {
|
||||
ir_add_error_node(ira, source_node, buf_sprintf("unable to infer variable type"));
|
||||
result_type = ira->codegen->builtin_types.entry_invalid;
|
||||
@ -7873,7 +7988,6 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
|
||||
result_type = ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
break;
|
||||
case TypeTableEntryIdUndefLit:
|
||||
case TypeTableEntryIdVoid:
|
||||
case TypeTableEntryIdBool:
|
||||
case TypeTableEntryIdInt:
|
||||
@ -10765,6 +10879,8 @@ static TypeTableEntry *ir_analyze_instruction_container_init_list(IrAnalyze *ira
|
||||
TypeTableEntry *this_field_type = field->type_entry;
|
||||
|
||||
IrInstruction *init_value = instruction->items[0]->other;
|
||||
if (type_is_invalid(init_value->value.type))
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *casted_init_value = ir_implicit_cast(ira, init_value, this_field_type);
|
||||
if (casted_init_value == ira->codegen->invalid_instruction)
|
||||
@ -12311,6 +12427,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||
case IrInstructionIdIntToPtr:
|
||||
case IrInstructionIdPtrToInt:
|
||||
case IrInstructionIdIntToEnum:
|
||||
case IrInstructionIdIntToErr:
|
||||
case IrInstructionIdErrToInt:
|
||||
case IrInstructionIdStructInit:
|
||||
case IrInstructionIdStructFieldPtr:
|
||||
case IrInstructionIdEnumFieldPtr:
|
||||
@ -12650,6 +12768,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
case IrInstructionIdPtrToInt:
|
||||
case IrInstructionIdIntToPtr:
|
||||
case IrInstructionIdIntToEnum:
|
||||
case IrInstructionIdIntToErr:
|
||||
case IrInstructionIdErrToInt:
|
||||
case IrInstructionIdTestType:
|
||||
case IrInstructionIdTypeName:
|
||||
case IrInstructionIdCanImplicitCast:
|
||||
|
@ -799,6 +799,16 @@ static void ir_print_int_to_enum(IrPrint *irp, IrInstructionIntToEnum *instructi
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_int_to_err(IrPrint *irp, IrInstructionIntToErr *instruction) {
|
||||
fprintf(irp->f, "inttoerr ");
|
||||
ir_print_other_instruction(irp, instruction->target);
|
||||
}
|
||||
|
||||
static void ir_print_err_to_int(IrPrint *irp, IrInstructionErrToInt *instruction) {
|
||||
fprintf(irp->f, "errtoint ");
|
||||
ir_print_other_instruction(irp, instruction->target);
|
||||
}
|
||||
|
||||
static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchProngs *instruction) {
|
||||
fprintf(irp->f, "@checkSwitchProngs(");
|
||||
ir_print_other_instruction(irp, instruction->target_value);
|
||||
@ -1117,6 +1127,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdIntToEnum:
|
||||
ir_print_int_to_enum(irp, (IrInstructionIntToEnum *)instruction);
|
||||
break;
|
||||
case IrInstructionIdIntToErr:
|
||||
ir_print_int_to_err(irp, (IrInstructionIntToErr *)instruction);
|
||||
break;
|
||||
case IrInstructionIdErrToInt:
|
||||
ir_print_err_to_int(irp, (IrInstructionErrToInt *)instruction);
|
||||
break;
|
||||
case IrInstructionIdCheckSwitchProngs:
|
||||
ir_print_check_switch_prongs(irp, (IrInstructionCheckSwitchProngs *)instruction);
|
||||
break;
|
||||
|
@ -3,8 +3,12 @@ const mem = @import("mem.zig");
|
||||
const debug = @import("debug.zig");
|
||||
const List = @import("list.zig").List;
|
||||
const Allocator = @import("mem.zig").Allocator;
|
||||
const os = @import("os/index.zig");
|
||||
const StdIo = os.ChildProcess.StdIo;
|
||||
const Term = os.ChildProcess.Term;
|
||||
|
||||
error ExtraArg;
|
||||
error UncleanExit;
|
||||
|
||||
pub const Builder = struct {
|
||||
zig_exe: []const u8,
|
||||
@ -33,9 +37,9 @@ pub const Builder = struct {
|
||||
return exe;
|
||||
}
|
||||
|
||||
pub fn make(self: &Builder, args: []const []const u8) -> %void {
|
||||
pub fn make(self: &Builder, cli_args: []const []const u8) -> %void {
|
||||
var verbose = false;
|
||||
for (args) |arg| {
|
||||
for (cli_args) |arg| {
|
||||
if (mem.eql(u8, arg, "--verbose")) {
|
||||
verbose = true;
|
||||
} else {
|
||||
@ -44,7 +48,27 @@ pub const Builder = struct {
|
||||
}
|
||||
}
|
||||
for (self.exe_list.toSlice()) |exe| {
|
||||
%%io.stderr.printf("TODO: invoke this command:\nzig build_exe {} --name {}\n", exe.root_src, exe.name);
|
||||
var zig_args = List([]const u8).init(self.allocator);
|
||||
defer zig_args.deinit();
|
||||
|
||||
%return zig_args.append("build_exe"[0...]); // TODO issue #296
|
||||
%return zig_args.append(exe.root_src);
|
||||
%return zig_args.append("--name"[0...]); // TODO issue #296
|
||||
%return zig_args.append(exe.name);
|
||||
|
||||
printInvocation(self.zig_exe, zig_args);
|
||||
const TODO_env: []const []const u8 = undefined; // TODO
|
||||
var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), TODO_env,
|
||||
StdIo.Ignore, StdIo.Inherit, StdIo.Inherit);
|
||||
const term = %return child.wait();
|
||||
switch (term) {
|
||||
Term.Clean => |code| {
|
||||
if (code != 0) {
|
||||
return error.UncleanExit;
|
||||
}
|
||||
},
|
||||
else => return error.UncleanExit,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -57,3 +81,11 @@ const Exe = struct {
|
||||
fn handleErr(err: error) -> noreturn {
|
||||
debug.panic("error: {}\n", @errorName(err));
|
||||
}
|
||||
|
||||
fn printInvocation(exe_name: []const u8, args: &const List([]const u8)) {
|
||||
%%io.stderr.printf("{}", exe_name);
|
||||
for (args.toSliceConst()) |arg| {
|
||||
%%io.stderr.printf(" {}", arg);
|
||||
}
|
||||
%%io.stderr.printf("\n");
|
||||
}
|
||||
|
13
std/io.zig
13
std/io.zig
@ -13,22 +13,18 @@ const mem = @import("mem.zig");
|
||||
const Buffer0 = @import("cstr.zig").Buffer0;
|
||||
const fmt = @import("fmt.zig");
|
||||
|
||||
pub const stdin_fileno = 0;
|
||||
pub const stdout_fileno = 1;
|
||||
pub const stderr_fileno = 2;
|
||||
|
||||
pub var stdin = InStream {
|
||||
.fd = stdin_fileno,
|
||||
.fd = system.STDIN_FILENO,
|
||||
};
|
||||
|
||||
pub var stdout = OutStream {
|
||||
.fd = stdout_fileno,
|
||||
.fd = system.STDOUT_FILENO,
|
||||
.buffer = undefined,
|
||||
.index = 0,
|
||||
};
|
||||
|
||||
pub var stderr = OutStream {
|
||||
.fd = stderr_fileno,
|
||||
.fd = system.STDERR_FILENO,
|
||||
.buffer = undefined,
|
||||
.index = 0,
|
||||
};
|
||||
@ -234,7 +230,6 @@ pub const InStream = struct {
|
||||
if (read_err > 0) {
|
||||
switch (read_err) {
|
||||
errno.EINTR => continue,
|
||||
|
||||
errno.EINVAL => unreachable,
|
||||
errno.EFAULT => unreachable,
|
||||
errno.EBADF => return error.BadFd,
|
||||
@ -247,7 +242,7 @@ pub const InStream = struct {
|
||||
}
|
||||
return index;
|
||||
},
|
||||
else => @compileError("unsupported OS"),
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,10 @@ const arch = switch (@compileVar("arch")) {
|
||||
|
||||
const errno = @import("errno.zig");
|
||||
|
||||
pub const STDIN_FILENO = 0;
|
||||
pub const STDOUT_FILENO = 1;
|
||||
pub const STDERR_FILENO = 2;
|
||||
|
||||
pub const O_LARGEFILE = 0x0000;
|
||||
pub const O_RDONLY = 0x0000;
|
||||
|
||||
|
343
std/os/index.zig
343
std/os/index.zig
@ -7,12 +7,26 @@ pub const posix = switch(@compileVar("os")) {
|
||||
Os.windows => windows,
|
||||
else => @compileError("Unsupported OS"),
|
||||
};
|
||||
const debug = @import("../debug.zig");
|
||||
const assert = debug.assert;
|
||||
|
||||
const errno = @import("errno.zig");
|
||||
const linking_libc = @import("../target.zig").linking_libc;
|
||||
const c = @import("../c/index.zig");
|
||||
|
||||
const mem = @import("../mem.zig");
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
const io = @import("../io.zig");
|
||||
|
||||
error Unexpected;
|
||||
error SysResources;
|
||||
error AccessDenied;
|
||||
error InvalidExe;
|
||||
error FileSystem;
|
||||
error IsDir;
|
||||
error FileNotFound;
|
||||
error FileBusy;
|
||||
|
||||
/// Fills `buf` with random bytes. If linking against libc, this calls the
|
||||
/// appropriate OS-specific library call. Otherwise it uses the zig standard
|
||||
@ -76,3 +90,332 @@ pub coldcc fn abort() -> noreturn {
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
|
||||
fn makePipe() -> %[2]i32 {
|
||||
var fds: [2]i32 = undefined;
|
||||
const err = posix.getErrno(posix.pipe(&fds));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
errno.EMFILE, errno.ENFILE => error.SysResources,
|
||||
else => error.Unexpected,
|
||||
}
|
||||
}
|
||||
return fds;
|
||||
}
|
||||
|
||||
fn destroyPipe(pipe: &const [2]i32) {
|
||||
closeNoIntr((*pipe)[0]);
|
||||
closeNoIntr((*pipe)[1]);
|
||||
}
|
||||
|
||||
fn closeNoIntr(fd: i32) {
|
||||
while (true) {
|
||||
const err = posix.getErrno(posix.close(fd));
|
||||
if (err == errno.EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn openNoIntr(path: []const u8, flags: usize, perm: usize) -> %i32 {
|
||||
while (true) {
|
||||
const result = posix.open(path, flags, perm);
|
||||
const err = posix.getErrno(result);
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
errno.EINTR => continue,
|
||||
|
||||
errno.EFAULT => unreachable,
|
||||
errno.EINVAL => unreachable,
|
||||
errno.EACCES => error.BadPerm,
|
||||
errno.EFBIG, errno.EOVERFLOW => error.FileTooBig,
|
||||
errno.EISDIR => error.IsDir,
|
||||
errno.ELOOP => error.SymLinkLoop,
|
||||
errno.EMFILE => error.ProcessFdQuotaExceeded,
|
||||
errno.ENAMETOOLONG => error.NameTooLong,
|
||||
errno.ENFILE => error.SystemFdQuotaExceeded,
|
||||
errno.ENODEV => error.NoDevice,
|
||||
errno.ENOENT => error.PathNotFound,
|
||||
errno.ENOMEM => error.NoMem,
|
||||
errno.ENOSPC => error.NoSpaceLeft,
|
||||
errno.ENOTDIR => error.NotDir,
|
||||
errno.EPERM => error.BadPerm,
|
||||
else => error.Unexpected,
|
||||
}
|
||||
}
|
||||
return i32(result);
|
||||
}
|
||||
}
|
||||
|
||||
const ErrInt = @intType(false, @sizeOf(error) * 8);
|
||||
fn writeIntFd(fd: i32, value: ErrInt) -> %void {
|
||||
var bytes: [@sizeOf(ErrInt)]u8 = undefined;
|
||||
mem.writeInt(bytes[0...], value, true);
|
||||
|
||||
var index: usize = 0;
|
||||
while (index < bytes.len) {
|
||||
const amt_written = posix.write(fd, &bytes[index], bytes.len - index);
|
||||
const err = posix.getErrno(amt_written);
|
||||
if (err > 0) {
|
||||
switch (err) {
|
||||
errno.EINTR => continue,
|
||||
errno.EINVAL => unreachable,
|
||||
else => return error.SysResources,
|
||||
}
|
||||
}
|
||||
index += amt_written;
|
||||
}
|
||||
}
|
||||
|
||||
fn readIntFd(fd: i32) -> %ErrInt {
|
||||
var bytes: [@sizeOf(ErrInt)]u8 = undefined;
|
||||
|
||||
var index: usize = 0;
|
||||
while (index < bytes.len) {
|
||||
const amt_written = posix.read(fd, &bytes[index], bytes.len - index);
|
||||
const err = posix.getErrno(amt_written);
|
||||
if (err > 0) {
|
||||
switch (err) {
|
||||
errno.EINTR => continue,
|
||||
errno.EINVAL => unreachable,
|
||||
else => return error.SysResources,
|
||||
}
|
||||
}
|
||||
index += amt_written;
|
||||
}
|
||||
|
||||
return mem.readInt(bytes[0...], ErrInt, true);
|
||||
}
|
||||
|
||||
// Child of fork calls this to report an error to the fork parent.
|
||||
// Then the child exits.
|
||||
fn forkChildErrReport(fd: i32, err: error) -> noreturn {
|
||||
_ = writeIntFd(fd, ErrInt(err));
|
||||
posix.exit(1);
|
||||
}
|
||||
|
||||
fn dup2NoIntr(old_fd: i32, new_fd: i32) -> %void {
|
||||
while (true) {
|
||||
const err = posix.getErrno(posix.dup2(old_fd, new_fd));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
errno.EBUSY, errno.EINTR => continue,
|
||||
errno.EMFILE => error.SysResources,
|
||||
errno.EINVAL => unreachable,
|
||||
else => error.Unexpected,
|
||||
};
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pub const ChildProcess = struct {
|
||||
pid: i32,
|
||||
err_pipe: [2]i32,
|
||||
|
||||
stdin: ?io.OutStream,
|
||||
stdout: ?io.InStream,
|
||||
stderr: ?io.InStream,
|
||||
|
||||
pub const Term = enum {
|
||||
Clean: i32,
|
||||
Signal: i32,
|
||||
Stopped: i32,
|
||||
Unknown: i32,
|
||||
};
|
||||
|
||||
pub const StdIo = enum {
|
||||
Inherit,
|
||||
Ignore,
|
||||
Pipe,
|
||||
Close,
|
||||
};
|
||||
|
||||
pub fn spawn(exe_path: []const u8, args: []const []const u8, env: []const []const u8,
|
||||
stdin: StdIo, stdout: StdIo, stderr: StdIo) -> %ChildProcess
|
||||
{
|
||||
switch (@compileVar("os")) {
|
||||
Os.linux, Os.macosx, Os.ios, Os.darwin => {
|
||||
return spawnPosix(exe_path, args, env, stdin, stdout, stderr);
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait(self: &ChildProcess) -> %Term {
|
||||
defer {
|
||||
closeNoIntr(self.err_pipe[0]);
|
||||
closeNoIntr(self.err_pipe[1]);
|
||||
};
|
||||
|
||||
var status: i32 = undefined;
|
||||
while (true) {
|
||||
const err = posix.getErrno(posix.waitpid(self.pid, &status, 0));
|
||||
if (err > 0) {
|
||||
switch (err) {
|
||||
errno.EINVAL, errno.ECHILD => unreachable,
|
||||
errno.EINTR => continue,
|
||||
else => {
|
||||
if (const *stdin ?= self.stdin) { stdin.close(); }
|
||||
if (const *stdout ?= self.stdin) { stdout.close(); }
|
||||
if (const *stderr ?= self.stdin) { stderr.close(); }
|
||||
return error.Unexpected;
|
||||
},
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (const *stdin ?= self.stdin) { stdin.close(); }
|
||||
if (const *stdout ?= self.stdin) { stdout.close(); }
|
||||
if (const *stderr ?= self.stdin) { stderr.close(); }
|
||||
|
||||
// Write @maxValue(ErrInt) to the write end of the err_pipe. This is after
|
||||
// waitpid, so this write is guaranteed to be after the child
|
||||
// pid potentially wrote an error. This way we can do a blocking
|
||||
// read on the error pipe and either get @maxValue(ErrInt) (no error) or
|
||||
// an error code.
|
||||
%return writeIntFd(self.err_pipe[1], @maxValue(ErrInt));
|
||||
const err_int = %return readIntFd(self.err_pipe[0]);
|
||||
// Here we potentially return the fork child's error
|
||||
// from the parent pid.
|
||||
if (err_int != @maxValue(ErrInt)) {
|
||||
return error(err_int);
|
||||
}
|
||||
|
||||
return statusToTerm(status);
|
||||
}
|
||||
|
||||
fn statusToTerm(status: i32) -> Term {
|
||||
return if (posix.WIFEXITED(status)) {
|
||||
Term.Clean { posix.WEXITSTATUS(status) }
|
||||
} else if (posix.WIFSIGNALED(status)) {
|
||||
Term.Signal { posix.WTERMSIG(status) }
|
||||
} else if (posix.WIFSTOPPED(status)) {
|
||||
Term.Stopped { posix.WSTOPSIG(status) }
|
||||
} else {
|
||||
Term.Unknown { status }
|
||||
};
|
||||
}
|
||||
|
||||
fn spawnPosix(exe_path: []const u8, args: []const []const u8, env: []const []const u8,
|
||||
stdin: StdIo, stdout: StdIo, stderr: StdIo) -> %ChildProcess
|
||||
{
|
||||
// TODO issue #295
|
||||
//const stdin_pipe = if (stdin == StdIo.Pipe) %return makePipe() else undefined;
|
||||
var stdin_pipe: [2]i32 = undefined;
|
||||
if (stdin == StdIo.Pipe)
|
||||
stdin_pipe = %return makePipe();
|
||||
%defer if (stdin == StdIo.Pipe) { destroyPipe(stdin_pipe); };
|
||||
|
||||
// TODO issue #295
|
||||
//const stdout_pipe = if (stdout == StdIo.Pipe) %return makePipe() else undefined;
|
||||
var stdout_pipe: [2]i32 = undefined;
|
||||
if (stdout == StdIo.Pipe)
|
||||
stdout_pipe = %return makePipe();
|
||||
%defer if (stdout == StdIo.Pipe) { destroyPipe(stdout_pipe); };
|
||||
|
||||
// TODO issue #295
|
||||
//const stderr_pipe = if (stderr == StdIo.Pipe) %return makePipe() else undefined;
|
||||
var stderr_pipe: [2]i32 = undefined;
|
||||
if (stderr == StdIo.Pipe)
|
||||
stderr_pipe = %return makePipe();
|
||||
%defer if (stderr == StdIo.Pipe) { destroyPipe(stderr_pipe); };
|
||||
|
||||
const any_ignore = (stdin == StdIo.Ignore or stdout == StdIo.Ignore or stderr == StdIo.Ignore);
|
||||
// TODO issue #295
|
||||
//const dev_null_fd = if (any_ignore) {
|
||||
// %return openNoIntr("/dev/null", posix.O_RDWR, 0)
|
||||
//} else {
|
||||
// undefined
|
||||
//};
|
||||
var dev_null_fd: i32 = undefined;
|
||||
if (any_ignore)
|
||||
dev_null_fd = %return openNoIntr("/dev/null", posix.O_RDWR, 0);
|
||||
|
||||
// This pipe is used to communicate errors between the time of fork
|
||||
// and execve from the child process to the parent process.
|
||||
const err_pipe = %return makePipe();
|
||||
%defer destroyPipe(err_pipe);
|
||||
|
||||
const pid = posix.fork();
|
||||
const pid_err = linux.getErrno(pid);
|
||||
if (pid_err > 0) {
|
||||
return switch (pid_err) {
|
||||
errno.EAGAIN, errno.ENOMEM, errno.ENOSYS => error.SysResources,
|
||||
else => error.Unexpected,
|
||||
};
|
||||
}
|
||||
if (pid == 0) {
|
||||
// we are the child
|
||||
setUpChildIo(stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) %%
|
||||
|err| forkChildErrReport(err_pipe[1], err);
|
||||
setUpChildIo(stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) %%
|
||||
|err| forkChildErrReport(err_pipe[1], err);
|
||||
setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
|
||||
|err| forkChildErrReport(err_pipe[1], err);
|
||||
|
||||
const err = posix.getErrno(posix.execve(exe_path, args, env));
|
||||
assert(err > 0);
|
||||
forkChildErrReport(err_pipe[1], switch (err) {
|
||||
errno.EFAULT => unreachable,
|
||||
errno.E2BIG, errno.EMFILE, errno.ENAMETOOLONG, errno.ENFILE, errno.ENOMEM => error.SysResources,
|
||||
errno.EACCES, errno.EPERM => error.AccessDenied,
|
||||
errno.EINVAL, errno.ENOEXEC => error.InvalidExe,
|
||||
errno.EIO, errno.ELOOP => error.FileSystem,
|
||||
errno.EISDIR => error.IsDir,
|
||||
errno.ENOENT, errno.ENOTDIR => error.FileNotFound,
|
||||
errno.ETXTBSY => error.FileBusy,
|
||||
else => error.Unexpected,
|
||||
});
|
||||
}
|
||||
|
||||
// we are the parent
|
||||
if (stdin == StdIo.Pipe) { closeNoIntr(stdin_pipe[0]); }
|
||||
if (stdout == StdIo.Pipe) { closeNoIntr(stdout_pipe[1]); }
|
||||
if (stderr == StdIo.Pipe) { closeNoIntr(stderr_pipe[1]); }
|
||||
if (any_ignore) { closeNoIntr(dev_null_fd); }
|
||||
|
||||
return ChildProcess {
|
||||
.pid = i32(pid),
|
||||
.err_pipe = err_pipe,
|
||||
|
||||
.stdin = if (stdin == StdIo.Pipe) {
|
||||
io.OutStream {
|
||||
.fd = stdin_pipe[1],
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
.stdout = if (stdout == StdIo.Pipe) {
|
||||
io.InStream {
|
||||
.fd = stdout_pipe[0],
|
||||
.buffer = undefined,
|
||||
.index = 0,
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
.stderr = if (stderr == StdIo.Pipe) {
|
||||
io.InStream {
|
||||
.fd = stderr_pipe[0],
|
||||
.buffer = undefined,
|
||||
.index = 0,
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {
|
||||
switch (stdio) {
|
||||
StdIo.Pipe => %return dup2NoIntr(pipe_fd, std_fileno),
|
||||
StdIo.Close => closeNoIntr(std_fileno),
|
||||
StdIo.Inherit => {},
|
||||
StdIo.Ignore => %return dup2NoIntr(dev_null_fd, std_fileno),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -5,6 +5,10 @@ const arch = switch (@compileVar("arch")) {
|
||||
};
|
||||
const errno = @import("errno.zig");
|
||||
|
||||
pub const STDIN_FILENO = 0;
|
||||
pub const STDOUT_FILENO = 1;
|
||||
pub const STDERR_FILENO = 2;
|
||||
|
||||
pub const PROT_NONE = 0;
|
||||
pub const PROT_READ = 1;
|
||||
pub const PROT_WRITE = 2;
|
||||
@ -237,12 +241,66 @@ pub const AF_NFC = PF_NFC;
|
||||
pub const AF_VSOCK = PF_VSOCK;
|
||||
pub const AF_MAX = PF_MAX;
|
||||
|
||||
|
||||
fn unsigned(s: i32) -> u32 { *@ptrcast(&u32, &s) }
|
||||
fn signed(s: u32) -> i32 { *@ptrcast(&i32, &s) }
|
||||
pub fn WEXITSTATUS(s: i32) -> i32 { signed((unsigned(s) & 0xff00) >> 8) }
|
||||
pub fn WTERMSIG(s: i32) -> i32 { signed(unsigned(s) & 0x7f) }
|
||||
pub fn WSTOPSIG(s: i32) -> i32 { WEXITSTATUS(s) }
|
||||
pub fn WIFEXITED(s: i32) -> bool { WTERMSIG(s) == 0 }
|
||||
pub fn WIFSTOPPED(s: i32) -> bool { (u16)(((unsigned(s)&0xffff)*%0x10001)>>8) > 0x7f00 }
|
||||
pub fn WIFSIGNALED(s: i32) -> bool { (unsigned(s)&0xffff)-%1 < 0xff }
|
||||
|
||||
/// Get the errno from a syscall return value, or 0 for no error.
|
||||
pub fn getErrno(r: usize) -> usize {
|
||||
const signed_r = *@ptrcast(&isize, &r);
|
||||
if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0
|
||||
}
|
||||
|
||||
pub fn dup2(old: i32, new: i32) -> usize {
|
||||
arch.syscall2(arch.SYS_dup2, usize(old), usize(new))
|
||||
}
|
||||
|
||||
pub fn execve_c(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) -> usize {
|
||||
arch.syscall3(arch.SYS_execve, path, argv, envp)
|
||||
}
|
||||
|
||||
/// This function must allocate memory to add a null terminating bytes on path, each arg,
|
||||
/// and each environment variable line, as well as a null pointer after the arg list and
|
||||
/// environment variable list. We allocate stack memory since the process is about to get
|
||||
/// wiped anyway.
|
||||
pub fn execve(path: []const u8, argv: []const []const u8, envp: []const []const u8) -> usize {
|
||||
const path_buf = @alloca(u8, path.len + 1);
|
||||
@memcpy(&path_buf[0], &path[0], path.len);
|
||||
path_buf[path.len] = 0;
|
||||
|
||||
const argv_buf = @alloca([]const ?&const u8, argv.len + 1);
|
||||
for (argv) |arg, i| {
|
||||
const arg_buf = @alloca(u8, arg.len + 1);
|
||||
@memcpy(&arg_buf[0], &arg[0], arg.len);
|
||||
arg_buf[arg.len] = 0;
|
||||
|
||||
argv[i] = arg_buf;
|
||||
}
|
||||
argv_buf[argv.len] = null;
|
||||
|
||||
const envp_buf = @alloca([]const ?&const u8, envp.len + 1);
|
||||
for (envp) |env, i| {
|
||||
const env_buf = @alloca(u8, env.len + 1);
|
||||
@memcpy(&env_buf[0], &env[0], env.len);
|
||||
env_buf[env.len] = 0;
|
||||
|
||||
envp[i] = env_buf;
|
||||
}
|
||||
envp_buf[envp.len] = null;
|
||||
|
||||
return execve_c(path_buf.ptr, argv_buf.ptr, envp_buf.ptr);
|
||||
}
|
||||
|
||||
pub fn fork() -> usize {
|
||||
arch.syscall0(arch.SYS_fork)
|
||||
}
|
||||
|
||||
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: usize)
|
||||
-> usize
|
||||
{
|
||||
@ -261,6 +319,14 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) -> usize {
|
||||
arch.syscall4(arch.SYS_pread, usize(fd), usize(buf), count, offset)
|
||||
}
|
||||
|
||||
pub fn pipe(fd: &[2]i32) -> usize {
|
||||
pipe2(fd, 0)
|
||||
}
|
||||
|
||||
pub fn pipe2(fd: &[2]i32, flags: usize) -> usize {
|
||||
arch.syscall2(arch.SYS_pipe2, usize(fd), flags)
|
||||
}
|
||||
|
||||
pub fn write(fd: i32, buf: &const u8, count: usize) -> usize {
|
||||
arch.syscall3(arch.SYS_write, usize(fd), usize(buf), count)
|
||||
}
|
||||
@ -319,8 +385,12 @@ pub fn getrandom(buf: &u8, count: usize, flags: u32) -> usize {
|
||||
arch.syscall3(arch.SYS_getrandom, usize(buf), count, usize(flags))
|
||||
}
|
||||
|
||||
pub fn kill(pid: i32, sig: i32) -> i32 {
|
||||
i32(arch.syscall2(arch.SYS_kill, usize(pid), usize(sig)))
|
||||
pub fn kill(pid: i32, sig: i32) -> usize {
|
||||
arch.syscall2(arch.SYS_kill, usize(pid), usize(sig))
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: i32, status: &i32, options: i32) -> usize {
|
||||
arch.syscall4(arch.SYS_wait4, usize(pid), usize(status), usize(options), 0)
|
||||
}
|
||||
|
||||
const NSIG = 65;
|
||||
|
@ -30,3 +30,14 @@ test "implicitly cast a pointer to a const pointer of it" {
|
||||
fn funcWithConstPtrPtr(x: &const &i32) {
|
||||
**x += 1;
|
||||
}
|
||||
|
||||
error ItBroke;
|
||||
test "explicit cast from integer to error type" {
|
||||
testCastIntToErr(error.ItBroke);
|
||||
comptime testCastIntToErr(error.ItBroke);
|
||||
}
|
||||
fn testCastIntToErr(err: error) {
|
||||
const x = usize(err);
|
||||
const y = error(x);
|
||||
assert(error.ItBroke == y);
|
||||
}
|
||||
|
@ -1758,6 +1758,13 @@ export fn foo() {
|
||||
}
|
||||
)SOURCE", 1, ".tmp_source.zig:3:5: error: unable to infer variable type");
|
||||
|
||||
add_compile_fail_case("undefined literal on a non-comptime var", R"SOURCE(
|
||||
export fn foo() {
|
||||
var i = undefined;
|
||||
i = i32(1);
|
||||
}
|
||||
)SOURCE", 1, ".tmp_source.zig:3:5: error: unable to infer variable type");
|
||||
|
||||
add_compile_fail_case("dereference an array", R"SOURCE(
|
||||
var s_buffer: [10]u8 = undefined;
|
||||
pub fn pass(in: []u8) -> []u8 {
|
||||
@ -1818,6 +1825,15 @@ export fn entry(a: &i32) -> usize {
|
||||
return @ptrcast(usize, a);
|
||||
}
|
||||
)SOURCE", 1, ".tmp_source.zig:3:21: error: expected pointer, found 'usize'");
|
||||
|
||||
add_compile_fail_case("too many error values to cast to small integer", R"SOURCE(
|
||||
error A; error B; error C; error D; error E; error F; error G; error H;
|
||||
const u2 = @intType(false, 2);
|
||||
fn foo(e: error) -> u2 {
|
||||
return u2(e);
|
||||
}
|
||||
export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
|
||||
)SOURCE", 1, ".tmp_source.zig:5:14: error: too many error values to fit in 'u2'");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@ -2043,6 +2059,18 @@ fn bar() -> %void {
|
||||
}
|
||||
)SOURCE");
|
||||
|
||||
add_debug_safety_case("cast integer to error and no code matches", R"SOURCE(
|
||||
pub fn panic(message: []const u8) -> noreturn {
|
||||
@breakpoint();
|
||||
while (true) {}
|
||||
}
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
_ = bar(9999);
|
||||
}
|
||||
fn bar(x: u32) -> error {
|
||||
return error(x);
|
||||
}
|
||||
)SOURCE");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
x
Reference in New Issue
Block a user