stage2: basic switch analysis

This commit is contained in:
Vexu
2020-10-12 15:35:48 +03:00
parent 2c12f4a993
commit 11998d2972
5 changed files with 102 additions and 1 deletions

View File

@@ -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", .{});

View File

@@ -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| {

View File

@@ -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 {

View File

@@ -2549,6 +2549,9 @@ const EmitZIR = struct {
},
.varptr => @panic("TODO"),
.@"switch" => {
@panic("TODO");
},
};
try self.metadata.put(new_inst, .{
.deaths = inst.deaths,

View File

@@ -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 {