| ... | @@ -135,7 +135,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! | ... | @@ -135,7 +135,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 135 | .slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?), | 135 | .slice => return analyzeInstSlice(mod, scope, old_inst.castTag(.slice).?), |
| 136 | .slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?), | 136 | .slice_start => return analyzeInstSliceStart(mod, scope, old_inst.castTag(.slice_start).?), |
| 137 | .import => return analyzeInstImport(mod, scope, old_inst.castTag(.import).?), | 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,6 +1206,126 @@ fn analyzeInstSliceStart(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn |
| 1205 | return mod.analyzeSlice(scope, inst.base.src, array_ptr, start, null, null); | 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 | fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | 1329 | fn analyzeInstImport(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { |
| 1209 | const operand = try resolveConstString(mod, scope, inst.positionals.operand); | 1330 | const operand = try resolveConstString(mod, scope, inst.positionals.operand); |
| 1210 | | 1331 | |