From 34be5784a3f19658d15d1fb24bb07800cdb025c4 Mon Sep 17 00:00:00 2001
From: Veikka Tuominen
Date: Sat, 26 Nov 2022 22:40:43 +0200
Subject: [PATCH 1/9] parser: disallow defer and variable declaration as else
branch
Closes #13658
---
lib/std/zig/parse.zig | 28 +++++++++++++++-------------
lib/std/zig/parser_test.zig | 24 ++++++++++++++++++++++++
2 files changed, 39 insertions(+), 13 deletions(-)
diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig
index 77ed67b3d2..2ae0221d8a 100644
--- a/lib/std/zig/parse.zig
+++ b/lib/std/zig/parse.zig
@@ -950,13 +950,15 @@ const Parser = struct {
/// / LabeledStatement
/// / SwitchExpr
/// / AssignExpr SEMICOLON
- fn parseStatement(p: *Parser) Error!Node.Index {
+ fn parseStatement(p: *Parser, allow_defer_var: bool) Error!Node.Index {
const comptime_token = p.eatToken(.keyword_comptime);
- const var_decl = try p.parseVarDecl();
- if (var_decl != 0) {
- try p.expectSemicolon(.expected_semi_after_decl, true);
- return var_decl;
+ if (allow_defer_var) {
+ const var_decl = try p.parseVarDecl();
+ if (var_decl != 0) {
+ try p.expectSemicolon(.expected_semi_after_decl, true);
+ return var_decl;
+ }
}
if (comptime_token) |token| {
@@ -993,7 +995,7 @@ const Parser = struct {
},
});
},
- .keyword_defer => return p.addNode(.{
+ .keyword_defer => if (allow_defer_var) return p.addNode(.{
.tag = .@"defer",
.main_token = p.nextToken(),
.data = .{
@@ -1001,7 +1003,7 @@ const Parser = struct {
.rhs = try p.expectBlockExprStatement(),
},
}),
- .keyword_errdefer => return p.addNode(.{
+ .keyword_errdefer => if (allow_defer_var) return p.addNode(.{
.tag = .@"errdefer",
.main_token = p.nextToken(),
.data = .{
@@ -1040,8 +1042,8 @@ const Parser = struct {
return null_node;
}
- fn expectStatement(p: *Parser) !Node.Index {
- const statement = try p.parseStatement();
+ fn expectStatement(p: *Parser, allow_defer_var: bool) !Node.Index {
+ const statement = try p.parseStatement(allow_defer_var);
if (statement == 0) {
return p.fail(.expected_statement);
}
@@ -1053,7 +1055,7 @@ const Parser = struct {
/// statement, returns 0.
fn expectStatementRecoverable(p: *Parser) Error!Node.Index {
while (true) {
- return p.expectStatement() catch |err| switch (err) {
+ return p.expectStatement(true) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.ParseError => {
p.findNextStmt(); // Try to skip to the next statement.
@@ -1114,7 +1116,7 @@ const Parser = struct {
});
};
_ = try p.parsePayload();
- const else_expr = try p.expectStatement();
+ const else_expr = try p.expectStatement(false);
return p.addNode(.{
.tag = .@"if",
.main_token = if_token,
@@ -1226,7 +1228,7 @@ const Parser = struct {
.lhs = array_expr,
.rhs = try p.addExtra(Node.If{
.then_expr = then_expr,
- .else_expr = try p.expectStatement(),
+ .else_expr = try p.expectStatement(false),
}),
},
});
@@ -1309,7 +1311,7 @@ const Parser = struct {
}
};
_ = try p.parsePayload();
- const else_expr = try p.expectStatement();
+ const else_expr = try p.expectStatement(false);
return p.addNode(.{
.tag = .@"while",
.main_token = while_token,
diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig
index e554c51f70..d2f7a15994 100644
--- a/lib/std/zig/parser_test.zig
+++ b/lib/std/zig/parser_test.zig
@@ -4233,6 +4233,30 @@ test "zig fmt: remove newlines surrounding doc comment within container decl" {
);
}
+test "zig fmt: invalid else branch statement" {
+ try testError(
+ \\comptime {
+ \\ if (true) {} else var a = 0;
+ \\ if (true) {} else defer {}
+ \\}
+ \\comptime {
+ \\ while (true) {} else var a = 0;
+ \\ while (true) {} else defer {}
+ \\}
+ \\comptime {
+ \\ for ("") |_| {} else var a = 0;
+ \\ for ("") |_| {} else defer {}
+ \\}
+ , &[_]Error{
+ .expected_statement,
+ .expected_statement,
+ .expected_statement,
+ .expected_statement,
+ .expected_statement,
+ .expected_statement,
+ });
+}
+
test "zig fmt: anytype struct field" {
try testError(
\\pub const Pointer = struct {
From 6f9c7e33b956686ebfd4690c7f85a602d0ac9ffe Mon Sep 17 00:00:00 2001
From: Veikka Tuominen
Date: Sun, 27 Nov 2022 15:37:48 +0200
Subject: [PATCH 2/9] llvm: implement `union_init` for packed unions
Closes #13664
---
src/codegen/llvm.zig | 15 +++++++++++++++
test/behavior.zig | 1 +
test/behavior/bugs/13664.zig | 27 +++++++++++++++++++++++++++
3 files changed, 43 insertions(+)
create mode 100644 test/behavior/bugs/13664.zig
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 02fa34fe87..bbbcbdd754 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -9228,6 +9228,21 @@ pub const FuncGen = struct {
const target = self.dg.module.getTarget();
const layout = union_ty.unionGetLayout(target);
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
+
+ if (union_obj.layout == .Packed) {
+ const big_bits = union_ty.bitSize(target);
+ const int_llvm_ty = self.dg.context.intType(@intCast(c_uint, big_bits));
+ const field = union_obj.fields.values()[extra.field_index];
+ const non_int_val = try self.resolveInst(extra.init);
+ const ty_bit_size = @intCast(u16, field.ty.bitSize(target));
+ const small_int_ty = self.dg.context.intType(ty_bit_size);
+ const small_int_val = if (field.ty.isPtrAtRuntime())
+ self.builder.buildPtrToInt(non_int_val, small_int_ty, "")
+ else
+ self.builder.buildBitCast(non_int_val, small_int_ty, "");
+ return self.builder.buildZExtOrBitCast(small_int_val, int_llvm_ty, "");
+ }
+
const tag_int = blk: {
const tag_ty = union_ty.unionTagTypeHypothetical();
const union_field_name = union_obj.fields.keys()[extra.field_index];
diff --git a/test/behavior.zig b/test/behavior.zig
index c45c819762..04c6d4c13d 100644
--- a/test/behavior.zig
+++ b/test/behavior.zig
@@ -116,6 +116,7 @@ test {
_ = @import("behavior/bugs/13171.zig");
_ = @import("behavior/bugs/13285.zig");
_ = @import("behavior/bugs/13435.zig");
+ _ = @import("behavior/bugs/13664.zig");
_ = @import("behavior/byteswap.zig");
_ = @import("behavior/byval_arg_var.zig");
_ = @import("behavior/call.zig");
diff --git a/test/behavior/bugs/13664.zig b/test/behavior/bugs/13664.zig
new file mode 100644
index 0000000000..48b2533b60
--- /dev/null
+++ b/test/behavior/bugs/13664.zig
@@ -0,0 +1,27 @@
+const std = @import("std");
+const builtin = @import("builtin");
+
+const Fields = packed struct {
+ timestamp: u50,
+ random_bits: u13,
+};
+const ID = packed union {
+ value: u63,
+ fields: Fields,
+};
+fn value() i64 {
+ return 1341;
+}
+test {
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+ const timestamp: i64 = value();
+ const id = ID{ .fields = Fields{
+ .timestamp = @intCast(u50, timestamp),
+ .random_bits = 420,
+ } };
+ try std.testing.expect((ID{ .value = id.value }).fields.timestamp == timestamp);
+}
From 6337c04244d9c27cc6535340347d4c127f4742eb Mon Sep 17 00:00:00 2001
From: Veikka Tuominen
Date: Tue, 29 Nov 2022 14:29:50 +0200
Subject: [PATCH 3/9] Sema: improve panic for slice start index being greater
than end index
Closes #13689
---
lib/std/builtin.zig | 11 +++-
src/Sema.zig | 64 ++++++++++++++++---
...ice start index greater than end index.zig | 23 +++++++
3 files changed, 86 insertions(+), 12 deletions(-)
create mode 100644 test/cases/safety/slice start index greater than end index.zig
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index 1f7d48ccb9..09833988ae 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -863,10 +863,9 @@ pub fn panicOutOfBounds(index: usize, len: usize) noreturn {
std.debug.panicExtra(null, @returnAddress(), "index out of bounds: index {d}, len {d}", .{ index, len });
}
-pub noinline fn returnError(st: *StackTrace) void {
+pub fn panicStartGreaterThanEnd(start: usize, end: usize) noreturn {
@setCold(true);
- @setRuntimeSafety(false);
- addErrRetTraceAddr(st, @returnAddress());
+ std.debug.panicExtra(null, @returnAddress(), "start index {d} is larger than end index {d}", .{ start, end });
}
pub const panic_messages = struct {
@@ -889,6 +888,12 @@ pub const panic_messages = struct {
pub const invalid_enum_value = "invalid enum value";
};
+pub noinline fn returnError(st: *StackTrace) void {
+ @setCold(true);
+ @setRuntimeSafety(false);
+ addErrRetTraceAddr(st, @returnAddress());
+}
+
pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void {
if (st.index < st.instruction_addresses.len)
st.instruction_addresses[st.index] = addr;
diff --git a/src/Sema.zig b/src/Sema.zig
index b8b8f3738a..d35c4e3881 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -12437,7 +12437,7 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
else
try sema.resolveInst(.zero);
- return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src);
+ return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true);
}
fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -12460,7 +12460,7 @@ fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
else
try sema.resolveInst(.zero);
- return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src);
+ return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true);
}
fn zirArithmetic(
@@ -12480,7 +12480,7 @@ fn zirArithmetic(
const lhs = try sema.resolveInst(extra.lhs);
const rhs = try sema.resolveInst(extra.rhs);
- return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src);
+ return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src, true);
}
fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -13776,6 +13776,7 @@ fn analyzeArithmetic(
src: LazySrcLoc,
lhs_src: LazySrcLoc,
rhs_src: LazySrcLoc,
+ want_safety: bool,
) CompileError!Air.Inst.Ref {
const lhs_ty = sema.typeOf(lhs);
const rhs_ty = sema.typeOf(rhs);
@@ -14204,7 +14205,7 @@ fn analyzeArithmetic(
};
try sema.requireRuntimeBlock(block, src, rs.src);
- if (block.wantSafety()) {
+ if (block.wantSafety() and want_safety) {
if (scalar_tag == .Int) {
const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) {
.add => .add_with_overflow,
@@ -22334,6 +22335,47 @@ fn panicIndexOutOfBounds(
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
}
+fn panicStartLargerThanEnd(
+ sema: *Sema,
+ parent_block: *Block,
+ src: LazySrcLoc,
+ start: Air.Inst.Ref,
+ end: Air.Inst.Ref,
+) !void {
+ assert(!parent_block.is_comptime);
+ const ok = try parent_block.addBinOp(.cmp_lte, start, end);
+ const gpa = sema.gpa;
+
+ var fail_block: Block = .{
+ .parent = parent_block,
+ .sema = sema,
+ .src_decl = parent_block.src_decl,
+ .namespace = parent_block.namespace,
+ .wip_capture_scope = parent_block.wip_capture_scope,
+ .instructions = .{},
+ .inlining = parent_block.inlining,
+ .is_comptime = false,
+ };
+
+ defer fail_block.instructions.deinit(gpa);
+
+ {
+ const this_feature_is_implemented_in_the_backend =
+ sema.mod.comp.bin_file.options.use_llvm;
+
+ if (!this_feature_is_implemented_in_the_backend) {
+ // TODO implement this feature in all the backends and then delete this branch
+ _ = try fail_block.addNoOp(.breakpoint);
+ _ = try fail_block.addNoOp(.unreach);
+ } else {
+ const panic_fn = try sema.getBuiltin("panicStartGreaterThanEnd");
+ const args: [2]Air.Inst.Ref = .{ start, end };
+ _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
+ }
+ }
+ try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
+}
+
fn panicSentinelMismatch(
sema: *Sema,
parent_block: *Block,
@@ -28028,7 +28070,11 @@ fn analyzeSlice(
}
}
- const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src);
+ if (block.wantSafety() and !block.is_comptime) {
+ // requirement: start <= end
+ try sema.panicStartLargerThanEnd(block, src, start, end);
+ }
+ const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
@@ -28063,10 +28109,10 @@ fn analyzeSlice(
const actual_len = if (slice_ty.sentinel() == null)
slice_len_inst
else
- try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
+ try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
const actual_end = if (slice_sentinel != null)
- try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src)
+ try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
else
end;
@@ -28131,11 +28177,11 @@ fn analyzeSlice(
if (slice_ty.sentinel() == null) break :blk slice_len_inst;
// we have to add one because slice lengths don't include the sentinel
- break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src);
+ break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
} else null;
if (opt_len_inst) |len_inst| {
const actual_end = if (slice_sentinel != null)
- try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src)
+ try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
else
end;
try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte);
diff --git a/test/cases/safety/slice start index greater than end index.zig b/test/cases/safety/slice start index greater than end index.zig
new file mode 100644
index 0000000000..ef0485ae60
--- /dev/null
+++ b/test/cases/safety/slice start index greater than end index.zig
@@ -0,0 +1,23 @@
+const std = @import("std");
+
+pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
+ _ = stack_trace;
+ if (std.mem.eql(u8, message, "start index 10 is larger than end index 1")) {
+ std.process.exit(0);
+ }
+ std.process.exit(1);
+}
+
+pub fn main() !void {
+ var a: usize = 1;
+ var b: usize = 10;
+ var buf: [16]u8 = undefined;
+
+ const slice = buf[b..a];
+ _ = slice;
+ return error.TestFailed;
+}
+
+// run
+// backend=llvm
+// target=native
From 17ff002bc0ac55850e647fc3a70a43d1d874f6ab Mon Sep 17 00:00:00 2001
From: Veikka Tuominen
Date: Tue, 29 Nov 2022 14:53:54 +0200
Subject: [PATCH 4/9] Sema: improve safety panic for access of inactive union
field
---
doc/langref.html.in | 2 +-
lib/std/builtin.zig | 5 +
src/Sema.zig | 119 ++++++-------------
test/cases/safety/bad union field access.zig | 2 +-
4 files changed, 45 insertions(+), 83 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 8974ab06cf..dcb15af9f6 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -3803,7 +3803,7 @@ test "switch on non-exhaustive enum" {
{#link|Accessing the non-active field|Wrong Union Field Access#} is
safety-checked {#link|Undefined Behavior#}:
- {#code_begin|test_err|inactive union field#}
+ {#code_begin|test_err|access of union field 'float' while field 'int' is active#}
const Payload = union {
int: i64,
float: f64,
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index 09833988ae..fcdf43bd31 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -868,6 +868,11 @@ pub fn panicStartGreaterThanEnd(start: usize, end: usize) noreturn {
std.debug.panicExtra(null, @returnAddress(), "start index {d} is larger than end index {d}", .{ start, end });
}
+pub fn panicInactiveUnionField(active: anytype, wanted: @TypeOf(active)) noreturn {
+ @setCold(true);
+ std.debug.panicExtra(null, @returnAddress(), "access of union field '{s}' while field '{s}' is active", .{ @tagName(wanted), @tagName(active) });
+}
+
pub const panic_messages = struct {
pub const unreach = "reached unreachable code";
pub const unwrap_null = "attempt to use null value";
diff --git a/src/Sema.zig b/src/Sema.zig
index d35c4e3881..396c5912fa 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -22120,7 +22120,6 @@ pub const PanicId = enum {
shr_overflow,
divide_by_zero,
exact_division_remainder,
- /// TODO make this call `std.builtin.panicInactiveUnionField`.
inactive_union_field,
integer_part_out_of_bounds,
corrupt_switch,
@@ -22296,90 +22295,40 @@ fn panicUnwrapError(
fn panicIndexOutOfBounds(
sema: *Sema,
parent_block: *Block,
- src: LazySrcLoc,
index: Air.Inst.Ref,
len: Air.Inst.Ref,
cmp_op: Air.Inst.Tag,
) !void {
assert(!parent_block.is_comptime);
const ok = try parent_block.addBinOp(cmp_op, index, len);
- const gpa = sema.gpa;
-
- var fail_block: Block = .{
- .parent = parent_block,
- .sema = sema,
- .src_decl = parent_block.src_decl,
- .namespace = parent_block.namespace,
- .wip_capture_scope = parent_block.wip_capture_scope,
- .instructions = .{},
- .inlining = parent_block.inlining,
- .is_comptime = false,
- };
-
- defer fail_block.instructions.deinit(gpa);
-
- {
- const this_feature_is_implemented_in_the_backend =
- sema.mod.comp.bin_file.options.use_llvm;
-
- if (!this_feature_is_implemented_in_the_backend) {
- // TODO implement this feature in all the backends and then delete this branch
- _ = try fail_block.addNoOp(.breakpoint);
- _ = try fail_block.addNoOp(.unreach);
- } else {
- const panic_fn = try sema.getBuiltin("panicOutOfBounds");
- const args: [2]Air.Inst.Ref = .{ index, len };
- _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
- }
- }
- try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
+ try sema.safetyPanicFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len });
}
fn panicStartLargerThanEnd(
sema: *Sema,
parent_block: *Block,
- src: LazySrcLoc,
start: Air.Inst.Ref,
end: Air.Inst.Ref,
) !void {
assert(!parent_block.is_comptime);
const ok = try parent_block.addBinOp(.cmp_lte, start, end);
- const gpa = sema.gpa;
+ try sema.safetyPanicFormatted(parent_block, ok, "panicStartGreaterThanEnd", &.{ start, end });
+}
- var fail_block: Block = .{
- .parent = parent_block,
- .sema = sema,
- .src_decl = parent_block.src_decl,
- .namespace = parent_block.namespace,
- .wip_capture_scope = parent_block.wip_capture_scope,
- .instructions = .{},
- .inlining = parent_block.inlining,
- .is_comptime = false,
- };
-
- defer fail_block.instructions.deinit(gpa);
-
- {
- const this_feature_is_implemented_in_the_backend =
- sema.mod.comp.bin_file.options.use_llvm;
-
- if (!this_feature_is_implemented_in_the_backend) {
- // TODO implement this feature in all the backends and then delete this branch
- _ = try fail_block.addNoOp(.breakpoint);
- _ = try fail_block.addNoOp(.unreach);
- } else {
- const panic_fn = try sema.getBuiltin("panicStartGreaterThanEnd");
- const args: [2]Air.Inst.Ref = .{ start, end };
- _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
- }
- }
- try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
+fn panicInactiveUnionField(
+ sema: *Sema,
+ parent_block: *Block,
+ active_tag: Air.Inst.Ref,
+ wanted_tag: Air.Inst.Ref,
+) !void {
+ assert(!parent_block.is_comptime);
+ const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
+ try sema.safetyPanicFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
}
fn panicSentinelMismatch(
sema: *Sema,
parent_block: *Block,
- src: LazySrcLoc,
maybe_sentinel: ?Value,
sentinel_ty: Type,
ptr: Air.Inst.Ref,
@@ -22413,9 +22362,20 @@ fn panicSentinelMismatch(
else {
const panic_fn = try sema.getBuiltin("checkNonScalarSentinel");
const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
- _ = try sema.analyzeCall(parent_block, panic_fn, src, src, .auto, false, &args, null);
+ _ = try sema.analyzeCall(parent_block, panic_fn, sema.src, sema.src, .auto, false, &args, null);
return;
};
+
+ try sema.safetyPanicFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel });
+}
+
+fn safetyPanicFormatted(
+ sema: *Sema,
+ parent_block: *Block,
+ ok: Air.Inst.Ref,
+ func: []const u8,
+ args: []const Air.Inst.Ref,
+) CompileError!void {
const gpa = sema.gpa;
var fail_block: Block = .{
@@ -22440,9 +22400,8 @@ fn panicSentinelMismatch(
_ = try fail_block.addNoOp(.breakpoint);
_ = try fail_block.addNoOp(.unreach);
} else {
- const panic_fn = try sema.getBuiltin("panicSentinelMismatch");
- const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
- _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
+ const panic_fn = try sema.getBuiltin(func);
+ _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, args, null);
}
}
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
@@ -23465,8 +23424,7 @@ fn unionFieldPtr(
// TODO would it be better if get_union_tag supported pointers to unions?
const union_val = try block.addTyOp(.load, union_ty, union_ptr);
const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_val);
- const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
- try sema.addSafetyCheck(block, ok, .inactive_union_field);
+ try sema.panicInactiveUnionField(block, active_tag, wanted_tag);
}
if (field.ty.zigTypeTag() == .NoReturn) {
_ = try block.addNoOp(.unreach);
@@ -23537,8 +23495,7 @@ fn unionFieldVal(
const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index);
const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val);
const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval);
- const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
- try sema.addSafetyCheck(block, ok, .inactive_union_field);
+ try sema.panicInactiveUnionField(block, active_tag, wanted_tag);
}
if (field.ty.zigTypeTag() == .NoReturn) {
_ = try block.addNoOp(.unreach);
@@ -23849,7 +23806,7 @@ fn elemValArray(
if (maybe_index_val == null) {
const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
- try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
+ try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
}
}
return block.addBinOp(.array_elem_val, array, elem_index);
@@ -23910,7 +23867,7 @@ fn elemPtrArray(
if (block.wantSafety() and offset == null) {
const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
- try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
+ try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
}
return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
@@ -23966,7 +23923,7 @@ fn elemValSlice(
else
try block.addTyOp(.slice_len, Type.usize, slice);
const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
- try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
+ try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
}
try sema.queueFullTypeResolution(sema.typeOf(slice));
return block.addBinOp(.slice_elem_val, slice, elem_index);
@@ -24025,7 +23982,7 @@ fn elemPtrSlice(
break :len try block.addTyOp(.slice_len, Type.usize, slice);
};
const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
- try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
+ try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
}
return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
}
@@ -28072,7 +28029,7 @@ fn analyzeSlice(
if (block.wantSafety() and !block.is_comptime) {
// requirement: start <= end
- try sema.panicStartLargerThanEnd(block, src, start, end);
+ try sema.panicStartLargerThanEnd(block, start, end);
}
const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
@@ -28116,11 +28073,11 @@ fn analyzeSlice(
else
end;
- try sema.panicIndexOutOfBounds(block, src, actual_end, actual_len, .cmp_lte);
+ try sema.panicIndexOutOfBounds(block, actual_end, actual_len, .cmp_lte);
}
// requirement: result[new_len] == slice_sentinel
- try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
+ try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len);
}
return result;
};
@@ -28184,11 +28141,11 @@ fn analyzeSlice(
try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
else
end;
- try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte);
+ try sema.panicIndexOutOfBounds(block, actual_end, len_inst, .cmp_lte);
}
// requirement: start <= end
- try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);
+ try sema.panicIndexOutOfBounds(block, start, end, .cmp_lte);
}
const result = try block.addInst(.{
.tag = .slice,
@@ -28202,7 +28159,7 @@ fn analyzeSlice(
});
if (block.wantSafety()) {
// requirement: result[new_len] == slice_sentinel
- try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
+ try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len);
}
return result;
}
diff --git a/test/cases/safety/bad union field access.zig b/test/cases/safety/bad union field access.zig
index 0badf380fb..fbe7718c66 100644
--- a/test/cases/safety/bad union field access.zig
+++ b/test/cases/safety/bad union field access.zig
@@ -2,7 +2,7 @@ const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = stack_trace;
- if (std.mem.eql(u8, message, "access of inactive union field")) {
+ if (std.mem.eql(u8, message, "access of union field 'float' while field 'int' is active")) {
std.process.exit(0);
}
std.process.exit(1);
From ed734299269d50083db27d68598ced7df42b8631 Mon Sep 17 00:00:00 2001
From: Veikka Tuominen
Date: Tue, 29 Nov 2022 15:09:21 +0200
Subject: [PATCH 5/9] Sema: explain why parameter must be declared comptime
Closes #13692
---
src/Sema.zig | 3 +++
.../compile_errors/comptime_parameter_not_declared_as_such.zig | 1 +
2 files changed, 4 insertions(+)
diff --git a/src/Sema.zig b/src/Sema.zig
index 396c5912fa..6c87f7d4b9 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -8709,6 +8709,9 @@ fn analyzeParameter(
});
errdefer msg.destroy(sema.gpa);
+ const src_decl = sema.mod.declPtr(block.src_decl);
+ try sema.explainWhyTypeIsComptime(block, param_src, msg, param_src.toSrcLoc(src_decl), param.ty);
+
try sema.addDeclaredHereNote(msg, param.ty);
break :msg msg;
};
diff --git a/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig b/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig
index 008d14f2fc..6758454ccd 100644
--- a/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig
+++ b/test/cases/compile_errors/comptime_parameter_not_declared_as_such.zig
@@ -21,4 +21,5 @@ pub export fn entry1() void {
// target=native
//
// :3:6: error: parameter of type '*const fn(anytype) void' must be declared comptime
+// :3:6: note: function is generic
// :10:34: error: parameter of type 'comptime_int' must be declared comptime
From e60db701d13a50798222bbef2d4560245f975671 Mon Sep 17 00:00:00 2001
From: Veikka Tuominen
Date: Tue, 29 Nov 2022 15:57:34 +0200
Subject: [PATCH 6/9] Sema: add option to disable formatted panics
Closes #13174
---
lib/std/builtin.zig | 4 +++
src/Compilation.zig | 5 +++
src/Sema.zig | 76 ++++++++++++++++++++++++++++-----------------
src/main.zig | 8 +++++
4 files changed, 64 insertions(+), 29 deletions(-)
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index fcdf43bd31..8c8d0b37b7 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -891,6 +891,10 @@ pub const panic_messages = struct {
pub const corrupt_switch = "switch on corrupt value";
pub const shift_rhs_too_big = "shift amount is greater than the type size";
pub const invalid_enum_value = "invalid enum value";
+ pub const sentinel_mismatch = "sentinel mismatch";
+ pub const unwrap_error = "attempt to unwrap error";
+ pub const index_out_of_bounds = "index out of bounds";
+ pub const start_index_greater_than_end = "start index is larger than end index";
};
pub noinline fn returnError(st: *StackTrace) void {
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 795eb493e2..dcd9ca5cf6 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -101,6 +101,7 @@ debug_compile_errors: bool,
job_queued_compiler_rt_lib: bool = false,
job_queued_compiler_rt_obj: bool = false,
alloc_failure_occurred: bool = false,
+formatted_panics: bool = false,
c_source_files: []const CSourceFile,
clang_argv: []const []const u8,
@@ -937,6 +938,7 @@ pub const InitOptions = struct {
use_stage1: ?bool = null,
single_threaded: ?bool = null,
strip: ?bool = null,
+ formatted_panics: ?bool = null,
rdynamic: bool = false,
function_sections: bool = false,
no_builtin: bool = false,
@@ -1457,6 +1459,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.Debug => @as(u8, 0),
else => @as(u8, 3),
};
+ const formatted_panics = options.formatted_panics orelse (options.optimize_mode == .Debug);
// We put everything into the cache hash that *cannot be modified
// during an incremental update*. For example, one cannot change the
@@ -1551,6 +1554,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
hash.addOptionalBytes(options.test_name_prefix);
hash.add(options.skip_linker_dependencies);
hash.add(options.parent_compilation_link_libc);
+ hash.add(formatted_panics);
// In the case of incremental cache mode, this `zig_cache_artifact_directory`
// is computed based on a hash of non-linker inputs, and it is where all
@@ -1957,6 +1961,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
.owned_link_dir = owned_link_dir,
.color = options.color,
.reference_trace = options.reference_trace,
+ .formatted_panics = formatted_panics,
.time_report = options.time_report,
.stack_report = options.stack_report,
.unwind_tables = unwind_tables,
diff --git a/src/Sema.zig b/src/Sema.zig
index 6c87f7d4b9..686d9c11cb 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -667,9 +667,9 @@ pub const Block = struct {
return result_index;
}
- fn addUnreachable(block: *Block, src: LazySrcLoc, safety_check: bool) !void {
+ fn addUnreachable(block: *Block, safety_check: bool) !void {
if (safety_check and block.wantSafety()) {
- _ = try block.sema.safetyPanic(block, src, .unreach);
+ try block.sema.safetyPanic(block, .unreach);
} else {
_ = try block.addNoOp(.unreach);
}
@@ -5003,7 +5003,8 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index, force_comptime: bo
if (block.is_comptime or force_comptime) {
return sema.fail(block, src, "encountered @panic at comptime", .{});
}
- return sema.panicWithMsg(block, src, msg_inst);
+ try sema.panicWithMsg(block, src, msg_inst);
+ return always_noreturn;
}
fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -7962,7 +7963,7 @@ fn analyzeErrUnionPayload(
if (safety_check and block.wantSafety() and
!err_union_ty.errorUnionSet().errorSetIsEmpty())
{
- try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
+ try sema.panicUnwrapError(block, operand, .unwrap_errunion_err, .is_non_err);
}
return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
@@ -8047,7 +8048,7 @@ fn analyzeErrUnionPayloadPtr(
if (safety_check and block.wantSafety() and
!err_union_ty.errorUnionSet().errorSetIsEmpty())
{
- try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
+ try sema.panicUnwrapError(block, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
}
const air_tag: Air.Inst.Tag = if (initializing)
@@ -9542,7 +9543,7 @@ fn zirSwitchCapture(
.ErrorSet => if (block.switch_else_err_ty) |some| {
return sema.bitCast(block, some, operand, operand_src);
} else {
- try block.addUnreachable(operand_src, false);
+ try block.addUnreachable(false);
return Air.Inst.Ref.unreachable_value;
},
else => return operand,
@@ -10975,7 +10976,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
// that it is unreachable.
if (case_block.wantSafety()) {
try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
- _ = try sema.safetyPanic(&case_block, src, .corrupt_switch);
+ try sema.safetyPanic(&case_block, .corrupt_switch);
} else {
_ = try case_block.addNoOp(.unreach);
}
@@ -11304,6 +11305,11 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op
const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
const src = inst_data.src();
+ if (!sema.mod.comp.formatted_panics) {
+ try sema.safetyPanic(block, .unwrap_error);
+ return true;
+ }
+
const panic_fn = try sema.getBuiltin("panicUnwrapError");
const err_return_trace = try sema.getErrorReturnTrace(block);
const args: [2]Air.Inst.Ref = .{ err_return_trace, operand };
@@ -16513,7 +16519,7 @@ fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
return sema.fail(block, src, "reached unreachable code", .{});
}
// TODO Add compile error for @optimizeFor occurring too late in a scope.
- try block.addUnreachable(src, true);
+ try block.addUnreachable(true);
return always_noreturn;
}
@@ -22128,6 +22134,10 @@ pub const PanicId = enum {
corrupt_switch,
shift_rhs_too_big,
invalid_enum_value,
+ sentinel_mismatch,
+ unwrap_error,
+ index_out_of_bounds,
+ start_index_greater_than_end,
};
fn addSafetyCheck(
@@ -22152,12 +22162,7 @@ fn addSafetyCheck(
defer fail_block.instructions.deinit(gpa);
- // This function doesn't actually need a src location but if
- // the panic function interface ever changes passing `.unneeded` here
- // will cause confusing panics.
- const src = sema.src;
- _ = try sema.safetyPanic(&fail_block, src, panic_id);
-
+ try sema.safetyPanic(&fail_block, panic_id);
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
}
@@ -22221,7 +22226,7 @@ fn panicWithMsg(
block: *Block,
src: LazySrcLoc,
msg_inst: Air.Inst.Ref,
-) !Zir.Inst.Index {
+) !void {
const mod = sema.mod;
const arena = sema.arena;
@@ -22232,7 +22237,7 @@ fn panicWithMsg(
// TODO implement this feature in all the backends and then delete this branch
_ = try block.addNoOp(.breakpoint);
_ = try block.addNoOp(.unreach);
- return always_noreturn;
+ return;
}
const panic_fn = try sema.getBuiltin("panic");
const unresolved_stack_trace_ty = try sema.getBuiltinType("StackTrace");
@@ -22248,19 +22253,20 @@ fn panicWithMsg(
);
const args: [3]Air.Inst.Ref = .{ msg_inst, null_stack_trace, .null_value };
_ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args, null);
- return always_noreturn;
}
fn panicUnwrapError(
sema: *Sema,
parent_block: *Block,
- src: LazySrcLoc,
operand: Air.Inst.Ref,
unwrap_err_tag: Air.Inst.Tag,
is_non_err_tag: Air.Inst.Tag,
) !void {
assert(!parent_block.is_comptime);
const ok = try parent_block.addUnOp(is_non_err_tag, operand);
+ if (!sema.mod.comp.formatted_panics) {
+ return sema.addSafetyCheck(parent_block, ok, .unwrap_error);
+ }
const gpa = sema.gpa;
var fail_block: Block = .{
@@ -22289,7 +22295,7 @@ fn panicUnwrapError(
const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
const err_return_trace = try sema.getErrorReturnTrace(&fail_block);
const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
- _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
+ _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, &args, null);
}
}
try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
@@ -22304,7 +22310,10 @@ fn panicIndexOutOfBounds(
) !void {
assert(!parent_block.is_comptime);
const ok = try parent_block.addBinOp(cmp_op, index, len);
- try sema.safetyPanicFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len });
+ if (!sema.mod.comp.formatted_panics) {
+ return sema.addSafetyCheck(parent_block, ok, .index_out_of_bounds);
+ }
+ try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len });
}
fn panicStartLargerThanEnd(
@@ -22315,7 +22324,10 @@ fn panicStartLargerThanEnd(
) !void {
assert(!parent_block.is_comptime);
const ok = try parent_block.addBinOp(.cmp_lte, start, end);
- try sema.safetyPanicFormatted(parent_block, ok, "panicStartGreaterThanEnd", &.{ start, end });
+ if (!sema.mod.comp.formatted_panics) {
+ return sema.addSafetyCheck(parent_block, ok, .start_index_greater_than_end);
+ }
+ try sema.safetyCheckFormatted(parent_block, ok, "panicStartGreaterThanEnd", &.{ start, end });
}
fn panicInactiveUnionField(
@@ -22326,7 +22338,10 @@ fn panicInactiveUnionField(
) !void {
assert(!parent_block.is_comptime);
const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
- try sema.safetyPanicFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
+ if (!sema.mod.comp.formatted_panics) {
+ return sema.addSafetyCheck(parent_block, ok, .inactive_union_field);
+ }
+ try sema.safetyCheckFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
}
fn panicSentinelMismatch(
@@ -22369,16 +22384,20 @@ fn panicSentinelMismatch(
return;
};
- try sema.safetyPanicFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel });
+ if (!sema.mod.comp.formatted_panics) {
+ return sema.addSafetyCheck(parent_block, ok, .sentinel_mismatch);
+ }
+ try sema.safetyCheckFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel });
}
-fn safetyPanicFormatted(
+fn safetyCheckFormatted(
sema: *Sema,
parent_block: *Block,
ok: Air.Inst.Ref,
func: []const u8,
args: []const Air.Inst.Ref,
) CompileError!void {
+ assert(sema.mod.comp.formatted_panics);
const gpa = sema.gpa;
var fail_block: Block = .{
@@ -22413,19 +22432,18 @@ fn safetyPanicFormatted(
fn safetyPanic(
sema: *Sema,
block: *Block,
- src: LazySrcLoc,
panic_id: PanicId,
-) CompileError!Zir.Inst.Index {
+) CompileError!void {
const panic_messages_ty = try sema.getBuiltinType("panic_messages");
const msg_decl_index = (try sema.namespaceLookup(
block,
- src,
+ sema.src,
panic_messages_ty.getNamespace().?,
@tagName(panic_id),
)).?;
- const msg_inst = try sema.analyzeDeclVal(block, src, msg_decl_index);
- return sema.panicWithMsg(block, src, msg_inst);
+ const msg_inst = try sema.analyzeDeclVal(block, sema.src, msg_decl_index);
+ try sema.panicWithMsg(block, sema.src, msg_inst);
}
fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
diff --git a/src/main.zig b/src/main.zig
index c372462365..e3721d1101 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -406,6 +406,8 @@ const usage_build_generic =
\\ -fno-function-sections All functions go into same section
\\ -fstrip Omit debug symbols
\\ -fno-strip Keep debug symbols
+ \\ -fformatted-panics Enable formatted safety panics
+ \\ -fno-formatted-panics Disable formatted safety panics
\\ -ofmt=[mode] Override target object format
\\ elf Executable and Linking Format
\\ c C source code
@@ -632,6 +634,7 @@ fn buildOutputType(
var have_version = false;
var compatibility_version: ?std.builtin.Version = null;
var strip: ?bool = null;
+ var formatted_panics: ?bool = null;
var function_sections = false;
var no_builtin = false;
var watch = false;
@@ -1242,6 +1245,10 @@ fn buildOutputType(
strip = true;
} else if (mem.eql(u8, arg, "-fno-strip")) {
strip = false;
+ } else if (mem.eql(u8, arg, "-fformatted-panics")) {
+ formatted_panics = true;
+ } else if (mem.eql(u8, arg, "-fno-formatted-panics")) {
+ formatted_panics = false;
} else if (mem.eql(u8, arg, "-fsingle-threaded")) {
single_threaded = true;
} else if (mem.eql(u8, arg, "-fno-single-threaded")) {
@@ -2938,6 +2945,7 @@ fn buildOutputType(
.stack_size_override = stack_size_override,
.image_base_override = image_base_override,
.strip = strip,
+ .formatted_panics = formatted_panics,
.single_threaded = single_threaded,
.function_sections = function_sections,
.no_builtin = no_builtin,
From 6f5a438946dce2b201acf5b7bbe42977bc990cc4 Mon Sep 17 00:00:00 2001
From: Veikka Tuominen
Date: Tue, 29 Nov 2022 18:40:28 +0200
Subject: [PATCH 7/9] AstGen: unstack block scope when creating opaque type
Closes #13697
---
src/AstGen.zig | 1 +
test/behavior/basic.zig | 11 +++++++++++
2 files changed, 12 insertions(+)
diff --git a/src/AstGen.zig b/src/AstGen.zig
index e6f83dc00b..6836b4f1cc 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -5070,6 +5070,7 @@ fn containerDecl(
try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len);
astgen.extra.appendSliceAssumeCapacity(decls_slice);
+ block_scope.unstack();
try gz.addNamespaceCaptures(&namespace);
return rvalue(gz, ri, indexToRef(decl_inst), node);
},
diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig
index 7351891959..3fc1cda04d 100644
--- a/test/behavior/basic.zig
+++ b/test/behavior/basic.zig
@@ -1127,3 +1127,14 @@ test "pointer to zero sized global is mutable" {
};
try expect(@TypeOf(&S.thing) == *S.Thing);
}
+
+test "returning an opaque type from a function" {
+ const S = struct {
+ fn foo(comptime a: u32) type {
+ return opaque {
+ const b = a;
+ };
+ }
+ };
+ try expect(S.foo(123).b == 123);
+}
From 4b0ef6a409a11946a999e277ed2a3f4b6120d0f5 Mon Sep 17 00:00:00 2001
From: Veikka Tuominen
Date: Tue, 29 Nov 2022 18:49:27 +0200
Subject: [PATCH 8/9] Sema: make non-existent field error point to field name
Closes #13698
---
src/Sema.zig | 4 ++--
.../invalid_field_in_struct_value_expression.zig | 12 ++++++++++++
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/src/Sema.zig b/src/Sema.zig
index 686d9c11cb..00d323a219 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -17613,11 +17613,11 @@ fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
const ty_src = inst_data.src();
- const field_src = inst_data.src();
+ const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
if (aggregate_ty.tag() == .var_args_param) return sema.addType(aggregate_ty);
const field_name = sema.code.nullTerminatedString(extra.name_start);
- return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
+ return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src);
}
fn fieldType(
diff --git a/test/cases/compile_errors/invalid_field_in_struct_value_expression.zig b/test/cases/compile_errors/invalid_field_in_struct_value_expression.zig
index fc9667910b..97f440da3b 100644
--- a/test/cases/compile_errors/invalid_field_in_struct_value_expression.zig
+++ b/test/cases/compile_errors/invalid_field_in_struct_value_expression.zig
@@ -12,9 +12,21 @@ export fn f() void {
_ = a;
}
+const Object = struct {
+ field_1: u32,
+ field_2: u32,
+};
+fn dump(_: Object) void {}
+pub export fn entry() void {
+ dump(.{ .field_1 = 123, .field_3 = 456 });
+}
+
+
// error
// backend=stage2
// target=native
//
// :10:10: error: no field named 'foo' in struct 'tmp.A'
// :1:11: note: struct declared here
+// :21:30: error: no field named 'field_3' in struct 'tmp.Object'
+// :15:16: note: struct declared here
From b2b1d421c35ba602ddfadf94190d956de3293c62 Mon Sep 17 00:00:00 2001
From: Veikka Tuominen
Date: Tue, 29 Nov 2022 18:59:58 +0200
Subject: [PATCH 9/9] Sema: add missing failWithBadMemberAccess to zirExport
The assumption that AstGen would error only holds when exporting
a identifier not a namespace member.
---
src/Sema.zig | 3 ++-
.../missing_member_in_namespace_export.zig | 10 ++++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
create mode 100644 test/cases/compile_errors/missing_member_in_namespace_export.zig
diff --git a/src/Sema.zig b/src/Sema.zig
index 00d323a219..eb465d25a3 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -5391,7 +5391,8 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
const container_namespace = container_ty.getNamespace().?;
const maybe_index = try sema.lookupInNamespace(block, operand_src, container_namespace, decl_name, false);
- break :index_blk maybe_index.?; // AstGen would produce error in case of unidentified name
+ break :index_blk maybe_index orelse
+ return sema.failWithBadMemberAccess(block, container_ty, operand_src, decl_name);
} else try sema.lookupIdentifier(block, operand_src, decl_name);
const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) {
error.NeededSourceLocation => {
diff --git a/test/cases/compile_errors/missing_member_in_namespace_export.zig b/test/cases/compile_errors/missing_member_in_namespace_export.zig
new file mode 100644
index 0000000000..413cf35319
--- /dev/null
+++ b/test/cases/compile_errors/missing_member_in_namespace_export.zig
@@ -0,0 +1,10 @@
+const S = struct {};
+comptime {
+ @export(S.foo, .{ .name = "foo" });
+}
+
+// error
+// target=native
+//
+// :3:14: error: struct 'tmp.S' has no member named 'foo'
+// :1:11: note: struct declared here