diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirPsiTypeProvider.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirPsiTypeProvider.kt index 2d9e245f816..e5ee1ff45c0 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirPsiTypeProvider.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/components/KtFirPsiTypeProvider.kt @@ -112,7 +112,7 @@ private fun ConeKotlinType.needLocalTypeApproximation( session: FirSession, useSitePosition: PsiElement ): Boolean { - if (!shouldApproximateLocalTypesOfNonLocalDeclaration(visibilityForApproximation, isInlineFunction)) return false + if (!shouldApproximateAnonymousTypesOfNonLocalDeclaration(visibilityForApproximation, isInlineFunction)) return false val localTypes: List = if (isLocal(session)) listOf(this) else { typeArguments.mapNotNull { if (it is ConeKotlinTypeProjection && it.type.isLocal(session)) { diff --git a/analysis/analysis-api/testData/components/typeInfoProvider/superTypes/anonymousObject.txt b/analysis/analysis-api/testData/components/typeInfoProvider/superTypes/anonymousObject.txt index cb0228a16ce..3eb3aa16b5f 100644 --- a/analysis/analysis-api/testData/components/typeInfoProvider/superTypes/anonymousObject.txt +++ b/analysis/analysis-api/testData/components/typeInfoProvider/superTypes/anonymousObject.txt @@ -1,17 +1,12 @@ [direct super types] -A -B +kotlin.Any [approximated direct super types] -A -B +kotlin.Any [all super types] -A -B kotlin.Any [approximated all super types] -A -B kotlin.Any + diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index ed1e60219ee..a952b2fcfe9 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -38397,6 +38397,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag 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 @TestMetadata("noInferAndLowPriority.kt") public void testNoInferAndLowPriority() throws Exception { diff --git a/compiler/fir/analysis-tests/testData/resolve/references/referenceToExtension.fir.txt b/compiler/fir/analysis-tests/testData/resolve/references/referenceToExtension.fir.txt index f1516bbb091..3b8add5ef49 100644 --- a/compiler/fir/analysis-tests/testData/resolve/references/referenceToExtension.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/references/referenceToExtension.fir.txt @@ -35,13 +35,13 @@ FILE: referenceToExtension.kt } public final fun test_1(): R|kotlin/Unit| { - lval memberValRef: R|kotlin/reflect/KProperty1, GenericTest.A>| = Q|GenericTest.B|::R|SubstitutionOverride|>| - lval memberFunRef: R|kotlin/reflect/KFunction1, GenericTest.A>| = Q|GenericTest.B|::R|SubstitutionOverride|>| + lval memberValRef: R|kotlin/reflect/KProperty1, GenericTest.A>| = Q|GenericTest.B|::R|SubstitutionOverride|>| + lval memberFunRef: R|kotlin/reflect/KFunction1, GenericTest.A>| = Q|GenericTest.B|::R|SubstitutionOverride|>| } public final fun test_2(): R|kotlin/Unit| { - lval extensionValRef: R|kotlin/reflect/KProperty1, GenericTest.A>| = Q|GenericTest.B|::R|/GenericTest.extensionVal| - lval extensionFunRef: R|@ExtensionFunctionType kotlin/reflect/KFunction1, GenericTest.A>| = Q|GenericTest.B|::R|/GenericTest.extensionFun| + lval extensionValRef: R|kotlin/reflect/KProperty1, GenericTest.A>| = Q|GenericTest.B|::R|/GenericTest.extensionVal| + lval extensionFunRef: R|kotlin/reflect/KFunction1, GenericTest.A>| = Q|GenericTest.B|::R|/GenericTest.extensionFun| } } diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 378026728c2..271fb55dab1 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -38397,6 +38397,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti 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 @TestMetadata("noInferAndLowPriority.kt") public void testNoInferAndLowPriority() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index f9be1529d69..c7846237b0a 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -38397,6 +38397,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac 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 @TestMetadata("noInferAndLowPriority.kt") public void testNoInferAndLowPriority() throws Exception { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAmbiguousAnonymousTypeChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAmbiguousAnonymousTypeChecker.kt index 1130ccc5098..60573c70bb9 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAmbiguousAnonymousTypeChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAmbiguousAnonymousTypeChecker.kt @@ -11,26 +11,42 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.* 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.types.coneType -import org.jetbrains.kotlin.fir.types.shouldApproximateLocalTypesOfNonLocalDeclaration -import org.jetbrains.kotlin.fir.types.toSymbol -import org.jetbrains.kotlin.fir.types.visibilityForApproximation +import org.jetbrains.kotlin.fir.types.* object FirAmbiguousAnonymousTypeChecker : FirBasicDeclarationChecker() { 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) + // 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 (!shouldApproximateLocalTypesOfNonLocalDeclaration( + if (!shouldApproximateAnonymousTypesOfNonLocalDeclaration( declaration.visibilityForApproximation(context.containingDeclarations.lastOrNull()), declaration.isInline ) ) 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 // org.jetbrains.kotlin.fir.types.TypeUtilsKt#hideLocalTypeIfNeeded. Hence, any remaining anonymous object must have more than // 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 } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index 63486a39747..c75fbb60bd8 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt @@ -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.ConeSubstitutorByMap 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.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* @@ -992,7 +993,12 @@ class CallAndReferenceGenerator( val typeParameter = access.findTypeParameter(index) val argumentFirType = (argument as FirTypeProjectionWithVariance).typeRef val argumentIrType = if (typeParameter?.isReified == true) { - argumentFirType.approximatedForPublicPosition(approximator).toIrType() + argumentFirType.approximateDeclarationType( + session, + containingCallableVisibility = null, + isLocal = false, + stripEnhancedNullability = false + ).toIrType() } else { argumentFirType.toIrType() } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt index 6c67a5adfa7..b199675c990 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt @@ -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.diagnostics.ConeRecursiveTypeParameterDuringErasureError 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.toSymbol import org.jetbrains.kotlin.fir.resolvedTypeFromPrototype @@ -270,10 +269,6 @@ fun ConeKotlinType.toFirResolvedTypeRef( } } -fun FirTypeRef.isUnsafeVarianceType(session: FirSession): Boolean { - return coneTypeSafe()?.isUnsafeVarianceType(session) == true -} - fun FirTypeRef.hasEnhancedNullability(): Boolean = coneTypeSafe()?.hasEnhancedNullability == true @@ -330,101 +325,7 @@ fun FirTypeRef.withReplacedConeType( } } -fun FirTypeRef.approximated( - 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 (), 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 { +fun shouldApproximateAnonymousTypesOfNonLocalDeclaration(containingCallableVisibility: Visibility?, isInlineFunction: Boolean): Boolean { // Approximate types for non-private (all but package private or private) members. // Also private inline functions, as per KT-33917. return when (containingCallableVisibility) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt index b546e12947c..ed664801b30 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt @@ -71,8 +71,6 @@ class FirCallCompletionResultsWriterTransformer( private val mode: Mode = Mode.Normal ) : FirAbstractTreeTransformer(phase = FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE) { - private val declarationWriter by lazy { FirDeclarationCompletionResultsWriter(finalSubstitutor, typeApproximator, session.typeContext) } - private val arrayOfCallTransformer = FirArrayOfCallTransformer() private var enableArrayOfCallTransformation = false @@ -187,10 +185,6 @@ class FirCallCompletionResultsWriterTransformer( session.lookupTracker?.recordTypeResolveAsLookup(resultType, qualifiedAccessExpression.source, context.file.source) if (mode == Mode.DelegatedPropertyCompletion) { - subCandidate.symbol.fir.transformSingle( - declarationWriter, - FirDeclarationCompletionResultsWriter.ApproximationData.NoApproximation - ) val typeUpdater = TypeUpdaterForDelegateArguments() result.transformExplicitReceiver(typeUpdater, null) } @@ -243,10 +237,6 @@ class FirCallCompletionResultsWriterTransformer( session.lookupTracker?.recordTypeResolveAsLookup(resultType, functionCall.source, context.file.source) if (mode == Mode.DelegatedPropertyCompletion) { - subCandidate.symbol.fir.transformSingle( - declarationWriter, - FirDeclarationCompletionResultsWriter.ApproximationData.NoApproximation - ) val typeUpdater = TypeUpdaterForDelegateArguments() result.argumentList.transformArguments(typeUpdater, null) result.transformExplicitReceiver(typeUpdater, null) @@ -914,63 +904,3 @@ private fun ExpectedArgumentType.getExpectedType(argument: FirElement): ConeKotl } fun ConeKotlinType.toExpectedType(): ExpectedArgumentType = ExpectedArgumentType.ExpectedType(this) - -internal class FirDeclarationCompletionResultsWriter( - private val finalSubstitutor: ConeSubstitutor, - private val typeApproximator: ConeTypeApproximator, - private val typeContext: ConeInferenceContext -) : FirDefaultTransformer() { - override fun 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() - } -} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/DeclarationApproximationUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/DeclarationApproximationUtils.kt new file mode 100644 index 00000000000..07323585813 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/DeclarationApproximationUtils.kt @@ -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) +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index 2d3394b0f15..7ecf28aa339 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt @@ -287,6 +287,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve val stubTypeCompletionResultsWriter = FirStubTypeTransformer(finalSubstitutor) property.transformSingle(stubTypeCompletionResultsWriter, null) + property.replaceReturnTypeRef(property.returnTypeRef.approximateDeclarationType(session, property.visibilityForApproximation(), property.isLocal)) val callCompletionResultsWriter = callCompleter.createCompletionResultsWriter( finalSubstitutor, @@ -296,9 +297,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve it.transformSingle(callCompletionResultsWriter, null) } - val declarationCompletionResultsWriter = - FirDeclarationCompletionResultsWriter(finalSubstitutor, session.typeApproximator, session.typeContext) - property.transformSingle(declarationCompletionResultsWriter, FirDeclarationCompletionResultsWriter.ApproximationData.Default) + property } } @@ -648,10 +647,10 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve result.transformReturnTypeRef( transformer, withExpectedType( - returnExpression.resultType.approximatedIfNeededOrSelf( - session.typeApproximator, - simpleFunction?.visibilityForApproximation(), + returnExpression.resultType.approximateDeclarationType( session, + simpleFunction?.visibilityForApproximation(), + isLocal = simpleFunction?.isLocal == true, isInlineFunction = simpleFunction?.isInline == true ) ) @@ -814,7 +813,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve val initialReturnTypeRef = lambda.returnTypeRef val valueParameters = when { resolvedLambdaAtom != null -> obtainValueParametersFromResolvedLambdaAtom(resolvedLambdaAtom, lambda) - else -> lambda.valueParameters + else -> obtainValueParametersFromExpectedType(expectedTypeRef.coneTypeSafe(), lambda) } val returnTypeRefFromResolvedAtom = resolvedLambdaAtom?.returnType?.let { lambda.returnTypeRef.resolvedTypeFromPrototype(it) } lambda = buildAnonymousFunctionCopy(lambda) { @@ -928,17 +927,37 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve listOf(itParam) } - else -> { - lambda.valueParameters.mapIndexed { index, param -> - if (param.returnTypeRef is FirResolvedTypeRef) { - param - } else { - val resolvedType = - param.returnTypeRef.resolvedTypeFromPrototype(resolvedLambdaAtom.parameters[index]) - param.replaceReturnTypeRef(resolvedType) - param - } - } + else -> obtainValueParametersFromExpectedParameterTypes(resolvedLambdaAtom.parameters, lambda) + } + } + + private fun obtainValueParametersFromExpectedType( + expectedType: ConeKotlinType?, + lambda: FirAnonymousFunction + ): List { + 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, + lambda: FirAnonymousFunction + ): List { + 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( transformer, withExpectedType( - expectedType.approximatedIfNeededOrSelf( - session.typeApproximator, - backingField.visibilityForApproximation(), - session - ) + expectedType.approximateDeclarationType(session, backingField.visibilityForApproximation(), isLocal = false) ) ) } @@ -1050,11 +1065,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirAbstractBodyResolve withExpectedType( resultType?.let { val expectedType = it.toExpectedTypeRef() - expectedType.approximatedIfNeededOrSelf( - session.typeApproximator, - variable.visibilityForApproximation(), - session - ) + expectedType.approximateDeclarationType(session, variable.visibilityForApproximation(), variable.isLocal) } ?: buildErrorTypeRef { diagnostic = ConeLocalVariableNoTypeOrInitializer(variable) 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 { return when (this) { is FirImplicitTypeRef -> buildErrorTypeRef { diff --git a/compiler/testData/asJava/ultraLightClasses/inferringAnonymousObjectTypes.fir.java b/compiler/testData/asJava/ultraLightClasses/inferringAnonymousObjectTypes.fir.java index 0b1dc671051..39006776184 100644 --- a/compiler/testData/asJava/ultraLightClasses/inferringAnonymousObjectTypes.fir.java +++ b/compiler/testData/asJava/ultraLightClasses/inferringAnonymousObjectTypes.fir.java @@ -70,13 +70,13 @@ public abstract interface I /* I*/ { public final class InvalidPublicSupertype /* InvalidPublicSupertype*/ { @org.jetbrains.annotations.NotNull() - private final java.lang.Object x; + private final java.lang.Runnable x; @org.jetbrains.annotations.NotNull() - public final java.lang.Object bar();// bar() + public final java.lang.Runnable bar();// bar() @org.jetbrains.annotations.NotNull() - public final java.lang.Object getX();// getX() + public final java.lang.Runnable getX();// getX() public InvalidPublicSupertype();// .ctor() diff --git a/compiler/testData/diagnostics/tests/inline/approximateReturnedAnonymousObjects.fir.kt b/compiler/testData/diagnostics/tests/inline/approximateReturnedAnonymousObjects.fir.kt index 6e949204ffa..5fc8d4066c9 100644 --- a/compiler/testData/diagnostics/tests/inline/approximateReturnedAnonymousObjects.fir.kt +++ b/compiler/testData/diagnostics/tests/inline/approximateReturnedAnonymousObjects.fir.kt @@ -36,11 +36,11 @@ fun test2(b: Boolean) { } fun test3(b: Boolean) { - var x = ")!>foo3 { 1 } + var x = foo3 { 1 } if (b) { - x = ")!>foo3 { 2 } + x = foo3 { 2 } } - x.bar() + x.bar() } fun test4(b: Boolean) { diff --git a/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.fir.kt b/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.fir.kt index 716c9dcd5f8..9b959fe753e 100644 --- a/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.fir.kt +++ b/compiler/testData/diagnostics/tests/inline/returnedAnonymousObjects.fir.kt @@ -36,11 +36,11 @@ fun test2(b: Boolean) { } fun test3(b: Boolean) { - var x = ")!>foo3 { 1 } + var x = foo3 { 1 } if (b) { - x = ")!>foo3 { 2 } + x = foo3 { 2 } } - x.bar() + x.bar() } fun test4(b: Boolean) { diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/moreOnlyInputTypes.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/moreOnlyInputTypes.kt new file mode 100644 index 00000000000..cf9ff422ea7 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/moreOnlyInputTypes.kt @@ -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] diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/moreOnlyInputTypes.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/moreOnlyInputTypes.txt new file mode 100644 index 00000000000..5dcfa47d8b3 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/moreOnlyInputTypes.txt @@ -0,0 +1,7 @@ +package + +private val PRIMITIVE_CLASSES: kotlin.collections.List> +private val PRIMITIVE_TO_WRAPPER: kotlin.collections.Map?, java.lang.Class> +private val WRAPPER_TO_PRIMITIVE: kotlin.collections.Map, java.lang.Class?> +public val java.lang.Class<*>.primitiveByWrapper: java.lang.Class<*>? +public val java.lang.Class<*>.wrapperByPrimitive: java.lang.Class<*>? diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index caf6d8b22d0..623d5e99b82 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -38487,6 +38487,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { 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 @TestMetadata("noInferAndLowPriority.kt") public void testNoInferAndLowPriority() throws Exception { diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt b/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt index cfe93e8b992..1029cb5c1f7 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt @@ -255,7 +255,7 @@ fun Map.compactIfPossible(): Map = else -> this } -inline fun T.applyIf(`if`: Boolean, body: T.() -> T): T = +inline fun R.applyIf(`if`: Boolean, body: R.() -> T): T = if (`if`) body() else this