diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.java index 99ef366d9af..dc2b0fb7339 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastManager.java @@ -198,6 +198,56 @@ public class SmartCastManager { return canBeCast; } + @Nullable + public SmartCastResult checkAndRecordPossibleCast( + @NotNull DataFlowValue dataFlowValue, + @NotNull JetType expectedType, + @Nullable JetExpression expression, + @NotNull ResolutionContext c, + boolean recordExpressionType + ) { + for (JetType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) { + if (ArgumentTypeResolver.isSubtypeOfForArgumentType(possibleType, expectedType)) { + if (expression != null) { + recordCastOrError(expression, possibleType, c.trace, dataFlowValue.isPredictable(), recordExpressionType); + } + return new SmartCastResult(possibleType, dataFlowValue.isPredictable()); + } + } + + if (!c.dataFlowInfo.getNullability(dataFlowValue).canBeNull() && !expectedType.isMarkedNullable()) { + // Handling cases like: + // fun bar(x: Any) {} + // fun foo(x: T) { + // if (x != null) { + // bar(x) // Should be allowed with smart cast + // } + // } + // + // It doesn't handled by lower code with getPossibleTypes because smart cast of T after `x != null` is still has same type T. + // But at the same time we're sure that `x` can't be null and just check for such cases manually + + // E.g. in case x!! when x has type of T where T is type parameter with nullable upper bounds + // x!! is immanently not null (see DataFlowValueFactory.createDataFlowValue for expression) + boolean immanentlyNotNull = !dataFlowValue.getImmanentNullability().canBeNull(); + JetType nullableExpectedType = TypeUtils.makeNullable(expectedType); + + if (ArgumentTypeResolver.isSubtypeOfForArgumentType(dataFlowValue.getType(), nullableExpectedType)) { + if (!immanentlyNotNull) { + if (expression != null) { + recordCastOrError(expression, dataFlowValue.getType(), c.trace, dataFlowValue.isPredictable(), + recordExpressionType); + } + } + + return new SmartCastResult(dataFlowValue.getType(), immanentlyNotNull || dataFlowValue.isPredictable()); + } + return checkAndRecordPossibleCast(dataFlowValue, nullableExpectedType, expression, c, recordExpressionType); + } + + return null; + } + public boolean recordSmartCastToNotNullIfPossible( @NotNull ReceiverValue receiver, @NotNull ResolutionContext context diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastResult.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastResult.kt new file mode 100644 index 00000000000..546d7fdd092 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/smartcasts/SmartCastResult.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.smartcasts + +import org.jetbrains.kotlin.types.JetType + +public class SmartCastResult(public val resultType: JetType, public val isCorrect: Boolean) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java index 3c55fe42d4a..c77db2d44b2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java @@ -28,10 +28,7 @@ import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingTrace; import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker; import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext; -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue; -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory; -import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastManager; +import org.jetbrains.kotlin.resolve.calls.smartcasts.*; import org.jetbrains.kotlin.resolve.constants.*; import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator; import org.jetbrains.kotlin.types.JetType; @@ -219,30 +216,8 @@ public class DataFlowAnalyzer { ) { DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, expressionType, c); - for (JetType possibleType : c.dataFlowInfo.getPossibleTypes(dataFlowValue)) { - if (JetTypeChecker.DEFAULT.isSubtypeOf(possibleType, c.expectedType)) { - smartCastManager.recordCastOrError(expression, possibleType, c.trace, dataFlowValue.isPredictable(), false); - return possibleType; - } - } - - // For cases like: - // fun bar(x: Any) {} - // fun foo(x: T) { - // if (x != null) { - // bar(x) // Should be allowed with smart cast - // } - // } - // - // It doesn't handled by upper code with getPossibleTypes because smart cast of T after `x != null` is still has same type T. - // But at the same time we're sure that `x` can't be null and just check for such cases manually - if (!c.dataFlowInfo.getNullability(dataFlowValue).canBeNull() - && JetTypeChecker.DEFAULT.isSubtypeOf(expressionType, TypeUtils.makeNullable(c.expectedType))) { - smartCastManager.recordCastOrError(expression, expressionType, c.trace, dataFlowValue.isPredictable(), false); - return expressionType; - } - - return null; + SmartCastResult result = smartCastManager.checkAndRecordPossibleCast(dataFlowValue, c.expectedType, expression, c, false); + return result != null ? result.getResultType() : null; } public void recordExpectedType(@NotNull BindingTrace trace, @NotNull JetExpression expression, @NotNull JetType expectedType) {