| author | |
| committer | |
| log | 4b3f18de3c5746b2ecfd6183351913de0909a83b |
| tree | e3d0d56fcd8c00d994a13c4d69d60f0ab28fe389 |
| parent | 32d8686da80d282e8cd6d84a0e5c331d269a1f69 |
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`9 files changed, 412 insertions(+), 38 deletions(-)
doc/langref.md+37-22| ... | @@ -370,10 +370,10 @@ TODO | ... | @@ -370,10 +370,10 @@ TODO |
| 370 | 370 | ||
| 371 | ## Built-in Functions | 371 | ## Built-in Functions |
| 372 | 372 | ||
| 373 | Built-in functions are prefixed with `@`. Remember that the `inline` keyword on | 373 | Built-in functions are prefixed with `@`. Remember that the `comptime` keyword on |
| 374 | a parameter means that the parameter must be known at compile time. | 374 | a parameter means that the parameter must be known at compile time. |
| 375 | 375 | ||
| 376 | ### @alloca(inline T: type, count: usize) -> []T | 376 | ### @alloca(comptime T: type, count: usize) -> []T |
| 377 | 377 | ||
| 378 | Allocates memory in the stack frame of the caller. This temporary space is | 378 | Allocates memory in the stack frame of the caller. This temporary space is |
| 379 | automatically freed when the function that called alloca returns to its caller, | 379 | automatically freed when the function that called alloca returns to its caller, |
| ... | @@ -391,13 +391,13 @@ The allocated memory contents are undefined. | ... | @@ -391,13 +391,13 @@ The allocated memory contents are undefined. |
| 391 | This function returns a compile-time constant, which is the type of the | 391 | This function returns a compile-time constant, which is the type of the |
| 392 | expression passed as an argument. The expression is *not evaluated*. | 392 | expression passed as an argument. The expression is *not evaluated*. |
| 393 | 393 | ||
| 394 | ### @sizeOf(inline T: type) -> (number literal) | 394 | ### @sizeOf(comptime T: type) -> (number literal) |
| 395 | 395 | ||
| 396 | This function returns the number of bytes it takes to store T in memory. | 396 | This function returns the number of bytes it takes to store T in memory. |
| 397 | 397 | ||
| 398 | The result is a target-specific compile time constant. | 398 | The result is a target-specific compile time constant. |
| 399 | 399 | ||
| 400 | ### @alignOf(inline T: type) -> (number literal) | 400 | ### @alignOf(comptime T: type) -> (number literal) |
| 401 | 401 | ||
| 402 | This function returns the number of bytes that this type should be aligned to | 402 | This function returns the number of bytes that this type should be aligned to |
| 403 | for the current target. | 403 | for the current target. |
| ... | @@ -414,10 +414,10 @@ false otherwise. | ... | @@ -414,10 +414,10 @@ false otherwise. |
| 414 | 414 | ||
| 415 | ``` | 415 | ``` |
| 416 | Function Operation | 416 | Function Operation |
| 417 | @addWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a + b | 417 | @addWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool *x = a + b |
| 418 | @subWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a - b | 418 | @subWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool *x = a - b |
| 419 | @mulWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a * b | 419 | @mulWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool *x = a * b |
| 420 | @shlWithOverflow(inline T: type, a: T, b: T, result: &T) -> bool *x = a << b | 420 | @shlWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool *x = a << b |
| 421 | ``` | 421 | ``` |
| 422 | 422 | ||
| 423 | ### @memset(dest: &u8, c: u8, byte_count: usize) | 423 | ### @memset(dest: &u8, c: u8, byte_count: usize) |
| ... | @@ -475,27 +475,27 @@ aggressive optimizations. | ... | @@ -475,27 +475,27 @@ aggressive optimizations. |
| 475 | 475 | ||
| 476 | This function is only valid within function scope. | 476 | This function is only valid within function scope. |
| 477 | 477 | ||
| 478 | ### @maxValue(inline T: type) -> (number literal) | 478 | ### @maxValue(comptime T: type) -> (number literal) |
| 479 | 479 | ||
| 480 | This function returns the maximum integer value of the integer type T. | 480 | This function returns the maximum integer value of the integer type T. |
| 481 | 481 | ||
| 482 | The result is a compile time constant. For some types such as `c_long`, the | 482 | The result is a compile time constant. For some types such as `c_long`, the |
| 483 | result is marked as depending on a compile variable. | 483 | result is marked as depending on a compile variable. |
| 484 | 484 | ||
| 485 | ### @minValue(inline T: type) -> (number literal) | 485 | ### @minValue(comptime T: type) -> (number literal) |
| 486 | 486 | ||
| 487 | This function returns the minimum integer value of the integer type T. | 487 | This function returns the minimum integer value of the integer type T. |
| 488 | 488 | ||
| 489 | The result is a compile time constant. For some types such as `c_long`, the | 489 | The result is a compile time constant. For some types such as `c_long`, the |
| 490 | result is marked as depending on a compile variable. | 490 | result is marked as depending on a compile variable. |
| 491 | 491 | ||
| 492 | ### @memberCount(inline T: type) -> (number literal) | 492 | ### @memberCount(comptime T: type) -> (number literal) |
| 493 | 493 | ||
| 494 | This function returns the number of enum values in an enum type. | 494 | This function returns the number of enum values in an enum type. |
| 495 | 495 | ||
| 496 | The result is a compile time constant. | 496 | The result is a compile time constant. |
| 497 | 497 | ||
| 498 | ### @import(inline path: []u8) -> (namespace) | 498 | ### @import(comptime path: []u8) -> (namespace) |
| 499 | 499 | ||
| 500 | This function finds a zig file corresponding to `path` and imports all the | 500 | This function finds a zig file corresponding to `path` and imports all the |
| 501 | public top level declarations into the resulting namespace. | 501 | public top level declarations into the resulting namespace. |
| ... | @@ -516,25 +516,25 @@ appending to a temporary buffer which is then parsed as C code. | ... | @@ -516,25 +516,25 @@ appending to a temporary buffer which is then parsed as C code. |
| 516 | 516 | ||
| 517 | This function is only valid at top level scope. | 517 | This function is only valid at top level scope. |
| 518 | 518 | ||
| 519 | ### @cInclude(inline path: []u8) | 519 | ### @cInclude(comptime path: []u8) |
| 520 | 520 | ||
| 521 | This function can only occur inside `@c_import`. | 521 | This function can only occur inside `@c_import`. |
| 522 | 522 | ||
| 523 | This appends `#include <$path>\n` to the `c_import` temporary buffer. | 523 | This appends `#include <$path>\n` to the `c_import` temporary buffer. |
| 524 | 524 | ||
| 525 | ### @cDefine(inline name: []u8, value) | 525 | ### @cDefine(comptime name: []u8, value) |
| 526 | 526 | ||
| 527 | This function can only occur inside `@c_import`. | 527 | This function can only occur inside `@c_import`. |
| 528 | 528 | ||
| 529 | This appends `#define $name $value` to the `c_import` temporary buffer. | 529 | This appends `#define $name $value` to the `c_import` temporary buffer. |
| 530 | 530 | ||
| 531 | ### @cUndef(inline name: []u8) | 531 | ### @cUndef(comptime name: []u8) |
| 532 | 532 | ||
| 533 | This function can only occur inside `@c_import`. | 533 | This function can only occur inside `@c_import`. |
| 534 | 534 | ||
| 535 | This appends `#undef $name` to the `c_import` temporary buffer. | 535 | This appends `#undef $name` to the `c_import` temporary buffer. |
| 536 | 536 | ||
| 537 | ### @compileVar(inline name: []u8) -> (varying type) | 537 | ### @compileVar(comptime name: []u8) -> (varying type) |
| 538 | 538 | ||
| 539 | This function returns a compile-time variable. There are built in compile | 539 | This function returns a compile-time variable. There are built in compile |
| 540 | variables: | 540 | variables: |
| ... | @@ -584,10 +584,14 @@ error OutOfMem; | ... | @@ -584,10 +584,14 @@ error OutOfMem; |
| 584 | 584 | ||
| 585 | Then the string representation is "OutOfMem". | 585 | Then the string representation is "OutOfMem". |
| 586 | 586 | ||
| 587 | If there are no calls to `@err_name` in an entire application, then no error | 587 | If there are no calls to `@errorName` in an entire application, then no error |
| 588 | name table will be generated. | 588 | name table will be generated. |
| 589 | 589 | ||
| 590 | ### @embedFile(inline path: []u8) -> [X]u8 | 590 | ### @typeName(T: type) -> []u8 |
| 591 | |||
| 592 | This function returns the string representation of a type. | ||
| 593 | |||
| 594 | ### @embedFile(comptime path: []u8) -> [X]u8 | ||
| 591 | 595 | ||
| 592 | This function returns a compile time constant fixed-size array with length | 596 | This function returns a compile time constant fixed-size array with length |
| 593 | equal to the byte count of the file given by `path`. The contents of the array | 597 | 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. | ... | @@ -610,7 +614,7 @@ The caller guarantees that this operation will have no remainder. |
| 610 | In debug mode, a remainder causes a panic. In release mode, a remainder is | 614 | In debug mode, a remainder causes a panic. In release mode, a remainder is |
| 611 | undefined behavior. | 615 | undefined behavior. |
| 612 | 616 | ||
| 613 | ### @truncate(inline T: type, integer) -> T | 617 | ### @truncate(comptime T: type, integer) -> T |
| 614 | 618 | ||
| 615 | This function truncates bits from an integer type, resulting in a smaller | 619 | This function truncates bits from an integer type, resulting in a smaller |
| 616 | integer type. | 620 | integer type. |
| ... | @@ -631,14 +635,14 @@ const b: u8 = @truncate(u8, a); | ... | @@ -631,14 +635,14 @@ const b: u8 = @truncate(u8, a); |
| 631 | // b is now 0xcd | 635 | // b is now 0xcd |
| 632 | ``` | 636 | ``` |
| 633 | 637 | ||
| 634 | ### @compileError(inline msg: []u8) | 638 | ### @compileError(comptime msg: []u8) |
| 635 | 639 | ||
| 636 | This function, when semantically analyzed, causes a compile error with the message `msg`. | 640 | This function, when semantically analyzed, causes a compile error with the message `msg`. |
| 637 | 641 | ||
| 638 | There are several ways that code avoids being semantically checked, such as using `if` | 642 | There are several ways that code avoids being semantically checked, such as using `if` |
| 639 | or `switch` with compile time constants, and inline functions. | 643 | or `switch` with compile time constants, and comptime functions. |
| 640 | 644 | ||
| 641 | ### @intType(inline is_signed: bool, inline bit_count: u8) -> type | 645 | ### @intType(comptime is_signed: bool, comptime bit_count: u8) -> type |
| 642 | 646 | ||
| 643 | This function returns an integer type with the given signness and bit count. | 647 | This function returns an integer type with the given signness and bit count. |
| 644 | 648 | ||
| ... | @@ -646,3 +650,14 @@ 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. |
| 646 | 650 | ||
| 647 | Makes the target function a test function. | 651 | Makes the target function a test function. |
| 648 | 652 | ||
| 653 | ### @isInteger(comptime T: type) -> bool | ||
| 654 | |||
| 655 | Returns whether a given type is an integer. | ||
| 656 | |||
| 657 | ### @isFloat(comptime T: type) -> bool | ||
| 658 | |||
| 659 | Returns whether a given type is a float. | ||
| 660 | |||
| 661 | ### @canImplicitCast(comptime T: type, value) -> bool | ||
| 662 | |||
| 663 | Returns whether a value can be implicitly casted to a given type. |
example/cat/main.zig-2| ... | @@ -2,8 +2,6 @@ const std = @import("std"); | ... | @@ -2,8 +2,6 @@ const std = @import("std"); |
| 2 | const io = std.io; | 2 | const io = std.io; |
| 3 | const str = std.str; | 3 | const str = std.str; |
| 4 | 4 | ||
| 5 | // TODO var args printing | ||
| 6 | |||
| 7 | pub fn main(args: [][]u8) -> %void { | 5 | pub fn main(args: [][]u8) -> %void { |
| 8 | const exe = args[0]; | 6 | const exe = args[0]; |
| 9 | var catted_anything = false; | 7 | var catted_anything = false; |
src/all_types.hpp+30| ... | @@ -978,6 +978,9 @@ struct TypeTableEntry { | ... | @@ -978,6 +978,9 @@ struct TypeTableEntry { |
| 978 | HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size; | 978 | HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size; |
| 979 | TypeTableEntry *maybe_parent; | 979 | TypeTableEntry *maybe_parent; |
| 980 | TypeTableEntry *error_parent; | 980 | TypeTableEntry *error_parent; |
| 981 | // If we generate a constant name value for this type, we memoize it here. | ||
| 982 | // The type of this is array | ||
| 983 | ConstExprValue *cached_const_name_val; | ||
| 981 | }; | 984 | }; |
| 982 | 985 | ||
| 983 | struct PackageTableEntry { | 986 | struct PackageTableEntry { |
| ... | @@ -1086,6 +1089,10 @@ enum BuiltinFnId { | ... | @@ -1086,6 +1089,10 @@ enum BuiltinFnId { |
| 1086 | BuiltinFnIdSetFnVisible, | 1089 | BuiltinFnIdSetFnVisible, |
| 1087 | BuiltinFnIdSetDebugSafety, | 1090 | BuiltinFnIdSetDebugSafety, |
| 1088 | BuiltinFnIdAlloca, | 1091 | BuiltinFnIdAlloca, |
| 1092 | BuiltinFnIdTypeName, | ||
| 1093 | BuiltinFnIdIsInteger, | ||
| 1094 | BuiltinFnIdIsFloat, | ||
| 1095 | BuiltinFnIdCanImplicitCast, | ||
| 1089 | }; | 1096 | }; |
| 1090 | 1097 | ||
| 1091 | struct BuiltinFnEntry { | 1098 | struct BuiltinFnEntry { |
| ... | @@ -1505,6 +1512,9 @@ enum IrInstructionId { | ... | @@ -1505,6 +1512,9 @@ enum IrInstructionId { |
| 1505 | IrInstructionIdPtrToInt, | 1512 | IrInstructionIdPtrToInt, |
| 1506 | IrInstructionIdIntToEnum, | 1513 | IrInstructionIdIntToEnum, |
| 1507 | IrInstructionIdCheckSwitchProngs, | 1514 | IrInstructionIdCheckSwitchProngs, |
| 1515 | IrInstructionIdTestType, | ||
| 1516 | IrInstructionIdTypeName, | ||
| 1517 | IrInstructionIdCanImplicitCast, | ||
| 1508 | }; | 1518 | }; |
| 1509 | 1519 | ||
| 1510 | struct IrInstruction { | 1520 | struct IrInstruction { |
| ... | @@ -2188,6 +2198,26 @@ struct IrInstructionCheckSwitchProngs { | ... | @@ -2188,6 +2198,26 @@ struct IrInstructionCheckSwitchProngs { |
| 2188 | size_t range_count; | 2198 | size_t range_count; |
| 2189 | }; | 2199 | }; |
| 2190 | 2200 | ||
| 2201 | struct IrInstructionTestType { | ||
| 2202 | IrInstruction base; | ||
| 2203 | |||
| 2204 | IrInstruction *type_value; | ||
| 2205 | TypeTableEntryId type_id; | ||
| 2206 | }; | ||
| 2207 | |||
| 2208 | struct IrInstructionTypeName { | ||
| 2209 | IrInstruction base; | ||
| 2210 | |||
| 2211 | IrInstruction *type_value; | ||
| 2212 | }; | ||
| 2213 | |||
| 2214 | struct IrInstructionCanImplicitCast { | ||
| 2215 | IrInstruction base; | ||
| 2216 | |||
| 2217 | IrInstruction *type_value; | ||
| 2218 | IrInstruction *target_value; | ||
| 2219 | }; | ||
| 2220 | |||
| 2191 | enum LValPurpose { | 2221 | enum LValPurpose { |
| 2192 | LValPurposeNone, | 2222 | LValPurposeNone, |
| 2193 | LValPurposeAssign, | 2223 | LValPurposeAssign, |
src/codegen.cpp+7| ... | @@ -2279,6 +2279,9 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, | ... | @@ -2279,6 +2279,9 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 2279 | case IrInstructionIdTestComptime: | 2279 | case IrInstructionIdTestComptime: |
| 2280 | case IrInstructionIdGeneratedCode: | 2280 | case IrInstructionIdGeneratedCode: |
| 2281 | case IrInstructionIdCheckSwitchProngs: | 2281 | case IrInstructionIdCheckSwitchProngs: |
| 2282 | case IrInstructionIdTestType: | ||
| 2283 | case IrInstructionIdTypeName: | ||
| 2284 | case IrInstructionIdCanImplicitCast: | ||
| 2282 | zig_unreachable(); | 2285 | zig_unreachable(); |
| 2283 | case IrInstructionIdReturn: | 2286 | case IrInstructionIdReturn: |
| 2284 | return ir_render_return(g, executable, (IrInstructionReturn *)instruction); | 2287 | return ir_render_return(g, executable, (IrInstructionReturn *)instruction); |
| ... | @@ -3652,6 +3655,10 @@ static void define_builtin_fns(CodeGen *g) { | ... | @@ -3652,6 +3655,10 @@ static void define_builtin_fns(CodeGen *g) { |
| 3652 | create_builtin_fn(g, BuiltinFnIdImport, "import", 1); | 3655 | create_builtin_fn(g, BuiltinFnIdImport, "import", 1); |
| 3653 | create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1); | 3656 | create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1); |
| 3654 | create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1); | 3657 | create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1); |
| 3658 | create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1); | ||
| 3659 | create_builtin_fn(g, BuiltinFnIdIsInteger, "isInteger", 1); | ||
| 3660 | create_builtin_fn(g, BuiltinFnIdIsFloat, "isFloat", 1); | ||
| 3661 | create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2); | ||
| 3655 | create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1); | 3662 | create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1); |
| 3656 | create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5); | 3663 | create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5); |
| 3657 | create_builtin_fn(g, BuiltinFnIdFence, "fence", 1); | 3664 | create_builtin_fn(g, BuiltinFnIdFence, "fence", 1); |
src/ir.cpp+186-1| ... | @@ -495,6 +495,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProng | ... | @@ -495,6 +495,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProng |
| 495 | return IrInstructionIdCheckSwitchProngs; | 495 | return IrInstructionIdCheckSwitchProngs; |
| 496 | } | 496 | } |
| 497 | 497 | ||
| 498 | static constexpr IrInstructionId ir_instruction_id(IrInstructionTestType *) { | ||
| 499 | return IrInstructionIdTestType; | ||
| 500 | } | ||
| 501 | |||
| 502 | static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeName *) { | ||
| 503 | return IrInstructionIdTypeName; | ||
| 504 | } | ||
| 505 | |||
| 506 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCanImplicitCast *) { | ||
| 507 | return IrInstructionIdCanImplicitCast; | ||
| 508 | } | ||
| 509 | |||
| 498 | template<typename T> | 510 | template<typename T> |
| 499 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { | 511 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 500 | T *special_instruction = allocate<T>(1); | 512 | T *special_instruction = allocate<T>(1); |
| ... | @@ -2001,6 +2013,45 @@ static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, | ... | @@ -2001,6 +2013,45 @@ static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, |
| 2001 | return &instruction->base; | 2013 | return &instruction->base; |
| 2002 | } | 2014 | } |
| 2003 | 2015 | ||
| 2016 | static IrInstruction *ir_build_test_type(IrBuilder *irb, Scope *scope, AstNode *source_node, | ||
| 2017 | IrInstruction *type_value, TypeTableEntryId type_id) | ||
| 2018 | { | ||
| 2019 | IrInstructionTestType *instruction = ir_build_instruction<IrInstructionTestType>( | ||
| 2020 | irb, scope, source_node); | ||
| 2021 | instruction->type_value = type_value; | ||
| 2022 | instruction->type_id = type_id; | ||
| 2023 | |||
| 2024 | ir_ref_instruction(type_value, irb->current_basic_block); | ||
| 2025 | |||
| 2026 | return &instruction->base; | ||
| 2027 | } | ||
| 2028 | |||
| 2029 | static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode *source_node, | ||
| 2030 | IrInstruction *type_value) | ||
| 2031 | { | ||
| 2032 | IrInstructionTypeName *instruction = ir_build_instruction<IrInstructionTypeName>( | ||
| 2033 | irb, scope, source_node); | ||
| 2034 | instruction->type_value = type_value; | ||
| 2035 | |||
| 2036 | ir_ref_instruction(type_value, irb->current_basic_block); | ||
| 2037 | |||
| 2038 | return &instruction->base; | ||
| 2039 | } | ||
| 2040 | |||
| 2041 | static IrInstruction *ir_build_can_implicit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, | ||
| 2042 | IrInstruction *type_value, IrInstruction *target_value) | ||
| 2043 | { | ||
| 2044 | IrInstructionCanImplicitCast *instruction = ir_build_instruction<IrInstructionCanImplicitCast>( | ||
| 2045 | irb, scope, source_node); | ||
| 2046 | instruction->type_value = type_value; | ||
| 2047 | instruction->target_value = target_value; | ||
| 2048 | |||
| 2049 | ir_ref_instruction(type_value, irb->current_basic_block); | ||
| 2050 | ir_ref_instruction(target_value, irb->current_basic_block); | ||
| 2051 | |||
| 2052 | return &instruction->base; | ||
| 2053 | } | ||
| 2054 | |||
| 2004 | static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) { | 2055 | static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) { |
| 2005 | return nullptr; | 2056 | return nullptr; |
| 2006 | } | 2057 | } |
| ... | @@ -2605,6 +2656,28 @@ static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionChec | ... | @@ -2605,6 +2656,28 @@ static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionChec |
| 2605 | return nullptr; | 2656 | return nullptr; |
| 2606 | } | 2657 | } |
| 2607 | 2658 | ||
| 2659 | static IrInstruction *ir_instruction_testtype_get_dep(IrInstructionTestType *instruction, size_t index) { | ||
| 2660 | switch (index) { | ||
| 2661 | case 0: return instruction->type_value; | ||
| 2662 | default: return nullptr; | ||
| 2663 | } | ||
| 2664 | } | ||
| 2665 | |||
| 2666 | static IrInstruction *ir_instruction_typename_get_dep(IrInstructionTypeName *instruction, size_t index) { | ||
| 2667 | switch (index) { | ||
| 2668 | case 0: return instruction->type_value; | ||
| 2669 | default: return nullptr; | ||
| 2670 | } | ||
| 2671 | } | ||
| 2672 | |||
| 2673 | static IrInstruction *ir_instruction_canimplicitcast_get_dep(IrInstructionCanImplicitCast *instruction, size_t index) { | ||
| 2674 | switch (index) { | ||
| 2675 | case 0: return instruction->type_value; | ||
| 2676 | case 1: return instruction->target_value; | ||
| 2677 | default: return nullptr; | ||
| 2678 | } | ||
| 2679 | } | ||
| 2680 | |||
| 2608 | static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) { | 2681 | static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) { |
| 2609 | switch (instruction->id) { | 2682 | switch (instruction->id) { |
| 2610 | case IrInstructionIdInvalid: | 2683 | case IrInstructionIdInvalid: |
| ... | @@ -2777,6 +2850,12 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t | ... | @@ -2777,6 +2850,12 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 2777 | return ir_instruction_inttoenum_get_dep((IrInstructionIntToEnum *) instruction, index); | 2850 | return ir_instruction_inttoenum_get_dep((IrInstructionIntToEnum *) instruction, index); |
| 2778 | case IrInstructionIdCheckSwitchProngs: | 2851 | case IrInstructionIdCheckSwitchProngs: |
| 2779 | return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index); | 2852 | return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index); |
| 2853 | case IrInstructionIdTestType: | ||
| 2854 | return ir_instruction_testtype_get_dep((IrInstructionTestType *) instruction, index); | ||
| 2855 | case IrInstructionIdTypeName: | ||
| 2856 | return ir_instruction_typename_get_dep((IrInstructionTypeName *) instruction, index); | ||
| 2857 | case IrInstructionIdCanImplicitCast: | ||
| 2858 | return ir_instruction_canimplicitcast_get_dep((IrInstructionCanImplicitCast *) instruction, index); | ||
| 2780 | } | 2859 | } |
| 2781 | zig_unreachable(); | 2860 | zig_unreachable(); |
| 2782 | } | 2861 | } |
| ... | @@ -3594,6 +3673,18 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode * | ... | @@ -3594,6 +3673,18 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode * |
| 3594 | return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr); | 3673 | return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr); |
| 3595 | } | 3674 | } |
| 3596 | 3675 | ||
| 3676 | static IrInstruction *ir_gen_test_type(IrBuilder *irb, Scope *scope, AstNode *node, TypeTableEntryId type_id) { | ||
| 3677 | assert(node->type == NodeTypeFnCallExpr); | ||
| 3678 | |||
| 3679 | AstNode *type_node = node->data.fn_call_expr.params.at(0); | ||
| 3680 | |||
| 3681 | IrInstruction *type_value = ir_gen_node(irb, type_node, scope); | ||
| 3682 | if (type_value == irb->codegen->invalid_instruction) | ||
| 3683 | return irb->codegen->invalid_instruction; | ||
| 3684 | |||
| 3685 | return ir_build_test_type(irb, scope, node, type_value, type_id); | ||
| 3686 | } | ||
| 3687 | |||
| 3597 | static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) { | 3688 | static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) { |
| 3598 | assert(node->type == NodeTypeFnCallExpr); | 3689 | assert(node->type == NodeTypeFnCallExpr); |
| 3599 | 3690 | ||
| ... | @@ -3995,6 +4086,33 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo | ... | @@ -3995,6 +4086,33 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3995 | return ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul); | 4086 | return ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul); |
| 3996 | case BuiltinFnIdShlWithOverflow: | 4087 | case BuiltinFnIdShlWithOverflow: |
| 3997 | return ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl); | 4088 | return ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl); |
| 4089 | case BuiltinFnIdTypeName: | ||
| 4090 | { | ||
| 4091 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | ||
| 4092 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | ||
| 4093 | if (arg0_value == irb->codegen->invalid_instruction) | ||
| 4094 | return arg0_value; | ||
| 4095 | |||
| 4096 | return ir_build_type_name(irb, scope, node, arg0_value); | ||
| 4097 | } | ||
| 4098 | case BuiltinFnIdIsInteger: | ||
| 4099 | return ir_gen_test_type(irb, scope, node, TypeTableEntryIdInt); | ||
| 4100 | case BuiltinFnIdIsFloat: | ||
| 4101 | return ir_gen_test_type(irb, scope, node, TypeTableEntryIdFloat); | ||
| 4102 | case BuiltinFnIdCanImplicitCast: | ||
| 4103 | { | ||
| 4104 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); | ||
| 4105 | IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); | ||
| 4106 | if (arg0_value == irb->codegen->invalid_instruction) | ||
| 4107 | return arg0_value; | ||
| 4108 | |||
| 4109 | AstNode *arg1_node = node->data.fn_call_expr.params.at(1); | ||
| 4110 | IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope); | ||
| 4111 | if (arg1_value == irb->codegen->invalid_instruction) | ||
| 4112 | return arg1_value; | ||
| 4113 | |||
| 4114 | return ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value); | ||
| 4115 | } | ||
| 3998 | } | 4116 | } |
| 3999 | zig_unreachable(); | 4117 | zig_unreachable(); |
| 4000 | } | 4118 | } |
| ... | @@ -8942,7 +9060,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi | ... | @@ -8942,7 +9060,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi |
| 8942 | case TypeTableEntryIdEnumTag: | 9060 | case TypeTableEntryIdEnumTag: |
| 8943 | case TypeTableEntryIdArgTuple: | 9061 | case TypeTableEntryIdArgTuple: |
| 8944 | { | 9062 | { |
| 8945 | ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base, false); | 9063 | ConstExprValue *out_val = ir_build_const_from(ira, &typeof_instruction->base, true); |
| 8946 | // TODO depends_on_compile_var should be set based on whether the type of the expression | 9064 | // TODO depends_on_compile_var should be set based on whether the type of the expression |
| 8947 | // depends_on_compile_var. but we currently don't have a thing to tell us if the type of | 9065 | // depends_on_compile_var. but we currently don't have a thing to tell us if the type of |
| 8948 | // something depends on a compile var | 9066 | // something depends on a compile var |
| ... | @@ -9622,6 +9740,8 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, | ... | @@ -9622,6 +9740,8 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, |
| 9622 | pointee_val = nullptr; | 9740 | pointee_val = nullptr; |
| 9623 | } | 9741 | } |
| 9624 | TypeTableEntry *canon_target_type = get_underlying_type(target_type); | 9742 | TypeTableEntry *canon_target_type = get_underlying_type(target_type); |
| 9743 | ensure_complete_type(ira->codegen, target_type); | ||
| 9744 | |||
| 9625 | switch (canon_target_type->id) { | 9745 | switch (canon_target_type->id) { |
| 9626 | case TypeTableEntryIdInvalid: | 9746 | case TypeTableEntryIdInvalid: |
| 9627 | case TypeTableEntryIdVar: | 9747 | case TypeTableEntryIdVar: |
| ... | @@ -10260,6 +10380,24 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc | ... | @@ -10260,6 +10380,24 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc |
| 10260 | return str_type; | 10380 | return str_type; |
| 10261 | } | 10381 | } |
| 10262 | 10382 | ||
| 10383 | static TypeTableEntry *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstructionTypeName *instruction) { | ||
| 10384 | IrInstruction *type_value = instruction->type_value->other; | ||
| 10385 | TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); | ||
| 10386 | if (type_entry->id == TypeTableEntryIdInvalid) | ||
| 10387 | return ira->codegen->builtin_types.entry_invalid; | ||
| 10388 | |||
| 10389 | TypeTableEntry *str_type = get_slice_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true); | ||
| 10390 | if (!type_entry->cached_const_name_val) { | ||
| 10391 | ConstExprValue *array_val = create_const_str_lit(ira->codegen, &type_entry->name); | ||
| 10392 | type_entry->cached_const_name_val = create_const_slice(ira->codegen, | ||
| 10393 | array_val, 0, buf_len(&type_entry->name), true); | ||
| 10394 | } | ||
| 10395 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, | ||
| 10396 | type_value->value.depends_on_compile_var); | ||
| 10397 | *out_val = *type_entry->cached_const_name_val; | ||
| 10398 | return str_type; | ||
| 10399 | } | ||
| 10400 | |||
| 10263 | static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCImport *instruction) { | 10401 | static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstructionCImport *instruction) { |
| 10264 | AstNode *node = instruction->base.source_node; | 10402 | AstNode *node = instruction->base.source_node; |
| 10265 | assert(node->type == NodeTypeFnCallExpr); | 10403 | assert(node->type == NodeTypeFnCallExpr); |
| ... | @@ -11370,6 +11508,44 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira | ... | @@ -11370,6 +11508,44 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira |
| 11370 | return ira->codegen->builtin_types.entry_void; | 11508 | return ira->codegen->builtin_types.entry_void; |
| 11371 | } | 11509 | } |
| 11372 | 11510 | ||
| 11511 | static TypeTableEntry *ir_analyze_instruction_test_type(IrAnalyze *ira, IrInstructionTestType *instruction) { | ||
| 11512 | IrInstruction *type_value = instruction->type_value->other; | ||
| 11513 | TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); | ||
| 11514 | if (type_entry->id == TypeTableEntryIdInvalid) | ||
| 11515 | return ira->codegen->builtin_types.entry_invalid; | ||
| 11516 | |||
| 11517 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, type_value->value.depends_on_compile_var); | ||
| 11518 | out_val->data.x_bool = (type_entry->id == instruction->type_id); | ||
| 11519 | return ira->codegen->builtin_types.entry_bool; | ||
| 11520 | } | ||
| 11521 | |||
| 11522 | static TypeTableEntry *ir_analyze_instruction_can_implicit_cast(IrAnalyze *ira, | ||
| 11523 | IrInstructionCanImplicitCast *instruction) | ||
| 11524 | { | ||
| 11525 | IrInstruction *type_value = instruction->type_value->other; | ||
| 11526 | TypeTableEntry *type_entry = ir_resolve_type(ira, type_value); | ||
| 11527 | if (type_entry->id == TypeTableEntryIdInvalid) | ||
| 11528 | return ira->codegen->builtin_types.entry_invalid; | ||
| 11529 | |||
| 11530 | IrInstruction *target_value = instruction->target_value->other; | ||
| 11531 | if (target_value->value.type->id == TypeTableEntryIdInvalid) | ||
| 11532 | return ira->codegen->builtin_types.entry_invalid; | ||
| 11533 | |||
| 11534 | ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, type_entry, target_value->value.type, | ||
| 11535 | target_value); | ||
| 11536 | |||
| 11537 | if (result == ImplicitCastMatchResultReportedError) { | ||
| 11538 | zig_panic("TODO refactor implicit cast tester to return bool without reporting errors"); | ||
| 11539 | } | ||
| 11540 | |||
| 11541 | // TODO in order to known depends_on_compile_var we have to known if the type of the target | ||
| 11542 | // depends on a compile var | ||
| 11543 | bool depends_on_compile_var = true; | ||
| 11544 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base, depends_on_compile_var); | ||
| 11545 | out_val->data.x_bool = (result == ImplicitCastMatchResultYes); | ||
| 11546 | return ira->codegen->builtin_types.entry_bool; | ||
| 11547 | } | ||
| 11548 | |||
| 11373 | static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { | 11549 | static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { |
| 11374 | switch (instruction->id) { | 11550 | switch (instruction->id) { |
| 11375 | case IrInstructionIdInvalid: | 11551 | case IrInstructionIdInvalid: |
| ... | @@ -11471,6 +11647,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi | ... | @@ -11471,6 +11647,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 11471 | return ir_analyze_instruction_compile_err(ira, (IrInstructionCompileErr *)instruction); | 11647 | return ir_analyze_instruction_compile_err(ira, (IrInstructionCompileErr *)instruction); |
| 11472 | case IrInstructionIdErrName: | 11648 | case IrInstructionIdErrName: |
| 11473 | return ir_analyze_instruction_err_name(ira, (IrInstructionErrName *)instruction); | 11649 | return ir_analyze_instruction_err_name(ira, (IrInstructionErrName *)instruction); |
| 11650 | case IrInstructionIdTypeName: | ||
| 11651 | return ir_analyze_instruction_type_name(ira, (IrInstructionTypeName *)instruction); | ||
| 11474 | case IrInstructionIdCImport: | 11652 | case IrInstructionIdCImport: |
| 11475 | return ir_analyze_instruction_c_import(ira, (IrInstructionCImport *)instruction); | 11653 | return ir_analyze_instruction_c_import(ira, (IrInstructionCImport *)instruction); |
| 11476 | case IrInstructionIdCInclude: | 11654 | case IrInstructionIdCInclude: |
| ... | @@ -11525,6 +11703,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi | ... | @@ -11525,6 +11703,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 11525 | return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction); | 11703 | return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction); |
| 11526 | case IrInstructionIdCheckSwitchProngs: | 11704 | case IrInstructionIdCheckSwitchProngs: |
| 11527 | return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction); | 11705 | return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction); |
| 11706 | case IrInstructionIdTestType: | ||
| 11707 | return ir_analyze_instruction_test_type(ira, (IrInstructionTestType *)instruction); | ||
| 11708 | case IrInstructionIdCanImplicitCast: | ||
| 11709 | return ir_analyze_instruction_can_implicit_cast(ira, (IrInstructionCanImplicitCast *)instruction); | ||
| 11528 | case IrInstructionIdMaybeWrap: | 11710 | case IrInstructionIdMaybeWrap: |
| 11529 | case IrInstructionIdErrWrapCode: | 11711 | case IrInstructionIdErrWrapCode: |
| 11530 | case IrInstructionIdErrWrapPayload: | 11712 | case IrInstructionIdErrWrapPayload: |
| ... | @@ -11691,6 +11873,9 @@ bool ir_has_side_effects(IrInstruction *instruction) { | ... | @@ -11691,6 +11873,9 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 11691 | case IrInstructionIdPtrToInt: | 11873 | case IrInstructionIdPtrToInt: |
| 11692 | case IrInstructionIdIntToPtr: | 11874 | case IrInstructionIdIntToPtr: |
| 11693 | case IrInstructionIdIntToEnum: | 11875 | case IrInstructionIdIntToEnum: |
| 11876 | case IrInstructionIdTestType: | ||
| 11877 | case IrInstructionIdTypeName: | ||
| 11878 | case IrInstructionIdCanImplicitCast: | ||
| 11694 | return false; | 11879 | return false; |
| 11695 | case IrInstructionIdAsm: | 11880 | case IrInstructionIdAsm: |
| 11696 | { | 11881 | { |
src/ir_print.cpp+29| ... | @@ -807,6 +807,26 @@ static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchP | ... | @@ -807,6 +807,26 @@ static void ir_print_check_switch_prongs(IrPrint *irp, IrInstructionCheckSwitchP |
| 807 | fprintf(irp->f, ")"); | 807 | fprintf(irp->f, ")"); |
| 808 | } | 808 | } |
| 809 | 809 | ||
| 810 | static void ir_print_test_type(IrPrint *irp, IrInstructionTestType *instruction) { | ||
| 811 | fprintf(irp->f, "@testType("); | ||
| 812 | ir_print_other_instruction(irp, instruction->type_value); | ||
| 813 | fprintf(irp->f, ")"); | ||
| 814 | } | ||
| 815 | |||
| 816 | static void ir_print_type_name(IrPrint *irp, IrInstructionTypeName *instruction) { | ||
| 817 | fprintf(irp->f, "@typeName("); | ||
| 818 | ir_print_other_instruction(irp, instruction->type_value); | ||
| 819 | fprintf(irp->f, ")"); | ||
| 820 | } | ||
| 821 | |||
| 822 | static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCast *instruction) { | ||
| 823 | fprintf(irp->f, "@canImplicitCast("); | ||
| 824 | ir_print_other_instruction(irp, instruction->type_value); | ||
| 825 | fprintf(irp->f, ","); | ||
| 826 | ir_print_other_instruction(irp, instruction->target_value); | ||
| 827 | fprintf(irp->f, ")"); | ||
| 828 | } | ||
| 829 | |||
| 810 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | 830 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 811 | ir_print_prefix(irp, instruction); | 831 | ir_print_prefix(irp, instruction); |
| 812 | switch (instruction->id) { | 832 | switch (instruction->id) { |
| ... | @@ -1064,6 +1084,15 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { | ... | @@ -1064,6 +1084,15 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1064 | case IrInstructionIdCheckSwitchProngs: | 1084 | case IrInstructionIdCheckSwitchProngs: |
| 1065 | ir_print_check_switch_prongs(irp, (IrInstructionCheckSwitchProngs *)instruction); | 1085 | ir_print_check_switch_prongs(irp, (IrInstructionCheckSwitchProngs *)instruction); |
| 1066 | break; | 1086 | break; |
| 1087 | case IrInstructionIdTestType: | ||
| 1088 | ir_print_test_type(irp, (IrInstructionTestType *)instruction); | ||
| 1089 | break; | ||
| 1090 | case IrInstructionIdTypeName: | ||
| 1091 | ir_print_type_name(irp, (IrInstructionTypeName *)instruction); | ||
| 1092 | break; | ||
| 1093 | case IrInstructionIdCanImplicitCast: | ||
| 1094 | ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction); | ||
| 1095 | break; | ||
| 1067 | } | 1096 | } |
| 1068 | fprintf(irp->f, "\n"); | 1097 | fprintf(irp->f, "\n"); |
| 1069 | } | 1098 | } |
std/debug.zig+1-4| ... | @@ -50,10 +50,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { | ... | @@ -50,10 +50,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void { |
| 50 | const compile_unit = findCompileUnit(st, return_address) ?? return error.MissingDebugInfo; | 50 | const compile_unit = findCompileUnit(st, return_address) ?? return error.MissingDebugInfo; |
| 51 | const name = %return compile_unit.die.getAttrString(st, DW.AT_name); | 51 | const name = %return compile_unit.die.getAttrString(st, DW.AT_name); |
| 52 | 52 | ||
| 53 | %return out_stream.printInt(usize, return_address); | 53 | %return out_stream.printf("{} -> {}\n", return_address, name); |
| 54 | %return out_stream.printf(" -> "); | ||
| 55 | %return out_stream.printf(name); | ||
| 56 | %return out_stream.printf("\n"); | ||
| 57 | maybe_fp = *(&const ?&const u8)(fp); | 54 | maybe_fp = *(&const ?&const u8)(fp); |
| 58 | } | 55 | } |
| 59 | }, | 56 | }, |
std/io.zig+73-9| ... | @@ -80,7 +80,7 @@ pub const OutStream = struct { | ... | @@ -80,7 +80,7 @@ pub const OutStream = struct { |
| 80 | self.index += 1; | 80 | self.index += 1; |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | pub fn write(self: &OutStream, bytes: []const u8) -> %usize { | 83 | pub fn write(self: &OutStream, bytes: []const u8) -> %void { |
| 84 | var src_bytes_left = bytes.len; | 84 | var src_bytes_left = bytes.len; |
| 85 | var src_index: usize = 0; | 85 | var src_index: usize = 0; |
| 86 | const dest_space_left = self.buffer.len - self.index; | 86 | const dest_space_left = self.buffer.len - self.index; |
| ... | @@ -94,25 +94,89 @@ pub const OutStream = struct { | ... | @@ -94,25 +94,89 @@ pub const OutStream = struct { |
| 94 | } | 94 | } |
| 95 | src_bytes_left -= copy_amt; | 95 | src_bytes_left -= copy_amt; |
| 96 | } | 96 | } |
| 97 | return bytes.len; | ||
| 98 | } | 97 | } |
| 99 | 98 | ||
| 100 | /// Prints a byte buffer, flushes the buffer, then returns the number of | 99 | const State = enum { // TODO put inside printf function and make sure the name and debug info is correct |
| 101 | /// bytes printed. The "f" is for "flush". | 100 | Start, |
| 102 | pub fn printf(self: &OutStream, str: []const u8) -> %usize { | 101 | OpenBrace, |
| 103 | const byte_count = %return self.write(str); | 102 | CloseBrace, |
| 103 | }; | ||
| 104 | |||
| 105 | /// Calls print and then flushes the buffer. | ||
| 106 | pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) -> %void { | ||
| 107 | comptime var start_index: usize = 0; | ||
| 108 | comptime var state = State.Start; | ||
| 109 | comptime var next_arg: usize = 0; | ||
| 110 | inline for (format) |c, i| { | ||
| 111 | switch (state) { | ||
| 112 | State.Start => switch (c) { | ||
| 113 | '{' => { | ||
| 114 | if (start_index < i) %return self.write(format[start_index...i]); | ||
| 115 | state = State.OpenBrace; | ||
| 116 | }, | ||
| 117 | '}' => { | ||
| 118 | if (start_index < i) %return self.write(format[start_index...i]); | ||
| 119 | state = State.CloseBrace; | ||
| 120 | }, | ||
| 121 | else => {}, | ||
| 122 | }, | ||
| 123 | State.OpenBrace => switch (c) { | ||
| 124 | '{' => { | ||
| 125 | state = State.Start; | ||
| 126 | start_index = i; | ||
| 127 | }, | ||
| 128 | '}' => { | ||
| 129 | %return self.printValue(args[next_arg]); | ||
| 130 | next_arg += 1; | ||
| 131 | state = State.Start; | ||
| 132 | start_index = i + 1; | ||
| 133 | }, | ||
| 134 | else => @compileError("Unknown format character: " ++ c), | ||
| 135 | }, | ||
| 136 | State.CloseBrace => switch (c) { | ||
| 137 | '}' => { | ||
| 138 | state = State.Start; | ||
| 139 | start_index = i; | ||
| 140 | }, | ||
| 141 | else => @compileError("Single '}' encountered in format string"), | ||
| 142 | }, | ||
| 143 | } | ||
| 144 | } | ||
| 145 | comptime { | ||
| 146 | if (args.len != next_arg) { | ||
| 147 | @compileError("Unused arguments"); | ||
| 148 | } | ||
| 149 | if (state != State.Start) { | ||
| 150 | @compileError("Incomplete format string: " ++ format); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | inline if (start_index < format.len) { | ||
| 154 | %return self.write(format[start_index...format.len]); | ||
| 155 | } | ||
| 104 | %return self.flush(); | 156 | %return self.flush(); |
| 105 | return byte_count; | ||
| 106 | } | 157 | } |
| 107 | 158 | ||
| 108 | pub fn printInt(self: &OutStream, comptime T: type, x: T) -> %usize { | 159 | pub fn printValue(self: &OutStream, value: var) -> %void { |
| 160 | const T = @typeOf(value); | ||
| 161 | if (@isInteger(T)) { | ||
| 162 | return self.printInt(T, value); | ||
| 163 | } else if (@isFloat(T)) { | ||
| 164 | return self.printFloat(T, value); | ||
| 165 | } else if (@canImplicitCast([]const u8, value)) { | ||
| 166 | const casted_value = ([]const u8)(value); | ||
| 167 | return self.write(casted_value); | ||
| 168 | } else { | ||
| 169 | @compileError("Unable to print type '" ++ @typeName(T) ++ "'"); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | pub fn printInt(self: &OutStream, comptime T: type, x: T) -> %void { | ||
| 109 | // TODO replace max_u64_base10_digits with math.log10(math.pow(2, @sizeOf(T))) | 174 | // TODO replace max_u64_base10_digits with math.log10(math.pow(2, @sizeOf(T))) |
| 110 | if (self.index + max_u64_base10_digits >= self.buffer.len) { | 175 | if (self.index + max_u64_base10_digits >= self.buffer.len) { |
| 111 | %return self.flush(); | 176 | %return self.flush(); |
| 112 | } | 177 | } |
| 113 | const amt_printed = bufPrintInt(T, self.buffer[self.index...], x); | 178 | const amt_printed = bufPrintInt(T, self.buffer[self.index...], x); |
| 114 | self.index += amt_printed; | 179 | self.index += amt_printed; |
| 115 | return amt_printed; | ||
| 116 | } | 180 | } |
| 117 | 181 | ||
| 118 | pub fn flush(self: &OutStream) -> %void { | 182 | pub fn flush(self: &OutStream) -> %void { |
test/cases/misc.zig+49| ... | @@ -517,3 +517,52 @@ fn testArray2DConstDoublePtr(ptr: &const f32) { | ... | @@ -517,3 +517,52 @@ fn testArray2DConstDoublePtr(ptr: &const f32) { |
| 517 | assert(ptr[0] == 1.0); | 517 | assert(ptr[0] == 1.0); |
| 518 | assert(ptr[1] == 2.0); | 518 | assert(ptr[1] == 2.0); |
| 519 | } | 519 | } |
| 520 | |||
| 521 | fn isInteger() { | ||
| 522 | @setFnTest(this); | ||
| 523 | |||
| 524 | comptime { | ||
| 525 | assert(@isInteger(i8)); | ||
| 526 | assert(@isInteger(u8)); | ||
| 527 | assert(@isInteger(i64)); | ||
| 528 | assert(@isInteger(u64)); | ||
| 529 | assert(!@isInteger(f32)); | ||
| 530 | assert(!@isInteger(f64)); | ||
| 531 | assert(!@isInteger(bool)); | ||
| 532 | assert(!@isInteger(&i32)); | ||
| 533 | } | ||
| 534 | } | ||
| 535 | |||
| 536 | fn isFloat() { | ||
| 537 | @setFnTest(this); | ||
| 538 | |||
| 539 | comptime { | ||
| 540 | assert(!@isFloat(i8)); | ||
| 541 | assert(!@isFloat(u8)); | ||
| 542 | assert(!@isFloat(i64)); | ||
| 543 | assert(!@isFloat(u64)); | ||
| 544 | assert(@isFloat(f32)); | ||
| 545 | assert(@isFloat(f64)); | ||
| 546 | assert(!@isFloat(bool)); | ||
| 547 | assert(!@isFloat(&f32)); | ||
| 548 | } | ||
| 549 | } | ||
| 550 | |||
| 551 | fn canImplicitCast() { | ||
| 552 | @setFnTest(this); | ||
| 553 | |||
| 554 | comptime { | ||
| 555 | assert(@canImplicitCast(i64, i32(3))); | ||
| 556 | assert(!@canImplicitCast(i32, f32(1.234))); | ||
| 557 | assert(@canImplicitCast([]const u8, "aoeu")); | ||
| 558 | } | ||
| 559 | } | ||
| 560 | |||
| 561 | fn typeName() { | ||
| 562 | @setFnTest(this); | ||
| 563 | |||
| 564 | comptime { | ||
| 565 | assert(str.eql(@typeName(i64), "i64")); | ||
| 566 | assert(str.eql(@typeName(&usize), "&usize")); | ||
| 567 | } | ||
| 568 | } |