|
12 | 12 | import com.laytonsmith.core.Optimizable; |
13 | 13 | import com.laytonsmith.core.ParseTree; |
14 | 14 | import com.laytonsmith.core.Script; |
| 15 | +import com.laytonsmith.core.compiler.CompilerEnvironment; |
| 16 | +import com.laytonsmith.core.compiler.CompilerWarning; |
15 | 17 | import com.laytonsmith.core.compiler.FileOptions; |
16 | 18 | import com.laytonsmith.core.compiler.analysis.StaticAnalysis; |
17 | 19 | import com.laytonsmith.core.compiler.signature.FunctionSignatures; |
@@ -1246,22 +1248,49 @@ public Mixed exec(Target t, Environment env, Mixed... args) throws ConfigRuntime |
1246 | 1248 | throw new CRECastException( |
1247 | 1249 | "Cannot cast from " + value.typeof().getSimpleName() + " to " + type.getSimpleName() + ".", t); |
1248 | 1250 | } |
1249 | | - // TODO - Perform runtime conversion to 'type' when necessary (cross-cast handling). |
1250 | 1251 | return value; |
1251 | 1252 | } |
1252 | 1253 |
|
1253 | 1254 | @Override |
1254 | 1255 | public CClassType typecheck(StaticAnalysis analysis, |
1255 | 1256 | ParseTree ast, Environment env, Set<ConfigCompileException> exceptions) { |
1256 | 1257 |
|
1257 | | - // Typecheck children and validate function signature through super call. |
1258 | | - super.typecheck(analysis, ast, env, exceptions); |
| 1258 | + // Fall back to default behavior for invalid usage. |
| 1259 | + if(ast.numberOfChildren() != 2) { |
| 1260 | + return super.typecheck(analysis, ast, env, exceptions); |
| 1261 | + } |
1259 | 1262 |
|
1260 | | - // Return type that is being cast to. |
1261 | | - if(ast.numberOfChildren() != 2 || !(ast.getChildAt(1).getData() instanceof CClassType)) { |
| 1263 | + // Typecheck value and type nodes. |
| 1264 | + ParseTree valNode = ast.getChildAt(0); |
| 1265 | + CClassType valType = analysis.typecheck(valNode, env, exceptions); |
| 1266 | + StaticAnalysis.requireType(valType, Mixed.TYPE, valType.getTarget(), env, exceptions); |
| 1267 | + ParseTree typeNode = ast.getChildAt(1); |
| 1268 | + CClassType typeType = analysis.typecheck(typeNode, env, exceptions); |
| 1269 | + StaticAnalysis.requireType(typeType, CClassType.TYPE, typeNode.getTarget(), env, exceptions); |
| 1270 | + |
| 1271 | + // Get cast-to type. |
| 1272 | + if(!(typeNode.getData() instanceof CClassType)) { |
| 1273 | + assert !exceptions.isEmpty() : "Missing compile-time type error for cast type argument."; |
1262 | 1274 | return CClassType.AUTO; |
1263 | 1275 | } |
1264 | | - return (CClassType) ast.getChildAt(1).getData(); |
| 1276 | + CClassType castToType = (CClassType) typeNode.getData(); |
| 1277 | + |
| 1278 | + // Generate redundancy warning for casts to the value type. |
| 1279 | + if(castToType.equals(valType)) { |
| 1280 | + env.getEnv(CompilerEnvironment.class).addCompilerWarning(ast.getFileOptions(), |
| 1281 | + new CompilerWarning("Redundant cast to " + castToType.getSimpleName(), ast.getTarget(), |
| 1282 | + FileOptions.SuppressWarning.UselessCode)); |
| 1283 | + } |
| 1284 | + |
| 1285 | + // Generate compile error for impossible casts. |
| 1286 | + if(!InstanceofUtil.isInstanceof(valType, castToType, env) |
| 1287 | + && !InstanceofUtil.isInstanceof(castToType, valType, env)) { |
| 1288 | + exceptions.add(new ConfigCompileException("Cannot cast from " |
| 1289 | + + valType.getSimpleName() + " to " + castToType.getSimpleName() + ".", ast.getTarget())); |
| 1290 | + } |
| 1291 | + |
| 1292 | + // Return type that is being cast to. |
| 1293 | + return castToType; |
1265 | 1294 | } |
1266 | 1295 | } |
1267 | 1296 | } |
0 commit comments