Commit 769d5a9c43
Changed files (5)
src/astgen.zig
@@ -1581,14 +1581,6 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
};
defer block_scope.instructions.deinit(mod.gpa);
- var item_scope: Scope.GenZIR = .{
- .parent = scope,
- .decl = scope.decl().?,
- .arena = scope.arena(),
- .instructions = .{},
- };
- defer item_scope.instructions.deinit(mod.gpa);
-
const tree = scope.tree();
const switch_src = tree.token_locs[switch_node.switch_token].start;
const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr);
@@ -1598,6 +1590,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
.target_ptr = target_ptr,
.cases = undefined, // populated below
.items = &[_]*zir.Inst{}, // populated below
+ .else_body = undefined, // populated below
}, .{})).castTag(.switchbr).?;
var items = std.ArrayList(*zir.Inst).init(mod.gpa);
@@ -1611,7 +1604,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
});
// then add block containing the switch.
const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
- .instructions = undefined, // populated below
+ .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
});
// Most result location types can be forwarded directly; however
@@ -1622,6 +1615,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
.inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
};
+ var item_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = scope.decl().?,
+ .arena = scope.arena(),
+ .instructions = .{},
+ };
+ defer item_scope.instructions.deinit(mod.gpa);
+
var case_scope: Scope.GenZIR = .{
.parent = scope,
.decl = block_scope.decl,
@@ -1630,6 +1631,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
};
defer case_scope.instructions.deinit(mod.gpa);
+ var else_scope: Scope.GenZIR = .{
+ .parent = scope,
+ .decl = block_scope.decl,
+ .arena = block_scope.arena,
+ .instructions = .{},
+ };
+ defer else_scope.instructions.deinit(mod.gpa);
+
// first we gather all the switch items and check else/'_' prongs
var else_src: ?usize = null;
var underscore_src: ?usize = null;
@@ -1701,12 +1710,12 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
if (first_range == null) first_range = range_inst;
// target >= start and target <= end
- const range_start_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .cmp_gte, target, start);
- const range_end_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .cmp_lte, target, end);
- const range_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .booland, range_start_ok, range_end_ok);
+ const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, start);
+ const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, end);
+ const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .booland, range_start_ok, range_end_ok);
if (any_ok) |some| {
- any_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .boolor, some, range_ok);
+ any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .boolor, some, range_ok);
} else {
any_ok = range_ok;
}
@@ -1715,16 +1724,16 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
const item_inst = try expr(mod, &item_scope.base, .none, item);
try items.append(item_inst);
- const cpm_ok = try addZIRBinOp(mod, &block_scope.base, item_inst.src, .cmp_eq, target, item_inst);
+ const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst);
if (any_ok) |some| {
- any_ok = try addZIRBinOp(mod, &block_scope.base, item_inst.src, .boolor, some, cpm_ok);
+ any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .boolor, some, cpm_ok);
} else {
any_ok = cpm_ok;
}
}
- const condbr = try addZIRInstSpecial(mod, &block_scope.base, case_src, zir.Inst.CondBr, .{
+ const condbr = try addZIRInstSpecial(mod, &else_scope.base, case_src, zir.Inst.CondBr, .{
.condition = any_ok.?,
.then_body = undefined, // populated below
.else_body = undefined, // populated below
@@ -1754,6 +1763,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
};
}
+ // Generate else block or a break last to finish the block.
+ if (special_case) |case| {
+ try switchCaseExpr(mod, &else_scope.base, case_rl, block, case);
+ } else {
+ // Not handling all possible cases is a compile error.
+ _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreach_nocheck);
+ }
+
// All items have been generated, add the instructions to the comptime block.
item_block.positionals.body = .{
.instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items),
@@ -1765,18 +1782,8 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items);
switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items);
switch_inst.kw_args.range = first_range;
-
- // Generate else block or a break last to finish the block.
- if (special_case) |case| {
- try switchCaseExpr(mod, &block_scope.base, case_rl, block, case);
- } else {
- // Not handling all possible cases is a compile error.
- _ = try addZIRNoOp(mod, &block_scope.base, switch_src, .unreach_nocheck);
- }
-
- // Set block instructions now that it is finished.
- block.positionals.body = .{
- .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
+ switch_inst.positionals.else_body = .{
+ .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
};
return &block.base;
}
src/ir.zig
@@ -472,8 +472,11 @@ pub const Inst = struct {
target_ptr: *Inst,
cases: []Case,
/// Set of instructions whose lifetimes end at the start of one of the cases.
- /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... , case_n_count ... else_count].
+ /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ].
deaths: [*]*Inst = undefined,
+ else_index: u32 = 0,
+ else_deaths: u32 = 0,
+ else_body: Body,
pub const Case = struct {
item: Value,
@@ -498,6 +501,9 @@ pub const Inst = struct {
const case = self.cases[case_index];
return (self.deaths + case.index)[0..case.deaths];
}
+ pub fn elseDeaths(self: *const SwitchBr) []*Inst {
+ return (self.deaths + self.else_index)[0..self.else_deaths];
+ }
};
};
src/Module.zig
@@ -2122,16 +2122,18 @@ pub fn addSwitchBr(
src: usize,
target_ptr: *Inst,
cases: []Inst.SwitchBr.Case,
+ else_body: ir.Body,
) !*Inst {
const inst = try block.arena.create(Inst.SwitchBr);
inst.* = .{
.base = .{
.tag = .switchbr,
- .ty = Type.initTag(.void),
+ .ty = Type.initTag(.noreturn),
.src = src,
},
.target_ptr = target_ptr,
.cases = cases,
+ .else_body = else_body,
};
try block.instructions.append(self.gpa, &inst.base);
return &inst.base;
src/zir.zig
@@ -509,7 +509,6 @@ pub const Inst = struct {
.slice,
.slice_start,
.import,
- .switchbr,
.switch_range,
=> false,
@@ -522,6 +521,7 @@ pub const Inst = struct {
.unreach_nocheck,
.@"unreachable",
.loop,
+ .switchbr,
=> true,
};
}
@@ -1012,9 +1012,10 @@ pub const Inst = struct {
positionals: struct {
target_ptr: *Inst,
- cases: []Case,
/// List of all individual items and ranges
items: []*Inst,
+ cases: []Case,
+ else_body: Module.Body,
},
kw_args: struct {
/// Pointer to first range if such exists.
@@ -2569,6 +2570,7 @@ const EmitZIR = struct {
.target_ptr = try self.resolveInst(new_body, old_inst.target_ptr),
.cases = cases,
.items = &[_]*Inst{}, // TODO this should actually be populated
+ .else_body = undefined, // populated below
},
.kw_args = .{},
};
@@ -2590,6 +2592,12 @@ const EmitZIR = struct {
.body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) },
};
}
+
+ body_tmp.items.len = 0;
+ try self.emitBody(old_inst.else_body, inst_table, &body_tmp);
+ new_inst.positionals.else_body = .{
+ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items),
+ };
break :blk &new_inst.base;
},
.varptr => @panic("TODO"),
src/zir_sema.zig
@@ -1238,7 +1238,20 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src);
try validateSwitch(mod, scope, target, inst);
- // TODO comptime execution
+ if (try mod.resolveDefinedValue(scope, target)) |target_val| {
+ for (inst.positionals.cases) |case| {
+ const resolved = try resolveInst(mod, scope, case.item);
+ const casted = try mod.coerce(scope, target.ty, resolved);
+ const item = try mod.resolveConstValue(scope, casted);
+
+ if (target_val.eql(item)) {
+ try analyzeBody(mod, scope, case.body);
+ return mod.constNoReturn(scope, inst.base.src);
+ }
+ }
+ try analyzeBody(mod, scope, inst.positionals.else_body);
+ return mod.constNoReturn(scope, inst.base.src);
+ }
const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len);
@@ -1253,7 +1266,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
};
defer case_block.instructions.deinit(mod.gpa);
- for (inst.positionals.cases[0..inst.positionals.cases.len]) |case, i| {
+ for (inst.positionals.cases) |case, i| {
// Reset without freeing.
case_block.instructions.items.len = 0;
@@ -1269,7 +1282,14 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
};
}
- return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases);
+ case_block.instructions.items.len = 0;
+ try analyzeBody(mod, &case_block.base, inst.positionals.else_body);
+
+ const else_body: ir.Body = .{
+ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),
+ };
+
+ return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases, else_body);
}
fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {
@@ -1354,14 +1374,14 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw
false_count += 1;
}
- if (true_count > 1 or false_count > 1) {
+ if (true_count + false_count > 2) {
return mod.fail(scope, item.src, "duplicate switch value", .{});
}
}
- if ((true_count == 0 or false_count == 0) and inst.kw_args.special_prong != .@"else") {
+ if ((true_count + false_count < 2) and inst.kw_args.special_prong != .@"else") {
return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
}
- if ((true_count == 1 and false_count == 1) and inst.kw_args.special_prong == .@"else") {
+ if ((true_count + false_count == 2) and inst.kw_args.special_prong == .@"else") {
return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
}
},
@@ -1696,7 +1716,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {
const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
try analyzeBody(mod, scope, body.*);
- return mod.constVoid(scope, inst.base.src);
+ return mod.constNoReturn(scope, inst.base.src);
}
const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);