Commit 11998d2972

Vexu <git@vexu.eu>
2020-10-12 14:35:48
stage2: basic switch analysis
1 parent 2c12f4a
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", .{});
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| {
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 {
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,
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 {