| ... | ... | @@ -229,10 +229,6 @@ pub fn analyzeBody( |
| 229 | 229 | .typeof => try sema.zirTypeof(block, inst), |
| 230 | 230 | .typeof_peer => try sema.zirTypeofPeer(block, inst), |
| 231 | 231 | .xor => try sema.zirBitwise(block, inst, .xor), |
| 232 | | // TODO |
| 233 | | //.switchbr => try sema.zirSwitchBr(block, inst, false), |
| 234 | | //.switchbr_ref => try sema.zirSwitchBr(block, inst, true), |
| 235 | | //.switch_range => try sema.zirSwitchRange(block, inst), |
| 236 | 232 | |
| 237 | 233 | // Instructions that we know to *always* be noreturn based solely on their tag. |
| 238 | 234 | // These functions match the return type of analyzeBody so that we can |
| ... | ... | @@ -246,6 +242,18 @@ pub fn analyzeBody( |
| 246 | 242 | .ret_tok => return sema.zirRetTok(block, inst, false), |
| 247 | 243 | .@"unreachable" => return sema.zirUnreachable(block, inst), |
| 248 | 244 | .repeat => return sema.zirRepeat(block, inst), |
| 245 | .switch_br => return sema.zirSwitchBr(block, inst, false, .full), |
| 246 | .switch_br_range => return sema.zirSwitchBrRange(block, inst, false, .full), |
| 247 | .switch_br_else => return sema.zirSwitchBr(block, inst, false, .@"else"), |
| 248 | .switch_br_else_range => return sema.zirSwitchBrRange(block, inst, false, .@"else"), |
| 249 | .switch_br_underscore => return sema.zirSwitchBr(block, inst, false, .under), |
| 250 | .switch_br_underscore_range => return sema.zirSwitchBrRange(block, inst, false, .under), |
| 251 | .switch_br_ref => return sema.zirSwitchBr(block, inst, true, .full), |
| 252 | .switch_br_ref_range => return sema.zirSwitchBrRange(block, inst, true, .full), |
| 253 | .switch_br_ref_else => return sema.zirSwitchBr(block, inst, true, .@"else"), |
| 254 | .switch_br_ref_else_range => return sema.zirSwitchBrRange(block, inst, true, .@"else"), |
| 255 | .switch_br_ref_underscore => return sema.zirSwitchBr(block, inst, true, .under), |
| 256 | .switch_br_ref_underscore_range => return sema.zirSwitchBrRange(block, inst, true, .under), |
| 249 | 257 | |
| 250 | 258 | // Instructions that we know can *never* be noreturn based solely on |
| 251 | 259 | // their tag. We avoid needlessly checking if they are noreturn and |
| ... | ... | @@ -2197,54 +2205,82 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne |
| 2197 | 2205 | return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src); |
| 2198 | 2206 | } |
| 2199 | 2207 | |
| 2200 | | fn zirSwitchRange(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { |
| 2208 | const ElseProng = enum { full, @"else", under }; |
| 2209 | |
| 2210 | fn zirSwitchBr( |
| 2211 | sema: *Sema, |
| 2212 | block: *Scope.Block, |
| 2213 | inst: zir.Inst.Index, |
| 2214 | is_ref: bool, |
| 2215 | else_prong: ElseProng, |
| 2216 | ) InnerError!zir.Inst.Index { |
| 2201 | 2217 | const tracy = trace(@src()); |
| 2202 | 2218 | defer tracy.end(); |
| 2203 | 2219 | |
| 2204 | | const src: LazySrcLoc = .todo; |
| 2205 | | const bin_inst = sema.code.instructions.items(.data)[inst].bin; |
| 2206 | | const start = try sema.resolveInst(bin_inst.lhs); |
| 2207 | | const end = try sema.resolveInst(bin_inst.rhs); |
| 2220 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 2221 | const src = inst_data.src(); |
| 2222 | const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node }; |
| 2223 | const extra = sema.code.extraData(zir.Inst.SwitchBr, inst_data.payload_index); |
| 2208 | 2224 | |
| 2209 | | switch (start.ty.zigTypeTag()) { |
| 2210 | | .Int, .ComptimeInt => {}, |
| 2211 | | else => return sema.mod.constVoid(sema.arena, .unneeded), |
| 2212 | | } |
| 2213 | | switch (end.ty.zigTypeTag()) { |
| 2214 | | .Int, .ComptimeInt => {}, |
| 2215 | | else => return sema.mod.constVoid(sema.arena, .unneeded), |
| 2216 | | } |
| 2217 | | // .switch_range must be inside a comptime scope |
| 2218 | | const start_val = start.value().?; |
| 2219 | | const end_val = end.value().?; |
| 2220 | | if (start_val.compare(.gte, end_val)) { |
| 2221 | | return sema.mod.fail(&block.base, src, "range start value must be smaller than the end value", .{}); |
| 2222 | | } |
| 2223 | | return sema.mod.constVoid(sema.arena, .unneeded); |
| 2225 | const operand_ptr = try sema.resolveInst(extra.data.operand); |
| 2226 | const operand = if (is_ref) |
| 2227 | try sema.analyzeLoad(block, src, operand_ptr, operand_src) |
| 2228 | else |
| 2229 | operand_ptr; |
| 2230 | |
| 2231 | return sema.analyzeSwitch(block, operand, extra.end, else_prong, extra.data.cases_len, 0, 0); |
| 2224 | 2232 | } |
| 2225 | 2233 | |
| 2226 | | fn zirSwitchBr( |
| 2234 | fn zirSwitchBrRange( |
| 2227 | 2235 | sema: *Sema, |
| 2228 | | parent_block: *Scope.Block, |
| 2236 | block: *Scope.Block, |
| 2229 | 2237 | inst: zir.Inst.Index, |
| 2230 | | ref: bool, |
| 2231 | | ) InnerError!zir.Inst.Ref { |
| 2238 | is_ref: bool, |
| 2239 | else_prong: ElseProng, |
| 2240 | ) InnerError!zir.Inst.Index { |
| 2232 | 2241 | const tracy = trace(@src()); |
| 2233 | 2242 | defer tracy.end(); |
| 2234 | 2243 | |
| 2235 | | if (true) @panic("TODO rework with zir-memory-layout in mind"); |
| 2244 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 2245 | const src = inst_data.src(); |
| 2246 | const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node }; |
| 2247 | const extra = sema.code.extraData(zir.Inst.SwitchBrRange, inst_data.payload_index); |
| 2236 | 2248 | |
| 2237 | | const target_ptr = try sema.resolveInst(inst.positionals.target); |
| 2238 | | const target = if (ref) |
| 2239 | | try sema.analyzeLoad(parent_block, inst.base.src, target_ptr, inst.positionals.target.src) |
| 2249 | const operand_ptr = try sema.resolveInst(extra.data.operand); |
| 2250 | const operand = if (is_ref) |
| 2251 | try sema.analyzeLoad(block, src, operand_ptr, operand_src) |
| 2240 | 2252 | else |
| 2241 | | target_ptr; |
| 2242 | | try sema.validateSwitch(parent_block, target, inst); |
| 2253 | operand_ptr; |
| 2254 | |
| 2255 | return sema.analyzeSwitch( |
| 2256 | block, |
| 2257 | operand, |
| 2258 | extra.end, |
| 2259 | else_prong, |
| 2260 | extra.data.scalar_cases_len, |
| 2261 | extra.data.multi_cases_len, |
| 2262 | extra.data.range_cases_len, |
| 2263 | ); |
| 2264 | } |
| 2243 | 2265 | |
| 2244 | | if (try sema.resolveDefinedValue(parent_block, inst.base.src, target)) |target_val| { |
| 2266 | fn analyzeSwitch( |
| 2267 | sema: *Sema, |
| 2268 | parent_block: *Scope.Block, |
| 2269 | operand: *Inst, |
| 2270 | extra_end: usize, |
| 2271 | else_prong: ElseProng, |
| 2272 | scalar_cases_len: usize, |
| 2273 | multi_cases_len: usize, |
| 2274 | range_cases_len: usize, |
| 2275 | ) InnerError!zir.Inst.Index { |
| 2276 | if (true) @panic("TODO rework for zir-memory-layout branch"); |
| 2277 | |
| 2278 | try sema.validateSwitch(parent_block, operand, inst); |
| 2279 | |
| 2280 | if (try sema.resolveDefinedValue(parent_block, inst.base.src, operand)) |target_val| { |
| 2245 | 2281 | for (inst.positionals.cases) |case| { |
| 2246 | 2282 | const resolved = try sema.resolveInst(case.item); |
| 2247 | | const casted = try sema.coerce(block, target.ty, resolved, resolved_src); |
| 2283 | const casted = try sema.coerce(block, operand.ty, resolved, resolved_src); |
| 2248 | 2284 | const item = try sema.resolveConstValue(parent_block, case_src, casted); |
| 2249 | 2285 | |
| 2250 | 2286 | if (target_val.eql(item)) { |
| ... | ... | @@ -2280,7 +2316,7 @@ fn zirSwitchBr( |
| 2280 | 2316 | case_block.instructions.items.len = 0; |
| 2281 | 2317 | |
| 2282 | 2318 | const resolved = try sema.resolveInst(case.item); |
| 2283 | | const casted = try sema.coerce(block, target.ty, resolved, resolved_src); |
| 2319 | const casted = try sema.coerce(block, operand.ty, resolved, resolved_src); |
| 2284 | 2320 | const item = try sema.resolveConstValue(parent_block, case_src, casted); |
| 2285 | 2321 | |
| 2286 | 2322 | _ = try sema.analyzeBody(&case_block, case.body); |
| ... | ... | @@ -2298,29 +2334,29 @@ fn zirSwitchBr( |
| 2298 | 2334 | .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items), |
| 2299 | 2335 | }; |
| 2300 | 2336 | |
| 2301 | | return mod.addSwitchBr(parent_block, inst.base.src, target, cases, else_body); |
| 2337 | return mod.addSwitchBr(parent_block, inst.base.src, operand, cases, else_body); |
| 2302 | 2338 | } |
| 2303 | 2339 | |
| 2304 | | fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Inst.Index) InnerError!void { |
| 2340 | fn validateSwitch(sema: *Sema, block: *Scope.Block, operand: *Inst, inst: zir.Inst.Index) InnerError!void { |
| 2305 | 2341 | // validate usage of '_' prongs |
| 2306 | | if (inst.positionals.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) { |
| 2342 | if (inst.positionals.special_prong == .underscore and operand.ty.zigTypeTag() != .Enum) { |
| 2307 | 2343 | return sema.mod.fail(&block.base, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); |
| 2308 | 2344 | // TODO notes "'_' prong here" inst.positionals.cases[last].src |
| 2309 | 2345 | } |
| 2310 | 2346 | |
| 2311 | | // check that target type supports ranges |
| 2347 | // check that operand type supports ranges |
| 2312 | 2348 | if (inst.positionals.range) |range_inst| { |
| 2313 | | switch (target.ty.zigTypeTag()) { |
| 2349 | switch (operand.ty.zigTypeTag()) { |
| 2314 | 2350 | .Int, .ComptimeInt => {}, |
| 2315 | 2351 | else => { |
| 2316 | | return sema.mod.fail(&block.base, target.src, "ranges not allowed when switching on type {}", .{target.ty}); |
| 2352 | return sema.mod.fail(&block.base, operand.src, "ranges not allowed when switching on type {}", .{operand.ty}); |
| 2317 | 2353 | // TODO notes "range used here" range_inst.src |
| 2318 | 2354 | }, |
| 2319 | 2355 | } |
| 2320 | 2356 | } |
| 2321 | 2357 | |
| 2322 | 2358 | // validate for duplicate items/missing else prong |
| 2323 | | switch (target.ty.zigTypeTag()) { |
| 2359 | switch (operand.ty.zigTypeTag()) { |
| 2324 | 2360 | .Enum => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Enum", .{}), |
| 2325 | 2361 | .ErrorSet => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .ErrorSet", .{}), |
| 2326 | 2362 | .Union => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Union", .{}), |
| ... | ... | @@ -2331,9 +2367,9 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins |
| 2331 | 2367 | for (inst.positionals.items) |item| { |
| 2332 | 2368 | const maybe_src = if (item.castTag(.switch_range)) |range| blk: { |
| 2333 | 2369 | const start_resolved = try sema.resolveInst(range.positionals.lhs); |
| 2334 | | const start_casted = try sema.coerce(block, target.ty, start_resolved); |
| 2370 | const start_casted = try sema.coerce(block, operand.ty, start_resolved); |
| 2335 | 2371 | const end_resolved = try sema.resolveInst(range.positionals.rhs); |
| 2336 | | const end_casted = try sema.coerce(block, target.ty, end_resolved); |
| 2372 | const end_casted = try sema.coerce(block, operand.ty, end_resolved); |
| 2337 | 2373 | |
| 2338 | 2374 | break :blk try range_set.add( |
| 2339 | 2375 | try sema.resolveConstValue(block, range_start_src, start_casted), |
| ... | ... | @@ -2342,7 +2378,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins |
| 2342 | 2378 | ); |
| 2343 | 2379 | } else blk: { |
| 2344 | 2380 | const resolved = try sema.resolveInst(item); |
| 2345 | | const casted = try sema.coerce(block, target.ty, resolved); |
| 2381 | const casted = try sema.coerce(block, operand.ty, resolved); |
| 2346 | 2382 | const value = try sema.resolveConstValue(block, item_src, casted); |
| 2347 | 2383 | break :blk try range_set.add(value, value, item.src); |
| 2348 | 2384 | }; |
| ... | ... | @@ -2353,12 +2389,12 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins |
| 2353 | 2389 | } |
| 2354 | 2390 | } |
| 2355 | 2391 | |
| 2356 | | if (target.ty.zigTypeTag() == .Int) { |
| 2392 | if (operand.ty.zigTypeTag() == .Int) { |
| 2357 | 2393 | var arena = std.heap.ArenaAllocator.init(sema.gpa); |
| 2358 | 2394 | defer arena.deinit(); |
| 2359 | 2395 | |
| 2360 | | const start = try target.ty.minInt(&arena, mod.getTarget()); |
| 2361 | | const end = try target.ty.maxInt(&arena, mod.getTarget()); |
| 2396 | const start = try operand.ty.minInt(&arena, mod.getTarget()); |
| 2397 | const end = try operand.ty.maxInt(&arena, mod.getTarget()); |
| 2362 | 2398 | if (try range_set.spans(start, end)) { |
| 2363 | 2399 | if (inst.positionals.special_prong == .@"else") { |
| 2364 | 2400 | return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{}); |
| ... | ... | @@ -2396,7 +2432,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins |
| 2396 | 2432 | }, |
| 2397 | 2433 | .EnumLiteral, .Void, .Fn, .Pointer, .Type => { |
| 2398 | 2434 | if (inst.positionals.special_prong != .@"else") { |
| 2399 | | return sema.mod.fail(&block.base, inst.base.src, "else prong required when switching on type '{}'", .{target.ty}); |
| 2435 | return sema.mod.fail(&block.base, inst.base.src, "else prong required when switching on type '{}'", .{operand.ty}); |
| 2400 | 2436 | } |
| 2401 | 2437 | |
| 2402 | 2438 | var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(sema.gpa); |
| ... | ... | @@ -2404,7 +2440,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins |
| 2404 | 2440 | |
| 2405 | 2441 | for (inst.positionals.items) |item| { |
| 2406 | 2442 | const resolved = try sema.resolveInst(item); |
| 2407 | | const casted = try sema.coerce(block, target.ty, resolved); |
| 2443 | const casted = try sema.coerce(block, operand.ty, resolved); |
| 2408 | 2444 | const val = try sema.resolveConstValue(block, item_src, casted); |
| 2409 | 2445 | |
| 2410 | 2446 | if (try seen_values.fetchPut(val, item.src)) |prev| { |
| ... | ... | @@ -2429,7 +2465,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins |
| 2429 | 2465 | .ComptimeFloat, |
| 2430 | 2466 | .Float, |
| 2431 | 2467 | => { |
| 2432 | | return sema.mod.fail(&block.base, target.src, "invalid switch target type '{}'", .{target.ty}); |
| 2468 | return sema.mod.fail(&block.base, operand.src, "invalid switch operand type '{}'", .{operand.ty}); |
| 2433 | 2469 | }, |
| 2434 | 2470 | } |
| 2435 | 2471 | } |