| ... | @@ -229,10 +229,6 @@ pub fn analyzeBody( | ... | @@ -229,10 +229,6 @@ pub fn analyzeBody( |
| 229 | .typeof => try sema.zirTypeof(block, inst), | 229 | .typeof => try sema.zirTypeof(block, inst), |
| 230 | .typeof_peer => try sema.zirTypeofPeer(block, inst), | 230 | .typeof_peer => try sema.zirTypeofPeer(block, inst), |
| 231 | .xor => try sema.zirBitwise(block, inst, .xor), | 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 | // Instructions that we know to *always* be noreturn based solely on their tag. | 233 | // Instructions that we know to *always* be noreturn based solely on their tag. |
| 238 | // These functions match the return type of analyzeBody so that we can | 234 | // These functions match the return type of analyzeBody so that we can |
| ... | @@ -246,6 +242,18 @@ pub fn analyzeBody( | ... | @@ -246,6 +242,18 @@ pub fn analyzeBody( |
| 246 | .ret_tok => return sema.zirRetTok(block, inst, false), | 242 | .ret_tok => return sema.zirRetTok(block, inst, false), |
| 247 | .@"unreachable" => return sema.zirUnreachable(block, inst), | 243 | .@"unreachable" => return sema.zirUnreachable(block, inst), |
| 248 | .repeat => return sema.zirRepeat(block, inst), | 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 | // Instructions that we know can *never* be noreturn based solely on | 258 | // Instructions that we know can *never* be noreturn based solely on |
| 251 | // their tag. We avoid needlessly checking if they are noreturn and | 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,54 +2205,82 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne |
| 2197 | return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src); | 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 | const tracy = trace(@src()); | 2217 | const tracy = trace(@src()); |
| 2202 | defer tracy.end(); | 2218 | defer tracy.end(); |
| 2203 | | 2219 | |
| 2204 | const src: LazySrcLoc = .todo; | 2220 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 2205 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | 2221 | const src = inst_data.src(); |
| 2206 | const start = try sema.resolveInst(bin_inst.lhs); | 2222 | const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node }; |
| 2207 | const end = try sema.resolveInst(bin_inst.rhs); | 2223 | const extra = sema.code.extraData(zir.Inst.SwitchBr, inst_data.payload_index); |
| 2208 | | 2224 | |
| 2209 | switch (start.ty.zigTypeTag()) { | 2225 | const operand_ptr = try sema.resolveInst(extra.data.operand); |
| 2210 | .Int, .ComptimeInt => {}, | 2226 | const operand = if (is_ref) |
| 2211 | else => return sema.mod.constVoid(sema.arena, .unneeded), | 2227 | try sema.analyzeLoad(block, src, operand_ptr, operand_src) |
| 2212 | } | 2228 | else |
| 2213 | switch (end.ty.zigTypeTag()) { | 2229 | operand_ptr; |
| 2214 | .Int, .ComptimeInt => {}, | 2230 | |
| 2215 | else => return sema.mod.constVoid(sema.arena, .unneeded), | 2231 | return sema.analyzeSwitch(block, operand, extra.end, else_prong, extra.data.cases_len, 0, 0); |
| 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); | | |
| 2224 | } | 2232 | } |
| 2225 | | 2233 | |
| 2226 | fn zirSwitchBr( | 2234 | fn zirSwitchBrRange( |
| 2227 | sema: *Sema, | 2235 | sema: *Sema, |
| 2228 | parent_block: *Scope.Block, | 2236 | block: *Scope.Block, |
| 2229 | inst: zir.Inst.Index, | 2237 | inst: zir.Inst.Index, |
| 2230 | ref: bool, | 2238 | is_ref: bool, |
| 2231 | ) InnerError!zir.Inst.Ref { | 2239 | else_prong: ElseProng, |
| | 2240 | ) InnerError!zir.Inst.Index { |
| 2232 | const tracy = trace(@src()); | 2241 | const tracy = trace(@src()); |
| 2233 | defer tracy.end(); | 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); | 2249 | const operand_ptr = try sema.resolveInst(extra.data.operand); |
| 2238 | const target = if (ref) | 2250 | const operand = if (is_ref) |
| 2239 | try sema.analyzeLoad(parent_block, inst.base.src, target_ptr, inst.positionals.target.src) | 2251 | try sema.analyzeLoad(block, src, operand_ptr, operand_src) |
| 2240 | else | 2252 | else |
| 2241 | target_ptr; | 2253 | operand_ptr; |
| 2242 | try sema.validateSwitch(parent_block, target, inst); | 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 | for (inst.positionals.cases) |case| { | 2281 | for (inst.positionals.cases) |case| { |
| 2246 | const resolved = try sema.resolveInst(case.item); | 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 | const item = try sema.resolveConstValue(parent_block, case_src, casted); | 2284 | const item = try sema.resolveConstValue(parent_block, case_src, casted); |
| 2249 | | 2285 | |
| 2250 | if (target_val.eql(item)) { | 2286 | if (target_val.eql(item)) { |
| ... | @@ -2280,7 +2316,7 @@ fn zirSwitchBr( | ... | @@ -2280,7 +2316,7 @@ fn zirSwitchBr( |
| 2280 | case_block.instructions.items.len = 0; | 2316 | case_block.instructions.items.len = 0; |
| 2281 | | 2317 | |
| 2282 | const resolved = try sema.resolveInst(case.item); | 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 | const item = try sema.resolveConstValue(parent_block, case_src, casted); | 2320 | const item = try sema.resolveConstValue(parent_block, case_src, casted); |
| 2285 | | 2321 | |
| 2286 | _ = try sema.analyzeBody(&case_block, case.body); | 2322 | _ = try sema.analyzeBody(&case_block, case.body); |
| ... | @@ -2298,29 +2334,29 @@ fn zirSwitchBr( | ... | @@ -2298,29 +2334,29 @@ fn zirSwitchBr( |
| 2298 | .instructions = try sema.arena.dupe(*Inst, case_block.instructions.items), | 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 | // validate usage of '_' prongs | 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 | return sema.mod.fail(&block.base, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); | 2343 | return sema.mod.fail(&block.base, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); |
| 2308 | // TODO notes "'_' prong here" inst.positionals.cases[last].src | 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 | if (inst.positionals.range) |range_inst| { | 2348 | if (inst.positionals.range) |range_inst| { |
| 2313 | switch (target.ty.zigTypeTag()) { | 2349 | switch (operand.ty.zigTypeTag()) { |
| 2314 | .Int, .ComptimeInt => {}, | 2350 | .Int, .ComptimeInt => {}, |
| 2315 | else => { | 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 | // TODO notes "range used here" range_inst.src | 2353 | // TODO notes "range used here" range_inst.src |
| 2318 | }, | 2354 | }, |
| 2319 | } | 2355 | } |
| 2320 | } | 2356 | } |
| 2321 | | 2357 | |
| 2322 | // validate for duplicate items/missing else prong | 2358 | // validate for duplicate items/missing else prong |
| 2323 | switch (target.ty.zigTypeTag()) { | 2359 | switch (operand.ty.zigTypeTag()) { |
| 2324 | .Enum => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Enum", .{}), | 2360 | .Enum => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Enum", .{}), |
| 2325 | .ErrorSet => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .ErrorSet", .{}), | 2361 | .ErrorSet => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .ErrorSet", .{}), |
| 2326 | .Union => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Union", .{}), | 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,9 +2367,9 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins |
| 2331 | for (inst.positionals.items) |item| { | 2367 | for (inst.positionals.items) |item| { |
| 2332 | const maybe_src = if (item.castTag(.switch_range)) |range| blk: { | 2368 | const maybe_src = if (item.castTag(.switch_range)) |range| blk: { |
| 2333 | const start_resolved = try sema.resolveInst(range.positionals.lhs); | 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 | const end_resolved = try sema.resolveInst(range.positionals.rhs); | 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 | break :blk try range_set.add( | 2374 | break :blk try range_set.add( |
| 2339 | try sema.resolveConstValue(block, range_start_src, start_casted), | 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,7 +2378,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins |
| 2342 | ); | 2378 | ); |
| 2343 | } else blk: { | 2379 | } else blk: { |
| 2344 | const resolved = try sema.resolveInst(item); | 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 | const value = try sema.resolveConstValue(block, item_src, casted); | 2382 | const value = try sema.resolveConstValue(block, item_src, casted); |
| 2347 | break :blk try range_set.add(value, value, item.src); | 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,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 | var arena = std.heap.ArenaAllocator.init(sema.gpa); | 2393 | var arena = std.heap.ArenaAllocator.init(sema.gpa); |
| 2358 | defer arena.deinit(); | 2394 | defer arena.deinit(); |
| 2359 | | 2395 | |
| 2360 | const start = try target.ty.minInt(&arena, mod.getTarget()); | 2396 | const start = try operand.ty.minInt(&arena, mod.getTarget()); |
| 2361 | const end = try target.ty.maxInt(&arena, mod.getTarget()); | 2397 | const end = try operand.ty.maxInt(&arena, mod.getTarget()); |
| 2362 | if (try range_set.spans(start, end)) { | 2398 | if (try range_set.spans(start, end)) { |
| 2363 | if (inst.positionals.special_prong == .@"else") { | 2399 | if (inst.positionals.special_prong == .@"else") { |
| 2364 | return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{}); | 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,7 +2432,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins |
| 2396 | }, | 2432 | }, |
| 2397 | .EnumLiteral, .Void, .Fn, .Pointer, .Type => { | 2433 | .EnumLiteral, .Void, .Fn, .Pointer, .Type => { |
| 2398 | if (inst.positionals.special_prong != .@"else") { | 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 | var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(sema.gpa); | 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,7 +2440,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins |
| 2404 | | 2440 | |
| 2405 | for (inst.positionals.items) |item| { | 2441 | for (inst.positionals.items) |item| { |
| 2406 | const resolved = try sema.resolveInst(item); | 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 | const val = try sema.resolveConstValue(block, item_src, casted); | 2444 | const val = try sema.resolveConstValue(block, item_src, casted); |
| 2409 | | 2445 | |
| 2410 | if (try seen_values.fetchPut(val, item.src)) |prev| { | 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,7 +2465,7 @@ fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Ins |
| 2429 | .ComptimeFloat, | 2465 | .ComptimeFloat, |
| 2430 | .Float, | 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 | } |