mirror of
https://github.com/ziglang/zig.git
synced 2025-12-14 18:23:12 +00:00
Merge remote-tracking branch 'origin/master' into llvm15
This commit is contained in:
commit
011663eea5
@ -5700,7 +5700,6 @@ fn bar() !void {
|
||||
try quux();
|
||||
} else |err| switch (err) {
|
||||
error.FileNotFound => try hello(),
|
||||
else => try another(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -5716,10 +5715,6 @@ fn hello() !void {
|
||||
try bang2();
|
||||
}
|
||||
|
||||
fn another() !void {
|
||||
try bang1();
|
||||
}
|
||||
|
||||
fn bang1() !void {
|
||||
return error.FileNotFound;
|
||||
}
|
||||
|
||||
272
src/AstGen.zig
272
src/AstGen.zig
@ -135,7 +135,6 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
|
||||
var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
|
||||
var gen_scope: GenZir = .{
|
||||
.force_comptime = true,
|
||||
.in_defer = false,
|
||||
.parent = &top_scope.base,
|
||||
.anon_name_strategy = .parent,
|
||||
.decl_node_index = 0,
|
||||
@ -1856,6 +1855,16 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
|
||||
.gen_zir => {
|
||||
const block_gz = scope.cast(GenZir).?;
|
||||
|
||||
if (block_gz.cur_defer_node != 0) {
|
||||
return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
|
||||
try astgen.errNoteNode(
|
||||
block_gz.cur_defer_node,
|
||||
"defer expression here",
|
||||
.{},
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
const block_inst = blk: {
|
||||
if (break_label != 0) {
|
||||
if (block_gz.label) |*label| {
|
||||
@ -1919,17 +1928,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
|
||||
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
|
||||
.namespace => break,
|
||||
.defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
|
||||
.defer_gen => {
|
||||
const defer_gen = scope.cast(Scope.DeferGen).?;
|
||||
|
||||
return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
|
||||
try astgen.errNoteNode(
|
||||
defer_gen.defer_node,
|
||||
"defer expression here",
|
||||
.{},
|
||||
),
|
||||
});
|
||||
},
|
||||
.top => unreachable,
|
||||
}
|
||||
}
|
||||
@ -1953,6 +1951,16 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
|
||||
switch (scope.tag) {
|
||||
.gen_zir => {
|
||||
const gen_zir = scope.cast(GenZir).?;
|
||||
|
||||
if (gen_zir.cur_defer_node != 0) {
|
||||
return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
|
||||
try astgen.errNoteNode(
|
||||
gen_zir.cur_defer_node,
|
||||
"defer expression here",
|
||||
.{},
|
||||
),
|
||||
});
|
||||
}
|
||||
const continue_block = gen_zir.continue_block;
|
||||
if (continue_block == 0) {
|
||||
scope = gen_zir.parent;
|
||||
@ -1985,21 +1993,9 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
|
||||
.defer_normal => {
|
||||
const defer_scope = scope.cast(Scope.Defer).?;
|
||||
scope = defer_scope.parent;
|
||||
const expr_node = node_datas[defer_scope.defer_node].rhs;
|
||||
try unusedResultDeferExpr(parent_gz, defer_scope, defer_scope.parent, expr_node);
|
||||
try parent_gz.addDefer(defer_scope.index, defer_scope.len);
|
||||
},
|
||||
.defer_error => scope = scope.cast(Scope.Defer).?.parent,
|
||||
.defer_gen => {
|
||||
const defer_gen = scope.cast(Scope.DeferGen).?;
|
||||
|
||||
return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
|
||||
try astgen.errNoteNode(
|
||||
defer_gen.defer_node,
|
||||
"defer expression here",
|
||||
.{},
|
||||
),
|
||||
});
|
||||
},
|
||||
.namespace => break,
|
||||
.top => unreachable,
|
||||
}
|
||||
@ -2064,7 +2060,6 @@ fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.Toke
|
||||
.local_val => scope = scope.cast(Scope.LocalVal).?.parent,
|
||||
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
|
||||
.defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
|
||||
.defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
|
||||
.namespace => break,
|
||||
.top => unreachable,
|
||||
}
|
||||
@ -2208,8 +2203,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
|
||||
.simple_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.simpleVarDecl(statement)),
|
||||
.aligned_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.alignedVarDecl(statement)),
|
||||
|
||||
.@"defer" => scope = try makeDeferScope(gz.astgen, scope, statement, block_arena_allocator, .defer_normal),
|
||||
.@"errdefer" => scope = try makeDeferScope(gz.astgen, scope, statement, block_arena_allocator, .defer_error),
|
||||
.@"defer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal),
|
||||
.@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error),
|
||||
|
||||
.assign => try assign(gz, scope, statement),
|
||||
|
||||
@ -2253,28 +2248,6 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
|
||||
try checkUsed(gz, parent_scope, scope);
|
||||
}
|
||||
|
||||
fn unusedResultDeferExpr(gz: *GenZir, defer_scope: *Scope.Defer, expr_scope: *Scope, expr_node: Ast.Node.Index) InnerError!void {
|
||||
const astgen = gz.astgen;
|
||||
const prev_offset = astgen.source_offset;
|
||||
const prev_line = astgen.source_line;
|
||||
const prev_column = astgen.source_column;
|
||||
defer {
|
||||
astgen.source_offset = prev_offset;
|
||||
astgen.source_line = prev_line;
|
||||
astgen.source_column = prev_column;
|
||||
}
|
||||
astgen.source_offset = defer_scope.source_offset;
|
||||
astgen.source_line = defer_scope.source_line;
|
||||
astgen.source_column = defer_scope.source_column;
|
||||
|
||||
var defer_gen: Scope.DeferGen = .{
|
||||
.parent = expr_scope,
|
||||
.defer_node = defer_scope.defer_node,
|
||||
};
|
||||
|
||||
_ = try unusedResultExpr(gz, &defer_gen.base, expr_node);
|
||||
}
|
||||
|
||||
/// Returns AST source node of the thing that is noreturn if the statement is
|
||||
/// definitely `noreturn`. Otherwise returns 0.
|
||||
fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
|
||||
@ -2584,6 +2557,9 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
|
||||
.validate_struct_init_ty,
|
||||
.validate_deref,
|
||||
=> break :b true,
|
||||
|
||||
.@"defer" => unreachable,
|
||||
.defer_err_code => unreachable,
|
||||
}
|
||||
} else switch (maybe_unused_result) {
|
||||
.none => unreachable,
|
||||
@ -2603,15 +2579,12 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
|
||||
return noreturn_src_node;
|
||||
}
|
||||
|
||||
fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct {
|
||||
fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
|
||||
have_any: bool,
|
||||
have_normal: bool,
|
||||
have_err: bool,
|
||||
need_err_code: bool,
|
||||
} {
|
||||
const tree = astgen.tree;
|
||||
const node_datas = tree.nodes.items(.data);
|
||||
|
||||
var have_normal = false;
|
||||
var have_err = false;
|
||||
var need_err_code = false;
|
||||
@ -2621,7 +2594,6 @@ fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct
|
||||
.gen_zir => scope = scope.cast(GenZir).?.parent,
|
||||
.local_val => scope = scope.cast(Scope.LocalVal).?.parent,
|
||||
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
|
||||
.defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
|
||||
.defer_normal => {
|
||||
const defer_scope = scope.cast(Scope.Defer).?;
|
||||
scope = defer_scope.parent;
|
||||
@ -2634,7 +2606,7 @@ fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct
|
||||
|
||||
have_err = true;
|
||||
|
||||
const have_err_payload = node_datas[defer_scope.defer_node].lhs != 0;
|
||||
const have_err_payload = defer_scope.remapped_err_code != 0;
|
||||
need_err_code = need_err_code or have_err_payload;
|
||||
},
|
||||
.namespace => unreachable,
|
||||
@ -2661,9 +2633,7 @@ fn genDefers(
|
||||
inner_scope: *Scope,
|
||||
which_ones: DefersToEmit,
|
||||
) InnerError!void {
|
||||
const astgen = gz.astgen;
|
||||
const tree = astgen.tree;
|
||||
const node_datas = tree.nodes.items(.data);
|
||||
const gpa = gz.astgen.gpa;
|
||||
|
||||
var scope = inner_scope;
|
||||
while (scope != outer_scope) {
|
||||
@ -2671,51 +2641,40 @@ fn genDefers(
|
||||
.gen_zir => scope = scope.cast(GenZir).?.parent,
|
||||
.local_val => scope = scope.cast(Scope.LocalVal).?.parent,
|
||||
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
|
||||
.defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
|
||||
.defer_normal => {
|
||||
const defer_scope = scope.cast(Scope.Defer).?;
|
||||
scope = defer_scope.parent;
|
||||
const expr_node = node_datas[defer_scope.defer_node].rhs;
|
||||
const prev_in_defer = gz.in_defer;
|
||||
gz.in_defer = true;
|
||||
defer gz.in_defer = prev_in_defer;
|
||||
try unusedResultDeferExpr(gz, defer_scope, defer_scope.parent, expr_node);
|
||||
try gz.addDefer(defer_scope.index, defer_scope.len);
|
||||
},
|
||||
.defer_error => {
|
||||
const defer_scope = scope.cast(Scope.Defer).?;
|
||||
scope = defer_scope.parent;
|
||||
switch (which_ones) {
|
||||
.both_sans_err => {
|
||||
const expr_node = node_datas[defer_scope.defer_node].rhs;
|
||||
const prev_in_defer = gz.in_defer;
|
||||
gz.in_defer = true;
|
||||
defer gz.in_defer = prev_in_defer;
|
||||
try unusedResultDeferExpr(gz, defer_scope, defer_scope.parent, expr_node);
|
||||
try gz.addDefer(defer_scope.index, defer_scope.len);
|
||||
},
|
||||
.both => |err_code| {
|
||||
const expr_node = node_datas[defer_scope.defer_node].rhs;
|
||||
const payload_token = node_datas[defer_scope.defer_node].lhs;
|
||||
const prev_in_defer = gz.in_defer;
|
||||
gz.in_defer = true;
|
||||
defer gz.in_defer = prev_in_defer;
|
||||
var local_val_scope: Scope.LocalVal = undefined;
|
||||
try gz.addDbgBlockBegin();
|
||||
const sub_scope = if (payload_token == 0) defer_scope.parent else blk: {
|
||||
const ident_name = try astgen.identAsString(payload_token);
|
||||
local_val_scope = .{
|
||||
.parent = defer_scope.parent,
|
||||
.gen_zir = gz,
|
||||
.name = ident_name,
|
||||
.inst = err_code,
|
||||
.token_src = payload_token,
|
||||
.id_cat = .@"capture",
|
||||
};
|
||||
try gz.addDbgVar(.dbg_var_val, ident_name, err_code);
|
||||
break :blk &local_val_scope.base;
|
||||
};
|
||||
try unusedResultDeferExpr(gz, defer_scope, sub_scope, expr_node);
|
||||
try checkUsed(gz, scope, sub_scope);
|
||||
try gz.addDbgBlockEnd();
|
||||
if (defer_scope.remapped_err_code == 0) {
|
||||
try gz.addDefer(defer_scope.index, defer_scope.len);
|
||||
} else {
|
||||
try gz.instructions.ensureUnusedCapacity(gpa, 1);
|
||||
try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
|
||||
|
||||
const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{
|
||||
.remapped_err_code = defer_scope.remapped_err_code,
|
||||
.index = defer_scope.index,
|
||||
.len = defer_scope.len,
|
||||
});
|
||||
const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
|
||||
gz.astgen.instructions.appendAssumeCapacity(.{
|
||||
.tag = .defer_err_code,
|
||||
.data = .{ .defer_err_code = .{
|
||||
.err_code = err_code,
|
||||
.payload_index = payload_index,
|
||||
} },
|
||||
});
|
||||
gz.instructions.appendAssumeCapacity(new_index);
|
||||
}
|
||||
},
|
||||
.normal_only => continue,
|
||||
}
|
||||
@ -2752,35 +2711,68 @@ fn checkUsed(
|
||||
scope = s.parent;
|
||||
},
|
||||
.defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
|
||||
.defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
|
||||
.namespace => unreachable,
|
||||
.top => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn makeDeferScope(
|
||||
astgen: *AstGen,
|
||||
fn deferStmt(
|
||||
gz: *GenZir,
|
||||
scope: *Scope,
|
||||
node: Ast.Node.Index,
|
||||
block_arena: Allocator,
|
||||
scope_tag: Scope.Tag,
|
||||
) InnerError!*Scope {
|
||||
const tree = astgen.tree;
|
||||
var defer_gen = gz.makeSubBlock(scope);
|
||||
defer_gen.cur_defer_node = node;
|
||||
defer_gen.any_defer_node = node;
|
||||
defer defer_gen.unstack();
|
||||
|
||||
const tree = gz.astgen.tree;
|
||||
const node_datas = tree.nodes.items(.data);
|
||||
const expr_node = node_datas[node].rhs;
|
||||
const token_starts = tree.tokens.items(.start);
|
||||
const node_start = token_starts[tree.firstToken(expr_node)];
|
||||
|
||||
const payload_token = node_datas[node].lhs;
|
||||
var local_val_scope: Scope.LocalVal = undefined;
|
||||
var remapped_err_code: Zir.Inst.Index = 0;
|
||||
const have_err_code = scope_tag == .defer_error and payload_token != 0;
|
||||
const sub_scope = if (!have_err_code) &defer_gen.base else blk: {
|
||||
try gz.addDbgBlockBegin();
|
||||
const ident_name = try gz.astgen.identAsString(payload_token);
|
||||
remapped_err_code = @intCast(u32, try gz.astgen.instructions.addOne(gz.astgen.gpa));
|
||||
const remapped_err_code_ref = Zir.indexToRef(remapped_err_code);
|
||||
local_val_scope = .{
|
||||
.parent = &defer_gen.base,
|
||||
.gen_zir = gz,
|
||||
.name = ident_name,
|
||||
.inst = remapped_err_code_ref,
|
||||
.token_src = payload_token,
|
||||
.id_cat = .@"capture",
|
||||
};
|
||||
try gz.addDbgVar(.dbg_var_val, ident_name, remapped_err_code_ref);
|
||||
break :blk &local_val_scope.base;
|
||||
};
|
||||
_ = try unusedResultExpr(&defer_gen, sub_scope, expr_node);
|
||||
try checkUsed(gz, scope, sub_scope);
|
||||
if (have_err_code) try gz.addDbgBlockEnd();
|
||||
_ = try defer_gen.addBreak(.break_inline, 0, .void_value);
|
||||
|
||||
const body = defer_gen.instructionsSlice();
|
||||
const body_len = gz.astgen.countBodyLenAfterFixups(body);
|
||||
|
||||
const index = @intCast(u32, gz.astgen.extra.items.len);
|
||||
try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len);
|
||||
gz.astgen.appendBodyWithFixups(body);
|
||||
|
||||
const defer_scope = try block_arena.create(Scope.Defer);
|
||||
astgen.advanceSourceCursor(node_start);
|
||||
|
||||
defer_scope.* = .{
|
||||
.base = .{ .tag = scope_tag },
|
||||
.parent = scope,
|
||||
.defer_node = node,
|
||||
.source_offset = astgen.source_offset,
|
||||
.source_line = astgen.source_line,
|
||||
.source_column = astgen.source_column,
|
||||
.index = index,
|
||||
.len = body_len,
|
||||
.remapped_err_code = remapped_err_code,
|
||||
};
|
||||
return &defer_scope.base;
|
||||
}
|
||||
@ -3461,7 +3453,6 @@ fn fnDecl(
|
||||
|
||||
var decl_gz: GenZir = .{
|
||||
.force_comptime = true,
|
||||
.in_defer = false,
|
||||
.decl_node_index = fn_proto.ast.proto_node,
|
||||
.decl_line = astgen.source_line,
|
||||
.parent = scope,
|
||||
@ -3473,7 +3464,6 @@ fn fnDecl(
|
||||
|
||||
var fn_gz: GenZir = .{
|
||||
.force_comptime = false,
|
||||
.in_defer = false,
|
||||
.decl_node_index = fn_proto.ast.proto_node,
|
||||
.decl_line = decl_gz.decl_line,
|
||||
.parent = &decl_gz.base,
|
||||
@ -3812,7 +3802,6 @@ fn globalVarDecl(
|
||||
.decl_line = astgen.source_line,
|
||||
.astgen = astgen,
|
||||
.force_comptime = true,
|
||||
.in_defer = false,
|
||||
.anon_name_strategy = .parent,
|
||||
.instructions = gz.instructions,
|
||||
.instructions_top = gz.instructions.items.len,
|
||||
@ -3964,7 +3953,6 @@ fn comptimeDecl(
|
||||
|
||||
var decl_block: GenZir = .{
|
||||
.force_comptime = true,
|
||||
.in_defer = false,
|
||||
.decl_node_index = node,
|
||||
.decl_line = astgen.source_line,
|
||||
.parent = scope,
|
||||
@ -4019,7 +4007,6 @@ fn usingnamespaceDecl(
|
||||
|
||||
var decl_block: GenZir = .{
|
||||
.force_comptime = true,
|
||||
.in_defer = false,
|
||||
.decl_node_index = node,
|
||||
.decl_line = astgen.source_line,
|
||||
.parent = scope,
|
||||
@ -4067,7 +4054,6 @@ fn testDecl(
|
||||
|
||||
var decl_block: GenZir = .{
|
||||
.force_comptime = true,
|
||||
.in_defer = false,
|
||||
.decl_node_index = node,
|
||||
.decl_line = astgen.source_line,
|
||||
.parent = scope,
|
||||
@ -4132,7 +4118,6 @@ fn testDecl(
|
||||
.local_val, .local_ptr => unreachable, // a test cannot be in a local scope
|
||||
.gen_zir => s = s.cast(GenZir).?.parent,
|
||||
.defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
|
||||
.defer_gen => s = s.cast(Scope.DeferGen).?.parent,
|
||||
.namespace => {
|
||||
const ns = s.cast(Scope.Namespace).?;
|
||||
if (ns.decls.get(name_str_index)) |i| {
|
||||
@ -4164,7 +4149,6 @@ fn testDecl(
|
||||
|
||||
var fn_block: GenZir = .{
|
||||
.force_comptime = false,
|
||||
.in_defer = false,
|
||||
.decl_node_index = node,
|
||||
.decl_line = decl_block.decl_line,
|
||||
.parent = &decl_block.base,
|
||||
@ -4288,7 +4272,6 @@ fn structDeclInner(
|
||||
.decl_line = decl_line,
|
||||
.astgen = astgen,
|
||||
.force_comptime = true,
|
||||
.in_defer = false,
|
||||
.instructions = gz.instructions,
|
||||
.instructions_top = gz.instructions.items.len,
|
||||
};
|
||||
@ -4489,7 +4472,6 @@ fn unionDeclInner(
|
||||
.decl_line = astgen.source_line,
|
||||
.astgen = astgen,
|
||||
.force_comptime = true,
|
||||
.in_defer = false,
|
||||
.instructions = gz.instructions,
|
||||
.instructions_top = gz.instructions.items.len,
|
||||
};
|
||||
@ -4773,7 +4755,6 @@ fn containerDecl(
|
||||
.decl_line = astgen.source_line,
|
||||
.astgen = astgen,
|
||||
.force_comptime = true,
|
||||
.in_defer = false,
|
||||
.instructions = gz.instructions,
|
||||
.instructions_top = gz.instructions.items.len,
|
||||
};
|
||||
@ -4879,7 +4860,6 @@ fn containerDecl(
|
||||
.decl_line = astgen.source_line,
|
||||
.astgen = astgen,
|
||||
.force_comptime = true,
|
||||
.in_defer = false,
|
||||
.instructions = gz.instructions,
|
||||
.instructions_top = gz.instructions.items.len,
|
||||
};
|
||||
@ -5110,7 +5090,15 @@ fn tryExpr(
|
||||
return astgen.failNode(node, "'try' outside function scope", .{});
|
||||
};
|
||||
|
||||
if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{});
|
||||
if (parent_gz.any_defer_node != 0) {
|
||||
return astgen.failNodeNotes(node, "'try' not allowed inside defer expression", .{}, &.{
|
||||
try astgen.errNoteNode(
|
||||
parent_gz.any_defer_node,
|
||||
"defer expression here",
|
||||
.{},
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure debug line/column information is emitted for this try expression.
|
||||
// Then we will save the line/column so that we can emit another one that goes
|
||||
@ -6656,7 +6644,15 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
return astgen.failNode(node, "'return' outside function scope", .{});
|
||||
}
|
||||
|
||||
if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{});
|
||||
if (gz.any_defer_node != 0) {
|
||||
return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{
|
||||
try astgen.errNoteNode(
|
||||
gz.any_defer_node,
|
||||
"defer expression here",
|
||||
.{},
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure debug line/column information is emitted for this return expression.
|
||||
// Then we will save the line/column so that we can emit another one that goes
|
||||
@ -6683,7 +6679,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
// for detecting whether to add something to the function's inferred error set.
|
||||
const ident_token = node_datas[operand_node].rhs;
|
||||
const err_name_str_index = try astgen.identAsString(ident_token);
|
||||
const defer_counts = countDefers(astgen, defer_outer, scope);
|
||||
const defer_counts = countDefers(defer_outer, scope);
|
||||
if (!defer_counts.need_err_code) {
|
||||
try genDefers(gz, defer_outer, scope, .both_sans_err);
|
||||
try emitDbgStmt(gz, ret_line, ret_column);
|
||||
@ -6724,7 +6720,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
},
|
||||
.maybe => {
|
||||
const defer_counts = countDefers(astgen, defer_outer, scope);
|
||||
const defer_counts = countDefers(defer_outer, scope);
|
||||
if (!defer_counts.have_err) {
|
||||
// Only regular defers; no branch needed.
|
||||
try genDefers(gz, defer_outer, scope, .normal_only);
|
||||
@ -6921,7 +6917,6 @@ fn localVarRef(
|
||||
},
|
||||
.gen_zir => s = s.cast(GenZir).?.parent,
|
||||
.defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
|
||||
.defer_gen => s = s.cast(Scope.DeferGen).?.parent,
|
||||
.namespace => {
|
||||
const ns = s.cast(Scope.Namespace).?;
|
||||
if (ns.decls.get(name_str_index)) |i| {
|
||||
@ -7550,7 +7545,6 @@ fn builtinCall(
|
||||
},
|
||||
.gen_zir => s = s.cast(GenZir).?.parent,
|
||||
.defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
|
||||
.defer_gen => s = s.cast(Scope.DeferGen).?.parent,
|
||||
.namespace => {
|
||||
const ns = s.cast(Scope.Namespace).?;
|
||||
if (ns.decls.get(decl_name)) |i| {
|
||||
@ -10046,7 +10040,6 @@ const Scope = struct {
|
||||
local_ptr,
|
||||
defer_normal,
|
||||
defer_error,
|
||||
defer_gen,
|
||||
namespace,
|
||||
top,
|
||||
};
|
||||
@ -10105,10 +10098,9 @@ const Scope = struct {
|
||||
base: Scope,
|
||||
/// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
|
||||
parent: *Scope,
|
||||
defer_node: Ast.Node.Index,
|
||||
source_offset: u32,
|
||||
source_line: u32,
|
||||
source_column: u32,
|
||||
index: u32,
|
||||
len: u32,
|
||||
remapped_err_code: Zir.Inst.Index = 0,
|
||||
};
|
||||
|
||||
/// Represents a global scope that has any number of declarations in it.
|
||||
@ -10144,13 +10136,6 @@ const Scope = struct {
|
||||
const base_tag: Scope.Tag = .top;
|
||||
base: Scope = Scope{ .tag = base_tag },
|
||||
};
|
||||
|
||||
const DeferGen = struct {
|
||||
const base_tag: Scope.Tag = .defer_gen;
|
||||
base: Scope = Scope{ .tag = base_tag },
|
||||
parent: *Scope,
|
||||
defer_node: Ast.Node.Index,
|
||||
};
|
||||
};
|
||||
|
||||
/// This is a temporary structure; references to it are valid only
|
||||
@ -10161,7 +10146,6 @@ const GenZir = struct {
|
||||
force_comptime: bool,
|
||||
/// This is set to true for inline loops; false otherwise.
|
||||
is_inline: bool = false,
|
||||
in_defer: bool,
|
||||
c_import: bool = false,
|
||||
/// How decls created in this scope should be named.
|
||||
anon_name_strategy: Zir.Inst.NameStrategy = .anon,
|
||||
@ -10204,6 +10188,10 @@ const GenZir = struct {
|
||||
|
||||
suspend_node: Ast.Node.Index = 0,
|
||||
nosuspend_node: Ast.Node.Index = 0,
|
||||
/// Set if this GenZir is a defer.
|
||||
cur_defer_node: Ast.Node.Index = 0,
|
||||
// Set if this GenZir is a defer or it is inside a defer.
|
||||
any_defer_node: Ast.Node.Index = 0,
|
||||
|
||||
/// Namespace members are lazy. When executing a decl within a namespace,
|
||||
/// any references to external instructions need to be treated specially.
|
||||
@ -10244,7 +10232,6 @@ const GenZir = struct {
|
||||
fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
|
||||
return .{
|
||||
.force_comptime = gz.force_comptime,
|
||||
.in_defer = gz.in_defer,
|
||||
.c_import = gz.c_import,
|
||||
.decl_node_index = gz.decl_node_index,
|
||||
.decl_line = gz.decl_line,
|
||||
@ -10253,6 +10240,7 @@ const GenZir = struct {
|
||||
.astgen = gz.astgen,
|
||||
.suspend_node = gz.suspend_node,
|
||||
.nosuspend_node = gz.nosuspend_node,
|
||||
.any_defer_node = gz.any_defer_node,
|
||||
.instructions = gz.instructions,
|
||||
.instructions_top = gz.instructions.items.len,
|
||||
};
|
||||
@ -11107,6 +11095,16 @@ const GenZir = struct {
|
||||
});
|
||||
}
|
||||
|
||||
fn addDefer(gz: *GenZir, index: u32, len: u32) !void {
|
||||
_ = try gz.add(.{
|
||||
.tag = .@"defer",
|
||||
.data = .{ .@"defer" = .{
|
||||
.index = index,
|
||||
.len = len,
|
||||
} },
|
||||
});
|
||||
}
|
||||
|
||||
fn addDecl(
|
||||
gz: *GenZir,
|
||||
tag: Zir.Inst.Tag,
|
||||
@ -11670,7 +11668,6 @@ fn detectLocalShadowing(
|
||||
},
|
||||
.gen_zir => s = s.cast(GenZir).?.parent,
|
||||
.defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
|
||||
.defer_gen => s = s.cast(Scope.DeferGen).?.parent,
|
||||
.top => break,
|
||||
};
|
||||
}
|
||||
@ -11809,7 +11806,6 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.
|
||||
.namespace => s = s.cast(Scope.Namespace).?.parent,
|
||||
.gen_zir => s = s.cast(GenZir).?.parent,
|
||||
.defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
|
||||
.defer_gen => s = s.cast(Scope.DeferGen).?.parent,
|
||||
.top => break,
|
||||
};
|
||||
gop.value_ptr.* = member_node;
|
||||
|
||||
34
src/Sema.zig
34
src/Sema.zig
@ -1461,6 +1461,29 @@ fn analyzeBodyInner(
|
||||
// break break_data.inst;
|
||||
// }
|
||||
//},
|
||||
.@"defer" => blk: {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].@"defer";
|
||||
const defer_body = sema.code.extra[inst_data.index..][0..inst_data.len];
|
||||
const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) {
|
||||
error.ComptimeBreak => sema.comptime_break_inst,
|
||||
else => |e| return e,
|
||||
};
|
||||
if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn;
|
||||
break :blk Air.Inst.Ref.void_value;
|
||||
},
|
||||
.defer_err_code => blk: {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].defer_err_code;
|
||||
const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data;
|
||||
const defer_body = sema.code.extra[extra.index..][0..extra.len];
|
||||
const err_code = try sema.resolveInst(inst_data.err_code);
|
||||
try sema.inst_map.put(sema.gpa, extra.remapped_err_code, err_code);
|
||||
const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) {
|
||||
error.ComptimeBreak => sema.comptime_break_inst,
|
||||
else => |e| return e,
|
||||
};
|
||||
if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn;
|
||||
break :blk Air.Inst.Ref.void_value;
|
||||
},
|
||||
};
|
||||
if (sema.typeOf(air_inst).isNoReturn())
|
||||
break always_noreturn;
|
||||
@ -9394,11 +9417,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
|
||||
}
|
||||
|
||||
if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames().len) {
|
||||
|
||||
// TODO re-enable if defer implementation is improved
|
||||
// https://github.com/ziglang/zig/issues/11798
|
||||
if (true) break :else_validation;
|
||||
|
||||
// In order to enable common patterns for generic code allow simple else bodies
|
||||
// else => unreachable,
|
||||
// else => return,
|
||||
@ -9415,6 +9433,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
|
||||
.as_node,
|
||||
.ret_node,
|
||||
.@"unreachable",
|
||||
.@"defer",
|
||||
.defer_err_code,
|
||||
.err_union_code,
|
||||
.ret_err_value_code,
|
||||
.is_non_err,
|
||||
.condbr,
|
||||
=> {},
|
||||
else => break,
|
||||
} else break :else_validation;
|
||||
|
||||
30
src/Zir.zig
30
src/Zir.zig
@ -995,6 +995,13 @@ pub const Inst = struct {
|
||||
/// closure_capture instruction ref.
|
||||
closure_get,
|
||||
|
||||
/// A defer statement.
|
||||
/// Uses the `defer` union field.
|
||||
@"defer",
|
||||
/// An errdefer statement with a code.
|
||||
/// Uses the `err_defer_code` union field.
|
||||
defer_err_code,
|
||||
|
||||
/// The ZIR instruction tag is one of the `Extended` ones.
|
||||
/// Uses the `extended` union field.
|
||||
extended,
|
||||
@ -1244,6 +1251,8 @@ pub const Inst = struct {
|
||||
.try_ptr,
|
||||
//.try_inline,
|
||||
//.try_ptr_inline,
|
||||
.@"defer",
|
||||
.defer_err_code,
|
||||
=> false,
|
||||
|
||||
.@"break",
|
||||
@ -1311,6 +1320,8 @@ pub const Inst = struct {
|
||||
.memcpy,
|
||||
.memset,
|
||||
.check_comptime_control_flow,
|
||||
.@"defer",
|
||||
.defer_err_code,
|
||||
=> true,
|
||||
|
||||
.param,
|
||||
@ -1819,6 +1830,9 @@ pub const Inst = struct {
|
||||
.closure_capture = .un_tok,
|
||||
.closure_get = .inst_node,
|
||||
|
||||
.@"defer" = .@"defer",
|
||||
.defer_err_code = .defer_err_code,
|
||||
|
||||
.extended = .extended,
|
||||
});
|
||||
};
|
||||
@ -2575,6 +2589,14 @@ pub const Inst = struct {
|
||||
return zir.nullTerminatedString(self.str);
|
||||
}
|
||||
},
|
||||
@"defer": struct {
|
||||
index: u32,
|
||||
len: u32,
|
||||
},
|
||||
defer_err_code: struct {
|
||||
err_code: Ref,
|
||||
payload_index: u32,
|
||||
},
|
||||
|
||||
// Make sure we don't accidentally add a field to make this union
|
||||
// bigger than expected. Note that in Debug builds, Zig is allowed
|
||||
@ -2611,6 +2633,8 @@ pub const Inst = struct {
|
||||
dbg_stmt,
|
||||
inst_node,
|
||||
str_op,
|
||||
@"defer",
|
||||
defer_err_code,
|
||||
};
|
||||
};
|
||||
|
||||
@ -3550,6 +3574,12 @@ pub const Inst = struct {
|
||||
line: u32,
|
||||
column: u32,
|
||||
};
|
||||
|
||||
pub const DeferErrCode = struct {
|
||||
remapped_err_code: Index,
|
||||
index: u32,
|
||||
len: u32,
|
||||
};
|
||||
};
|
||||
|
||||
pub const SpecialProng = enum { none, @"else", under };
|
||||
|
||||
@ -446,6 +446,9 @@ const Writer = struct {
|
||||
|
||||
.closure_get => try self.writeInstNode(stream, inst),
|
||||
|
||||
.@"defer" => try self.writeDefer(stream, inst),
|
||||
.defer_err_code => try self.writeDeferErrCode(stream, inst),
|
||||
|
||||
.extended => try self.writeExtended(stream, inst),
|
||||
}
|
||||
}
|
||||
@ -2364,6 +2367,26 @@ const Writer = struct {
|
||||
try stream.print("{d}, {d})", .{ inst_data.line + 1, inst_data.column + 1 });
|
||||
}
|
||||
|
||||
fn writeDefer(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].@"defer";
|
||||
const body = self.code.extra[inst_data.index..][0..inst_data.len];
|
||||
try self.writeBracedBody(stream, body);
|
||||
try stream.writeByte(')');
|
||||
}
|
||||
|
||||
fn writeDeferErrCode(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].defer_err_code;
|
||||
const extra = self.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data;
|
||||
|
||||
try self.writeInstRef(stream, Zir.indexToRef(extra.remapped_err_code));
|
||||
try stream.writeAll(" = ");
|
||||
try self.writeInstRef(stream, inst_data.err_code);
|
||||
try stream.writeAll(", ");
|
||||
const body = self.code.extra[extra.index..][0..extra.len];
|
||||
try self.writeBracedBody(stream, body);
|
||||
try stream.writeByte(')');
|
||||
}
|
||||
|
||||
fn writeInstRef(self: *Writer, stream: anytype, ref: Zir.Inst.Ref) !void {
|
||||
var i: usize = @enumToInt(ref);
|
||||
|
||||
|
||||
@ -127,3 +127,20 @@ test "errdefer with payload" {
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "simple else prong doesn't emit an error for unreachable else prong" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
fn foo() error{Foo}!void {
|
||||
return error.Foo;
|
||||
}
|
||||
};
|
||||
var a: u32 = 0;
|
||||
defer a += 1;
|
||||
S.foo() catch |err| switch (err) {
|
||||
error.Foo => a += 1,
|
||||
else => |e| return e,
|
||||
};
|
||||
try expect(a == 1);
|
||||
}
|
||||
|
||||
@ -19,3 +19,4 @@ export fn entry() usize { return @sizeOf(@TypeOf(testTrickyDefer)); }
|
||||
// target=native
|
||||
//
|
||||
// :4:11: error: 'try' not allowed inside defer expression
|
||||
// :4:5: note: defer expression here
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
pub export fn complex() void {
|
||||
var a: error{ Foo, Bar } = error.Foo;
|
||||
switch (a) {
|
||||
error.Foo => unreachable,
|
||||
error.Bar => unreachable,
|
||||
else => {
|
||||
@compileError("<something complex here>");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub export fn simple() void {
|
||||
var a: error{ Foo, Bar } = error.Foo;
|
||||
switch (a) {
|
||||
error.Foo => unreachable,
|
||||
error.Bar => unreachable,
|
||||
else => |e| return e,
|
||||
}
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=llvm
|
||||
// target=native
|
||||
//
|
||||
// :6:14: error: unreachable else prong; all cases already handled
|
||||
@ -13,4 +13,6 @@ pub fn b() !void {
|
||||
// error
|
||||
//
|
||||
// :7:11: error: 'try' not allowed inside defer expression
|
||||
// :7:5: note: defer expression here
|
||||
// :10:11: error: cannot return from defer expression
|
||||
// :10:5: note: defer expression here
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user