mirror of
https://github.com/ziglang/zig.git
synced 2025-01-09 19:50:29 +00:00
printf var args proof of concept
See #167 Need to troubleshoot when we send 2 slices to printf. It goes into an infinite loop. This commit introduces 4 builtin functions: * `@isInteger` * `@isFloat` * `@canImplictCast` * `@typeName`
This commit is contained in:
parent
32d8686da8
commit
4b3f18de3c
@ -370,10 +370,10 @@ TODO
|
||||
|
||||
## Built-in Functions
|
||||
|
||||
Built-in functions are prefixed with `@`. Remember that the `inline` keyword on
|
||||
Built-in functions are prefixed with `@`. Remember that the `comptime` keyword on
|
||||
a parameter means that the parameter must be known at compile time.
|
||||
|
||||
### @alloca(inline T: type, count: usize) -> []T
|
||||
### @alloca(comptime T: type, count: usize) -> []T
|
||||
|
||||
Allocates memory in the stack frame of the caller. This temporary space is
|
||||
automatically freed when the function that called alloca returns to its caller,
|
||||
@ -391,13 +391,13 @@ The allocated memory contents are undefined.
|
||||
This function returns a compile-time constant, which is the type of the
|
||||
expression passed as an argument. The expression is *not evaluated*.
|
||||
|
||||
### @sizeOf(inline T: type) -> (number literal)
|
||||
### @sizeOf(comptime T: type) -> (number literal)
|
||||
|
||||
This function returns the number of bytes it takes to store T in memory.
|
||||
|
||||
The result is a target-specific compile time constant.
|
||||
|
||||
### @alignOf(inline T: type) -> (number literal)
|
||||
### @alignOf(comptime T: type) -> (number literal)
|
||||
|
||||
This function returns the number of bytes that this type should be aligned to
|
||||
for the current target.
|
||||
@ -414,10 +414,10 @@ false otherwise.
|
||||
|
||||
```
|
||||
Function Operation
|
||||
@addWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a + b
|
||||
@subWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a - b
|
||||
@mulWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a * b
|
||||
@shlWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a << b
|
||||
@addWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool *x = a + b
|
||||
@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool *x = a - b
|
||||
@mulWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool *x = a * b
|
||||
@shlWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool *x = a << b
|
||||
```
|
||||
|
||||
### @memset(dest: &u8, c: u8, byte_count: usize)
|
||||
@ -475,27 +475,27 @@ aggressive optimizations.
|
||||
|
||||
This function is only valid within function scope.
|
||||
|
||||
### @maxValue(inline T: type) -> (number literal)
|
||||
### @maxValue(comptime T: type) -> (number literal)
|
||||
|
||||
This function returns the maximum integer value of the integer type T.
|
||||
|
||||
The result is a compile time constant. For some types such as `c_long`, the
|
||||
result is marked as depending on a compile variable.
|
||||
|
||||
### @minValue(inline T: type) -> (number literal)
|
||||
### @minValue(comptime T: type) -> (number literal)
|
||||
|
||||
This function returns the minimum integer value of the integer type T.
|
||||
|
||||
The result is a compile time constant. For some types such as `c_long`, the
|
||||
result is marked as depending on a compile variable.
|
||||
|
||||
### @memberCount(inline T: type) -> (number literal)
|
||||
### @memberCount(comptime T: type) -> (number literal)
|
||||
|
||||
This function returns the number of enum values in an enum type.
|
||||
|
||||
The result is a compile time constant.
|
||||
|
||||
### @import(inline path: []u8) -> (namespace)
|
||||
### @import(comptime path: []u8) -> (namespace)
|
||||
|
||||
This function finds a zig file corresponding to `path` and imports all the
|
||||
public top level declarations into the resulting namespace.
|
||||
@ -516,25 +516,25 @@ appending to a temporary buffer which is then parsed as C code.
|
||||
|
||||
This function is only valid at top level scope.
|
||||
|
||||
### @cInclude(inline path: []u8)
|
||||
### @cInclude(comptime path: []u8)
|
||||
|
||||
This function can only occur inside `@c_import`.
|
||||
|
||||
This appends `#include <$path>\n` to the `c_import` temporary buffer.
|
||||
|
||||
### @cDefine(inline name: []u8, value)
|
||||
### @cDefine(comptime name: []u8, value)
|
||||
|
||||
This function can only occur inside `@c_import`.
|
||||
|
||||
This appends `#define $name $value` to the `c_import` temporary buffer.
|
||||
|
||||
### @cUndef(inline name: []u8)
|
||||
### @cUndef(comptime name: []u8)
|
||||
|
||||
This function can only occur inside `@c_import`.
|
||||
|
||||
This appends `#undef $name` to the `c_import` temporary buffer.
|
||||
|
||||
### @compileVar(inline name: []u8) -> (varying type)
|
||||
### @compileVar(comptime name: []u8) -> (varying type)
|
||||
|
||||
This function returns a compile-time variable. There are built in compile
|
||||
variables:
|
||||
@ -584,10 +584,14 @@ error OutOfMem;
|
||||
|
||||
Then the string representation is "OutOfMem".
|
||||
|
||||
If there are no calls to `@err_name` in an entire application, then no error
|
||||
If there are no calls to `@errorName` in an entire application, then no error
|
||||
name table will be generated.
|
||||
|
||||
### @embedFile(inline path: []u8) -> [X]u8
|
||||
### @typeName(T: type) -> []u8
|
||||
|
||||
This function returns the string representation of a type.
|
||||
|
||||
### @embedFile(comptime path: []u8) -> [X]u8
|
||||
|
||||
This function returns a compile time constant fixed-size array with length
|
||||
equal to the byte count of the file given by `path`. The contents of the array
|
||||
@ -610,7 +614,7 @@ The caller guarantees that this operation will have no remainder.
|
||||
In debug mode, a remainder causes a panic. In release mode, a remainder is
|
||||
undefined behavior.
|
||||
|
||||
### @truncate(inline T: type, integer) -> T
|
||||
### @truncate(comptime T: type, integer) -> T
|
||||
|
||||
This function truncates bits from an integer type, resulting in a smaller
|
||||
integer type.
|
||||
@ -631,14 +635,14 @@ const b: u8 = @truncate(u8, a);
|
||||
// b is now 0xcd
|
||||
```
|
||||
|
||||
### @compileError(inline msg: []u8)
|
||||
### @compileError(comptime msg: []u8)
|
||||
|
||||
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 inline functions.
|
||||
or `switch` with compile time constants, and comptime functions.
|
||||
|
||||
### @intType(inline is_signed: bool, inline bit_count: u8) -> type
|
||||
### @intType(comptime is_signed: bool, comptime bit_count: u8) -> type
|
||||
|
||||
This function returns an integer type with the given signness and bit count.
|
||||
|
||||
@ -646,3 +650,14 @@ This function returns an integer type with the given signness and bit count.
|
||||
|
||||
Makes the target function a test function.
|
||||
|
||||
### @isInteger(comptime T: type) -> bool
|
||||
|
||||
Returns whether a given type is an integer.
|
||||
|
||||
### @isFloat(comptime T: type) -> bool
|
||||
|
||||
Returns whether a given type is a float.
|
||||
|
||||
### @canImplicitCast(comptime T: type, value) -> bool
|
||||
|
||||
Returns whether a value can be implicitly casted to a given type.
|
||||
|
@ -2,8 +2,6 @@ const std = @import("std");
|
||||
const io = std.io;
|
||||
const str = std.str;
|
||||
|
||||
// TODO var args printing
|
||||
|
||||
pub fn main(args: [][]u8) -> %void {
|
||||
const exe = args[0];
|
||||
var catted_anything = false;
|
||||
|
@ -978,6 +978,9 @@ struct TypeTableEntry {
|
||||
HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size;
|
||||
TypeTableEntry *maybe_parent;
|
||||
TypeTableEntry *error_parent;
|
||||
// If we generate a constant name value for this type, we memoize it here.
|
||||
// The type of this is array
|
||||
ConstExprValue *cached_const_name_val;
|
||||
};
|
||||
|
||||
struct PackageTableEntry {
|
||||
@ -1086,6 +1089,10 @@ enum BuiltinFnId {
|
||||
BuiltinFnIdSetFnVisible,
|
||||
BuiltinFnIdSetDebugSafety,
|
||||
BuiltinFnIdAlloca,
|
||||
BuiltinFnIdTypeName,
|
||||
BuiltinFnIdIsInteger,
|
||||
BuiltinFnIdIsFloat,
|
||||
BuiltinFnIdCanImplicitCast,
|
||||
};
|
||||
|
||||
struct BuiltinFnEntry {
|
||||
@ -1505,6 +1512,9 @@ enum IrInstructionId {
|
||||
IrInstructionIdPtrToInt,
|
||||
IrInstructionIdIntToEnum,
|
||||
IrInstructionIdCheckSwitchProngs,
|
||||
IrInstructionIdTestType,
|
||||
IrInstructionIdTypeName,
|
||||
IrInstructionIdCanImplicitCast,
|
||||
};
|
||||
|
||||
struct IrInstruction {
|
||||
@ -2188,6 +2198,26 @@ struct IrInstructionCheckSwitchProngs {
|
||||
size_t range_count;
|
||||
};
|
||||
|
||||
struct IrInstructionTestType {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *type_value;
|
||||
TypeTableEntryId type_id;
|
||||
};
|
||||
|
||||
struct IrInstructionTypeName {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *type_value;
|
||||
};
|
||||
|
||||
struct IrInstructionCanImplicitCast {
|
||||
IrInstruction base;
|
||||
|
||||
IrInstruction *type_value;
|
||||
IrInstruction *target_value;
|
||||
};
|
||||
|
||||
enum LValPurpose {
|
||||
LValPurposeNone,
|
||||
LValPurposeAssign,
|
||||
|
@ -2279,6 +2279,9 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
||||
case IrInstructionIdTestComptime:
|
||||
case IrInstructionIdGeneratedCode:
|
||||
case IrInstructionIdCheckSwitchProngs:
|
||||
case IrInstructionIdTestType:
|
||||
case IrInstructionIdTypeName:
|
||||
case IrInstructionIdCanImplicitCast:
|
||||
zig_unreachable();
|
||||
case IrInstructionIdReturn:
|
||||
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
|
||||
@ -3652,6 +3655,10 @@ static void define_builtin_fns(CodeGen *g) {
|
||||
create_builtin_fn(g, BuiltinFnIdImport, "import", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdIsInteger, "isInteger", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdIsFloat, "isFloat", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2);
|
||||
create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);
|
||||
create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);
|
||||
create_builtin_fn(g, BuiltinFnIdFence, "fence", 1);
|
||||
|
187
src/ir.cpp
187
src/ir.cpp
@ -495,6 +495,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProng
|
||||
return IrInstructionIdCheckSwitchProngs;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionTestType *) {
|
||||
return IrInstructionIdTestType;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeName *) {
|
||||
return IrInstructionIdTypeName;
|
||||
}
|
||||
|
||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionCanImplicitCast *) {
|
||||
return IrInstructionIdCanImplicitCast;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
||||
T *special_instruction = allocate<T>(1);
|
||||
@ -2001,6 +2013,45 @@ static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope,
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_test_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *type_value, TypeTableEntryId type_id)
|
||||
{
|
||||
IrInstructionTestType *instruction = ir_build_instruction<IrInstructionTestType>(
|
||||
irb, scope, source_node);
|
||||
instruction->type_value = type_value;
|
||||
instruction->type_id = type_id;
|
||||
|
||||
ir_ref_instruction(type_value, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *type_value)
|
||||
{
|
||||
IrInstructionTypeName *instruction = ir_build_instruction<IrInstructionTypeName>(
|
||||
irb, scope, source_node);
|
||||
instruction->type_value = type_value;
|
||||
|
||||
ir_ref_instruction(type_value, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_build_can_implicit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *type_value, IrInstruction *target_value)
|
||||
{
|
||||
IrInstructionCanImplicitCast *instruction = ir_build_instruction<IrInstructionCanImplicitCast>(
|
||||
irb, scope, source_node);
|
||||
instruction->type_value = type_value;
|
||||
instruction->target_value = target_value;
|
||||
|
||||
ir_ref_instruction(type_value, irb->current_basic_block);
|
||||
ir_ref_instruction(target_value, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -2605,6 +2656,28 @@ static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionChec
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_testtype_get_dep(IrInstructionTestType *instruction, size_t index) {
|
||||
switch (index) {
|
||||
case 0: return instruction->type_value;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_typename_get_dep(IrInstructionTypeName *instruction, size_t index) {
|
||||
switch (index) {
|
||||
case 0: return instruction->type_value;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_canimplicitcast_get_dep(IrInstructionCanImplicitCast *instruction, size_t index) {
|
||||
switch (index) {
|
||||
case 0: return instruction->type_value;
|
||||
case 1: return instruction->target_value;
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
|
||||
switch (instruction->id) {
|
||||
case IrInstructionIdInvalid:
|
||||
@ -2777,6 +2850,12 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
|
||||
return ir_instruction_inttoenum_get_dep((IrInstructionIntToEnum *) instruction, index);
|
||||
case IrInstructionIdCheckSwitchProngs:
|
||||
return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index);
|
||||
case IrInstructionIdTestType:
|
||||
return ir_instruction_testtype_get_dep((IrInstructionTestType *) instruction, index);
|
||||
case IrInstructionIdTypeName:
|
||||
return ir_instruction_typename_get_dep((IrInstructionTypeName *) instruction, index);
|
||||
case IrInstructionIdCanImplicitCast:
|
||||
return ir_instruction_canimplicitcast_get_dep((IrInstructionCanImplicitCast *) instruction, index);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -3594,6 +3673,18 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *
|
||||
return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_test_type(IrBuilder *irb, Scope *scope, AstNode *node, TypeTableEntryId type_id) {
|
||||
assert(node->type == NodeTypeFnCallExpr);
|
||||
|
||||
AstNode *type_node = node->data.fn_call_expr.params.at(0);
|
||||
|
||||
IrInstruction *type_value = ir_gen_node(irb, type_node, scope);
|
||||
if (type_value == irb->codegen->invalid_instruction)
|
||||
return irb->codegen->invalid_instruction;
|
||||
|
||||
return ir_build_test_type(irb, scope, node, type_value, type_id);
|
||||
}
|
||||
|
||||
static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) {
|
||||
assert(node->type == NodeTypeFnCallExpr);
|
||||
|
||||
@ -3995,6 +4086,33 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
||||
return ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul);
|
||||
case BuiltinFnIdShlWithOverflow:
|
||||
return ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl);
|
||||
case BuiltinFnIdTypeName:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
||||
if (arg0_value == irb->codegen->invalid_instruction)
|
||||
return arg0_value;
|
||||
|
||||
return ir_build_type_name(irb, scope, node, arg0_value);
|
||||
}
|
||||
case BuiltinFnIdIsInteger:
|
||||
return ir_gen_test_type(irb, scope, node, TypeTableEntryIdInt);
|
||||
case BuiltinFnIdIsFloat:
|
||||
return ir_gen_test_type(irb, scope, node, TypeTableEntryIdFloat);
|
||||
case BuiltinFnIdCanImplicitCast:
|
||||
{
|
||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
|
||||
if (arg0_value == irb->codegen->invalid_instruction)
|
||||
return arg0_value;
|
||||
|
||||
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
|
||||
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
|
||||
if (arg1_value == irb->codegen->invalid_instruction)
|
||||
return arg1_value;
|
||||
|
||||
return ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value);
|
||||
}
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -8942,7 +9060,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi
|
||||
case TypeTableEntryIdEnumTag:
|
||||
case TypeTableEntryIdArgTuple:
|
||||
{
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base, false);
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base, true);
|
||||
// TODO depends_on_compile_var should be set based on whether the type of the expression
|
||||
// depends_on_compile_var. but we currently don't have a thing to tell us if the type of
|
||||
// something depends on a compile var
|
||||
@ -9622,6 +9740,8 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
|
||||
pointee_val = nullptr;
|
||||
}
|
||||
TypeTableEntry *canon_target_type = get_underlying_type(target_type);
|
||||
ensure_complete_type(ira->codegen, target_type);
|
||||
|
||||
switch (canon_target_type->id) {
|
||||
case TypeTableEntryIdInvalid:
|
||||
case TypeTableEntryIdVar:
|
||||
@ -10260,6 +10380,24 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc
|
||||
return str_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) {
|
||||
IrInstruction *type_value = instruction->type_value->other;
|
||||
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
|
||||
if (type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
TypeTableEntry *str_type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true);
|
||||
if (!type_entry->cached_const_name_val) {
|
||||
ConstExprValue *array_val = create_const_str_lit(ira->codegen, &type_entry->name);
|
||||
type_entry->cached_const_name_val = create_const_slice(ira->codegen,
|
||||
array_val, 0, buf_len(&type_entry->name), true);
|
||||
}
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base,
|
||||
type_value->value.depends_on_compile_var);
|
||||
*out_val = *type_entry->cached_const_name_val;
|
||||
return str_type;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCImport *instruction) {
|
||||
AstNode *node = instruction->base.source_node;
|
||||
assert(node->type == NodeTypeFnCallExpr);
|
||||
@ -11370,6 +11508,44 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
|
||||
return ira->codegen->builtin_types.entry_void;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_test_type(IrAnalyze *ira, IrInstructionTestType *instruction) {
|
||||
IrInstruction *type_value = instruction->type_value->other;
|
||||
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
|
||||
if (type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, type_value->value.depends_on_compile_var);
|
||||
out_val->data.x_bool = (type_entry->id == instruction->type_id);
|
||||
return ira->codegen->builtin_types.entry_bool;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_can_implicit_cast(IrAnalyze *ira,
|
||||
IrInstructionCanImplicitCast *instruction)
|
||||
{
|
||||
IrInstruction *type_value = instruction->type_value->other;
|
||||
TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
|
||||
if (type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
IrInstruction *target_value = instruction->target_value->other;
|
||||
if (target_value->value.type->id == TypeTableEntryIdInvalid)
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
|
||||
ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, type_entry, target_value->value.type,
|
||||
target_value);
|
||||
|
||||
if (result == ImplicitCastMatchResultReportedError) {
|
||||
zig_panic("TODO refactor implicit cast tester to return bool without reporting errors");
|
||||
}
|
||||
|
||||
// TODO in order to known depends_on_compile_var we have to known if the type of the target
|
||||
// depends on a compile var
|
||||
bool depends_on_compile_var = true;
|
||||
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var);
|
||||
out_val->data.x_bool = (result == ImplicitCastMatchResultYes);
|
||||
return ira->codegen->builtin_types.entry_bool;
|
||||
}
|
||||
|
||||
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
|
||||
switch (instruction->id) {
|
||||
case IrInstructionIdInvalid:
|
||||
@ -11471,6 +11647,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||
return ir_analyze_instruction_compile_err(ira, (IrInstructionCompileErr *)instruction);
|
||||
case IrInstructionIdErrName:
|
||||
return ir_analyze_instruction_err_name(ira, (IrInstructionErrName *)instruction);
|
||||
case IrInstructionIdTypeName:
|
||||
return ir_analyze_instruction_type_name(ira, (IrInstructionTypeName *)instruction);
|
||||
case IrInstructionIdCImport:
|
||||
return ir_analyze_instruction_c_import(ira, (IrInstructionCImport *)instruction);
|
||||
case IrInstructionIdCInclude:
|
||||
@ -11525,6 +11703,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
||||
return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction);
|
||||
case IrInstructionIdCheckSwitchProngs:
|
||||
return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction);
|
||||
case IrInstructionIdTestType:
|
||||
return ir_analyze_instruction_test_type(ira, (IrInstructionTestType *)instruction);
|
||||
case IrInstructionIdCanImplicitCast:
|
||||
return ir_analyze_instruction_can_implicit_cast(ira, (IrInstructionCanImplicitCast *)instruction);
|
||||
case IrInstructionIdMaybeWrap:
|
||||
case IrInstructionIdErrWrapCode:
|
||||
case IrInstructionIdErrWrapPayload:
|
||||
@ -11691,6 +11873,9 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
case IrInstructionIdPtrToInt:
|
||||
case IrInstructionIdIntToPtr:
|
||||
case IrInstructionIdIntToEnum:
|
||||
case IrInstructionIdTestType:
|
||||
case IrInstructionIdTypeName:
|
||||
case IrInstructionIdCanImplicitCast:
|
||||
return false;
|
||||
case IrInstructionIdAsm:
|
||||
{
|
||||
|
@ -807,6 +807,26 @@ static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchP
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_test_type(IrPrint *irp, IrInstructionTestType *instruction) {
|
||||
fprintf(irp->f, "@testType(");
|
||||
ir_print_other_instruction(irp, instruction->type_value);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_type_name(IrPrint *irp, IrInstructionTypeName *instruction) {
|
||||
fprintf(irp->f, "@typeName(");
|
||||
ir_print_other_instruction(irp, instruction->type_value);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCast *instruction) {
|
||||
fprintf(irp->f, "@canImplicitCast(");
|
||||
ir_print_other_instruction(irp, instruction->type_value);
|
||||
fprintf(irp->f, ",");
|
||||
ir_print_other_instruction(irp, instruction->target_value);
|
||||
fprintf(irp->f, ")");
|
||||
}
|
||||
|
||||
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
ir_print_prefix(irp, instruction);
|
||||
switch (instruction->id) {
|
||||
@ -1064,6 +1084,15 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
||||
case IrInstructionIdCheckSwitchProngs:
|
||||
ir_print_check_switch_prongs(irp, (IrInstructionCheckSwitchProngs *)instruction);
|
||||
break;
|
||||
case IrInstructionIdTestType:
|
||||
ir_print_test_type(irp, (IrInstructionTestType *)instruction);
|
||||
break;
|
||||
case IrInstructionIdTypeName:
|
||||
ir_print_type_name(irp, (IrInstructionTypeName *)instruction);
|
||||
break;
|
||||
case IrInstructionIdCanImplicitCast:
|
||||
ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction);
|
||||
break;
|
||||
}
|
||||
fprintf(irp->f, "\n");
|
||||
}
|
||||
|
@ -50,10 +50,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void {
|
||||
const compile_unit = findCompileUnit(st, return_address) ?? return error.MissingDebugInfo;
|
||||
const name = %return compile_unit.die.getAttrString(st, DW.AT_name);
|
||||
|
||||
%return out_stream.printInt(usize, return_address);
|
||||
%return out_stream.printf(" -> ");
|
||||
%return out_stream.printf(name);
|
||||
%return out_stream.printf("\n");
|
||||
%return out_stream.printf("{} -> {}\n", return_address, name);
|
||||
maybe_fp = *(&const ?&const u8)(fp);
|
||||
}
|
||||
},
|
||||
|
82
std/io.zig
82
std/io.zig
@ -80,7 +80,7 @@ pub const OutStream = struct {
|
||||
self.index += 1;
|
||||
}
|
||||
|
||||
pub fn write(self: &OutStream, bytes: []const u8) -> %usize {
|
||||
pub fn write(self: &OutStream, bytes: []const u8) -> %void {
|
||||
var src_bytes_left = bytes.len;
|
||||
var src_index: usize = 0;
|
||||
const dest_space_left = self.buffer.len - self.index;
|
||||
@ -94,25 +94,89 @@ pub const OutStream = struct {
|
||||
}
|
||||
src_bytes_left -= copy_amt;
|
||||
}
|
||||
return bytes.len;
|
||||
}
|
||||
|
||||
/// Prints a byte buffer, flushes the buffer, then returns the number of
|
||||
/// bytes printed. The "f" is for "flush".
|
||||
pub fn printf(self: &OutStream, str: []const u8) -> %usize {
|
||||
const byte_count = %return self.write(str);
|
||||
const State = enum { // TODO put inside printf function and make sure the name and debug info is correct
|
||||
Start,
|
||||
OpenBrace,
|
||||
CloseBrace,
|
||||
};
|
||||
|
||||
/// 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 state = State.Start;
|
||||
comptime var next_arg: usize = 0;
|
||||
inline for (format) |c, i| {
|
||||
switch (state) {
|
||||
State.Start => switch (c) {
|
||||
'{' => {
|
||||
if (start_index < i) %return self.write(format[start_index...i]);
|
||||
state = State.OpenBrace;
|
||||
},
|
||||
'}' => {
|
||||
if (start_index < i) %return self.write(format[start_index...i]);
|
||||
state = State.CloseBrace;
|
||||
},
|
||||
else => {},
|
||||
},
|
||||
State.OpenBrace => switch (c) {
|
||||
'{' => {
|
||||
state = State.Start;
|
||||
start_index = i;
|
||||
},
|
||||
'}' => {
|
||||
%return self.printValue(args[next_arg]);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
},
|
||||
else => @compileError("Unknown format character: " ++ c),
|
||||
},
|
||||
State.CloseBrace => switch (c) {
|
||||
'}' => {
|
||||
state = State.Start;
|
||||
start_index = i;
|
||||
},
|
||||
else => @compileError("Single '}' encountered in format string"),
|
||||
},
|
||||
}
|
||||
}
|
||||
comptime {
|
||||
if (args.len != next_arg) {
|
||||
@compileError("Unused arguments");
|
||||
}
|
||||
if (state != State.Start) {
|
||||
@compileError("Incomplete format string: " ++ format);
|
||||
}
|
||||
}
|
||||
inline if (start_index < format.len) {
|
||||
%return self.write(format[start_index...format.len]);
|
||||
}
|
||||
%return self.flush();
|
||||
return byte_count;
|
||||
}
|
||||
|
||||
pub fn printInt(self: &OutStream, comptime T: type, x: T) -> %usize {
|
||||
pub fn printValue(self: &OutStream, value: var) -> %void {
|
||||
const T = @typeOf(value);
|
||||
if (@isInteger(T)) {
|
||||
return self.printInt(T, value);
|
||||
} else if (@isFloat(T)) {
|
||||
return self.printFloat(T, value);
|
||||
} else if (@canImplicitCast([]const u8, value)) {
|
||||
const casted_value = ([]const u8)(value);
|
||||
return self.write(casted_value);
|
||||
} else {
|
||||
@compileError("Unable to print type '" ++ @typeName(T) ++ "'");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn printInt(self: &OutStream, comptime T: type, x: T) -> %void {
|
||||
// TODO replace max_u64_base10_digits with math.log10(math.pow(2, @sizeOf(T)))
|
||||
if (self.index + max_u64_base10_digits >= self.buffer.len) {
|
||||
%return self.flush();
|
||||
}
|
||||
const amt_printed = bufPrintInt(T, self.buffer[self.index...], x);
|
||||
self.index += amt_printed;
|
||||
return amt_printed;
|
||||
}
|
||||
|
||||
pub fn flush(self: &OutStream) -> %void {
|
||||
|
@ -517,3 +517,52 @@ fn testArray2DConstDoublePtr(ptr: &const f32) {
|
||||
assert(ptr[0] == 1.0);
|
||||
assert(ptr[1] == 2.0);
|
||||
}
|
||||
|
||||
fn isInteger() {
|
||||
@setFnTest(this);
|
||||
|
||||
comptime {
|
||||
assert(@isInteger(i8));
|
||||
assert(@isInteger(u8));
|
||||
assert(@isInteger(i64));
|
||||
assert(@isInteger(u64));
|
||||
assert(!@isInteger(f32));
|
||||
assert(!@isInteger(f64));
|
||||
assert(!@isInteger(bool));
|
||||
assert(!@isInteger(&i32));
|
||||
}
|
||||
}
|
||||
|
||||
fn isFloat() {
|
||||
@setFnTest(this);
|
||||
|
||||
comptime {
|
||||
assert(!@isFloat(i8));
|
||||
assert(!@isFloat(u8));
|
||||
assert(!@isFloat(i64));
|
||||
assert(!@isFloat(u64));
|
||||
assert(@isFloat(f32));
|
||||
assert(@isFloat(f64));
|
||||
assert(!@isFloat(bool));
|
||||
assert(!@isFloat(&f32));
|
||||
}
|
||||
}
|
||||
|
||||
fn canImplicitCast() {
|
||||
@setFnTest(this);
|
||||
|
||||
comptime {
|
||||
assert(@canImplicitCast(i64, i32(3)));
|
||||
assert(!@canImplicitCast(i32, f32(1.234)));
|
||||
assert(@canImplicitCast([]const u8, "aoeu"));
|
||||
}
|
||||
}
|
||||
|
||||
fn typeName() {
|
||||
@setFnTest(this);
|
||||
|
||||
comptime {
|
||||
assert(str.eql(@typeName(i64), "i64"));
|
||||
assert(str.eql(@typeName(&usize), "&usize"));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user