diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index 97f1f1b97f4..9d17491bd96 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -533,6 +533,7 @@ public interface Errors { DiagnosticFactory0 USELESS_CAST_STATIC_ASSERT_IS_FINE = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 USELESS_CAST = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 CAST_NEVER_SUCCEEDS = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 DYNAMIC_NOT_ALLOWED = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 IMPLICIT_CAST_TO_UNIT_OR_ANY = DiagnosticFactory1.create(WARNING); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java index 81528f2a707..1bdd87380b4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/rendering/DefaultErrorMessages.java @@ -259,6 +259,7 @@ public class DefaultErrorMessages { MAP.put(USELESS_CAST_STATIC_ASSERT_IS_FINE, "No cast needed. You can use ':' if you need a cast to a super type"); MAP.put(USELESS_CAST, "No cast needed"); MAP.put(CAST_NEVER_SUCCEEDS, "This cast can never succeed"); + MAP.put(DYNAMIC_NOT_ALLOWED, "Dynamic types are not allowed in this position"); MAP.put(USELESS_NULLABLE_CHECK, "Non-null type is checked for instance of nullable type"); MAP.put(WRONG_SETTER_PARAMETER_TYPE, "Setter parameter type must be equal to the type of the property, i.e. ''{0}''", RENDER_TYPE, RENDER_TYPE); MAP.put(WRONG_GETTER_RETURN_TYPE, "Getter return type must be equal to the type of the property, i.e. ''{0}''", RENDER_TYPE, RENDER_TYPE); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index 3b3de48d0b5..18d8f6883ea 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -209,6 +209,13 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { ) { if (actualType == null || noExpectedType(targetType)) return; + if (TypesPackage.isDynamic(targetType)) { + JetTypeReference right = expression.getRight(); + assert right != null : "We know target is dynamic, but RHS is missing"; + context.trace.report(DYNAMIC_NOT_ALLOWED.on(right)); + return; + } + if (!CastDiagnosticsUtil.isCastPossible(actualType, targetType, components.platformToKotlinClassMap)) { context.trace.report(CAST_NEVER_SUCCEEDS.on(expression.getOperationReference())); return; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java index d2d4ff18cd5..f98a244b6f0 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java @@ -283,18 +283,23 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { } TypeResolutionContext typeResolutionContext = new TypeResolutionContext(context.scope, context.trace, true, /*allowBareTypes=*/ true); PossiblyBareType possiblyBareTarget = components.expressionTypingServices.getTypeResolver().resolvePossiblyBareType(typeResolutionContext, typeReferenceAfterIs); - JetType type = TypeReconstructionUtil.reconstructBareType(typeReferenceAfterIs, possiblyBareTarget, subjectType, context.trace); - if (!subjectType.isNullable() && type.isNullable()) { + JetType targetType = TypeReconstructionUtil.reconstructBareType(typeReferenceAfterIs, possiblyBareTarget, subjectType, context.trace); + + if (TypesPackage.isDynamic(targetType)) { + context.trace.report(DYNAMIC_NOT_ALLOWED.on(typeReferenceAfterIs)); + } + + if (!subjectType.isNullable() && targetType.isNullable()) { JetTypeElement element = typeReferenceAfterIs.getTypeElement(); assert element instanceof JetNullableType : "element must be instance of " + JetNullableType.class.getName(); JetNullableType nullableType = (JetNullableType) element; context.trace.report(Errors.USELESS_NULLABLE_CHECK.on(nullableType)); } - checkTypeCompatibility(context, type, subjectType, typeReferenceAfterIs); - if (CastDiagnosticsUtil.isCastErased(subjectType, type, JetTypeChecker.DEFAULT)) { - context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, type)); + checkTypeCompatibility(context, targetType, subjectType, typeReferenceAfterIs); + if (CastDiagnosticsUtil.isCastErased(subjectType, targetType, JetTypeChecker.DEFAULT)) { + context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, targetType)); } - return new DataFlowInfos(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, type), context.dataFlowInfo); + return new DataFlowInfos(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, targetType), context.dataFlowInfo); } private static DataFlowInfos noChange(ExpressionTypingContext context) { diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/dynamicCastTarget.kt b/compiler/testData/diagnostics/tests/dynamicTypes/dynamicCastTarget.kt new file mode 100644 index 00000000000..7d89f605786 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dynamicTypes/dynamicCastTarget.kt @@ -0,0 +1,32 @@ +// !DIAGNOSTICS: -REDUNDANT_NULLABLE + +// MODULE[js]: m1 +// FILE: k.kt + +fun test(d: Any, dl: Collection) { + d as dynamic + d as dynamic? + + d as? dynamic + d as? dynamic? + + d is dynamic + d is dynamic? + + d !is dynamic + d !is dynamic? + + when (d) { + is dynamic -> {} + is dynamic? -> {} + !is dynamic -> {} + !is dynamic? -> {} + } + + dl as List + dl is List + + when (dl) { + is List -> {} + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/dynamicTypes/dynamicCastTarget.txt b/compiler/testData/diagnostics/tests/dynamicTypes/dynamicCastTarget.txt new file mode 100644 index 00000000000..a29dc809d5c --- /dev/null +++ b/compiler/testData/diagnostics/tests/dynamicTypes/dynamicCastTarget.txt @@ -0,0 +1,3 @@ +package + +internal fun test(/*0*/ d: kotlin.Any, /*1*/ dl: kotlin.Collection): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index dd831531cae..f293d794ae7 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -3734,6 +3734,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("dynamicCastTarget.kt") + public void testDynamicCastTarget() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/dynamicCastTarget.kt"); + doTest(fileName); + } + @TestMetadata("dynamicSafeCalls.kt") public void testDynamicSafeCalls() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/dynamicTypes/dynamicSafeCalls.kt"); diff --git a/spec-docs/dynamic-types.md b/spec-docs/dynamic-types.md index 8d9777e6a7d..0613110cb8e 100644 --- a/spec-docs/dynamic-types.md +++ b/spec-docs/dynamic-types.md @@ -79,3 +79,4 @@ but it's hard to be sure it's worth the while ## Notes - dynamic types are not supported on the JVM back-end +- dynamic types are forbidden on the right-hand side of `is`, `!is`, `as` and `as?` (but not as generic arguments, e.g. `x is List` is allowed)