From 34eb9f18acc2370973adcc7be0d010d62300e9a9 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 9 Feb 2019 20:41:26 -0500
Subject: [PATCH 1/4] fix not updating debug info type of optional error sets
There's an unfortunate footgun in the current design of error sets.
The debug info type for every error set is the same as the debug info
type of the global error set, which is essentially an enum forward
declaration. The problem is that when we "replace" the forward
declaration with the final value, once we know all the possible errors,
we have to update the pointers of every error set.
So the footgun is that if you ever copy the debug info type of the
global error set, you have to add the address of the pointer to a list
of pointers that need to be updated once we "replace" the forward
declaration. I activated the footgun when I introduced the optimization
that `?anyerror` types are the same size as `anyerror` types (using 0 as
the null value), because I introduced a pointer copy of the global error
set debug info type, but forgot to add it to the list.
I'm sure that there is a better way to code this, which does not have
the footgun, but this commit contains only a fix, not a reworking of the
logic.
closes #1937
---
src/analyze.cpp | 3 +++
test/stage1/behavior/error.zig | 5 +++++
2 files changed, 8 insertions(+)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 0c493ebda17bbe105e88f47f052675e0ee55ef49..970d1cc3820cb12f32e961fb8e1c2def78c99c11 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -594,6 +594,9 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) {
// function types are technically pointers
entry->type_ref = child_type->type_ref;
entry->di_type = child_type->di_type;
+ if (entry->di_type == g->builtin_types.entry_global_error_set->di_type) {
+ g->error_di_types.append(&entry->di_type);
+ }
} else {
assert(child_type->di_type);
// create a struct with a boolean whether this is the null value
diff --git a/test/stage1/behavior/error.zig b/test/stage1/behavior/error.zig
index 265ddd9d6c081af0e23ff4fcbcac19ad5fdd4fb9..7d9dae7bdd38ac942b1b7297773c69a80a2d2525 100644
--- a/test/stage1/behavior/error.zig
+++ b/test/stage1/behavior/error.zig
@@ -330,3 +330,8 @@ test "optional error set is the same size as error set" {
expect(S.returnsOptErrSet() == null);
comptime expect(S.returnsOptErrSet() == null);
}
+
+test "debug info for optional error set" {
+ const SomeError = error{Hello};
+ var a_local_variable: ?SomeError = null;
+}
--
2.54.0
From 31be1ddf09fafae270d9946a3f09aa25816dd153 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 9 Feb 2019 20:57:45 -0500
Subject: [PATCH 2/4] docs: add threadlocal keyword to grammar
---
doc/langref.html.in | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 2cd35c2f4ef3fe518e7777b8f4a6d3ec1c28ed46..dfea8e1e0410245c12c629cc31eb8073141a1138 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -7870,7 +7870,7 @@ TopLevelComptime <- KEYWORD_comptime BlockExpr
TopLevelDecl
<- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block)
- / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? VarDecl
+ / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl
/ KEYWORD_use Expr SEMICOLON
FnProto <- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)
@@ -8330,6 +8330,7 @@ KEYWORD_struct <- 'struct' end_of_word
KEYWORD_suspend <- 'suspend' end_of_word
KEYWORD_switch <- 'switch' end_of_word
KEYWORD_test <- 'test' end_of_word
+KEYWORD_threadlocal <- 'threadlocal' end_of_word
KEYWORD_true <- 'true' end_of_word
KEYWORD_try <- 'try' end_of_word
KEYWORD_undefined <- 'undefined' end_of_word
@@ -8350,7 +8351,7 @@ keyword <- KEYWORD_align / KEYWORD_and / KEYWORD_anyerror / KEYWORD_asm
/ KEYWORD_orelse / KEYWORD_packed / KEYWORD_promise / KEYWORD_pub
/ KEYWORD_resume / KEYWORD_return / KEYWORD_linksection
/ KEYWORD_stdcallcc / KEYWORD_struct / KEYWORD_suspend
- / KEYWORD_switch / KEYWORD_test / KEYWORD_true / KEYWORD_try
+ / KEYWORD_switch / KEYWORD_test / KEYWORD_threadlocal / KEYWORD_true / KEYWORD_try
/ KEYWORD_undefined / KEYWORD_union / KEYWORD_unreachable
/ KEYWORD_use / KEYWORD_var / KEYWORD_volatile / KEYWORD_while
{#header_close#}
--
2.54.0
From caf672c49586f1af5e3d41ae200aded991b8b0f7 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 9 Feb 2019 21:10:59 -0500
Subject: [PATCH 3/4] `@truncate`: comptime 0 when target type is 0 bits
also if the dest type is a comptime_int, then treat it
as an implicit cast.
also compile error for attempting to truncate undefined
closes #1568
---
doc/langref.html.in | 11 +++++++----
src/ir.cpp | 24 ++++++++++++++++--------
test/compile_errors.zig | 11 ++++++++++-
test/stage1/behavior/truncate.zig | 23 +++++++++++++++++++++++
4 files changed, 56 insertions(+), 13 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index dfea8e1e0410245c12c629cc31eb8073141a1138..779eb6a31b9ce1be9f651e63559a81f4cd2ef3ef 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -6381,14 +6381,14 @@ fn List(comptime T: type) type {
{#header_close#}
{#header_open|@truncate#}
- {#syntax#}@truncate(comptime T: type, integer) T{#endsyntax#}
+ {#syntax#}@truncate(comptime T: type, integer: var) T{#endsyntax#}
This function truncates bits from an integer type, resulting in a smaller
integer type.
- The following produces a crash in debug mode and undefined behavior in
- release mode:
+ The following produces a crash in {#link|Debug#} mode and {#link|Undefined Behavior#} in
+ {#link|ReleaseFast#} mode:
{#syntax#}const a: u16 = 0xabcd;
const b: u8 = u8(a);{#endsyntax#}
@@ -6402,7 +6402,10 @@ const b: u8 = @truncate(u8, a);
This function always truncates the significant bits of the integer, regardless
of endianness on the target platform.
-
+
+ If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#},
+ then this is semantically equivalent to an {#link|implicit cast|Implicit Casts#}.
+
{#header_close#}
{#header_open|@typeId#}
diff --git a/src/ir.cpp b/src/ir.cpp
index d87486bbdd1e554a8cef1370fe54fcfe91de39b4..5d4013b4b91e9c06096d89c900dee7a9845c4a6b 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -18491,7 +18491,22 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct
return ira->codegen->invalid_instruction;
}
- if (src_type->data.integral.bit_count == 0) {
+ if (dest_type->id == ZigTypeIdComptimeInt) {
+ return ir_implicit_cast(ira, target, dest_type);
+ }
+
+ if (instr_is_comptime(target)) {
+ ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
+ if (val == nullptr)
+ return ira->codegen->invalid_instruction;
+
+ IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
+ bigint_truncate(&result->value.data.x_bigint, &val->data.x_bigint,
+ dest_type->data.integral.bit_count, dest_type->data.integral.is_signed);
+ return result;
+ }
+
+ if (src_type->data.integral.bit_count == 0 || dest_type->data.integral.bit_count == 0) {
IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
bigint_init_unsigned(&result->value.data.x_bigint, 0);
return result;
@@ -18507,13 +18522,6 @@ static IrInstruction *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstruct
return ira->codegen->invalid_instruction;
}
- if (target->value.special == ConstValSpecialStatic) {
- IrInstruction *result = ir_const(ira, &instruction->base, dest_type);
- bigint_truncate(&result->value.data.x_bigint, &target->value.data.x_bigint,
- dest_type->data.integral.bit_count, dest_type->data.integral.is_signed);
- return result;
- }
-
IrInstruction *new_instruction = ir_build_truncate(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, dest_type_value, target);
new_instruction->value.type = dest_type;
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index de01a5ac45584841f5810c6b4d8cfb39b47f3c87..b47cdf2ed1a113412bd501b1e27563ef0e0d850b 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,15 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.addTest(
+ "@truncate undefined value",
+ \\export fn entry() void {
+ \\ var z = @truncate(u8, u16(undefined));
+ \\}
+ ,
+ ".tmp_source.zig:2:30: error: use of undefined value",
+ );
+
cases.addTest(
"return invalid type from test",
\\test "example" { return 1; }
@@ -3335,7 +3344,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.add(
"truncate sign mismatch",
\\fn f() i8 {
- \\ const x: u32 = 10;
+ \\ var x: u32 = 10;
\\ return @truncate(i8, x);
\\}
\\
diff --git a/test/stage1/behavior/truncate.zig b/test/stage1/behavior/truncate.zig
index c195b64cbf66eb0003c8df8c5c577f2a92b87dfc..568346369f41275e05cd0da8cdcde95a78b3b723 100644
--- a/test/stage1/behavior/truncate.zig
+++ b/test/stage1/behavior/truncate.zig
@@ -6,3 +6,26 @@ test "truncate u0 to larger integer allowed and has comptime known result" {
const y = @truncate(u8, x);
comptime expect(y == 0);
}
+
+test "truncate.u0.literal" {
+ var z = @truncate(u0, 0);
+ expect(z == 0);
+}
+
+test "truncate.u0.const" {
+ const c0: usize = 0;
+ var z = @truncate(u0, c0);
+ expect(z == 0);
+}
+
+test "truncate.u0.var" {
+ var d: u8 = 2;
+ var z = @truncate(u0, d);
+ expect(z == 0);
+}
+
+test "truncate sign mismatch but comptime known so it works anyway" {
+ const x: u32 = 10;
+ var result = @truncate(i8, x);
+ expect(result == 10);
+}
--
2.54.0
From 2f9fedabf0805a47aba5c348e5369c1c28f6cf21 Mon Sep 17 00:00:00 2001
From: Jimmi HC
Date: Sun, 10 Feb 2019 12:43:49 +0100
Subject: [PATCH 4/4] testing.expectEqual use expected type as the type of
actual This allows for impl casts
---
std/testing.zig | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/std/testing.zig b/std/testing.zig
index ade6e8b0dd4ffd79381376a3a7787e08e0266f50..bece76ee5ca8d69f4be1be54bfefc7b9d3378f31 100644
--- a/std/testing.zig
+++ b/std/testing.zig
@@ -24,11 +24,7 @@ pub fn expectError(expected_error: anyerror, actual_error_union: var) void {
/// equal, prints diagnostics to stderr to show exactly how they are not equal,
/// then aborts.
/// The types must match exactly.
-pub fn expectEqual(expected: var, actual: var) void {
- if (@typeOf(actual) != @typeOf(expected)) {
- @compileError("type mismatch. expected " ++ @typeName(@typeOf(expected)) ++ ", found " ++ @typeName(@typeOf(actual)));
- }
-
+pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
switch (@typeInfo(@typeOf(actual))) {
TypeId.NoReturn,
TypeId.BoundFn,
--
2.54.0