| author | |
| committer | |
| log | bcbcb2e9ffbfef63d3692ea45eea2e2babacc3a9 |
| tree | 48f4df2e6ce5e12077743a84433c4c2537dd4fc2 |
| parent | 1864acd32608ae917f8afc11b11f08a4bb362cef |
| parent | 2f9fedabf0805a47aba5c348e5369c1c28f6cf21 |
| signature |
7 files changed, 68 insertions(+), 20 deletions(-)
doc/langref.html.in+10-6| ... | @@ -6381,14 +6381,14 @@ fn List(comptime T: type) type { | ... | @@ -6381,14 +6381,14 @@ fn List(comptime T: type) type { |
| 6381 | {#header_close#} | 6381 | {#header_close#} |
| 6382 | 6382 | ||
| 6383 | {#header_open|@truncate#} | 6383 | {#header_open|@truncate#} |
| 6384 | <pre>{#syntax#}@truncate(comptime T: type, integer) T{#endsyntax#}</pre> | 6384 | <pre>{#syntax#}@truncate(comptime T: type, integer: var) T{#endsyntax#}</pre> |
| 6385 | <p> | 6385 | <p> |
| 6386 | This function truncates bits from an integer type, resulting in a smaller | 6386 | This function truncates bits from an integer type, resulting in a smaller |
| 6387 | integer type. | 6387 | integer type. |
| 6388 | </p> | 6388 | </p> |
| 6389 | <p> | 6389 | <p> |
| 6390 | The following produces a crash in debug mode and undefined behavior in | 6390 | The following produces a crash in {#link|Debug#} mode and {#link|Undefined Behavior#} in |
| 6391 | release mode: | 6391 | {#link|ReleaseFast#} mode: |
| 6392 | </p> | 6392 | </p> |
| 6393 | <pre>{#syntax#}const a: u16 = 0xabcd; | 6393 | <pre>{#syntax#}const a: u16 = 0xabcd; |
| 6394 | const b: u8 = u8(a);{#endsyntax#}</pre> | 6394 | const b: u8 = u8(a);{#endsyntax#}</pre> |
| ... | @@ -6402,7 +6402,10 @@ const b: u8 = @truncate(u8, a); | ... | @@ -6402,7 +6402,10 @@ const b: u8 = @truncate(u8, a); |
| 6402 | This function always truncates the significant bits of the integer, regardless | 6402 | This function always truncates the significant bits of the integer, regardless |
| 6403 | of endianness on the target platform. | 6403 | of endianness on the target platform. |
| 6404 | </p> | 6404 | </p> |
| 6405 | 6405 | <p> | |
| 6406 | If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#}, | ||
| 6407 | then this is semantically equivalent to an {#link|implicit cast|Implicit Casts#}. | ||
| 6408 | </p> | ||
| 6406 | {#header_close#} | 6409 | {#header_close#} |
| 6407 | 6410 | ||
| 6408 | {#header_open|@typeId#} | 6411 | {#header_open|@typeId#} |
| ... | @@ -7870,7 +7873,7 @@ TopLevelComptime &lt;- KEYWORD_comptime BlockExpr | ... | @@ -7870,7 +7873,7 @@ TopLevelComptime &lt;- KEYWORD_comptime BlockExpr |
| 7870 | 7873 | ||
| 7871 | TopLevelDecl | 7874 | TopLevelDecl |
| 7872 | &lt;- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block) | 7875 | &lt;- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block) |
| 7873 | / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? VarDecl | 7876 | / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl |
| 7874 | / KEYWORD_use Expr SEMICOLON | 7877 | / KEYWORD_use Expr SEMICOLON |
| 7875 | 7878 | ||
| 7876 | FnProto &lt;- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr) | 7879 | FnProto &lt;- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr) |
| ... | @@ -8330,6 +8333,7 @@ KEYWORD_struct &lt;- 'struct' end_of_word | ... | @@ -8330,6 +8333,7 @@ KEYWORD_struct &lt;- 'struct' end_of_word |
| 8330 | KEYWORD_suspend &lt;- 'suspend' end_of_word | 8333 | KEYWORD_suspend &lt;- 'suspend' end_of_word |
| 8331 | KEYWORD_switch &lt;- 'switch' end_of_word | 8334 | KEYWORD_switch &lt;- 'switch' end_of_word |
| 8332 | KEYWORD_test &lt;- 'test' end_of_word | 8335 | KEYWORD_test &lt;- 'test' end_of_word |
| 8336 | KEYWORD_threadlocal &lt;- 'threadlocal' end_of_word | ||
| 8333 | KEYWORD_true &lt;- 'true' end_of_word | 8337 | KEYWORD_true &lt;- 'true' end_of_word |
| 8334 | KEYWORD_try &lt;- 'try' end_of_word | 8338 | KEYWORD_try &lt;- 'try' end_of_word |
| 8335 | KEYWORD_undefined &lt;- 'undefined' end_of_word | 8339 | KEYWORD_undefined &lt;- 'undefined' end_of_word |
| ... | @@ -8350,7 +8354,7 @@ keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_anyerror / KEYWORD_asm | ... | @@ -8350,7 +8354,7 @@ keyword &lt;- KEYWORD_align / KEYWORD_and / KEYWORD_anyerror / KEYWORD_asm |
| 8350 | / KEYWORD_orelse / KEYWORD_packed / KEYWORD_promise / KEYWORD_pub | 8354 | / KEYWORD_orelse / KEYWORD_packed / KEYWORD_promise / KEYWORD_pub |
| 8351 | / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection | 8355 | / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection |
| 8352 | / KEYWORD_stdcallcc / KEYWORD_struct / KEYWORD_suspend | 8356 | / KEYWORD_stdcallcc / KEYWORD_struct / KEYWORD_suspend |
| 8353 | / KEYWORD_switch / KEYWORD_test / KEYWORD_true / KEYWORD_try | 8357 | / KEYWORD_switch / KEYWORD_test / KEYWORD_threadlocal / KEYWORD_true / KEYWORD_try |
| 8354 | / KEYWORD_undefined / KEYWORD_union / KEYWORD_unreachable | 8358 | / KEYWORD_undefined / KEYWORD_union / KEYWORD_unreachable |
| 8355 | / KEYWORD_use / KEYWORD_var / KEYWORD_volatile / KEYWORD_while</code></pre> | 8359 | / KEYWORD_use / KEYWORD_var / KEYWORD_volatile / KEYWORD_while</code></pre> |
| 8356 | {#header_close#} | 8360 | {#header_close#} |
src/analyze.cpp+3| ... | @@ -594,6 +594,9 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) { | ... | @@ -594,6 +594,9 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) { |
| 594 | // function types are technically pointers | 594 | // function types are technically pointers |
| 595 | entry->type_ref = child_type->type_ref; | 595 | entry->type_ref = child_type->type_ref; |
| 596 | entry->di_type = child_type->di_type; | 596 | entry->di_type = child_type->di_type; |
| 597 | if (entry->di_type == g->builtin_types.entry_global_error_set->di_type) { | ||
| 598 | g->error_di_types.append(&entry->di_type); | ||
| 599 | } | ||
| 597 | } else { | 600 | } else { |
| 598 | assert(child_type->di_type); | 601 | assert(child_type->di_type); |
| 599 | // create a struct with a boolean whether this is the null value | 602 | // create a struct with a boolean whether this is the null value |
src/ir.cpp+16-8| ... | @@ -18491,7 +18491,22 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct | ... | @@ -18491,7 +18491,22 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct |
| 18491 | return ira->codegen->invalid_instruction; | 18491 | return ira->codegen->invalid_instruction; |
| 18492 | } | 18492 | } |
| 18493 | 18493 | ||
| 18494 | if (src_type->data.integral.bit_count == 0) { | 18494 | if (dest_type->id == ZigTypeIdComptimeInt) { |
| 18495 | return ir_implicit_cast(ira, target, dest_type); | ||
| 18496 | } | ||
| 18497 | |||
| 18498 | if (instr_is_comptime(target)) { | ||
| 18499 | ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); | ||
| 18500 | if (val == nullptr) | ||
| 18501 | return ira->codegen->invalid_instruction; | ||
| 18502 | |||
| 18503 | IrInstruction *result = ir_const(ira, &instruction->base, dest_type); | ||
| 18504 | bigint_truncate(&result->value.data.x_bigint, &val->data.x_bigint, | ||
| 18505 | dest_type->data.integral.bit_count, dest_type->data.integral.is_signed); | ||
| 18506 | return result; | ||
| 18507 | } | ||
| 18508 | |||
| 18509 | if (src_type->data.integral.bit_count == 0 || dest_type->data.integral.bit_count == 0) { | ||
| 18495 | IrInstruction *result = ir_const(ira, &instruction->base, dest_type); | 18510 | IrInstruction *result = ir_const(ira, &instruction->base, dest_type); |
| 18496 | bigint_init_unsigned(&result->value.data.x_bigint, 0); | 18511 | bigint_init_unsigned(&result->value.data.x_bigint, 0); |
| 18497 | return result; | 18512 | return result; |
| ... | @@ -18507,13 +18522,6 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct | ... | @@ -18507,13 +18522,6 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct |
| 18507 | return ira->codegen->invalid_instruction; | 18522 | return ira->codegen->invalid_instruction; |
| 18508 | } | 18523 | } |
| 18509 | 18524 | ||
| 18510 | if (target->value.special == ConstValSpecialStatic) { | ||
| 18511 | IrInstruction *result = ir_const(ira, &instruction->base, dest_type); | ||
| 18512 | bigint_truncate(&result->value.data.x_bigint, &target->value.data.x_bigint, | ||
| 18513 | dest_type->data.integral.bit_count, dest_type->data.integral.is_signed); | ||
| 18514 | return result; | ||
| 18515 | } | ||
| 18516 | |||
| 18517 | IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope, | 18525 | IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope, |
| 18518 | instruction->base.source_node, dest_type_value, target); | 18526 | instruction->base.source_node, dest_type_value, target); |
| 18519 | new_instruction->value.type = dest_type; | 18527 | new_instruction->value.type = dest_type; |
std/testing.zig+1-5| ... | @@ -24,11 +24,7 @@ pub fn expectError(expected_error: anyerror, actual_error_union: var) void { | ... | @@ -24,11 +24,7 @@ pub fn expectError(expected_error: anyerror, actual_error_union: var) void { |
| 24 | /// equal, prints diagnostics to stderr to show exactly how they are not equal, | 24 | /// equal, prints diagnostics to stderr to show exactly how they are not equal, |
| 25 | /// then aborts. | 25 | /// then aborts. |
| 26 | /// The types must match exactly. | 26 | /// The types must match exactly. |
| 27 | pub fn expectEqual(expected: var, actual: var) void { | 27 | pub fn expectEqual(expected: var, actual: @typeOf(expected)) void { |
| 28 | if (@typeOf(actual) != @typeOf(expected)) { | ||
| 29 | @compileError("type mismatch. expected " ++ @typeName(@typeOf(expected)) ++ ", found " ++ @typeName(@typeOf(actual))); | ||
| 30 | } | ||
| 31 | |||
| 32 | switch (@typeInfo(@typeOf(actual))) { | 28 | switch (@typeInfo(@typeOf(actual))) { |
| 33 | TypeId.NoReturn, | 29 | TypeId.NoReturn, |
| 34 | TypeId.BoundFn, | 30 | TypeId.BoundFn, |
test/compile_errors.zig+10-1| ... | @@ -1,6 +1,15 @@ | ... | @@ -1,6 +1,15 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4 | cases.addTest( | ||
| 5 | "@truncate undefined value", | ||
| 6 | \\export fn entry() void { | ||
| 7 | \\ var z = @truncate(u8, u16(undefined)); | ||
| 8 | \\} | ||
| 9 | , | ||
| 10 | ".tmp_source.zig:2:30: error: use of undefined value", | ||
| 11 | ); | ||
| 12 | |||
| 4 | cases.addTest( | 13 | cases.addTest( |
| 5 | "return invalid type from test", | 14 | "return invalid type from test", |
| 6 | \\test "example" { return 1; } | 15 | \\test "example" { return 1; } |
| ... | @@ -3335,7 +3344,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -3335,7 +3344,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3335 | cases.add( | 3344 | cases.add( |
| 3336 | "truncate sign mismatch", | 3345 | "truncate sign mismatch", |
| 3337 | \\fn f() i8 { | 3346 | \\fn f() i8 { |
| 3338 | \\ const x: u32 = 10; | 3347 | \\ var x: u32 = 10; |
| 3339 | \\ return @truncate(i8, x); | 3348 | \\ return @truncate(i8, x); |
| 3340 | \\} | 3349 | \\} |
| 3341 | \\ | 3350 | \\ |
test/stage1/behavior/error.zig+5| ... | @@ -330,3 +330,8 @@ test "optional error set is the same size as error set" { | ... | @@ -330,3 +330,8 @@ test "optional error set is the same size as error set" { |
| 330 | expect(S.returnsOptErrSet() == null); | 330 | expect(S.returnsOptErrSet() == null); |
| 331 | comptime expect(S.returnsOptErrSet() == null); | 331 | comptime expect(S.returnsOptErrSet() == null); |
| 332 | } | 332 | } |
| 333 | |||
| 334 | test "debug info for optional error set" { | ||
| 335 | const SomeError = error{Hello}; | ||
| 336 | var a_local_variable: ?SomeError = null; | ||
| 337 | } |
test/stage1/behavior/truncate.zig+23| ... | @@ -6,3 +6,26 @@ test "truncate u0 to larger integer allowed and has comptime known result" { | ... | @@ -6,3 +6,26 @@ test "truncate u0 to larger integer allowed and has comptime known result" { |
| 6 | const y = @truncate(u8, x); | 6 | const y = @truncate(u8, x); |
| 7 | comptime expect(y == 0); | 7 | comptime expect(y == 0); |
| 8 | } | 8 | } |
| 9 | |||
| 10 | test "truncate.u0.literal" { | ||
| 11 | var z = @truncate(u0, 0); | ||
| 12 | expect(z == 0); | ||
| 13 | } | ||
| 14 | |||
| 15 | test "truncate.u0.const" { | ||
| 16 | const c0: usize = 0; | ||
| 17 | var z = @truncate(u0, c0); | ||
| 18 | expect(z == 0); | ||
| 19 | } | ||
| 20 | |||
| 21 | test "truncate.u0.var" { | ||
| 22 | var d: u8 = 2; | ||
| 23 | var z = @truncate(u0, d); | ||
| 24 | expect(z == 0); | ||
| 25 | } | ||
| 26 | |||
| 27 | test "truncate sign mismatch but comptime known so it works anyway" { | ||
| 28 | const x: u32 = 10; | ||
| 29 | var result = @truncate(i8, x); | ||
| 30 | expect(result == 10); | ||
| 31 | } |