diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Module.zig | 4 | ||||
| -rw-r--r-- | src/astgen.zig | 131 | ||||
| -rw-r--r-- | src/ir.zig | 8 | ||||
| -rw-r--r-- | src/zir.zig | 54 | ||||
| -rw-r--r-- | src/zir_sema.zig | 95 |
5 files changed, 139 insertions, 153 deletions
diff --git a/src/Module.zig b/src/Module.zig index dc4a739790..a9b4272298 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2122,18 +2122,16 @@ pub fn addSwitchBr( src: usize, target_ptr: *Inst, cases: []Inst.SwitchBr.Case, - else_body: ?Module.Body, ) !*Inst { const inst = try block.arena.create(Inst.SwitchBr); inst.* = .{ .base = .{ .tag = .switchbr, - .ty = Type.initTag(.noreturn), + .ty = Type.initTag(.void), .src = src, }, .target_ptr = target_ptr, .cases = cases, - .@"else" = else_body, }; try block.instructions.append(self.gpa, &inst.base); return &inst.base; diff --git a/src/astgen.zig b/src/astgen.zig index d7a7e55f2b..607a5a32cd 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -1570,16 +1570,33 @@ 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); - const cases = try scope.arena().alloc(zir.Inst.SwitchBr.Case, switch_node.cases_len); - var kw_args: std.meta.fieldInfo(zir.Inst.SwitchBr, "kw_args").field_type = .{}; + // Add the switch instruction here so that it comes before any range checks. + const switch_inst = (try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{ + .target_ptr = target_ptr, + .cases = undefined, // populated below + .items = &[_]*zir.Inst{}, // populated below + }, .{})).castTag(.switchbr).?; + + var items = std.ArrayList(*zir.Inst).init(mod.gpa); + defer items.deinit(); + var cases = std.ArrayList(zir.Inst.SwitchBr.Case).init(mod.gpa); + defer cases.deinit(); // first we gather all the switch items and check else/'_' prongs - var case_index: usize = 0; var else_src: ?usize = null; var underscore_src: ?usize = null; + var range_inst: ?*zir.Inst = null; for (switch_node.cases()) |uncasted_case| { const case = uncasted_case.castTag(.SwitchCase).?; const case_src = tree.token_locs[case.firstToken()].start; @@ -1593,12 +1610,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node return mod.fail(scope, case_src, "multiple else prongs in switch expression", .{}); // TODO notes "previous else prong is here" } - kw_args.special_case = .@"else"; else_src = case_src; - cases[cases.len - 1] = .{ - .items = &[0]*zir.Inst{}, - .body = undefined, // filled below - }; continue; } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) @@ -1607,48 +1619,44 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node return mod.fail(scope, case_src, "multiple '_' prongs in switch expression", .{}); // TODO notes "previous '_' prong is here" } - kw_args.special_case = .underscore; underscore_src = case_src; - cases[cases.len - 1] = .{ - .items = &[0]*zir.Inst{}, - .body = undefined, // filled below - }; continue; } if (else_src) |some_else| { if (underscore_src) |some_underscore| { - return mod.fail(scope, case_src, "else and '_' prong in switch expression", .{}); + return mod.fail(scope, switch_src, "else and '_' prong in switch expression", .{}); // TODO notes "else prong is here" // TODO notes "'_' prong is here" } } - // Regular case, we need to fill `items`. - const items = try block_scope.arena.alloc(*zir.Inst, case.items_len); - for (case.items()) |item, i| { - if (item.castTag(.Range)) |range| { - items[i] = try switchRange(mod, &block_scope.base, range); - if (kw_args.support_range == null) - kw_args.support_range = items[i]; - } else { - items[i] = try expr(mod, &block_scope.base, .none, item); - } + // TODO and not range + if (case.items_len == 1) { + const item = try expr(mod, &item_scope.base, .none, case.items()[0]); + try cases.append(.{ + .item = item, + .body = undefined, // populated below + }); + continue; } - cases[case_index] = .{ - .items = items, - .body = undefined, // filled below - }; - case_index += 1; + return mod.fail(scope, case_src, "TODO switch ranges", .{}); } - // Then we add the switch instruction to finish the block. - _ = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{ - .target_ptr = target_ptr, - .cases = cases, - }, kw_args); + // Actually populate switch instruction values. + if (else_src != null) switch_inst.kw_args.special_prong = .@"else"; + if (underscore_src != null) switch_inst.kw_args.special_prong = .underscore; + 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 = range_inst; + + // Add comptime block containing all prong items first, + _ = try addZIRInstBlock(mod, scope, switch_src, .block_comptime_flat, .{ + .instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items), + }); + // then add block containing the switch. const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{ - .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), + .instructions = undefined, // populated below }); // Most result location types can be forwarded directly; however @@ -1668,39 +1676,64 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node defer case_scope.instructions.deinit(mod.gpa); // And finally we fill generate the bodies of each case. - case_index = 0; + var case_index: usize = 0; + var special_case: ?*ast.Node.SwitchCase = null; for (switch_node.cases()) |uncasted_case| { const case = uncasted_case.castTag(.SwitchCase).?; const case_src = tree.token_locs[case.firstToken()].start; // reset without freeing to reduce allocations. defer case_scope.instructions.items.len = 0; - // What index in positionals.cases should this one be placed at. - // For special cases it will be at the end. - var cur_index = case_index; if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) { - // validated above - cur_index = cases.len - 1; + // validated earlier + special_case = case; + continue; } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_")) { - // validated above - cur_index = cases.len - 1; + // validated earlier + special_case = case; + continue; } - // Generate the body of this case. - const case_body = try expr(mod, &case_scope.base, case_rl, case.expr); + if (case.items_len == 1) { + // Generate the body of this case. + const case_body = try expr(mod, &case_scope.base, case_rl, case.expr); + if (!case_body.tag.isNoReturn()) { + _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.Break, .{ + .block = block, + .operand = case_body, + }, .{}); + } + switch_inst.positionals.cases[case_index].body = .{ + .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), + }; + case_index += 1; + continue; + } + return mod.fail(scope, case_src, "TODO switch ranges", .{}); + } + + // Generate else block or a break last to finish the block. + if (special_case) |case| { + const case_src = tree.token_locs[case.firstToken()].start; + const case_body = try expr(mod, &block_scope.base, case_rl, case.expr); if (!case_body.tag.isNoReturn()) { - _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.Break, .{ + _ = try addZIRInst(mod, &block_scope.base, case_src, zir.Inst.Break, .{ .block = block, .operand = case_body, }, .{}); } - cases[cur_index].body = .{ - .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items), - }; + } else { + _ = try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.BreakVoid, .{ + .block = block, + }, .{}); } + // Set block instructions now that it is finished. + block.positionals.body = .{ + .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), + }; return &block.base; } diff --git a/src/ir.zig b/src/ir.zig index da20387175..a6ee75bd88 100644 --- a/src/ir.zig +++ b/src/ir.zig @@ -467,15 +467,12 @@ pub const Inst = struct { base: Inst, target_ptr: *Inst, cases: []Case, - @"else": ?Body, /// 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]. deaths: [*]*Inst = undefined, - else_index: u32 = 0, - else_deaths: u32 = 0, pub const Case = struct { - items: []Value, + item: Value, body: Body, index: u32 = 0, deaths: u32 = 0, @@ -497,9 +494,6 @@ 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_deaths)[0..self.else_deaths]; - } }; }; diff --git a/src/zir.zig b/src/zir.zig index e4a3402bac..e7ca2ce9c4 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -501,7 +501,7 @@ pub const Inst = struct { .slice, .slice_start, .import, - .switch_range, + .switchbr, => false, .@"break", @@ -513,7 +513,7 @@ pub const Inst = struct { .unreach_nocheck, .@"unreachable", .loop, - .switchbr, + .switch_range, => true, }; } @@ -1005,22 +1005,21 @@ pub const Inst = struct { positionals: struct { target_ptr: *Inst, cases: []Case, + /// List of all individual items and ranges + items: []*Inst, }, kw_args: struct { - /// if not null target must support ranges, (be int) - support_range: ?*Inst = null, - special_case: enum { - /// all of positionals.cases are regular cases + /// Pointer to first range if such exists. + range: ?*Inst = null, + special_prong: enum { none, - /// last case in positionals.cases is an else case @"else", - /// last case in positionals.cases is an underscore case underscore, } = .none, }, pub const Case = struct { - items: []*Inst, + item: *Inst, body: Module.Body, }; }; @@ -1286,7 +1285,7 @@ const Writer = struct { } try stream.writeByteNTimes(' ', self.indent); self.indent += 2; - try self.writeParamToStream(stream, &case.items); + try self.writeParamToStream(stream, &case.item); try stream.writeAll(" => "); try self.writeParamToStream(stream, &case.body); self.indent -= 2; @@ -1716,7 +1715,7 @@ const Parser = struct { while (true) { const cur = try cases.addOne(); skipSpace(self); - cur.items = try self.parseParameterGeneric([]*Inst, body_ctx); + cur.item = try self.parseParameterGeneric(*Inst, body_ctx); skipSpace(self); try requireEatBytes(self, "=>"); cur.body = try self.parseBody(body_ctx); @@ -2549,8 +2548,7 @@ const EmitZIR = struct { }, .switchbr => blk: { const old_inst = inst.castTag(.switchbr).?; - const case_count = old_inst.cases.len + @boolToInt(old_inst.@"else" != null); - const cases = try self.arena.allocator.alloc(Inst.SwitchBr.Case, case_count); + const cases = try self.arena.allocator.alloc(Inst.SwitchBr.Case, old_inst.cases.len); const new_inst = try self.arena.allocator.create(Inst.SwitchBr); new_inst.* = .{ .base = .{ @@ -2560,11 +2558,9 @@ const EmitZIR = struct { .positionals = .{ .target_ptr = try self.resolveInst(new_body, old_inst.target_ptr), .cases = cases, + .items = &[_]*Inst{}, // TODO this should actually be populated }, - .kw_args = .{ - .special_case = if (old_inst.@"else" != null) .@"else" else .none, - .support_range = null, - }, + .kw_args = .{}, }; var body_tmp = std.ArrayList(*Inst).init(self.allocator); @@ -2574,25 +2570,13 @@ const EmitZIR = struct { body_tmp.items.len = 0; try self.emitBody(case.body, inst_table, &body_tmp); - const items = try self.arena.allocator.alloc(*Inst, case.items.len); - for (case.items) |item, j| { - items[j] = (try self.emitTypedValue(inst.src, .{ - .ty = old_inst.target_ptr.ty.elemType(), - .val = item, - })).inst; - } + const item = (try self.emitTypedValue(inst.src, .{ + .ty = old_inst.target_ptr.ty.elemType(), + .val = case.item, + })).inst; cases[i] = .{ - .items = items, - .body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) }, - }; - } - if (old_inst.@"else") |some| { - body_tmp.items.len = 0; - - try self.emitBody(some, inst_table, &body_tmp); - cases[cases.len - 1] = .{ - .items = &[0]*Inst{}, + .item = item, .body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) }, }; } @@ -2846,7 +2830,7 @@ pub fn dumpZir(allocator: *Allocator, kind: []const u8, decl_name: [*:0]const u8 .block_table = std.AutoHashMap(*Inst.Block, []const u8).init(allocator), .loop_table = std.AutoHashMap(*Inst.Loop, []const u8).init(allocator), .arena = std.heap.ArenaAllocator.init(allocator), - .indent = 2, + .indent = 4, .next_instr_index = 0, }; defer write.arena.deinit(); diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 095fc4ede2..c0b5b7b7bc 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -553,10 +553,13 @@ fn analyzeInstBlockFlat(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_c try analyzeBody(mod, &child_block.base, inst.positionals.body); - const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items); - try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); + try parent_block.instructions.appendSlice(mod.gpa, child_block.instructions.items); - return copied_instructions[copied_instructions.len - 1]; + // comptime blocks won't generate any runtime values + if (child_block.instructions.items.len == 0) + return mod.constVoid(scope, inst.base.src); + + return parent_block.instructions.items[parent_block.instructions.items.len - 1]; } fn analyzeInstBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block, is_comptime: bool) InnerError!*Inst { @@ -1235,11 +1238,8 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In // 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 cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, case_count); + const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len); var case_block: Scope.Block = .{ .parent = parent_block, @@ -1251,58 +1251,39 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In }; 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| { + for (inst.positionals.cases[0..inst.positionals.cases.len]) |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); - } + const resolved = try resolveInst(mod, scope, case.item); + const casted = try mod.coerce(scope, target.ty, resolved); + const item = try mod.resolveConstValue(scope, casted); try analyzeBody(mod, &case_block.base, case.body); cases[i] = .{ - .items = try parent_block.arena.dupe(Value, items_tmp.items), + .item = item, .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) }, }; } - - const else_body = if (inst.kw_args.special_case != .none) blk: { - case_block.instructions.items.len = 0; - - try analyzeBody(mod, &case_block.base, inst.positionals.cases[case_count].body); - break: blk Body{ - .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items), - }; - } else null; - return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases, else_body); + return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases); } fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void { // validate usage of '_' prongs - if (inst.kw_args.special_case == .underscore and target.ty.zigTypeTag() != .Enum) { + if (inst.kw_args.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) { return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); // TODO notes "'_' prong here" inst.positionals.cases[last].src } // check that target type supports ranges - if (inst.kw_args.support_range) |some| { + if (inst.kw_args.range) |range_inst| { switch (target.ty.zigTypeTag()) { .Int, .ComptimeInt, .Float, .ComptimeFloat => {}, else => { return mod.fail(scope, target.src, "ranges not allowed when switching on type {}", .{target.ty}); - // TODO notes "range used here" some.src + // TODO notes "range used here" range_inst.src }, } } @@ -1317,46 +1298,42 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw .Bool => { var true_count: u8 = 0; var false_count: u8 = 0; - for (inst.positionals.cases) |case| { - for (case.items) |item| { - const resolved = try resolveInst(mod, scope, item); - const casted = try mod.coerce(scope, Type.initTag(.bool), resolved); - if ((try mod.resolveConstValue(scope, casted)).toBool()) { - true_count += 1; - } else { - false_count += 1; - } + for (inst.positionals.items) |item| { + const resolved = try resolveInst(mod, scope, item); + const casted = try mod.coerce(scope, Type.initTag(.bool), resolved); + if ((try mod.resolveConstValue(scope, casted)).toBool()) { + true_count += 1; + } else { + false_count += 1; + } - if (true_count > 1 or false_count > 1) { - return mod.fail(scope, item.src, "duplicate switch value", .{}); - } + if (true_count > 1 or false_count > 1) { + return mod.fail(scope, item.src, "duplicate switch value", .{}); } } - if ((true_count == 0 or false_count == 0) and inst.kw_args.special_case != .@"else") { + if ((true_count == 0 or false_count == 0) 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_case == .@"else") { + if ((true_count == 1 and false_count == 1) and inst.kw_args.special_prong == .@"else") { return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); } }, .EnumLiteral, .Void, .Fn, .Pointer, .Type => { - if (inst.kw_args.special_case != .@"else") { + if (inst.kw_args.special_prong != .@"else") { return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty}); } var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa); defer seen_values.deinit(); - for (inst.positionals.cases) |case| { - for (case.items) |item| { - const resolved = try resolveInst(mod, scope, item); - const casted = try mod.coerce(scope, target.ty, resolved); - const val = try mod.resolveConstValue(scope, casted); + for (inst.positionals.items) |item| { + const resolved = try resolveInst(mod, scope, item); + const casted = try mod.coerce(scope, target.ty, resolved); + const val = try mod.resolveConstValue(scope, casted); - if (try seen_values.fetchPut(val, item.src)) |prev| { - return mod.fail(scope, item.src, "duplicate switch value", .{}); - // TODO notes "previous value here" prev.value - } + if (try seen_values.fetchPut(val, item.src)) |prev| { + return mod.fail(scope, item.src, "duplicate switch value", .{}); + // TODO notes "previous value here" prev.value } } }, |
