lots of miscellaneous things all in one big commit
* add `@compileLog(...)` builtin function - Helps debug code running at compile time - See #240 * fix crash when there is an error on the start value of a slice * add implicit cast from int and float types to int and float literals if the value is known at compile time * make array concatenation work with slices in addition to arrays and c string literals * fix compile error message for something not having field access * fix crash when `@setDebugSafety()` was called from a function being evaluated at compile-time * fix compile-time evaluation of overflow math builtins. * avoid debug safety panic handler in builtin.o and compiler_rt.o since we use no debug safety in these modules anyway * add compiler_rt functions for division on ARM - Closes #254 * move default panic handler to std.debug so users can call it manually * std.io.printf supports a width in the format specifier
This commit is contained in:
parent
8a859afd58
commit
fc100d7b3b
@ -639,10 +639,23 @@ const b: u8 = @truncate(u8, a);
|
||||
|
||||
### @compileError(comptime msg: []u8)
|
||||
|
||||
This function, when semantically analyzed, causes a compile error with the message `msg`.
|
||||
This function, when semantically analyzed, causes a compile error with the
|
||||
message `msg`.
|
||||
|
||||
There are several ways that code avoids being semantically checked, such as using `if`
|
||||
or `switch` with compile time constants, and comptime functions.
|
||||
There are several ways that code avoids being semantically checked, such as
|
||||
using `if` or `switch` with compile time constants, and comptime functions.
|
||||
|
||||
### @compileLog(args: ...)
|
||||
|
||||
This function, when semantically analyzed, causes a compile error, but it does
|
||||
not prevent compile-time code from continuing to run, and it otherwise does not
|
||||
interfere with analysis.
|
||||
|
||||
Each of the arguments will be serialized to a printable debug value and output
|
||||
to stderr, and then a newline at the end.
|
||||
|
||||
This function can be used to do "printf debugging" on compile-time executing
|
||||
code.
|
||||
|
||||
### @intType(comptime is_signed: bool, comptime bit_count: u8) -> type
|
||||
|
||||
|
@ -1096,6 +1096,7 @@ enum BuiltinFnId {
|
||||
BuiltinFnIdCUndef,
|
||||
BuiltinFnIdCompileVar,
|
||||
BuiltinFnIdCompileErr,
|
||||
BuiltinFnIdCompileLog,
|
||||
BuiltinFnIdGeneratedCode,
|
||||
BuiltinFnIdCtz,
|
||||
BuiltinFnIdClz,
|
||||
@ -1541,6 +1542,7 @@ enum IrInstructionId {
|
||||
IrInstructionIdMinValue,
|
||||
IrInstructionIdMaxValue,
|
||||
IrInstructionIdCompileErr,
|
||||
IrInstructionIdCompileLog,
|
||||
IrInstructionIdErrName,
|
||||
IrInstructionIdEmbedFile,
|
||||
IrInstructionIdCmpxchg,
|
||||
@ -1993,6 +1995,13 @@ struct IrInstructionCompileErr {
|
||||
IrInstruction *msg;
|
||||
};
|
||||
|
||||
struct IrInstructionCompileLog {
|
||||
IrInstruction base;
|
||||
|
||||
size_t msg_count;
|
||||
IrInstruction **msg_list;
|
||||
};
|
||||
|
||||
struct IrInstructionErrName {
|
||||
IrInstruction base;
|
||||
|
||||
|
@ -3439,7 +3439,8 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *
|
||||
void render_const_value(Buf *buf, ConstExprValue *const_val) {
|
||||
switch (const_val->special) {
|
||||
case ConstValSpecialRuntime:
|
||||
zig_unreachable();
|
||||
buf_appendf(buf, "(runtime value)");
|
||||
return;
|
||||
case ConstValSpecialUndef:
|
||||
buf_appendf(buf, "undefined");
|
||||
return;
|
||||
@ -3522,7 +3523,28 @@ void render_const_value(Buf *buf, ConstExprValue *const_val) {
|
||||
}
|
||||
case TypeTableEntryIdArray:
|
||||
{
|
||||
TypeTableEntry *child_type = canon_type->data.array.child_type;
|
||||
uint64_t len = canon_type->data.array.len;
|
||||
|
||||
// if it's []u8, assume UTF-8 and output a string
|
||||
if (child_type->id == TypeTableEntryIdInt &&
|
||||
child_type->data.integral.bit_count == 8 &&
|
||||
!child_type->data.integral.is_signed)
|
||||
{
|
||||
buf_append_char(buf, '"');
|
||||
for (uint64_t i = 0; i < len; i += 1) {
|
||||
ConstExprValue *child_value = &const_val->data.x_array.elements[i];
|
||||
uint64_t x = child_value->data.x_bignum.data.x_uint;
|
||||
if (x == '"') {
|
||||
buf_append_str(buf, "\\\"");
|
||||
} else {
|
||||
buf_append_char(buf, x);
|
||||
}
|
||||
}
|
||||
buf_append_char(buf, '"');
|
||||
return;
|
||||
}
|
||||
|
||||
buf_appendf(buf, "%s{", buf_ptr(&canon_type->name));
|
||||
for (uint64_t i = 0; i < len; i += 1) {
|
||||
if (i != 0)
|
||||
|
@ -2377,6 +2377,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
case IrInstructionIdMinValue:
|
||||
case IrInstructionIdMaxValue:
|
||||
case IrInstructionIdCompileErr:
|
||||
case IrInstructionIdCompileLog:
|
||||
case IrInstructionIdArrayLen:
|
||||
case IrInstructionIdImport:
|
||||
case IrInstructionIdCImport:
|
||||
@ -3791,6 +3792,7 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
|
||||
create_builtin_fn(g, BuiltinFnIdIntType, "intType", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdUnreachable, "unreachable", 0);
|
||||
create_builtin_fn(g, BuiltinFnIdSetFnTest, "setFnTest", 1);
|
||||
|
131
src/ir.cpp
131
src/ir.cpp
@ -375,6 +375,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileErr *) {
|
||||
return IrInstructionIdCompileErr;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileLog *) {
|
||||
return IrInstructionIdCompileLog;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionErrName *) {
|
||||
return IrInstructionIdErrName;
|
||||
}
|
||||
@ -1510,6 +1514,20 @@ static IrInstruction *ir_build_compile_err(IrBuilder *irb, Scope *scope, AstNode
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_compile_log(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
size_t msg_count, IrInstruction **msg_list)
|
||||
{
|
||||
IrInstructionCompileLog *instruction = ir_build_instruction<IrInstructionCompileLog>(irb, scope, source_node);
|
||||
instruction->msg_count = msg_count;
|
||||
instruction->msg_list = msg_list;
|
||||
|
||||
for (size_t i = 0; i < msg_count; i += 1) {
|
||||
ir_ref_instruction(msg_list[i], irb->current_basic_block);
|
||||
}
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_err_name(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
|
||||
IrInstructionErrName *instruction = ir_build_instruction<IrInstructionErrName>(irb, scope, source_node);
|
||||
instruction->value = value;
|
||||
@ -2461,6 +2479,12 @@ static IrInstruction *ir_instruction_compileerr_get_dep(IrInstructionCompileErr
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_compilelog_get_dep(IrInstructionCompileLog *instruction, size_t index) {
|
||||
if (index < instruction->msg_count)
|
||||
return instruction->msg_list[index];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_errname_get_dep(IrInstructionErrName *instruction, size_t index) {
|
||||
switch (index) {
|
||||
case 0: return instruction->value;
|
||||
@ -2848,6 +2872,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
|
||||
return ir_instruction_maxvalue_get_dep((IrInstructionMaxValue *) instruction, index);
|
||||
case IrInstructionIdCompileErr:
|
||||
return ir_instruction_compileerr_get_dep((IrInstructionCompileErr *) instruction, index);
|
||||
case IrInstructionIdCompileLog:
|
||||
return ir_instruction_compilelog_get_dep((IrInstructionCompileLog *) instruction, index);
|
||||
case IrInstructionIdErrName:
|
||||
return ir_instruction_errname_get_dep((IrInstructionErrName *) instruction, index);
|
||||
case IrInstructionIdEmbedFile:
|
||||
@ -3767,7 +3793,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
||||
BuiltinFnEntry *builtin_fn = entry->value;
|
||||
size_t actual_param_count = node->data.fn_call_expr.params.length;
|
||||
|
||||
if (builtin_fn->param_count != actual_param_count) {
|
||||
if (builtin_fn->param_count != SIZE_MAX && builtin_fn->param_count != actual_param_count) {
|
||||
add_node_error(irb->codegen, node,
|
||||
buf_sprintf("expected %zu arguments, found %zu",
|
||||
builtin_fn->param_count, actual_param_count));
|
||||
@ -3958,6 +3984,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
||||
|
||||
return ir_build_compile_err(irb, scope, node, arg0_value);
|
||||
}
|
||||
case BuiltinFnIdCompileLog:
|
||||
{
|
||||
IrInstruction **args = allocate<IrInstruction*>(actual_param_count);
|
||||
|
||||
for (size_t i = 0; i < actual_param_count; i += 1) {
|
||||
AstNode *arg_node = node->data.fn_call_expr.params.at(i);
|
||||
args[i] = ir_gen_node(irb, arg_node, scope);
|
||||
if (args[i] == irb->codegen->invalid_instruction)
|
||||
return irb->codegen->invalid_instruction;
|
||||
}
|
||||
|
||||
return ir_build_compile_log(irb, scope, node, actual_param_count, args);
|
||||
}
|
||||
case BuiltinFnIdErrName:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
@ -5254,7 +5293,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node)
|
||||
return irb->codegen->invalid_instruction;
|
||||
|
||||
IrInstruction *start_value = ir_gen_node(irb, start_node, scope);
|
||||
if (ptr_value == irb->codegen->invalid_instruction)
|
||||
if (start_value == irb->codegen->invalid_instruction)
|
||||
return irb->codegen->invalid_instruction;
|
||||
|
||||
IrInstruction *end_value;
|
||||
@ -5800,6 +5839,16 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
|
||||
}
|
||||
}
|
||||
|
||||
// implicit typed number to integer or float literal.
|
||||
// works when the number is known
|
||||
if (value->value.special == ConstValSpecialStatic) {
|
||||
if (actual_type->id == TypeTableEntryIdInt && expected_type->id == TypeTableEntryIdNumLitInt) {
|
||||
return ImplicitCastMatchResultYes;
|
||||
} else if (actual_type->id == TypeTableEntryIdFloat && expected_type->id == TypeTableEntryIdNumLitFloat) {
|
||||
return ImplicitCastMatchResultYes;
|
||||
}
|
||||
}
|
||||
|
||||
// implicit undefined literal to anything
|
||||
if (actual_type->id == TypeTableEntryIdUndefLit) {
|
||||
return ImplicitCastMatchResultYes;
|
||||
@ -6654,6 +6703,19 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour
|
||||
return result;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction *source_instr,
|
||||
IrInstruction *target, TypeTableEntry *wanted_type)
|
||||
{
|
||||
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, true);
|
||||
bignum_init_bignum(&result->value.data.x_bignum, &val->data.x_bignum);
|
||||
return result;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
|
||||
TypeTableEntry *wanted_type, IrInstruction *value)
|
||||
{
|
||||
@ -6858,6 +6920,15 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
|
||||
}
|
||||
}
|
||||
|
||||
// explicit cast from typed number to integer or float literal.
|
||||
// works when the number is known at compile time
|
||||
if (instr_is_comptime(value) &&
|
||||
((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdNumLitInt) ||
|
||||
(actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdNumLitFloat)))
|
||||
{
|
||||
return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type);
|
||||
}
|
||||
|
||||
// explicit cast from %void to integer type which can fit it
|
||||
bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion &&
|
||||
!type_has_bits(actual_type->data.error.child_type);
|
||||
@ -7552,6 +7623,13 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *
|
||||
op1_array_val = op1_val->data.x_ptr.base_ptr;
|
||||
op1_array_index = op1_val->data.x_ptr.index;
|
||||
op1_array_end = op1_array_val->data.x_array.size - 1;
|
||||
} else if (is_slice(op1_canon_type)) {
|
||||
TypeTableEntry *ptr_type = op1_canon_type->data.structure.fields[slice_ptr_index].type_entry;
|
||||
child_type = ptr_type->data.pointer.child_type;
|
||||
ConstExprValue *ptr_val = &op1_val->data.x_struct.fields[slice_ptr_index];
|
||||
op1_array_val = ptr_val->data.x_ptr.base_ptr;
|
||||
op1_array_index = ptr_val->data.x_ptr.index;
|
||||
op1_array_end = op1_array_val->data.x_array.size;
|
||||
} else {
|
||||
ir_add_error(ira, op1,
|
||||
buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->value.type->name)));
|
||||
@ -7585,6 +7663,18 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp *
|
||||
op2_array_val = op2_val->data.x_ptr.base_ptr;
|
||||
op2_array_index = op2_val->data.x_ptr.index;
|
||||
op2_array_end = op2_array_val->data.x_array.size - 1;
|
||||
} else if (is_slice(op2_canon_type)) {
|
||||
TypeTableEntry *ptr_type = op2_canon_type->data.structure.fields[slice_ptr_index].type_entry;
|
||||
if (ptr_type->data.pointer.child_type != child_type) {
|
||||
ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'",
|
||||
buf_ptr(&child_type->name),
|
||||
buf_ptr(&op2->value.type->name)));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
ConstExprValue *ptr_val = &op2_val->data.x_struct.fields[slice_ptr_index];
|
||||
op2_array_val = ptr_val->data.x_ptr.base_ptr;
|
||||
op2_array_index = ptr_val->data.x_ptr.index;
|
||||
op2_array_end = op2_array_val->data.x_array.size;
|
||||
} else {
|
||||
ir_add_error(ira, op2,
|
||||
buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name)));
|
||||
@ -9177,7 +9267,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
|
||||
}
|
||||
} else {
|
||||
ir_add_error(ira, &field_ptr_instruction->base,
|
||||
buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name)));
|
||||
buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name)));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
} else if (container_type->id == TypeTableEntryIdNamespace) {
|
||||
@ -9528,6 +9618,12 @@ static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira,
|
||||
if (!target_val)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
if (ira->new_irb.exec->is_inline) {
|
||||
// ignore setDebugSafety when running functions at compile time
|
||||
ir_build_const_from(ira, &set_debug_safety_instruction->base, false);
|
||||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
bool *safety_off_ptr;
|
||||
AstNode **safety_set_node_ptr;
|
||||
if (target_type->id == TypeTableEntryIdBlock) {
|
||||
@ -10703,6 +10799,26 @@ static TypeTableEntry *ir_analyze_instruction_compile_err(IrAnalyze *ira,
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_compile_log(IrAnalyze *ira, IrInstructionCompileLog *instruction) {
|
||||
Buf buf = BUF_INIT;
|
||||
fprintf(stderr, "| ");
|
||||
for (size_t i = 0; i < instruction->msg_count; i += 1) {
|
||||
IrInstruction *msg = instruction->msg_list[i]->other;
|
||||
if (msg->value.type->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
buf_resize(&buf, 0);
|
||||
render_const_value(&buf, &msg->value);
|
||||
const char *comma_str = (i != 0) ? ", " : "";
|
||||
fprintf(stderr, "%s%s", comma_str, buf_ptr(&buf));
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("found compile log statement"));
|
||||
|
||||
ir_build_const_from(ira, &instruction->base, false);
|
||||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstructionErrName *instruction) {
|
||||
IrInstruction *value = instruction->value->other;
|
||||
if (value->value.type->id == TypeTableEntryIdInvalid)
|
||||
@ -11602,13 +11718,13 @@ static TypeTableEntry *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInst
|
||||
out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
|
||||
break;
|
||||
case IrOverflowOpSub:
|
||||
out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
|
||||
out_val->data.x_bool = bignum_sub(dest_bignum, op1_bignum, op2_bignum);
|
||||
break;
|
||||
case IrOverflowOpMul:
|
||||
out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
|
||||
out_val->data.x_bool = bignum_mul(dest_bignum, op1_bignum, op2_bignum);
|
||||
break;
|
||||
case IrOverflowOpShl:
|
||||
out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum);
|
||||
out_val->data.x_bool = bignum_shl(dest_bignum, op1_bignum, op2_bignum);
|
||||
break;
|
||||
}
|
||||
if (!bignum_fits_in_bits(dest_bignum, canon_type->data.integral.bit_count,
|
||||
@ -12007,6 +12123,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||
return ir_analyze_instruction_max_value(ira, (IrInstructionMaxValue *)instruction);
|
||||
case IrInstructionIdCompileErr:
|
||||
return ir_analyze_instruction_compile_err(ira, (IrInstructionCompileErr *)instruction);
|
||||
case IrInstructionIdCompileLog:
|
||||
return ir_analyze_instruction_compile_log(ira, (IrInstructionCompileLog *)instruction);
|
||||
case IrInstructionIdErrName:
|
||||
return ir_analyze_instruction_err_name(ira, (IrInstructionErrName *)instruction);
|
||||
case IrInstructionIdTypeName:
|
||||
@ -12164,6 +12282,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
case IrInstructionIdSetDebugSafety:
|
||||
case IrInstructionIdImport:
|
||||
case IrInstructionIdCompileErr:
|
||||
case IrInstructionIdCompileLog:
|
||||
case IrInstructionIdCImport:
|
||||
case IrInstructionIdCInclude:
|
||||
case IrInstructionIdCDefine:
|
||||
|
@ -532,6 +532,17 @@ static void ir_print_compile_err(IrPrint *irp, IrInstructionCompileErr *instruct
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_compile_log(IrPrint *irp, IrInstructionCompileLog *instruction) {
|
||||
fprintf(irp->f, "@compileLog(");
|
||||
for (size_t i = 0; i < instruction->msg_count; i += 1) {
|
||||
if (i != 0)
|
||||
fprintf(irp->f, ",");
|
||||
IrInstruction *msg = instruction->msg_list[i];
|
||||
ir_print_other_instruction(irp, msg);
|
||||
}
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_err_name(IrPrint *irp, IrInstructionErrName *instruction) {
|
||||
fprintf(irp->f, "@errorName(");
|
||||
ir_print_other_instruction(irp, instruction->value);
|
||||
@ -990,6 +1001,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdCompileErr:
|
||||
ir_print_compile_err(irp, (IrInstructionCompileErr *)instruction);
|
||||
break;
|
||||
case IrInstructionIdCompileLog:
|
||||
ir_print_compile_log(irp, (IrInstructionCompileLog *)instruction);
|
||||
break;
|
||||
case IrInstructionIdErrName:
|
||||
ir_print_err_name(irp, (IrInstructionErrName *)instruction);
|
||||
break;
|
||||
|
@ -29,3 +29,8 @@ export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) {
|
||||
while (index != n; index += 1)
|
||||
d[index] = s[index];
|
||||
}
|
||||
|
||||
// Avoid dragging in the debug safety mechanisms into this .o file.
|
||||
pub fn panic(message: []const u8) -> unreachable {
|
||||
@unreachable();
|
||||
}
|
||||
|
@ -1,3 +1,13 @@
|
||||
// Avoid dragging in the debug safety mechanisms into this .o file,
|
||||
// unless we're trying to test this file.
|
||||
pub fn panic(message: []const u8) -> unreachable {
|
||||
if (@compileVar("is_test")) {
|
||||
@import("std").debug.panic(message);
|
||||
} else {
|
||||
@unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
const CHAR_BIT = 8;
|
||||
const du_int = u64;
|
||||
const di_int = i64;
|
||||
@ -212,6 +222,106 @@ export fn __umoddi3(a: du_int, b: du_int) -> du_int {
|
||||
return r;
|
||||
}
|
||||
|
||||
fn isArmArch() -> bool {
|
||||
return switch (@compileVar("arch")) {
|
||||
Arch.armv8_2a,
|
||||
Arch.armv8_1a,
|
||||
Arch.armv8,
|
||||
Arch.armv8m_baseline,
|
||||
Arch.armv8m_mainline,
|
||||
Arch.armv7,
|
||||
Arch.armv7em,
|
||||
Arch.armv7m,
|
||||
Arch.armv7s,
|
||||
Arch.armv7k,
|
||||
Arch.armv6,
|
||||
Arch.armv6m,
|
||||
Arch.armv6k,
|
||||
Arch.armv6t2,
|
||||
Arch.armv5,
|
||||
Arch.armv5te,
|
||||
Arch.armv4t,
|
||||
Arch.armeb => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
export nakedcc fn __aeabi_uidivmod() {
|
||||
@setDebugSafety(this, false);
|
||||
|
||||
if (comptime isArmArch()) {
|
||||
asm volatile (
|
||||
\\ push { lr }
|
||||
\\ sub sp, sp, #4
|
||||
\\ mov r2, sp
|
||||
\\ bl __udivmodsi4
|
||||
\\ ldr r1, [sp]
|
||||
\\ add sp, sp, #4
|
||||
\\ pop { pc }
|
||||
::: "r2", "r1");
|
||||
@unreachable();
|
||||
}
|
||||
|
||||
@setFnVisible(this, false);
|
||||
}
|
||||
|
||||
export fn __udivmodsi4(a: su_int, b: su_int, rem: &su_int) -> su_int {
|
||||
@setDebugSafety(this, false);
|
||||
|
||||
const d = __udivsi3(a, b);
|
||||
*rem = su_int(si_int(a) -% (si_int(d) * si_int(b)));
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
// TODO make this an alias instead of an extra function call
|
||||
// https://github.com/andrewrk/zig/issues/256
|
||||
|
||||
export fn __aeabi_uidiv(n: su_int, d: su_int) -> su_int {
|
||||
@setDebugSafety(this, false);
|
||||
|
||||
return __udivsi3(n, d);
|
||||
}
|
||||
|
||||
export fn __udivsi3(n: su_int, d: su_int) -> su_int {
|
||||
@setDebugSafety(this, false);
|
||||
|
||||
const n_uword_bits: c_uint = @sizeOf(su_int) * CHAR_BIT;
|
||||
// special cases
|
||||
if (d == 0)
|
||||
return 0; // ?!
|
||||
if (n == 0)
|
||||
return 0;
|
||||
var sr: c_uint = @clz(d) - @clz(n);
|
||||
// 0 <= sr <= n_uword_bits - 1 or sr large
|
||||
if (sr > n_uword_bits - 1) // d > r
|
||||
return 0;
|
||||
if (sr == n_uword_bits - 1) // d == 1
|
||||
return n;
|
||||
sr += 1;
|
||||
// 1 <= sr <= n_uword_bits - 1
|
||||
// Not a special case
|
||||
var q: su_int = n << (n_uword_bits - sr);
|
||||
var r: su_int = n >> sr;
|
||||
var carry: su_int = 0;
|
||||
while (sr > 0; sr -= 1) {
|
||||
// r:q = ((r:q) << 1) | carry
|
||||
r = (r << 1) | (q >> (n_uword_bits - 1));
|
||||
q = (q << 1) | carry;
|
||||
// carry = 0;
|
||||
// if (r.all >= d.all)
|
||||
// {
|
||||
// r.all -= d.all;
|
||||
// carry = 1;
|
||||
// }
|
||||
const s = si_int(d - r - 1) >> si_int(n_uword_bits - 1);
|
||||
carry = su_int(s & 1);
|
||||
r -= d & su_int(s);
|
||||
}
|
||||
q = (q << 1) | carry;
|
||||
return q;
|
||||
}
|
||||
|
||||
fn test_umoddi3() {
|
||||
@setFnTest(this);
|
||||
|
||||
@ -257,6 +367,155 @@ fn test_one_udivmoddi4(a: du_int, b: du_int, expected_q: du_int, expected_r: du_
|
||||
assert(r == expected_r);
|
||||
}
|
||||
|
||||
fn assert(b: bool) {
|
||||
if (!b) @unreachable();
|
||||
fn test_udivsi3() {
|
||||
@setFnTest(this);
|
||||
|
||||
const cases = [][3]su_int {
|
||||
[]su_int{0x00000000, 0x00000001, 0x00000000},
|
||||
[]su_int{0x00000000, 0x00000002, 0x00000000},
|
||||
[]su_int{0x00000000, 0x00000003, 0x00000000},
|
||||
[]su_int{0x00000000, 0x00000010, 0x00000000},
|
||||
[]su_int{0x00000000, 0x078644FA, 0x00000000},
|
||||
[]su_int{0x00000000, 0x0747AE14, 0x00000000},
|
||||
[]su_int{0x00000000, 0x7FFFFFFF, 0x00000000},
|
||||
[]su_int{0x00000000, 0x80000000, 0x00000000},
|
||||
[]su_int{0x00000000, 0xFFFFFFFD, 0x00000000},
|
||||
[]su_int{0x00000000, 0xFFFFFFFE, 0x00000000},
|
||||
[]su_int{0x00000000, 0xFFFFFFFF, 0x00000000},
|
||||
[]su_int{0x00000001, 0x00000001, 0x00000001},
|
||||
[]su_int{0x00000001, 0x00000002, 0x00000000},
|
||||
[]su_int{0x00000001, 0x00000003, 0x00000000},
|
||||
[]su_int{0x00000001, 0x00000010, 0x00000000},
|
||||
[]su_int{0x00000001, 0x078644FA, 0x00000000},
|
||||
[]su_int{0x00000001, 0x0747AE14, 0x00000000},
|
||||
[]su_int{0x00000001, 0x7FFFFFFF, 0x00000000},
|
||||
[]su_int{0x00000001, 0x80000000, 0x00000000},
|
||||
[]su_int{0x00000001, 0xFFFFFFFD, 0x00000000},
|
||||
[]su_int{0x00000001, 0xFFFFFFFE, 0x00000000},
|
||||
[]su_int{0x00000001, 0xFFFFFFFF, 0x00000000},
|
||||
[]su_int{0x00000002, 0x00000001, 0x00000002},
|
||||
[]su_int{0x00000002, 0x00000002, 0x00000001},
|
||||
[]su_int{0x00000002, 0x00000003, 0x00000000},
|
||||
[]su_int{0x00000002, 0x00000010, 0x00000000},
|
||||
[]su_int{0x00000002, 0x078644FA, 0x00000000},
|
||||
[]su_int{0x00000002, 0x0747AE14, 0x00000000},
|
||||
[]su_int{0x00000002, 0x7FFFFFFF, 0x00000000},
|
||||
[]su_int{0x00000002, 0x80000000, 0x00000000},
|
||||
[]su_int{0x00000002, 0xFFFFFFFD, 0x00000000},
|
||||
[]su_int{0x00000002, 0xFFFFFFFE, 0x00000000},
|
||||
[]su_int{0x00000002, 0xFFFFFFFF, 0x00000000},
|
||||
[]su_int{0x00000003, 0x00000001, 0x00000003},
|
||||
[]su_int{0x00000003, 0x00000002, 0x00000001},
|
||||
[]su_int{0x00000003, 0x00000003, 0x00000001},
|
||||
[]su_int{0x00000003, 0x00000010, 0x00000000},
|
||||
[]su_int{0x00000003, 0x078644FA, 0x00000000},
|
||||
[]su_int{0x00000003, 0x0747AE14, 0x00000000},
|
||||
[]su_int{0x00000003, 0x7FFFFFFF, 0x00000000},
|
||||
[]su_int{0x00000003, 0x80000000, 0x00000000},
|
||||
[]su_int{0x00000003, 0xFFFFFFFD, 0x00000000},
|
||||
[]su_int{0x00000003, 0xFFFFFFFE, 0x00000000},
|
||||
[]su_int{0x00000003, 0xFFFFFFFF, 0x00000000},
|
||||
[]su_int{0x00000010, 0x00000001, 0x00000010},
|
||||
[]su_int{0x00000010, 0x00000002, 0x00000008},
|
||||
[]su_int{0x00000010, 0x00000003, 0x00000005},
|
||||
[]su_int{0x00000010, 0x00000010, 0x00000001},
|
||||
[]su_int{0x00000010, 0x078644FA, 0x00000000},
|
||||
[]su_int{0x00000010, 0x0747AE14, 0x00000000},
|
||||
[]su_int{0x00000010, 0x7FFFFFFF, 0x00000000},
|
||||
[]su_int{0x00000010, 0x80000000, 0x00000000},
|
||||
[]su_int{0x00000010, 0xFFFFFFFD, 0x00000000},
|
||||
[]su_int{0x00000010, 0xFFFFFFFE, 0x00000000},
|
||||
[]su_int{0x00000010, 0xFFFFFFFF, 0x00000000},
|
||||
[]su_int{0x078644FA, 0x00000001, 0x078644FA},
|
||||
[]su_int{0x078644FA, 0x00000002, 0x03C3227D},
|
||||
[]su_int{0x078644FA, 0x00000003, 0x028216FE},
|
||||
[]su_int{0x078644FA, 0x00000010, 0x0078644F},
|
||||
[]su_int{0x078644FA, 0x078644FA, 0x00000001},
|
||||
[]su_int{0x078644FA, 0x0747AE14, 0x00000001},
|
||||
[]su_int{0x078644FA, 0x7FFFFFFF, 0x00000000},
|
||||
[]su_int{0x078644FA, 0x80000000, 0x00000000},
|
||||
[]su_int{0x078644FA, 0xFFFFFFFD, 0x00000000},
|
||||
[]su_int{0x078644FA, 0xFFFFFFFE, 0x00000000},
|
||||
[]su_int{0x078644FA, 0xFFFFFFFF, 0x00000000},
|
||||
[]su_int{0x0747AE14, 0x00000001, 0x0747AE14},
|
||||
[]su_int{0x0747AE14, 0x00000002, 0x03A3D70A},
|
||||
[]su_int{0x0747AE14, 0x00000003, 0x026D3A06},
|
||||
[]su_int{0x0747AE14, 0x00000010, 0x00747AE1},
|
||||
[]su_int{0x0747AE14, 0x078644FA, 0x00000000},
|
||||
[]su_int{0x0747AE14, 0x0747AE14, 0x00000001},
|
||||
[]su_int{0x0747AE14, 0x7FFFFFFF, 0x00000000},
|
||||
[]su_int{0x0747AE14, 0x80000000, 0x00000000},
|
||||
[]su_int{0x0747AE14, 0xFFFFFFFD, 0x00000000},
|
||||
[]su_int{0x0747AE14, 0xFFFFFFFE, 0x00000000},
|
||||
[]su_int{0x0747AE14, 0xFFFFFFFF, 0x00000000},
|
||||
[]su_int{0x7FFFFFFF, 0x00000001, 0x7FFFFFFF},
|
||||
[]su_int{0x7FFFFFFF, 0x00000002, 0x3FFFFFFF},
|
||||
[]su_int{0x7FFFFFFF, 0x00000003, 0x2AAAAAAA},
|
||||
[]su_int{0x7FFFFFFF, 0x00000010, 0x07FFFFFF},
|
||||
[]su_int{0x7FFFFFFF, 0x078644FA, 0x00000011},
|
||||
[]su_int{0x7FFFFFFF, 0x0747AE14, 0x00000011},
|
||||
[]su_int{0x7FFFFFFF, 0x7FFFFFFF, 0x00000001},
|
||||
[]su_int{0x7FFFFFFF, 0x80000000, 0x00000000},
|
||||
[]su_int{0x7FFFFFFF, 0xFFFFFFFD, 0x00000000},
|
||||
[]su_int{0x7FFFFFFF, 0xFFFFFFFE, 0x00000000},
|
||||
[]su_int{0x7FFFFFFF, 0xFFFFFFFF, 0x00000000},
|
||||
[]su_int{0x80000000, 0x00000001, 0x80000000},
|
||||
[]su_int{0x80000000, 0x00000002, 0x40000000},
|
||||
[]su_int{0x80000000, 0x00000003, 0x2AAAAAAA},
|
||||
[]su_int{0x80000000, 0x00000010, 0x08000000},
|
||||
[]su_int{0x80000000, 0x078644FA, 0x00000011},
|
||||
[]su_int{0x80000000, 0x0747AE14, 0x00000011},
|
||||
[]su_int{0x80000000, 0x7FFFFFFF, 0x00000001},
|
||||
[]su_int{0x80000000, 0x80000000, 0x00000001},
|
||||
[]su_int{0x80000000, 0xFFFFFFFD, 0x00000000},
|
||||
[]su_int{0x80000000, 0xFFFFFFFE, 0x00000000},
|
||||
[]su_int{0x80000000, 0xFFFFFFFF, 0x00000000},
|
||||
[]su_int{0xFFFFFFFD, 0x00000001, 0xFFFFFFFD},
|
||||
[]su_int{0xFFFFFFFD, 0x00000002, 0x7FFFFFFE},
|
||||
[]su_int{0xFFFFFFFD, 0x00000003, 0x55555554},
|
||||
[]su_int{0xFFFFFFFD, 0x00000010, 0x0FFFFFFF},
|
||||
[]su_int{0xFFFFFFFD, 0x078644FA, 0x00000022},
|
||||
[]su_int{0xFFFFFFFD, 0x0747AE14, 0x00000023},
|
||||
[]su_int{0xFFFFFFFD, 0x7FFFFFFF, 0x00000001},
|
||||
[]su_int{0xFFFFFFFD, 0x80000000, 0x00000001},
|
||||
[]su_int{0xFFFFFFFD, 0xFFFFFFFD, 0x00000001},
|
||||
[]su_int{0xFFFFFFFD, 0xFFFFFFFE, 0x00000000},
|
||||
[]su_int{0xFFFFFFFD, 0xFFFFFFFF, 0x00000000},
|
||||
[]su_int{0xFFFFFFFE, 0x00000001, 0xFFFFFFFE},
|
||||
[]su_int{0xFFFFFFFE, 0x00000002, 0x7FFFFFFF},
|
||||
[]su_int{0xFFFFFFFE, 0x00000003, 0x55555554},
|
||||
[]su_int{0xFFFFFFFE, 0x00000010, 0x0FFFFFFF},
|
||||
[]su_int{0xFFFFFFFE, 0x078644FA, 0x00000022},
|
||||
[]su_int{0xFFFFFFFE, 0x0747AE14, 0x00000023},
|
||||
[]su_int{0xFFFFFFFE, 0x7FFFFFFF, 0x00000002},
|
||||
[]su_int{0xFFFFFFFE, 0x80000000, 0x00000001},
|
||||
[]su_int{0xFFFFFFFE, 0xFFFFFFFD, 0x00000001},
|
||||
[]su_int{0xFFFFFFFE, 0xFFFFFFFE, 0x00000001},
|
||||
[]su_int{0xFFFFFFFE, 0xFFFFFFFF, 0x00000000},
|
||||
[]su_int{0xFFFFFFFF, 0x00000001, 0xFFFFFFFF},
|
||||
[]su_int{0xFFFFFFFF, 0x00000002, 0x7FFFFFFF},
|
||||
[]su_int{0xFFFFFFFF, 0x00000003, 0x55555555},
|
||||
[]su_int{0xFFFFFFFF, 0x00000010, 0x0FFFFFFF},
|
||||
[]su_int{0xFFFFFFFF, 0x078644FA, 0x00000022},
|
||||
[]su_int{0xFFFFFFFF, 0x0747AE14, 0x00000023},
|
||||
[]su_int{0xFFFFFFFF, 0x7FFFFFFF, 0x00000002},
|
||||
[]su_int{0xFFFFFFFF, 0x80000000, 0x00000001},
|
||||
[]su_int{0xFFFFFFFF, 0xFFFFFFFD, 0x00000001},
|
||||
[]su_int{0xFFFFFFFF, 0xFFFFFFFE, 0x00000001},
|
||||
[]su_int{0xFFFFFFFF, 0xFFFFFFFF, 0x00000001},
|
||||
};
|
||||
|
||||
for (cases) |case| {
|
||||
test_one_udivsi3(case[0], case[1], case[2]);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_one_udivsi3(a: su_int, b: su_int, expected_q: su_int) {
|
||||
const q: su_int = __udivsi3(a, b);
|
||||
assert(q == expected_q);
|
||||
}
|
||||
|
||||
|
||||
fn assert(ok: bool) {
|
||||
if (!ok) @unreachable();
|
||||
}
|
||||
|
@ -13,6 +13,27 @@ pub fn assert(ok: bool) {
|
||||
if (!ok) @unreachable()
|
||||
}
|
||||
|
||||
var panicking = false;
|
||||
/// This is the default panic implementation.
|
||||
pub coldcc fn panic(message: []const u8) -> unreachable {
|
||||
// TODO
|
||||
// if (@atomicRmw(AtomicOp.XChg, &panicking, true, AtomicOrder.SeqCst)) { }
|
||||
if (panicking) {
|
||||
// Panicked during a panic.
|
||||
// TODO detect if a different thread caused the panic, because in that case
|
||||
// we would want to return here instead of calling abort, so that the thread
|
||||
// which first called panic can finish printing a stack trace.
|
||||
os.abort();
|
||||
} else {
|
||||
panicking = true;
|
||||
}
|
||||
|
||||
%%io.stderr.printf("{}\n", message);
|
||||
%%printStackTrace();
|
||||
|
||||
os.abort();
|
||||
}
|
||||
|
||||
pub fn printStackTrace() -> %void {
|
||||
%return writeStackTrace(&io.stderr);
|
||||
%return io.stderr.flush();
|
||||
|
134
std/io.zig
134
std/io.zig
@ -100,14 +100,20 @@ pub const OutStream = struct {
|
||||
Start,
|
||||
OpenBrace,
|
||||
CloseBrace,
|
||||
Hex: bool,
|
||||
Integer,
|
||||
IntegerWidth,
|
||||
};
|
||||
|
||||
/// Calls print and then flushes the buffer.
|
||||
pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) -> %void {
|
||||
comptime var start_index: usize = 0;
|
||||
comptime var start_index = 0;
|
||||
comptime var state = State.Start;
|
||||
comptime var next_arg: usize = 0;
|
||||
comptime var next_arg = 0;
|
||||
comptime var radix = 0;
|
||||
comptime var uppercase = false;
|
||||
comptime var width = 0;
|
||||
comptime var width_start = 0;
|
||||
|
||||
inline for (format) |c, i| {
|
||||
switch (state) {
|
||||
State.Start => switch (c) {
|
||||
@ -132,11 +138,23 @@ pub const OutStream = struct {
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
},
|
||||
'd' => {
|
||||
radix = 10;
|
||||
uppercase = false;
|
||||
width = 0;
|
||||
state = State.Integer;
|
||||
},
|
||||
'x' => {
|
||||
state = State.Hex { false };
|
||||
radix = 16;
|
||||
uppercase = false;
|
||||
width = 0;
|
||||
state = State.Integer;
|
||||
},
|
||||
'X' => {
|
||||
state = State.Hex { true };
|
||||
radix = 16;
|
||||
uppercase = true;
|
||||
width = 0;
|
||||
state = State.Integer;
|
||||
},
|
||||
else => @compileError("Unknown format character: " ++ c),
|
||||
},
|
||||
@ -147,14 +165,29 @@ pub const OutStream = struct {
|
||||
},
|
||||
else => @compileError("Single '}' encountered in format string"),
|
||||
},
|
||||
State.Hex => |uppercase| switch (c) {
|
||||
State.Integer => switch (c) {
|
||||
'}' => {
|
||||
self.printInt(args[next_arg], 16, uppercase);
|
||||
self.printInt(args[next_arg], radix, uppercase, width);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
},
|
||||
else => @compileError("Expected '}' after 'x'/'X' in format string"),
|
||||
'0' ... '9' => {
|
||||
width_start = i;
|
||||
state = State.IntegerWidth;
|
||||
},
|
||||
else => @compileError("Unexpected character in format string: " ++ []u8{c}),
|
||||
},
|
||||
State.IntegerWidth => switch (c) {
|
||||
'}' => {
|
||||
width = comptime %%parseUnsigned(usize, format[width_start...i], 10);
|
||||
self.printInt(args[next_arg], radix, uppercase, width);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
},
|
||||
'0' ... '9' => {},
|
||||
else => @compileError("Unexpected character in format string: " ++ []u8{c}),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -162,10 +195,8 @@ pub const OutStream = struct {
|
||||
if (args.len != next_arg) {
|
||||
@compileError("Unused arguments");
|
||||
}
|
||||
// TODO https://github.com/andrewrk/zig/issues/253
|
||||
switch (state) {
|
||||
State.Start => {},
|
||||
else => @compileError("Incomplete format string: " ++ format),
|
||||
if (state != State.Start) {
|
||||
@compileError("Incomplete format string: " ++ format);
|
||||
}
|
||||
}
|
||||
if (start_index < format.len) {
|
||||
@ -177,7 +208,7 @@ pub const OutStream = struct {
|
||||
pub fn printValue(self: &OutStream, value: var) -> %void {
|
||||
const T = @typeOf(value);
|
||||
if (@isInteger(T)) {
|
||||
return self.printInt(value, 10, false);
|
||||
return self.printInt(value, 10, false, 0);
|
||||
} else if (@isFloat(T)) {
|
||||
return self.printFloat(T, value);
|
||||
} else if (@canImplicitCast([]const u8, value)) {
|
||||
@ -190,11 +221,11 @@ pub const OutStream = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn printInt(self: &OutStream, x: var, base: u8, uppercase: bool) -> %void {
|
||||
pub fn printInt(self: &OutStream, x: var, base: u8, uppercase: bool, width: usize) -> %void {
|
||||
if (self.index + max_int_digits >= self.buffer.len) {
|
||||
%return self.flush();
|
||||
}
|
||||
const amt_printed = bufPrintInt(self.buffer[self.index...], x, base, uppercase);
|
||||
const amt_printed = bufPrintInt(self.buffer[self.index...], x, base, uppercase, width);
|
||||
self.index += amt_printed;
|
||||
}
|
||||
|
||||
@ -474,24 +505,29 @@ fn digitToChar(digit: u8, uppercase: bool) -> u8 {
|
||||
}
|
||||
|
||||
/// Guaranteed to not use more than max_int_digits
|
||||
pub fn bufPrintInt(out_buf: []u8, x: var, base: u8, uppercase: bool) -> usize {
|
||||
pub fn bufPrintInt(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize {
|
||||
if (@typeOf(x).is_signed)
|
||||
bufPrintSigned(out_buf, x, base, uppercase)
|
||||
bufPrintSigned(out_buf, x, base, uppercase, width)
|
||||
else
|
||||
bufPrintUnsigned(out_buf, x, base, uppercase)
|
||||
bufPrintUnsigned(out_buf, x, base, uppercase, width)
|
||||
}
|
||||
|
||||
fn bufPrintSigned(out_buf: []u8, x: var, base: u8, uppercase: bool) -> usize {
|
||||
fn bufPrintSigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize {
|
||||
const uint = @intType(false, @typeOf(x).bit_count);
|
||||
// include the sign in the width
|
||||
const new_width = if (width == 0) 0 else (width - 1);
|
||||
var new_value: uint = undefined;
|
||||
if (x < 0) {
|
||||
out_buf[0] = '-';
|
||||
return 1 + bufPrintUnsigned(out_buf[1...], uint(-(x + 1)) + 1, base, uppercase);
|
||||
new_value = uint(-(x + 1)) + 1;
|
||||
} else {
|
||||
return bufPrintUnsigned(out_buf, uint(x), base, uppercase);
|
||||
out_buf[0] = '+';
|
||||
new_value = uint(x);
|
||||
}
|
||||
return 1 + bufPrintUnsigned(out_buf[1...], new_value, base, uppercase, new_width);
|
||||
}
|
||||
|
||||
fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool) -> usize {
|
||||
fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize {
|
||||
// max_int_digits accounts for the minus sign. when printing an unsigned
|
||||
// number we don't need to do that.
|
||||
var buf: [max_int_digits - 1]u8 = undefined;
|
||||
@ -508,18 +544,11 @@ fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool) -> usize {
|
||||
}
|
||||
|
||||
const src_buf = buf[index...];
|
||||
mem.copy(u8, out_buf, src_buf);
|
||||
return src_buf.len;
|
||||
}
|
||||
const padding = if (width > src_buf.len) (width - src_buf.len) else 0;
|
||||
|
||||
fn parseU64DigitTooBig() {
|
||||
@setFnTest(this);
|
||||
|
||||
parseUnsigned(u64, "123a", 10) %% |err| {
|
||||
if (err == error.InvalidChar) return;
|
||||
@unreachable();
|
||||
};
|
||||
@unreachable();
|
||||
mem.set(u8, out_buf[0...padding], '0');
|
||||
mem.copy(u8, out_buf[padding...], src_buf);
|
||||
return src_buf.len + padding;
|
||||
}
|
||||
|
||||
pub fn openSelfExe(stream: &InStream) -> %void {
|
||||
@ -535,18 +564,43 @@ pub fn openSelfExe(stream: &InStream) -> %void {
|
||||
}
|
||||
}
|
||||
|
||||
fn bufPrintIntToSlice(buf: []u8, x: var, base: u8, uppercase: bool) -> []u8 {
|
||||
return buf[0...bufPrintInt(buf, x, base, uppercase)];
|
||||
fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> []u8 {
|
||||
return buf[0...bufPrintInt(buf, value, base, uppercase, width)];
|
||||
}
|
||||
|
||||
fn testParseU64DigitTooBig() {
|
||||
@setFnTest(this);
|
||||
|
||||
parseUnsigned(u64, "123a", 10) %% |err| {
|
||||
if (err == error.InvalidChar) return;
|
||||
@unreachable();
|
||||
};
|
||||
@unreachable();
|
||||
}
|
||||
|
||||
fn testParseUnsignedComptime() {
|
||||
@setFnTest(this);
|
||||
|
||||
comptime {
|
||||
assert(%%parseUnsigned(usize, "2", 10) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn testBufPrintInt() {
|
||||
@setFnTest(this);
|
||||
|
||||
var buf: [max_int_digits]u8 = undefined;
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 2, false), "-101111000110000101001110"));
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 10, false), "-12345678"));
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, false), "-bc614e"));
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, true), "-BC614E"));
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110"));
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678"));
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e"));
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E"));
|
||||
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, u32(12345678), 10, true), "12345678"));
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678"));
|
||||
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666"));
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234"));
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234"));
|
||||
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42"));
|
||||
assert(mem.eql(bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42"));
|
||||
}
|
||||
|
17
std/math.zig
17
std/math.zig
@ -1,3 +1,5 @@
|
||||
const assert = @import("debug.zig").assert;
|
||||
|
||||
pub const Cmp = enum {
|
||||
Equal,
|
||||
Greater,
|
||||
@ -66,3 +68,18 @@ fn getReturnTypeForAbs(comptime T: type) -> type {
|
||||
}
|
||||
}
|
||||
|
||||
fn testMath() {
|
||||
@setFnTest(this);
|
||||
|
||||
assert(%%mulOverflow(i32, 3, 4) == 12);
|
||||
assert(%%addOverflow(i32, 3, 4) == 7);
|
||||
assert(%%subOverflow(i32, 3, 4) == -1);
|
||||
assert(%%shlOverflow(i32, 0b11, 4) == 0b110000);
|
||||
|
||||
comptime {
|
||||
assert(%%mulOverflow(i32, 3, 4) == 12);
|
||||
assert(%%addOverflow(i32, 3, 4) == 7);
|
||||
assert(%%subOverflow(i32, 3, 4) == -1);
|
||||
assert(%%shlOverflow(i32, 0b11, 4) == 0b110000);
|
||||
}
|
||||
}
|
||||
|
@ -3,31 +3,10 @@
|
||||
// If this file wants to import other files *by name*, support for that would
|
||||
// have to be added in the compiler.
|
||||
|
||||
var panicking = false;
|
||||
pub coldcc fn panic(message: []const u8) -> unreachable {
|
||||
if (@compileVar("os") == Os.freestanding) {
|
||||
while (true) {}
|
||||
} else {
|
||||
const std = @import("std");
|
||||
const io = std.io;
|
||||
const debug = std.debug;
|
||||
const os = std.os;
|
||||
|
||||
// TODO
|
||||
// if (@atomicRmw(AtomicOp.XChg, &panicking, true, AtomicOrder.SeqCst)) {
|
||||
if (panicking) {
|
||||
// Panicked during a panic.
|
||||
// TODO detect if a different thread caused the panic, because in that case
|
||||
// we would want to return here instead of calling abort, so that the thread
|
||||
// which first called panic can finish printing a stack trace.
|
||||
os.abort();
|
||||
} else {
|
||||
panicking = true;
|
||||
}
|
||||
|
||||
%%io.stderr.printf("{}\n", message);
|
||||
%%debug.printStackTrace();
|
||||
|
||||
os.abort();
|
||||
@import("std").debug.panic(message);
|
||||
}
|
||||
}
|
||||
|
@ -8,8 +8,8 @@ const ET = enum {
|
||||
|
||||
pub fn print(a: &const ET, buf: []u8) -> %usize {
|
||||
return switch (*a) {
|
||||
ET.SINT => |x| { io.bufPrintInt(buf, x, 10, false) },
|
||||
ET.UINT => |x| { io.bufPrintInt(buf, x, 10, false) },
|
||||
ET.SINT => |x| { io.bufPrintInt(buf, x, 10, false, 0) },
|
||||
ET.UINT => |x| { io.bufPrintInt(buf, x, 10, false, 0) },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -251,3 +251,15 @@ fn comptimeIterateOverFnPtrList() {
|
||||
assert(performFn('o', 0) == 1);
|
||||
assert(performFn('w', 99) == 99);
|
||||
}
|
||||
|
||||
fn evalSetDebugSafetyAtCompileTime() {
|
||||
@setFnTest(this);
|
||||
|
||||
const result = comptime fnWithSetDebugSafety();
|
||||
assert(result == 1234);
|
||||
}
|
||||
|
||||
fn fnWithSetDebugSafety() -> i32{
|
||||
@setDebugSafety(this, true);
|
||||
return 1234;
|
||||
}
|
||||
|
@ -1015,8 +1015,9 @@ const x = foo();
|
||||
|
||||
add_compile_fail_case("array concatenation with wrong type", R"SOURCE(
|
||||
const src = "aoeu";
|
||||
const a = src[0...] ++ "foo";
|
||||
)SOURCE", 1, ".tmp_source.zig:3:14: error: expected array or C string literal, found '[]u8'");
|
||||
const derp = usize(1234);
|
||||
const a = derp ++ "foo";
|
||||
)SOURCE", 1, ".tmp_source.zig:4:11: error: expected array or C string literal, found 'usize'");
|
||||
|
||||
add_compile_fail_case("non compile time array concatenation", R"SOURCE(
|
||||
fn f(s: [10]u8) -> []u8 {
|
||||
@ -1632,6 +1633,23 @@ const some_data: [100]u8 = {
|
||||
};
|
||||
)SOURCE", 1, ".tmp_source.zig:3:32: error: alignment value must be power of 2");
|
||||
|
||||
add_compile_fail_case("compile log", R"SOURCE(
|
||||
fn foo() {
|
||||
comptime bar(12, "hi");
|
||||
}
|
||||
fn bar(a: i32, b: []const u8) {
|
||||
@compileLog("begin");
|
||||
@compileLog("a", a, "b", b);
|
||||
@compileLog("end");
|
||||
}
|
||||
)SOURCE", 6,
|
||||
".tmp_source.zig:6:5: error: found compile log statement",
|
||||
".tmp_source.zig:3:17: note: called from here",
|
||||
".tmp_source.zig:7:5: error: found compile log statement",
|
||||
".tmp_source.zig:3:17: note: called from here",
|
||||
".tmp_source.zig:8:5: error: found compile log statement",
|
||||
".tmp_source.zig:3:17: note: called from here");
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
x
Reference in New Issue
Block a user