| ... | ... | @@ -135,7 +135,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 135 | 135 | .slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?), |
| 136 | 136 | .slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?), |
| 137 | 137 | .import => return analyzeInstImport(mod, scope, old_inst.castTag(.import).?), |
| 138 | | .@"switch", .switch_range => @panic("TODO switch sema"), |
| 138 | .@"switch" => return analyzeInstSwitch(mod, scope, old_inst.castTag(.@"switch").?), |
| 139 | .switch_range => return analyzeInstSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), |
| 139 | 140 | } |
| 140 | 141 | } |
| 141 | 142 | |
| ... | ... | @@ -1205,6 +1206,126 @@ fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn |
| 1205 | 1206 | return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null); |
| 1206 | 1207 | } |
| 1207 | 1208 | |
| 1209 | fn analyzeInstSwitchRange(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst { |
| 1210 | const start = try resolveInst(mod, scope, inst.positionals.lhs); |
| 1211 | const end = try resolveInst(mod, scope, inst.positionals.rhs); |
| 1212 | |
| 1213 | switch (start.ty.zigTypeTag()) { |
| 1214 | .Int, .ComptimeInt, .Float, .ComptimeFloat => {}, |
| 1215 | else => return mod.constVoid(scope, inst.base.src), |
| 1216 | } |
| 1217 | switch (end.ty.zigTypeTag()) { |
| 1218 | .Int, .ComptimeInt, .Float, .ComptimeFloat => {}, |
| 1219 | else => return mod.constVoid(scope, inst.base.src), |
| 1220 | } |
| 1221 | if (start.value()) |start_val| { |
| 1222 | if (end.value()) |end_val| { |
| 1223 | if (start_val.compare(.gte, end_val)) { |
| 1224 | return mod.fail(scope, inst.base.src, "range start value is greater than the end value", .{}); |
| 1225 | } |
| 1226 | } |
| 1227 | } |
| 1228 | return mod.constVoid(scope, inst.base.src); |
| 1229 | } |
| 1230 | |
| 1231 | fn analyzeInstSwitch(mod: *Module, scope: *Scope, inst: *zir.Inst.Switch) InnerError!*Inst { |
| 1232 | const target_ptr = try resolveInst(mod, scope, inst.positionals.target_ptr); |
| 1233 | const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src); |
| 1234 | try validateSwitch(mod, scope, target, inst); |
| 1235 | |
| 1236 | return mod.fail(scope, inst.base.src, "TODO analyzeInstSwitch", .{}); |
| 1237 | } |
| 1238 | |
| 1239 | fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Switch) InnerError!void { |
| 1240 | // validate usage of '_' prongs |
| 1241 | if (inst.kw_args.special_case == .underscore and target.ty.zigTypeTag() != .Enum) { |
| 1242 | return mod.fail(scope, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); |
| 1243 | // TODO notes "'_' prong here" inst.positionals.cases[last].src |
| 1244 | } |
| 1245 | |
| 1246 | // check that target type supports ranges |
| 1247 | if (inst.kw_args.support_range) |some| { |
| 1248 | switch (target.ty.zigTypeTag()) { |
| 1249 | .Int, .ComptimeInt, .Float, .ComptimeFloat => {}, |
| 1250 | else => { |
| 1251 | return mod.fail(scope, target.src, "ranges not allowed when switching on type {}", .{target.ty}); |
| 1252 | // TODO notes "range used here" some.src |
| 1253 | }, |
| 1254 | } |
| 1255 | } |
| 1256 | |
| 1257 | // validate for duplicate items/missing else prong |
| 1258 | switch (target.ty.zigTypeTag()) { |
| 1259 | .Int, .ComptimeInt => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Int, .ComptimeInt", .{}), |
| 1260 | .Float, .ComptimeFloat => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Float, .ComptimeFloat", .{}), |
| 1261 | .Enum => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Enum", .{}), |
| 1262 | .ErrorSet => return mod.fail(scope, inst.base.src, "TODO validateSwitch .ErrorSet", .{}), |
| 1263 | .Union => return mod.fail(scope, inst.base.src, "TODO validateSwitch .Union", .{}), |
| 1264 | .Bool => { |
| 1265 | var true_count: u8 = 0; |
| 1266 | var false_count: u8 = 0; |
| 1267 | for (inst.positionals.cases) |case| { |
| 1268 | for (case.items) |item| { |
| 1269 | const resolved = try resolveInst(mod, scope, item); |
| 1270 | const casted = try mod.coerce(scope, Type.initTag(.bool), resolved); |
| 1271 | if ((try mod.resolveConstValue(scope, casted)).toBool()) { |
| 1272 | true_count += 1; |
| 1273 | } else { |
| 1274 | false_count += 1; |
| 1275 | } |
| 1276 | |
| 1277 | if (true_count > 1 or false_count > 1) { |
| 1278 | return mod.fail(scope, item.src, "duplicate switch value", .{}); |
| 1279 | } |
| 1280 | } |
| 1281 | } |
| 1282 | if ((true_count == 0 or false_count == 0) and inst.kw_args.special_case != .@"else") { |
| 1283 | return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{}); |
| 1284 | } |
| 1285 | if ((true_count == 1 and false_count == 1) and inst.kw_args.special_case == .@"else") { |
| 1286 | return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{}); |
| 1287 | } |
| 1288 | }, |
| 1289 | .EnumLiteral, .Void, .Fn, .Pointer, .Type => { |
| 1290 | if (inst.kw_args.special_case != .@"else") { |
| 1291 | return mod.fail(scope, inst.base.src, "else prong required when switching on type '{}'", .{target.ty}); |
| 1292 | } |
| 1293 | |
| 1294 | var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa); |
| 1295 | defer seen_values.deinit(); |
| 1296 | |
| 1297 | for (inst.positionals.cases) |case| { |
| 1298 | for (case.items) |item| { |
| 1299 | const resolved = try resolveInst(mod, scope, item); |
| 1300 | const casted = try mod.coerce(scope, target.ty, resolved); |
| 1301 | const val = try mod.resolveConstValue(scope, casted); |
| 1302 | |
| 1303 | if (try seen_values.fetchPut(val, item.src)) |prev| { |
| 1304 | return mod.fail(scope, item.src, "duplicate switch value", .{}); |
| 1305 | // TODO notes "previous value here" prev.value |
| 1306 | } |
| 1307 | } |
| 1308 | } |
| 1309 | }, |
| 1310 | |
| 1311 | .ErrorUnion, |
| 1312 | .NoReturn, |
| 1313 | .Array, |
| 1314 | .Struct, |
| 1315 | .Undefined, |
| 1316 | .Null, |
| 1317 | .Optional, |
| 1318 | .BoundFn, |
| 1319 | .Opaque, |
| 1320 | .Vector, |
| 1321 | .Frame, |
| 1322 | .AnyFrame, |
| 1323 | => { |
| 1324 | return mod.fail(scope, target.src, "invalid switch target type '{}'", .{target.ty}); |
| 1325 | }, |
| 1326 | } |
| 1327 | } |
| 1328 | |
| 1208 | 1329 | fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { |
| 1209 | 1330 | const operand = try resolveConstString(mod, scope, inst.positionals.operand); |
| 1210 | 1331 | |