Commit 4b3f18de3c
Changed files (9)
doc
example
cat
test
cases
doc/langref.md
@@ -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.
example/cat/main.zig
@@ -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;
src/all_types.hpp
@@ -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,
src/codegen.cpp
@@ -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);
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:
{
src/ir_print.cpp
@@ -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");
}
std/debug.zig
@@ -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);
}
},
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 {
test/cases/misc.zig
@@ -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"));
+ }
+}