[FIR] Properly approximate return type of callable declarations

- approximate intersection types in all non-local declarations
- approximate local types in non-private non-local declarations
This commit is contained in:
Dmitriy Novozhilov
2022-11-10 15:34:40 +02:00
committed by Space Team
parent 0f70635fcb
commit 01c6c7dc59
19 changed files with 190 additions and 230 deletions
@@ -112,7 +112,7 @@ private fun ConeKotlinType.needLocalTypeApproximation(
session: FirSession, session: FirSession,
useSitePosition: PsiElement useSitePosition: PsiElement
): Boolean { ): Boolean {
if (!shouldApproximateLocalTypesOfNonLocalDeclaration(visibilityForApproximation, isInlineFunction)) return false if (!shouldApproximateAnonymousTypesOfNonLocalDeclaration(visibilityForApproximation, isInlineFunction)) return false
val localTypes: List<ConeKotlinType> = if (isLocal(session)) listOf(this) else { val localTypes: List<ConeKotlinType> = if (isLocal(session)) listOf(this) else {
typeArguments.mapNotNull { typeArguments.mapNotNull {
if (it is ConeKotlinTypeProjection && it.type.isLocal(session)) { if (it is ConeKotlinTypeProjection && it.type.isLocal(session)) {
@@ -1,17 +1,12 @@
[direct super types] [direct super types]
A kotlin.Any
B
[approximated direct super types] [approximated direct super types]
A kotlin.Any
B
[all super types] [all super types]
A
B
kotlin.Any kotlin.Any
[approximated all super types] [approximated all super types]
A
B
kotlin.Any kotlin.Any
@@ -38397,6 +38397,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/kt35210.kt"); runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/kt35210.kt");
} }
@Test
@TestMetadata("moreOnlyInputTypes.kt")
public void testMoreOnlyInputTypes() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/moreOnlyInputTypes.kt");
}
@Test @Test
@TestMetadata("noInferAndLowPriority.kt") @TestMetadata("noInferAndLowPriority.kt")
public void testNoInferAndLowPriority() throws Exception { public void testNoInferAndLowPriority() throws Exception {
@@ -35,13 +35,13 @@ FILE: referenceToExtension.kt
} }
public final fun test_1(): R|kotlin/Unit| { public final fun test_1(): R|kotlin/Unit| {
lval memberValRef: R|kotlin/reflect/KProperty1<GenericTest.B<*>, GenericTest.A<CapturedType(*)>>| = Q|GenericTest.B|::R|SubstitutionOverride</GenericTest.B.memberVal: R|GenericTest.A<CapturedType(*)>|>| lval memberValRef: R|kotlin/reflect/KProperty1<GenericTest.B<*>, GenericTest.A<out kotlin/Any?>>| = Q|GenericTest.B|::R|SubstitutionOverride</GenericTest.B.memberVal: R|GenericTest.A<CapturedType(*)>|>|
lval memberFunRef: R|kotlin/reflect/KFunction1<GenericTest.B<*>, GenericTest.A<CapturedType(*)>>| = Q|GenericTest.B|::R|SubstitutionOverride</GenericTest.B.memberFun: R|GenericTest.A<CapturedType(*)>|>| lval memberFunRef: R|kotlin/reflect/KFunction1<GenericTest.B<*>, GenericTest.A<out kotlin/Any?>>| = Q|GenericTest.B|::R|SubstitutionOverride</GenericTest.B.memberFun: R|GenericTest.A<CapturedType(*)>|>|
} }
public final fun test_2(): R|kotlin/Unit| { public final fun test_2(): R|kotlin/Unit| {
lval extensionValRef: R|kotlin/reflect/KProperty1<GenericTest.B<*>, GenericTest.A<CapturedType(*)>>| = Q|GenericTest.B|::R|/GenericTest.extensionVal<CapturedType(*)>| lval extensionValRef: R|kotlin/reflect/KProperty1<GenericTest.B<*>, GenericTest.A<out kotlin/Any?>>| = Q|GenericTest.B|::R|/GenericTest.extensionVal<CapturedType(*)>|
lval extensionFunRef: R|@ExtensionFunctionType kotlin/reflect/KFunction1<GenericTest.B<*>, GenericTest.A<CapturedType(*)>>| = Q|GenericTest.B|::R|/GenericTest.extensionFun<CapturedType(*)>| lval extensionFunRef: R|kotlin/reflect/KFunction1<GenericTest.B<*>, GenericTest.A<out kotlin/Any?>>| = Q|GenericTest.B|::R|/GenericTest.extensionFun<CapturedType(*)>|
} }
} }
@@ -38397,6 +38397,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/kt35210.kt"); runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/kt35210.kt");
} }
@Test
@TestMetadata("moreOnlyInputTypes.kt")
public void testMoreOnlyInputTypes() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/moreOnlyInputTypes.kt");
}
@Test @Test
@TestMetadata("noInferAndLowPriority.kt") @TestMetadata("noInferAndLowPriority.kt")
public void testNoInferAndLowPriority() throws Exception { public void testNoInferAndLowPriority() throws Exception {
@@ -38397,6 +38397,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/kt35210.kt"); runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/kt35210.kt");
} }
@Test
@TestMetadata("moreOnlyInputTypes.kt")
public void testMoreOnlyInputTypes() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/moreOnlyInputTypes.kt");
}
@Test @Test
@TestMetadata("noInferAndLowPriority.kt") @TestMetadata("noInferAndLowPriority.kt")
public void testNoInferAndLowPriority() throws Exception { public void testNoInferAndLowPriority() throws Exception {
@@ -11,26 +11,42 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isInline import org.jetbrains.kotlin.fir.declarations.utils.isInline
import org.jetbrains.kotlin.fir.expressions.FirBlock
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol
import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.shouldApproximateLocalTypesOfNonLocalDeclaration
import org.jetbrains.kotlin.fir.types.toSymbol
import org.jetbrains.kotlin.fir.types.visibilityForApproximation
object FirAmbiguousAnonymousTypeChecker : FirBasicDeclarationChecker() { object FirAmbiguousAnonymousTypeChecker : FirBasicDeclarationChecker() {
override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
if (declaration !is FirSimpleFunction && declaration !is FirProperty) return if (declaration !is FirFunction && declaration !is FirProperty) return
require(declaration is FirCallableDeclaration) require(declaration is FirCallableDeclaration)
// if source is not null then this type was declared in source
// so it can not be inferred to anonymous type
if (declaration.symbol.hasExplicitReturnType) return
if (context.containingDeclarations.any { it.isLocalMember || it is FirAnonymousObject }) return if (context.containingDeclarations.any { it.isLocalMember || it is FirAnonymousObject }) return
if (!shouldApproximateLocalTypesOfNonLocalDeclaration( if (!shouldApproximateAnonymousTypesOfNonLocalDeclaration(
declaration.visibilityForApproximation(context.containingDeclarations.lastOrNull()), declaration.visibilityForApproximation(context.containingDeclarations.lastOrNull()),
declaration.isInline declaration.isInline
) )
) return ) return
val classSymbol = declaration.returnTypeRef.coneType.toSymbol(context.session) ?: return /*
if (classSymbol is FirAnonymousObjectSymbol) { * Here we want to check only cases when type of declaration was inferred from single-expression block
* There are three possible cases for it:
* 1. `fun foo() = ...`
* 2. `val x = ...`
* 3. `val x get() = ...`
*/
val typeRef = when (declaration) {
is FirProperty -> declaration.initializer?.typeRef ?: declaration.getter?.body?.singleExpressionType
is FirFunction -> declaration.body?.singleExpressionType
else -> error("Should not be there")
} ?: return
val classSymbol = typeRef.coneType.toSymbol(context.session) ?: return
if (classSymbol is FirAnonymousObjectSymbol && classSymbol.resolvedSuperTypeRefs.size > 1) {
// Any anonymous object that has only one super type is already approximated to the super type by // Any anonymous object that has only one super type is already approximated to the super type by
// org.jetbrains.kotlin.fir.types.TypeUtilsKt#hideLocalTypeIfNeeded. Hence, any remaining anonymous object must have more than // org.jetbrains.kotlin.fir.types.TypeUtilsKt#hideLocalTypeIfNeeded. Hence, any remaining anonymous object must have more than
// one super types and hence are ambiguous. // one super types and hence are ambiguous.
@@ -42,4 +58,7 @@ object FirAmbiguousAnonymousTypeChecker : FirBasicDeclarationChecker() {
) )
} }
} }
private val FirBlock.singleExpressionType
get() = ((this as? FirSingleExpressionBlock)?.statement as? FirReturnExpression)?.result?.typeRef
} }
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.approximateDeclarationType
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.symbols.impl.*
@@ -992,7 +993,12 @@ class CallAndReferenceGenerator(
val typeParameter = access.findTypeParameter(index) val typeParameter = access.findTypeParameter(index)
val argumentFirType = (argument as FirTypeProjectionWithVariance).typeRef val argumentFirType = (argument as FirTypeProjectionWithVariance).typeRef
val argumentIrType = if (typeParameter?.isReified == true) { val argumentIrType = if (typeParameter?.isReified == true) {
argumentFirType.approximatedForPublicPosition(approximator).toIrType() argumentFirType.approximateDeclarationType(
session,
containingCallableVisibility = null,
isLocal = false,
stripEnhancedNullability = false
).toIrType()
} else { } else {
argumentFirType.toIrType() argumentFirType.toIrType()
} }
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.declarations.utils.visibility import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.diagnostics.ConeRecursiveTypeParameterDuringErasureError import org.jetbrains.kotlin.fir.diagnostics.ConeRecursiveTypeParameterDuringErasureError
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.resolve.toSymbol import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype
@@ -270,10 +269,6 @@ fun ConeKotlinType.toFirResolvedTypeRef(
} }
} }
fun FirTypeRef.isUnsafeVarianceType(session: FirSession): Boolean {
return coneTypeSafe<ConeKotlinType>()?.isUnsafeVarianceType(session) == true
}
fun FirTypeRef.hasEnhancedNullability(): Boolean = fun FirTypeRef.hasEnhancedNullability(): Boolean =
coneTypeSafe<ConeKotlinType>()?.hasEnhancedNullability == true coneTypeSafe<ConeKotlinType>()?.hasEnhancedNullability == true
@@ -330,101 +325,7 @@ fun FirTypeRef.withReplacedConeType(
} }
} }
fun FirTypeRef.approximated( fun shouldApproximateAnonymousTypesOfNonLocalDeclaration(containingCallableVisibility: Visibility?, isInlineFunction: Boolean): Boolean {
typeApproximator: ConeTypeApproximator,
toSuper: Boolean,
): FirTypeRef {
val alternativeType = (coneType as? ConeIntersectionType)?.alternativeType ?: coneType
if (alternativeType !== coneType && !alternativeType.requiresApproximationInPublicPosition()) {
return withReplacedConeType(alternativeType)
}
val approximatedType = if (toSuper)
typeApproximator.approximateToSuperType(alternativeType, TypeApproximatorConfiguration.PublicDeclaration.SaveAnonymousTypes)
else
typeApproximator.approximateToSubType(alternativeType, TypeApproximatorConfiguration.PublicDeclaration.SaveAnonymousTypes)
return withReplacedConeType(approximatedType)
}
fun FirTypeRef.approximatedIfNeededOrSelf(
approximator: ConeTypeApproximator,
containingCallableVisibility: Visibility?,
session: FirSession,
isInlineFunction: Boolean = false
): FirTypeRef {
val approximated = if (containingCallableVisibility == Visibilities.Public || containingCallableVisibility == Visibilities.Protected)
approximatedForPublicPosition(approximator)
else
this
return approximated.hideLocalTypeIfNeeded(containingCallableVisibility, session, isInlineFunction).withoutEnhancedNullability()
}
fun FirTypeRef.approximatedForPublicPosition(approximator: ConeTypeApproximator): FirTypeRef =
if (this is FirResolvedTypeRef && type.requiresApproximationInPublicPosition())
this.approximated(approximator, toSuper = true)
else
this
private fun ConeKotlinType.requiresApproximationInPublicPosition(): Boolean = contains {
it is ConeIntegerLiteralType || it is ConeCapturedType || it is ConeDefinitelyNotNullType || it is ConeIntersectionType
}
/*
* Suppose a function without an explicit return type just returns an anonymous object:
*
* fun foo(...) = object : ObjectSuperType {
* override fun ...
* }
*
* Without unwrapping, the return type ended up with that anonymous object (<no name provided>), while the resolved super type, which
* acts like an implementing interface, is a better fit. In fact, exposing an anonymous object types is prohibited for certain cases,
* e.g., KT-33917. We can also apply this to any local types.
*/
private fun FirTypeRef.hideLocalTypeIfNeeded(
containingCallableVisibility: Visibility?,
session: FirSession,
isInlineFunction: Boolean = false
): FirTypeRef {
if (this !is FirResolvedTypeRef || !shouldApproximateLocalTypesOfNonLocalDeclaration(containingCallableVisibility, isInlineFunction)) return this
return withReplacedConeType(type.approximateToOnlySupertype(session))
}
private fun ConeKotlinType.approximateToOnlySupertype(session: FirSession): ConeKotlinType? {
if (this is ConeFlexibleType) {
val lower = lowerBound.approximateToOnlySupertype(session)?.coneLowerBoundIfFlexible()
val upper = upperBound.approximateToOnlySupertype(session)?.coneUpperBoundIfFlexible()
if (lower == null && upper == null) {
return null
}
return coneFlexibleOrSimpleType(session.typeContext, lower ?: lowerBound, upper ?: upperBound)
}
if (this !is ConeClassLikeType) {
return null
}
val firClass = (lookupTag as? ConeClassLookupTagWithFixedSymbol)?.symbol?.fir
if (firClass !is FirAnonymousObject) {
// NB: local classes are acceptable here, but reported by EXPOSED_* checkers as errors
return null
}
if (firClass.superTypeRefs.size > 1) {
// NB: don't approximate so members can be resolved. The error is reported by FirAmbiguousAnonymousTypeChecker.
return null
}
val superType = firClass.superTypeRefs.single()
if (superType !is FirResolvedTypeRef) {
return null
}
val result = superType.type.withNullability(nullability, session.typeContext)
if (typeArguments.isNotEmpty() && typeArguments.size == firClass.typeParameters.size) {
val substitution = firClass.typeParameters.zip(typeArguments).associate { (parameter, argument) ->
parameter.symbol to argument.type!!
}
return ConeSubstitutorByMap(substitution, session).substituteOrSelf(result)
}
return result
}
fun shouldApproximateLocalTypesOfNonLocalDeclaration(containingCallableVisibility: Visibility?, isInlineFunction: Boolean): Boolean {
// Approximate types for non-private (all but package private or private) members. // Approximate types for non-private (all but package private or private) members.
// Also private inline functions, as per KT-33917. // Also private inline functions, as per KT-33917.
return when (containingCallableVisibility) { return when (containingCallableVisibility) {
@@ -71,8 +71,6 @@ class FirCallCompletionResultsWriterTransformer(
private val mode: Mode = Mode.Normal private val mode: Mode = Mode.Normal
) : FirAbstractTreeTransformer<ExpectedArgumentType?>(phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) { ) : FirAbstractTreeTransformer<ExpectedArgumentType?>(phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) {
private val declarationWriter by lazy { FirDeclarationCompletionResultsWriter(finalSubstitutor, typeApproximator, session.typeContext) }
private val arrayOfCallTransformer = FirArrayOfCallTransformer() private val arrayOfCallTransformer = FirArrayOfCallTransformer()
private var enableArrayOfCallTransformation = false private var enableArrayOfCallTransformation = false
@@ -187,10 +185,6 @@ class FirCallCompletionResultsWriterTransformer(
session.lookupTracker?.recordTypeResolveAsLookup(resultType, qualifiedAccessExpression.source, context.file.source) session.lookupTracker?.recordTypeResolveAsLookup(resultType, qualifiedAccessExpression.source, context.file.source)
if (mode == Mode.DelegatedPropertyCompletion) { if (mode == Mode.DelegatedPropertyCompletion) {
subCandidate.symbol.fir.transformSingle(
declarationWriter,
FirDeclarationCompletionResultsWriter.ApproximationData.NoApproximation
)
val typeUpdater = TypeUpdaterForDelegateArguments() val typeUpdater = TypeUpdaterForDelegateArguments()
result.transformExplicitReceiver(typeUpdater, null) result.transformExplicitReceiver(typeUpdater, null)
} }
@@ -243,10 +237,6 @@ class FirCallCompletionResultsWriterTransformer(
session.lookupTracker?.recordTypeResolveAsLookup(resultType, functionCall.source, context.file.source) session.lookupTracker?.recordTypeResolveAsLookup(resultType, functionCall.source, context.file.source)
if (mode == Mode.DelegatedPropertyCompletion) { if (mode == Mode.DelegatedPropertyCompletion) {
subCandidate.symbol.fir.transformSingle(
declarationWriter,
FirDeclarationCompletionResultsWriter.ApproximationData.NoApproximation
)
val typeUpdater = TypeUpdaterForDelegateArguments() val typeUpdater = TypeUpdaterForDelegateArguments()
result.argumentList.transformArguments(typeUpdater, null) result.argumentList.transformArguments(typeUpdater, null)
result.transformExplicitReceiver(typeUpdater, null) result.transformExplicitReceiver(typeUpdater, null)
@@ -914,63 +904,3 @@ private fun ExpectedArgumentType.getExpectedType(argument: FirElement): ConeKotl
} }
fun ConeKotlinType.toExpectedType(): ExpectedArgumentType = ExpectedArgumentType.ExpectedType(this) fun ConeKotlinType.toExpectedType(): ExpectedArgumentType = ExpectedArgumentType.ExpectedType(this)
internal class FirDeclarationCompletionResultsWriter(
private val finalSubstitutor: ConeSubstitutor,
private val typeApproximator: ConeTypeApproximator,
private val typeContext: ConeInferenceContext
) : FirDefaultTransformer<FirDeclarationCompletionResultsWriter.ApproximationData>() {
override fun <E : FirElement> transformElement(element: E, data: ApproximationData): E {
return element
}
override fun transformAnonymousObject(anonymousObject: FirAnonymousObject, data: ApproximationData): FirStatement {
return super.transformAnonymousObject(anonymousObject, ApproximationData.NoApproximation)
}
override fun transformSimpleFunction(simpleFunction: FirSimpleFunction, data: ApproximationData): FirStatement {
val newData = if (simpleFunction.isLocal || data == ApproximationData.NoApproximation) ApproximationData.NoApproximation
else ApproximationData.ApproximateByStatus(simpleFunction.visibility, simpleFunction.isInline)
simpleFunction.transformReturnTypeRef(this, newData)
simpleFunction.transformValueParameters(this, ApproximationData.NoApproximation)
simpleFunction.transformReceiverParameter(this, newData)
return simpleFunction
}
override fun transformProperty(property: FirProperty, data: ApproximationData): FirStatement {
val newData = if (property.isLocal || data == ApproximationData.NoApproximation) ApproximationData.NoApproximation
else ApproximationData.ApproximateByStatus(property.visibility, false)
property.transformGetter(this, newData)
property.transformSetter(this, newData)
property.transformReturnTypeRef(this, newData)
property.transformReceiverParameter(this, newData)
return property
}
override fun transformPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: ApproximationData): FirStatement {
propertyAccessor.transformReturnTypeRef(this, data)
propertyAccessor.transformValueParameters(this, ApproximationData.NoApproximation)
return propertyAccessor
}
override fun transformValueParameter(valueParameter: FirValueParameter, data: ApproximationData): FirStatement {
valueParameter.transformReturnTypeRef(this, ApproximationData.NoApproximation)
return valueParameter
}
override fun transformTypeRef(typeRef: FirTypeRef, data: ApproximationData): FirTypeRef {
val result = finalSubstitutor.substituteOrNull(typeRef.coneType)?.let {
typeRef.resolvedTypeFromPrototype(it)
} ?: typeRef
if (data is ApproximationData.ApproximateByStatus) {
return result.approximatedIfNeededOrSelf(typeApproximator, data.visibility, this.typeContext.session, data.isInline)
}
return result
}
sealed class ApproximationData {
class ApproximateByStatus(val visibility: Visibility?, val isInline: Boolean) : ApproximationData()
object NoApproximation : ApproximationData()
object Default : ApproximationData()
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2010-2022 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.resolve.transformers.body.resolve
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.utils.addToStdlib.applyIf
fun FirTypeRef.approximateDeclarationType(
session: FirSession,
containingCallableVisibility: Visibility?,
isLocal: Boolean,
isInlineFunction: Boolean = false,
stripEnhancedNullability: Boolean = true
): FirTypeRef {
val baseType = (this as? FirResolvedTypeRef)?.type ?: return this
val configuration = when (isLocal) {
true -> TypeApproximatorConfiguration.LocalDeclaration
false -> when (shouldApproximateAnonymousTypesOfNonLocalDeclaration(containingCallableVisibility, isInlineFunction)) {
true -> TypeApproximatorConfiguration.PublicDeclaration.ApproximateAnonymousTypes
false -> TypeApproximatorConfiguration.PublicDeclaration.SaveAnonymousTypes
}
}
val preparedType = if (isLocal) baseType else baseType.substituteAlternativesInPublicType(session)
val approximatedType = session.typeApproximator.approximateToSuperType(preparedType, configuration) ?: preparedType
return this.withReplacedConeType(approximatedType).applyIf(stripEnhancedNullability) { withoutEnhancedNullability() }
}
private fun ConeKotlinType.substituteAlternativesInPublicType(session: FirSession): ConeKotlinType {
val substitutor = object : AbstractConeSubstitutor(session.typeContext) {
override fun substituteType(type: ConeKotlinType): ConeKotlinType? {
if (type !is ConeIntersectionType) return null
val alternativeType = type.alternativeType ?: return null
return substituteOrSelf(alternativeType)
}
}
return substitutor.substituteOrSelf(this)
}
@@ -287,6 +287,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
val stubTypeCompletionResultsWriter = FirStubTypeTransformer(finalSubstitutor) val stubTypeCompletionResultsWriter = FirStubTypeTransformer(finalSubstitutor)
property.transformSingle(stubTypeCompletionResultsWriter, null) property.transformSingle(stubTypeCompletionResultsWriter, null)
property.replaceReturnTypeRef(property.returnTypeRef.approximateDeclarationType(session, property.visibilityForApproximation(), property.isLocal))
val callCompletionResultsWriter = callCompleter.createCompletionResultsWriter( val callCompletionResultsWriter = callCompleter.createCompletionResultsWriter(
finalSubstitutor, finalSubstitutor,
@@ -296,9 +297,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
it.transformSingle(callCompletionResultsWriter, null) it.transformSingle(callCompletionResultsWriter, null)
} }
val declarationCompletionResultsWriter = property
FirDeclarationCompletionResultsWriter(finalSubstitutor, session.typeApproximator, session.typeContext)
property.transformSingle(declarationCompletionResultsWriter, FirDeclarationCompletionResultsWriter.ApproximationData.Default)
} }
} }
@@ -648,10 +647,10 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
result.transformReturnTypeRef( result.transformReturnTypeRef(
transformer, transformer,
withExpectedType( withExpectedType(
returnExpression.resultType.approximatedIfNeededOrSelf( returnExpression.resultType.approximateDeclarationType(
session.typeApproximator,
simpleFunction?.visibilityForApproximation(),
session, session,
simpleFunction?.visibilityForApproximation(),
isLocal = simpleFunction?.isLocal == true,
isInlineFunction = simpleFunction?.isInline == true isInlineFunction = simpleFunction?.isInline == true
) )
) )
@@ -814,7 +813,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
val initialReturnTypeRef = lambda.returnTypeRef val initialReturnTypeRef = lambda.returnTypeRef
val valueParameters = when { val valueParameters = when {
resolvedLambdaAtom != null -> obtainValueParametersFromResolvedLambdaAtom(resolvedLambdaAtom, lambda) resolvedLambdaAtom != null -> obtainValueParametersFromResolvedLambdaAtom(resolvedLambdaAtom, lambda)
else -> lambda.valueParameters else -> obtainValueParametersFromExpectedType(expectedTypeRef.coneTypeSafe(), lambda)
} }
val returnTypeRefFromResolvedAtom = resolvedLambdaAtom?.returnType?.let { lambda.returnTypeRef.resolvedTypeFromPrototype(it) } val returnTypeRefFromResolvedAtom = resolvedLambdaAtom?.returnType?.let { lambda.returnTypeRef.resolvedTypeFromPrototype(it) }
lambda = buildAnonymousFunctionCopy(lambda) { lambda = buildAnonymousFunctionCopy(lambda) {
@@ -928,17 +927,37 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
listOf(itParam) listOf(itParam)
} }
else -> { else -> obtainValueParametersFromExpectedParameterTypes(resolvedLambdaAtom.parameters, lambda)
lambda.valueParameters.mapIndexed { index, param -> }
if (param.returnTypeRef is FirResolvedTypeRef) { }
param
} else { private fun obtainValueParametersFromExpectedType(
val resolvedType = expectedType: ConeKotlinType?,
param.returnTypeRef.resolvedTypeFromPrototype(resolvedLambdaAtom.parameters[index]) lambda: FirAnonymousFunction
param.replaceReturnTypeRef(resolvedType) ): List<FirValueParameter> {
param if (expectedType == null) return lambda.valueParameters
} if (!expectedType.isFunctionalOrSuspendFunctionalType(session)) return lambda.valueParameters
} val parameterTypes = expectedType.typeArguments
.mapTo(mutableListOf()) { it.type ?: session.builtinTypes.nullableAnyType.type }
.also { it.removeLastOrNull() }
if (expectedType.isExtensionFunctionType) {
parameterTypes.removeFirstOrNull()
}
return obtainValueParametersFromExpectedParameterTypes(parameterTypes, lambda)
}
private fun obtainValueParametersFromExpectedParameterTypes(
expectedTypeParameterTypes: List<ConeKotlinType>,
lambda: FirAnonymousFunction
): List<FirValueParameter> {
return lambda.valueParameters.mapIndexed { index, param ->
if (param.returnTypeRef is FirResolvedTypeRef) {
param
} else {
val resolvedType =
param.returnTypeRef.resolvedTypeFromPrototype(expectedTypeParameterTypes[index])
param.replaceReturnTypeRef(resolvedType)
param
} }
} }
} }
@@ -1024,11 +1043,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
return backingField.transformReturnTypeRef( return backingField.transformReturnTypeRef(
transformer, transformer,
withExpectedType( withExpectedType(
expectedType.approximatedIfNeededOrSelf( expectedType.approximateDeclarationType(session, backingField.visibilityForApproximation(), isLocal = false)
session.typeApproximator,
backingField.visibilityForApproximation(),
session
)
) )
) )
} }
@@ -1050,11 +1065,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
withExpectedType( withExpectedType(
resultType?.let { resultType?.let {
val expectedType = it.toExpectedTypeRef() val expectedType = it.toExpectedTypeRef()
expectedType.approximatedIfNeededOrSelf( expectedType.approximateDeclarationType(session, variable.visibilityForApproximation(), variable.isLocal)
session.typeApproximator,
variable.visibilityForApproximation(),
session
)
} ?: buildErrorTypeRef { } ?: buildErrorTypeRef {
diagnostic = ConeLocalVariableNoTypeOrInitializer(variable) diagnostic = ConeLocalVariableNoTypeOrInitializer(variable)
source = variable.source source = variable.source
@@ -1067,6 +1078,13 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve
} }
} }
private val FirVariable.isLocal: Boolean
get() = when (this) {
is FirProperty -> this.isLocal
is FirValueParameter -> true
else -> false
}
private fun FirTypeRef.toExpectedTypeRef(): FirResolvedTypeRef { private fun FirTypeRef.toExpectedTypeRef(): FirResolvedTypeRef {
return when (this) { return when (this) {
is FirImplicitTypeRef -> buildErrorTypeRef { is FirImplicitTypeRef -> buildErrorTypeRef {
@@ -70,13 +70,13 @@ public abstract interface I /* I*/ {
public final class InvalidPublicSupertype /* InvalidPublicSupertype*/ { public final class InvalidPublicSupertype /* InvalidPublicSupertype*/ {
@org.jetbrains.annotations.NotNull() @org.jetbrains.annotations.NotNull()
private final java.lang.Object x; private final java.lang.Runnable x;
@org.jetbrains.annotations.NotNull() @org.jetbrains.annotations.NotNull()
public final java.lang.Object bar();// bar() public final java.lang.Runnable bar();// bar()
@org.jetbrains.annotations.NotNull() @org.jetbrains.annotations.NotNull()
public final java.lang.Object getX();// getX() public final java.lang.Runnable getX();// getX()
public InvalidPublicSupertype();// .ctor() public InvalidPublicSupertype();// .ctor()
@@ -36,11 +36,11 @@ fun test2(b: Boolean) {
} }
fun test3(b: Boolean) { fun test3(b: Boolean) {
var x = <!DEBUG_INFO_EXPRESSION_TYPE("<anonymous>")!>foo3 { 1 }<!> var x = <!DEBUG_INFO_EXPRESSION_TYPE("I1")!>foo3 { 1 }<!>
if (b) { if (b) {
x = <!DEBUG_INFO_EXPRESSION_TYPE("<anonymous>")!>foo3 { 2 }<!> x = <!DEBUG_INFO_EXPRESSION_TYPE("I1")!>foo3 { 2 }<!>
} }
x.bar() x.<!UNRESOLVED_REFERENCE!>bar<!>()
} }
fun test4(b: Boolean) { fun test4(b: Boolean) {
@@ -36,11 +36,11 @@ fun test2(b: Boolean) {
} }
fun test3(b: Boolean) { fun test3(b: Boolean) {
var x = <!DEBUG_INFO_EXPRESSION_TYPE("<anonymous>")!>foo3 { 1 }<!> var x = <!DEBUG_INFO_EXPRESSION_TYPE("I1")!>foo3 { 1 }<!>
if (b) { if (b) {
x = <!DEBUG_INFO_EXPRESSION_TYPE("<anonymous>")!>foo3 { 2 }<!> x = <!DEBUG_INFO_EXPRESSION_TYPE("I1")!>foo3 { 2 }<!>
} }
x.bar() x.<!UNRESOLVED_REFERENCE!>bar<!>()
} }
fun test4(b: Boolean) { fun test4(b: Boolean) {
@@ -0,0 +1,14 @@
// FIR_IDENTICAL
// WITH_STDLIB
private val PRIMITIVE_CLASSES = listOf(Boolean::class, Byte::class, Char::class, Double::class, Float::class, Int::class, Long::class, Short::class)
// Map
private val PRIMITIVE_TO_WRAPPER = PRIMITIVE_CLASSES.map { it.javaPrimitiveType to it.javaObjectType }.toMap()
private val WRAPPER_TO_PRIMITIVE = PRIMITIVE_CLASSES.map { it.javaObjectType to it.javaPrimitiveType }.toMap()
val Class<*>.primitiveByWrapper: Class<*>?
get() = WRAPPER_TO_PRIMITIVE[this]
val Class<*>.wrapperByPrimitive: Class<*>?
get() = PRIMITIVE_TO_WRAPPER[this]
@@ -0,0 +1,7 @@
package
private val PRIMITIVE_CLASSES: kotlin.collections.List<kotlin.reflect.KClass<out kotlin.Any>>
private val PRIMITIVE_TO_WRAPPER: kotlin.collections.Map<java.lang.Class<out kotlin.Any>?, java.lang.Class<out kotlin.Any>>
private val WRAPPER_TO_PRIMITIVE: kotlin.collections.Map<java.lang.Class<out kotlin.Any>, java.lang.Class<out kotlin.Any>?>
public val java.lang.Class<*>.primitiveByWrapper: java.lang.Class<*>?
public val java.lang.Class<*>.wrapperByPrimitive: java.lang.Class<*>?
@@ -38487,6 +38487,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/kt35210.kt"); runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/kt35210.kt");
} }
@Test
@TestMetadata("moreOnlyInputTypes.kt")
public void testMoreOnlyInputTypes() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/moreOnlyInputTypes.kt");
}
@Test @Test
@TestMetadata("noInferAndLowPriority.kt") @TestMetadata("noInferAndLowPriority.kt")
public void testNoInferAndLowPriority() throws Exception { public void testNoInferAndLowPriority() throws Exception {
@@ -255,7 +255,7 @@ fun <K, V> Map<K, V>.compactIfPossible(): Map<K, V> =
else -> this else -> this
} }
inline fun <T> T.applyIf(`if`: Boolean, body: T.() -> T): T = inline fun <T, R : T> R.applyIf(`if`: Boolean, body: R.() -> T): T =
if (`if`) body() else this if (`if`) body() else this