stage2: basic switch analysis
This commit is contained in:
@@ -1584,6 +1584,10 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
|
||||
const case = uncasted_case.castTag(.SwitchCase).?;
|
||||
const case_src = tree.token_locs[case.firstToken()].start;
|
||||
|
||||
if (case.payload != null) {
|
||||
return mod.fail(scope, case_src, "TODO switch case payload capture", .{});
|
||||
}
|
||||
|
||||
if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {
|
||||
if (else_src) |src| {
|
||||
return mod.fail(scope, case_src, "multiple else prongs in switch expression", .{});
|
||||
|
||||
@@ -786,6 +786,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?),
|
||||
.wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
|
||||
.varptr => return self.genVarPtr(inst.castTag(.varptr).?),
|
||||
.@"switch" => return self.genSwitch(inst.castTag(.@"switch").?),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1989,6 +1990,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return @bitCast(MCValue, inst.codegen.mcv);
|
||||
}
|
||||
|
||||
fn genSwitch(self: *Self, inst: *ir.Inst.Switch) !MCValue {
|
||||
switch (arch) {
|
||||
else => return self.fail(inst.base.src, "TODO genSwitch for {}", .{self.target.cpu.arch}),
|
||||
}
|
||||
}
|
||||
|
||||
fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
|
||||
switch (reloc) {
|
||||
.rel32 => |pos| {
|
||||
|
||||
24
src/ir.zig
24
src/ir.zig
@@ -91,6 +91,7 @@ pub const Inst = struct {
|
||||
intcast,
|
||||
unwrap_optional,
|
||||
wrap_optional,
|
||||
@"switch",
|
||||
|
||||
pub fn Type(tag: Tag) type {
|
||||
return switch (tag) {
|
||||
@@ -137,6 +138,7 @@ pub const Inst = struct {
|
||||
.constant => Constant,
|
||||
.loop => Loop,
|
||||
.varptr => VarPtr,
|
||||
.@"switch" => Switch,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -458,6 +460,28 @@ pub const Inst = struct {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Switch = struct {
|
||||
pub const base_tag = Tag.@"switch";
|
||||
|
||||
base: Inst,
|
||||
target_ptr: *Inst,
|
||||
cases: []Case,
|
||||
@"else": ?Body,
|
||||
|
||||
pub const Case = struct {
|
||||
items: []Value,
|
||||
body: Body,
|
||||
};
|
||||
|
||||
pub fn operandCount(self: *const Switch) usize {
|
||||
return 1;
|
||||
}
|
||||
pub fn getOperand(self: *const Switch, index: usize) ?*Inst {
|
||||
return self.target_ptr;
|
||||
}
|
||||
// TODO case body deaths
|
||||
};
|
||||
};
|
||||
|
||||
pub const Body = struct {
|
||||
|
||||
@@ -2549,6 +2549,9 @@ const EmitZIR = struct {
|
||||
},
|
||||
|
||||
.varptr => @panic("TODO"),
|
||||
.@"switch" => {
|
||||
@panic("TODO");
|
||||
},
|
||||
};
|
||||
try self.metadata.put(new_inst, .{
|
||||
.deaths = inst.deaths,
|
||||
|
||||
@@ -1233,7 +1233,70 @@ fn analyzeInstSwitch(mod: *Module, scope: *Scope, inst: *zir.Inst.Switch) InnerE
|
||||
const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src);
|
||||
try validateSwitch(mod, scope, target, inst);
|
||||
|
||||
return mod.fail(scope, inst.base.src, "TODO analyzeInstSwitch", .{});
|
||||
// TODO comptime execution
|
||||
|
||||
// excludes else and '_' cases
|
||||
const case_count = inst.positionals.cases.len - @boolToInt(inst.kw_args.special_case != .none);
|
||||
|
||||
const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
|
||||
const switch_inst = try parent_block.arena.create(Inst.Switch);
|
||||
switch_inst.* = .{
|
||||
.base = .{
|
||||
.tag = Inst.Switch.base_tag,
|
||||
.ty = Type.initTag(.noreturn),
|
||||
.src = inst.base.src,
|
||||
},
|
||||
.target_ptr = target_ptr,
|
||||
.@"else" = null,
|
||||
.cases = try parent_block.arena.alloc(Inst.Switch.Case, case_count),
|
||||
};
|
||||
|
||||
var case_block: Scope.Block = .{
|
||||
.parent = parent_block,
|
||||
.func = parent_block.func,
|
||||
.decl = parent_block.decl,
|
||||
.instructions = .{},
|
||||
.arena = parent_block.arena,
|
||||
.is_comptime = parent_block.is_comptime,
|
||||
};
|
||||
defer case_block.instructions.deinit(mod.gpa);
|
||||
|
||||
var items_tmp = std.ArrayList(Value).init(mod.gpa);
|
||||
defer items_tmp.deinit();
|
||||
|
||||
for (inst.positionals.cases[0..case_count]) |case, i| {
|
||||
// Reset without freeing.
|
||||
case_block.instructions.items.len = 0;
|
||||
items_tmp.items.len = 0;
|
||||
|
||||
for (case.items) |item| {
|
||||
if (item.castTag(.switch_range)) |range| {
|
||||
return mod.fail(scope, item.src, "genSwitch expand range", .{});
|
||||
}
|
||||
const resolved = try resolveInst(mod, scope, item);
|
||||
const casted = try mod.coerce(scope, target.ty, resolved);
|
||||
const val = try mod.resolveConstValue(scope, casted);
|
||||
try items_tmp.append(val);
|
||||
}
|
||||
|
||||
try analyzeBody(mod, &case_block.base, case.body);
|
||||
|
||||
switch_inst.cases[i] = .{
|
||||
.items = try parent_block.arena.dupe(Value, items_tmp.items),
|
||||
.body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) },
|
||||
};
|
||||
}
|
||||
|
||||
if (inst.kw_args.special_case != .none) {
|
||||
case_block.instructions.items.len = 0;
|
||||
|
||||
try analyzeBody(mod, &case_block.base, inst.positionals.cases[case_count].body);
|
||||
switch_inst.@"else" = .{
|
||||
.instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),
|
||||
};
|
||||
}
|
||||
|
||||
return &switch_inst.base;
|
||||
}
|
||||
|
||||
fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Switch) InnerError!void {
|
||||
|
||||
Reference in New Issue
Block a user