Revert "FIR checkers: report SMARTCAST_IMPOSSIBLE"

This reverts commit 84334b08
This commit is contained in:
Dmitriy Novozhilov
2021-06-03 09:48:50 +03:00
parent 20d1a84002
commit 796f8e6bce
51 changed files with 297 additions and 308 deletions
@@ -492,11 +492,11 @@ digraph nullability_kt {
176 [label="Access variable R|/Q.data|"];
177 [label="Access variable R|<local>/q|"];
178 [label="Access variable R|/Q.data|"];
179 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
179 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
180 [label="Access variable R|<local>/q|"];
181 [label="Access variable R|/Q.data|"];
182 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
183 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()"];
182 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
183 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()"];
184 [label="Exit block"];
}
185 [label="Exit when branch result"];
@@ -566,11 +566,11 @@ digraph nullability_kt {
208 [label="Access variable R|/Q.data|"];
209 [label="Access variable R|<local>/q|"];
210 [label="Access variable R|/Q.data|"];
211 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
211 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
212 [label="Access variable R|<local>/q|"];
213 [label="Access variable R|/Q.data|"];
214 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
215 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()"];
214 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
215 [label="Function call: R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()"];
216 [label="Exit block"];
}
217 [label="Exit function test_6" style="filled" fillcolor=red];
@@ -1280,11 +1280,11 @@ digraph nullability_kt {
490 [label="Access variable R|/QImplWithCustomGetter.data|"];
491 [label="Access variable R|<local>/q|"];
492 [label="Access variable R|/QImplWithCustomGetter.data|"];
493 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
493 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
494 [label="Access variable R|<local>/q|"];
495 [label="Access variable R|/QImplWithCustomGetter.data|"];
496 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
497 [label="Function call: R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()"];
496 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
497 [label="Function call: R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()"];
498 [label="Exit block"];
}
499 [label="Exit when branch result"];
@@ -1363,11 +1363,11 @@ digraph nullability_kt {
524 [label="Access variable R|/QImplMutable.data|"];
525 [label="Access variable R|<local>/q|"];
526 [label="Access variable R|/QImplMutable.data|"];
527 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
527 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
528 [label="Access variable R|<local>/q|"];
529 [label="Access variable R|/QImplMutable.data|"];
530 [label="Access variable <Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#"];
531 [label="Function call: R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()"];
530 [label="Access variable <Inapplicable(UNSAFE_CALL): /MyData.s>#"];
531 [label="Function call: R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()"];
532 [label="Exit block"];
}
533 [label="Exit when branch result"];
@@ -102,8 +102,8 @@ FILE: nullability.kt
when () {
!=(R|<local>/q|?.{ $subj$.R|/Q.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() }, Null(null)) -> {
R|<local>/q|.R|/Q.data|
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()
}
}
@@ -111,8 +111,8 @@ FILE: nullability.kt
public final fun test_6(q: R|Q?|): R|kotlin/Unit| {
R|<local>/q|?.{ $subj$.R|/Q.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() } ?: ^test_6 Unit
R|<local>/q|.R|/Q.data|
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#
R|<local>/q|.R|/Q.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()
}
public final fun test_7(q: R|Q?|): R|kotlin/Unit| {
when () {
@@ -216,8 +216,8 @@ FILE: nullability.kt
when () {
!=(R|<local>/q|?.{ $subj$.R|/QImplWithCustomGetter.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() }, Null(null)) -> {
R|<local>/q|.R|/QImplWithCustomGetter.data|
R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#
R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()
R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#
R|<local>/q|.R|/QImplWithCustomGetter.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()
}
}
@@ -226,8 +226,8 @@ FILE: nullability.kt
when () {
!=(R|<local>/q|?.{ $subj$.R|/QImplMutable.data| }?.{ $subj$.R|/MyData.s| }?.{ $subj$.R|kotlin/Int.inc|() }, Null(null)) -> {
R|<local>/q|.R|/QImplMutable.data|
R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#
R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSTABLE_SMARTCAST): /MyData.s>#.R|kotlin/Int.inc|()
R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#
R|<local>/q|.R|/QImplMutable.data|.<Inapplicable(UNSAFE_CALL): /MyData.s>#.R|kotlin/Int.inc|()
}
}
@@ -67,8 +67,8 @@ fun test_5(q: Q?) {
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
if (q?.data?.s?.inc() != null) {
q.data // good
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s // should be bad
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s.inc() // should be bad
q.data<!UNSAFE_CALL!>.<!>s // should be bad
q.data<!UNSAFE_CALL!>.<!>s.inc() // should be bad
}
}
@@ -76,8 +76,8 @@ fun test_6(q: Q?) {
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
q?.data?.s?.inc() ?: return
q.data // good
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s // should be bad
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s.inc() // should be bad
q.data<!UNSAFE_CALL!>.<!>s // should be bad
q.data<!UNSAFE_CALL!>.<!>s.inc() // should be bad
}
fun test_7(q: Q?) {
@@ -162,8 +162,8 @@ fun test_12(q: QImplWithCustomGetter?) {
// `q.data` is a property that has an open getter, so we can NOT smartcast it to non-nullable MyData.
if (q?.data?.s?.inc() != null) {
q.data // good
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s // should be bad
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s.inc() // should be bad
q.data<!UNSAFE_CALL!>.<!>s // should be bad
q.data<!UNSAFE_CALL!>.<!>s.inc() // should be bad
}
}
@@ -171,7 +171,7 @@ fun test_13(q: QImplMutable?) {
// `q.data` is a property that is mutable, so we can NOT smartcast it to non-nullable MyData.
if (q?.data?.s?.inc() != null) {
q.data // good
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s // should be bad
<!SMARTCAST_IMPOSSIBLE!>q.data<!>.s.inc() // should be bad
q.data<!UNSAFE_CALL!>.<!>s // should be bad
q.data<!UNSAFE_CALL!>.<!>s.inc() // should be bad
}
}
@@ -465,12 +465,6 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<Variance>("variance")
parameter<ConeKotlinType>("containingType")
}
val SMARTCAST_IMPOSSIBLE by error<KtExpression> {
parameter<ConeKotlinType>("desiredType")
parameter<FirExpression>("subject")
parameter<String>("description")
}
}
val REFLECTION by object : DiagnosticGroup("Reflection") {
@@ -303,7 +303,6 @@ object FirErrors {
val INCOMPATIBLE_TYPES_WARNING by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
val TYPE_VARIANCE_CONFLICT by error4<PsiElement, FirTypeParameterSymbol, Variance, Variance, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE by error4<PsiElement, FirTypeParameterSymbol, Variance, Variance, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
val SMARTCAST_IMPOSSIBLE by error3<KtExpression, ConeKotlinType, FirExpression, String>()
// Reflection
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<KtExpression, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -10,16 +10,13 @@ import org.jetbrains.kotlin.fir.analysis.checkers.isSubtypeForTypeMismatch
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NULL_FOR_NONNULL_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMATCH
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SMARTCAST_IMPOSSIBLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.fir.expressions.FirWhenExpression
import org.jetbrains.kotlin.fir.expressions.isExhaustive
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.types.SmartcastStability
object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() {
override fun check(expression: FirReturnExpression, context: CheckerContext, reporter: DiagnosticReporter) {
@@ -39,23 +36,10 @@ object FirFunctionReturnTypeMismatchChecker : FirReturnExpressionChecker() {
if (resultExpression.isNullLiteral && functionReturnType.nullability == ConeNullability.NOT_NULL) {
reporter.reportOn(resultExpression.source, NULL_FOR_NONNULL_TYPE, context)
} else {
if (resultExpression is FirExpressionWithSmartcast && resultExpression.smartcastStability != SmartcastStability.STABLE_VALUE &&
isSubtypeForTypeMismatch(typeContext, subtype = resultExpression.smartcastType.coneType, supertype = functionReturnType)
) {
reporter.reportOn(
returnExpressionSource,
SMARTCAST_IMPOSSIBLE,
functionReturnType,
resultExpression,
resultExpression.smartcastStability.description,
context
)
} else {
reporter.report(
RETURN_TYPE_MISMATCH.on(returnExpressionSource, functionReturnType, returnExpressionType, targetElement),
context
)
}
reporter.report(
RETURN_TYPE_MISMATCH.on(returnExpressionSource, functionReturnType, returnExpressionType, targetElement),
context
)
}
}
}
@@ -273,7 +273,6 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_SUPERTYPE_
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SECONDARY_CONSTRUCTOR_WITH_BODY_INSIDE_INLINE_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SINGLETON_IN_SUPERTYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SMARTCAST_IMPOSSIBLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPES_FOR_ANNOTATION_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_APPEARS_TWICE
@@ -635,13 +634,6 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
map.put(UPPER_BOUND_IS_EXTENSION_FUNCTION_TYPE, "Extension function type can not be used as an upper bound")
map.put(INCOMPATIBLE_TYPES, "Incompatible types: {0} and {1}", RENDER_TYPE, RENDER_TYPE)
map.put(INCOMPATIBLE_TYPES_WARNING, "Potentially incompatible types: {0} and {1}", RENDER_TYPE, RENDER_TYPE)
map.put(
SMARTCAST_IMPOSSIBLE,
"Smart cast to ''{0}'' is impossible, because ''{1}'' is a {2}",
RENDER_TYPE,
FIR,
TO_STRING
)
map.put(
TYPE_VARIANCE_CONFLICT,
@@ -46,15 +46,6 @@ private fun ConeDiagnostic.toFirDiagnostic(
val candidate = candidates.first { it.currentApplicability == CandidateApplicability.UNSAFE_CALL }
val unsafeCall = candidate.diagnostics.firstIsInstance<UnsafeCall>()
mapUnsafeCallError(candidate, unsafeCall, source, qualifiedAccessSource)
} else if (this.applicability == CandidateApplicability.UNSTABLE_SMARTCAST) {
val unstableSmartcast =
this.candidates.first { it.currentApplicability == CandidateApplicability.UNSTABLE_SMARTCAST }.diagnostics.firstIsInstance<UnstableSmartCast>()
FirErrors.SMARTCAST_IMPOSSIBLE.on(
unstableSmartcast.argument.source,
unstableSmartcast.targetType,
unstableSmartcast.argument,
unstableSmartcast.argument.smartcastStability.description
)
} else {
FirErrors.NONE_APPLICABLE.on(source, this.candidates.map { it.symbol })
}
@@ -143,8 +134,8 @@ private fun mapInapplicableCandidateError(
source: FirSourceElement,
qualifiedAccessSource: FirSourceElement?,
): List<FirDiagnostic<FirSourceElement>> {
val genericDiagnostic = FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)
val diagnostics = diagnostic.candidate.diagnostics.filter { it.applicability == diagnostic.applicability }.mapNotNull { rootCause ->
// TODO: Need to distinguish SMARTCAST_IMPOSSIBLE
return diagnostic.candidate.diagnostics.filter { it.applicability == diagnostic.applicability }.mapNotNull { rootCause ->
when (rootCause) {
is VarargArgumentOutsideParentheses -> FirErrors.VARARG_OUTSIDE_PARENTHESES.on(
rootCause.argument.source ?: qualifiedAccessSource
@@ -175,21 +166,9 @@ private fun mapInapplicableCandidateError(
is InfixCallOfNonInfixFunction -> FirErrors.INFIX_MODIFIER_REQUIRED.on(source, rootCause.function)
is OperatorCallOfNonOperatorFunction ->
FirErrors.OPERATOR_MODIFIER_REQUIRED.on(source, rootCause.function, rootCause.function.fir.name.asString())
is UnstableSmartCast -> FirErrors.SMARTCAST_IMPOSSIBLE.on(
rootCause.argument.source,
rootCause.targetType,
rootCause.argument,
rootCause.argument.smartcastStability.description
)
else -> genericDiagnostic
else -> null
}
}.distinct()
return if (diagnostics.size > 1) {
// If there are more specific diagnostics, filter out the generic diagnostic.
diagnostics.filter { it != genericDiagnostic }
} else {
diagnostics
}
}.ifEmpty { listOf(FirErrors.INAPPLICABLE_CANDIDATE.on(source, diagnostic.candidate.symbol)) }
}
@OptIn(ExperimentalStdlibApi::class)
@@ -31,7 +31,6 @@ import org.jetbrains.kotlin.ir.util.classId
import org.jetbrains.kotlin.ir.util.coerceToUnitIfNeeded
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.SmartcastStability
class Fir2IrImplicitCastInserter(
private val components: Fir2IrComponents,
@@ -10,11 +10,9 @@ import org.jetbrains.kotlin.fir.declarations.FirAnonymousObject
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolved
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
import org.jetbrains.kotlin.fir.scopes.FirUnstableSmartcastTypeScope
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.scopes.impl.FirScopeWithFakeOverrideTypeCalculator
import org.jetbrains.kotlin.fir.scopes.impl.FirStandardOverrideChecker
@@ -27,25 +25,6 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.types.SmartcastStability
fun FirExpressionWithSmartcast.smartcastScope(
useSiteSession: FirSession,
scopeSession: ScopeSession
): FirTypeScope? {
val smartcastType = smartcastType.coneType
val smartcastScope = smartcastType.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing)
if (smartcastStability == SmartcastStability.STABLE_VALUE) {
return smartcastScope
}
val originalScope = originalType.coneType.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing)
?: return smartcastScope
if (smartcastScope == null) {
return originalScope
}
return FirUnstableSmartcastTypeScope(smartcastType, smartcastScope, originalScope)
}
fun ConeKotlinType.scope(
useSiteSession: FirSession,
@@ -30,9 +30,9 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.SmartcastStability
import org.jetbrains.kotlin.types.model.CaptureStatus
import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext
import org.jetbrains.kotlin.utils.addToStdlib.runIf
@@ -358,7 +358,17 @@ private fun checkApplicabilityForArgumentType(
) {
if (expectedType == null) return
fun subtypeError(actualExpectedType: ConeKotlinType): ResolutionDiagnostic {
fun unstableSmartCastOrSubtypeError(
unstableType: ConeKotlinType?,
actualExpectedType: ConeKotlinType,
position: ConstraintPosition
): ResolutionDiagnostic {
if (unstableType != null) {
if (csBuilder.addSubtypeConstraintIfCompatible(unstableType, actualExpectedType, position)) {
return UnstableSmartCast.ResolutionError(argument, unstableType)
}
}
if (argument.isNullLiteral && actualExpectedType.nullability == ConeNullability.NOT_NULL) {
return NullForNotNullType(argument)
}
@@ -396,17 +406,14 @@ private fun checkApplicabilityForArgumentType(
}
if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) {
val smartcastExpression = argument as? FirExpressionWithSmartcast
if (smartcastExpression != null && smartcastExpression.smartcastStability != SmartcastStability.STABLE_VALUE) {
val unstableType = smartcastExpression.smartcastType.coneType
if (csBuilder.addSubtypeConstraintIfCompatible(unstableType, expectedType, position)) {
sink.reportDiagnostic(UnstableSmartCast(smartcastExpression, expectedType))
return
}
}
if (!isReceiver) {
sink.reportDiagnosticIfNotNull(subtypeError(expectedType))
sink.reportDiagnosticIfNotNull(
unstableSmartCastOrSubtypeError(
unstableType = null, // TODO: handle unstable smartcasts
expectedType,
position
)
)
return
}
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
import org.jetbrains.kotlin.fir.expressions.builder.buildExpressionWithSmartcast
import org.jetbrains.kotlin.fir.expressions.builder.buildThisReceiverExpression
@@ -17,7 +16,6 @@ import org.jetbrains.kotlin.fir.renderWithType
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.resolve.constructType
import org.jetbrains.kotlin.fir.resolve.scope
import org.jetbrains.kotlin.fir.resolve.smartcastScope
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
import org.jetbrains.kotlin.fir.scopes.FakeOverrideTypeCalculator
import org.jetbrains.kotlin.fir.scopes.FirTypeScope
@@ -59,11 +57,7 @@ abstract class AbstractExplicitReceiverValue<E : FirExpression> : AbstractExplic
class ExpressionReceiverValue(
override val explicitReceiver: FirExpression
) : AbstractExplicitReceiverValue<FirExpression>(), ReceiverValue {
override fun scope(useSiteSession: FirSession, scopeSession: ScopeSession): FirTypeScope? =
(receiverExpression as? FirExpressionWithSmartcast)?.smartcastScope(useSiteSession, scopeSession)
?: type.scope(useSiteSession, scopeSession, FakeOverrideTypeCalculator.DoNothing)
}
) : AbstractExplicitReceiverValue<FirExpression>(), ReceiverValue
sealed class ImplicitReceiverValue<S : AbstractFirBasedSymbol<*>>(
val boundSymbol: S,
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirExpressionWithSmartcast
import org.jetbrains.kotlin.fir.expressions.FirNamedArgumentExpression
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
@@ -75,7 +74,21 @@ object LowerPriorityToPreserveCompatibilityDiagnostic : ResolutionDiagnostic(RES
object CandidateChosenUsingOverloadResolutionByLambdaAnnotation : ResolutionDiagnostic(RESOLVED)
class UnstableSmartCast(val argument: FirExpressionWithSmartcast, val targetType: ConeKotlinType) : ResolutionDiagnostic(UNSTABLE_SMARTCAST)
sealed class UnstableSmartCast(
val argument: FirExpression,
val targetType: ConeKotlinType,
applicability: CandidateApplicability
) : ResolutionDiagnostic(applicability) {
class ResolutionError(
argument: FirExpression,
targetType: ConeKotlinType,
) : UnstableSmartCast(argument, targetType, MAY_THROW_RUNTIME_ERROR)
class DiagnosticError(
argument: FirExpression,
targetType: ConeKotlinType,
) : UnstableSmartCast(argument, targetType, RESOLVED_WITH_ERROR)
}
class ArgumentTypeMismatch(
val expectedType: ConeKotlinType,
@@ -26,8 +26,6 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind.*
import org.jetbrains.kotlin.types.AbstractNullabilityChecker
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.SmartcastStability
abstract class ResolutionStage {
abstract suspend fun check(candidate: Candidate, callInfo: CallInfo, sink: CheckerSink, context: ResolutionContext)
@@ -107,19 +105,6 @@ object CheckDispatchReceiver : ResolutionStage() {
}
}
if (explicitReceiverExpression is FirExpressionWithSmartcast && explicitReceiverExpression.smartcastStability != SmartcastStability.STABLE_VALUE) {
val expectedDispatchReceiverType = (candidate.symbol.fir as? FirCallableMemberDeclaration)?.dispatchReceiverType
if (expectedDispatchReceiverType != null &&
!AbstractTypeChecker.isSubtypeOf(
context.session.typeContext,
explicitReceiverExpression.originalType.coneType,
expectedDispatchReceiverType
)
) {
sink.yieldDiagnostic(UnstableSmartCast(explicitReceiverExpression, expectedDispatchReceiverType))
}
}
val dispatchReceiverValueType = candidate.dispatchReceiverValue?.type ?: return
if (!AbstractNullabilityChecker.isSubtypeOfAny(context.session.typeContext, dispatchReceiverValueType)) {
@@ -1,106 +0,0 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.scopes
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.name.Name
/**
* Special type scope for unstable smartcast. The purpose of this scope is only to report "SMARTCAST_IMPOSSIBLE" diagnostics.
*
* This scope will serve all candidates available in the original scope. In addition, it also serve all additional members that are
* available from the smartcast type. This way, these additional members can be resolved. Later in
* [org.jetbrains.kotlin.fir.resolve.calls.CheckDispatchReceiver], these additional members are rejected with "UnstableSmartcast"
* diagnostic, which surfaces as "SMARTCAST_IMPOSSIBLE" diagnostic.
*/
class FirUnstableSmartcastTypeScope(
private val smartcastType: ConeKotlinType,
private val smartcastScope: FirTypeScope,
private val originalScope: FirTypeScope
) : FirTypeScope(), FirContainingNamesAwareScope {
private val scopes = listOf(smartcastScope, originalScope)
override fun processClassifiersByNameWithSubstitution(
name: Name,
processor: (FirClassifierSymbol<*>, ConeSubstitutor) -> Unit
) {
for (scope in scopes) {
scope.processClassifiersByNameWithSubstitution(name, processor)
}
}
private inline fun <T> processComposite(
process: FirTypeScope.(Name, (T) -> Unit) -> Unit,
name: Name,
noinline processor: (T) -> Unit
) {
val unique = mutableSetOf<T>()
for (scope in scopes) {
scope.process(name) {
if (unique.add(it)) {
processor(it)
}
}
}
}
override fun processFunctionsByName(name: Name, processor: (FirNamedFunctionSymbol) -> Unit) {
return processComposite(FirScope::processFunctionsByName, name, processor)
}
override fun processPropertiesByName(name: Name, processor: (FirVariableSymbol<*>) -> Unit) {
return processComposite(FirScope::processPropertiesByName, name, processor)
}
private inline fun <N, T : FirCallableSymbol<*>> processTypedComposite(
process: FirTypeScope.(N, (T, FirTypeScope) -> ProcessorAction) -> ProcessorAction,
name: N,
noinline processor: (T, FirTypeScope) -> ProcessorAction
): ProcessorAction {
originalScope.process(name) { symbol, firTypeScope ->
processor(symbol, firTypeScope)
}.let { if (it == ProcessorAction.STOP) return ProcessorAction.STOP }
smartcastScope.process(name) { symbol, firTypeScope ->
// Only process the symbol if the dispatcher type is exactly the smartcast type. This way, we don't add any additional
// symbols that already exists in the original scope.
if ((symbol.fir as? FirCallableMemberDeclaration)?.dispatchReceiverType == smartcastType) {
processor(symbol, firTypeScope)
} else {
ProcessorAction.NEXT
}
}.let { if (it == ProcessorAction.STOP) return ProcessorAction.STOP }
return ProcessorAction.NEXT
}
override fun processDirectOverriddenFunctionsWithBaseScope(
functionSymbol: FirNamedFunctionSymbol,
processor: (FirNamedFunctionSymbol, FirTypeScope) -> ProcessorAction
): ProcessorAction {
return processTypedComposite(FirTypeScope::processDirectOverriddenFunctionsWithBaseScope, functionSymbol, processor)
}
override fun processDirectOverriddenPropertiesWithBaseScope(
propertySymbol: FirPropertySymbol,
processor: (FirPropertySymbol, FirTypeScope) -> ProcessorAction
): ProcessorAction {
return processTypedComposite(FirTypeScope::processDirectOverriddenPropertiesWithBaseScope, propertySymbol, processor)
}
override fun getCallableNames(): Set<Name> {
return scopes.flatMapTo(hashSetOf()) { it.getContainingCallableNamesIfPresent() }
}
override fun getClassifierNames(): Set<Name> {
return scopes.flatMapTo(hashSetOf()) { it.getContainingClassifierNamesIfPresent() }
}
override val scopeOwnerLookupNames: List<String> by lazy(LazyThreadSafetyMode.PUBLICATION) {
scopes.flatMap { it.scopeOwnerLookupNames }
}
}