diff --git a/src/astgen.zig b/src/astgen.zig index 0b74886729..fa624c31ee 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -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", .{}); diff --git a/src/codegen.zig b/src/codegen.zig index 5e5215c992..9deeab82a5 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -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| { diff --git a/src/ir.zig b/src/ir.zig index 26afa52929..e71935e18a 100644 --- a/src/ir.zig +++ b/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 { diff --git a/src/zir.zig b/src/zir.zig index 5dc7a2eee6..6bee72c66b 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -2549,6 +2549,9 @@ const EmitZIR = struct { }, .varptr => @panic("TODO"), + .@"switch" => { + @panic("TODO"); + }, }; try self.metadata.put(new_inst, .{ .deaths = inst.deaths, diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 26b2714ec8..6c2b7a864a 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -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 {