From 55a58e54fe6c870d2e2c1dbbe50d3a2b44330fb4 Mon Sep 17 00:00:00 2001 From: "Denis.Zharkov" Date: Fri, 24 Mar 2023 17:21:03 +0100 Subject: [PATCH] K2: Rework scopes for types with projection arguments for Out types The only case when behavior is change is described at computeNonTrivialTypeArgumentForScopeSubstitutor The idea is to avoid depending on the presence of @UnsafeVariance and instead approximate captured types in covariant argument positions before building substitution scopes It's correct because for Captured(*) <: Supertype, Out <: Out and when we've got @UnsafeVariance value parameters at Out, it's ok to allow passing Supertype there. ^KT-57602 Fixed ^KT-54894 Fixed --- ...CompilerTestFE10TestdataTestGenerated.java | 28 +++++++++ ...sticCompilerFE10TestDataTestGenerated.java | 28 +++++++++ .../testData/resolve/cfg/complex.dot | 2 +- .../testData/resolve/cfg/complex.fir.txt | 2 +- .../typeAliasWithTypeArguments.fir.txt | 2 +- ...eeOldFrontendDiagnosticsTestGenerated.java | 28 +++++++++ ...siOldFrontendDiagnosticsTestGenerated.java | 28 +++++++++ .../checkers/FirUpperBoundViolatedHelpers.kt | 9 --- .../fir/resolve/scopes/JvmMappedScopes.kt | 8 ++- .../kotlin/fir/resolve/ScopeUtils.kt | 2 +- .../kotlin/fir/resolve/SupertypeUtils.kt | 58 ++++++++++++++----- .../fir/resolve/substitution/Substitutors.kt | 14 +---- .../fir/scopes/FirKotlinScopeProvider.kt | 2 +- .../jetbrains/kotlin/fir/types/TypeUtils.kt | 5 -- ...safeVarianceInAliasedFunctionalType.fir.kt | 11 ---- .../unsafeVarianceInAliasedFunctionalType.kt | 2 + ...edTypesSubstitutionIntoAbbreviation.fir.kt | 6 +- .../variantProjections/dataClassCopy.fir.kt | 13 +++++ .../variantProjections/dataClassCopy.kt | 13 +++++ .../interdependentStarProjections.fir.kt | 13 +++++ .../interdependentStarProjections.kt | 13 +++++ .../unsafeVarianceAndCovariantProjection.kt | 32 ++++++++++ .../typeAliasWithUnsafeVariance.fir.ir.txt | 2 +- .../test/runners/DiagnosticTestGenerated.java | 28 +++++++++ .../diagnostics/notLinked/dfa/pos/9.fir.kt | 30 +++++----- 25 files changed, 300 insertions(+), 79 deletions(-) delete mode 100644 compiler/testData/diagnostics/tests/generics/projectionsScope/unsafeVarianceInAliasedFunctionalType.fir.kt create mode 100644 compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.fir.kt create mode 100644 compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.kt create mode 100644 compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.fir.kt create mode 100644 compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.kt create mode 100644 compiler/testData/diagnostics/tests/scopes/variantProjections/unsafeVarianceAndCovariantProjection.kt diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java index 9e6b2cec09e..3e720a6b3f5 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosticCompilerTestFE10TestdataTestGenerated.java @@ -28149,6 +28149,34 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia runTest("compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt"); } } + + @Nested + @TestMetadata("compiler/testData/diagnostics/tests/scopes/variantProjections") + @TestDataPath("$PROJECT_ROOT") + public class VariantProjections { + @Test + public void testAllFilesPresentInVariantProjections() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/scopes/variantProjections"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); + } + + @Test + @TestMetadata("dataClassCopy.kt") + public void testDataClassCopy() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.kt"); + } + + @Test + @TestMetadata("interdependentStarProjections.kt") + public void testInterdependentStarProjections() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.kt"); + } + + @Test + @TestMetadata("unsafeVarianceAndCovariantProjection.kt") + public void testUnsafeVarianceAndCovariantProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/unsafeVarianceAndCovariantProjection.kt"); + } + } } @Nested diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java index 36bed8abf90..3c7a433aa18 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated.java @@ -28149,6 +28149,34 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated runTest("compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt"); } } + + @Nested + @TestMetadata("compiler/testData/diagnostics/tests/scopes/variantProjections") + @TestDataPath("$PROJECT_ROOT") + public class VariantProjections { + @Test + public void testAllFilesPresentInVariantProjections() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/scopes/variantProjections"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); + } + + @Test + @TestMetadata("dataClassCopy.kt") + public void testDataClassCopy() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.kt"); + } + + @Test + @TestMetadata("interdependentStarProjections.kt") + public void testInterdependentStarProjections() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.kt"); + } + + @Test + @TestMetadata("unsafeVarianceAndCovariantProjection.kt") + public void testUnsafeVarianceAndCovariantProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/unsafeVarianceAndCovariantProjection.kt"); + } + } } @Nested diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/complex.dot b/compiler/fir/analysis-tests/testData/resolve/cfg/complex.dot index e68bd6e6ab2..61e266364b3 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/complex.dot +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/complex.dot @@ -185,7 +185,7 @@ digraph complex_kt { color=blue 55 [label="Enter block"]; 56 [label="Access variable this@R|/firstIsInstanceOrNull|"]; - 57 [label="Function call: this@R|/firstIsInstanceOrNull|.R|SubstitutionOverride|>|()" style="filled" fillcolor=yellow]; + 57 [label="Function call: this@R|/firstIsInstanceOrNull|.R|SubstitutionOverride|>|()" style="filled" fillcolor=yellow]; 58 [label="Variable declaration: lval : R|kotlin/collections/Iterator|"]; subgraph cluster_19 { color=blue diff --git a/compiler/fir/analysis-tests/testData/resolve/cfg/complex.fir.txt b/compiler/fir/analysis-tests/testData/resolve/cfg/complex.fir.txt index 560e2100256..65905e471e7 100644 --- a/compiler/fir/analysis-tests/testData/resolve/cfg/complex.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/cfg/complex.fir.txt @@ -24,7 +24,7 @@ FILE: complex.kt } public final inline fun R|kotlin/collections/List<*>|.firstIsInstanceOrNull(): R|T?| { { - lval : R|kotlin/collections/Iterator| = this@R|/firstIsInstanceOrNull|.R|SubstitutionOverride|>|() + lval : R|kotlin/collections/Iterator| = this@R|/firstIsInstanceOrNull|.R|SubstitutionOverride|>|() while(R|/|.R|SubstitutionOverride|()) { lval element: R|kotlin/Any?| = R|/|.R|SubstitutionOverride|() { diff --git a/compiler/fir/analysis-tests/testData/resolve/typeAliasWithTypeArguments.fir.txt b/compiler/fir/analysis-tests/testData/resolve/typeAliasWithTypeArguments.fir.txt index a8fbfe5eed5..b6fa3dc7732 100644 --- a/compiler/fir/analysis-tests/testData/resolve/typeAliasWithTypeArguments.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/typeAliasWithTypeArguments.fir.txt @@ -77,7 +77,7 @@ FILE: typeAliasWithTypeArguments.kt public final fun test_6(a: R|A|, out1: R|Out1|, out2: R|Out1|, out3: R|Out1|): R|kotlin/Unit| { R|/out1|.R|SubstitutionOverride|().R|/A.foo|() R|/out2|.#().#() - R|/out3|.R|SubstitutionOverride|().R|/A.foo|() + R|/out3|.R|SubstitutionOverride|().R|/A.foo|() } public final fun test_7(a: R|A|, inv1: R|Invariant1|, inv2: R|Invariant1|, inv3: R|Invariant1|): R|kotlin/Unit| { R|/inv1|.R|SubstitutionOverride|().R|/A.foo|() diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java index 29701d5bd17..c7378628e93 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirLightTreeOldFrontendDiagnosticsTestGenerated.java @@ -28149,6 +28149,34 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir runTest("compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt"); } } + + @Nested + @TestMetadata("compiler/testData/diagnostics/tests/scopes/variantProjections") + @TestDataPath("$PROJECT_ROOT") + public class VariantProjections { + @Test + public void testAllFilesPresentInVariantProjections() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/scopes/variantProjections"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); + } + + @Test + @TestMetadata("dataClassCopy.kt") + public void testDataClassCopy() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.kt"); + } + + @Test + @TestMetadata("interdependentStarProjections.kt") + public void testInterdependentStarProjections() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.kt"); + } + + @Test + @TestMetadata("unsafeVarianceAndCovariantProjection.kt") + public void testUnsafeVarianceAndCovariantProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/unsafeVarianceAndCovariantProjection.kt"); + } + } } @Nested diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java index b02311885f1..6078b46b511 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirPsiOldFrontendDiagnosticsTestGenerated.java @@ -28161,6 +28161,34 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia runTest("compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt"); } } + + @Nested + @TestMetadata("compiler/testData/diagnostics/tests/scopes/variantProjections") + @TestDataPath("$PROJECT_ROOT") + public class VariantProjections { + @Test + public void testAllFilesPresentInVariantProjections() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/scopes/variantProjections"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true, "multiplatform"); + } + + @Test + @TestMetadata("dataClassCopy.kt") + public void testDataClassCopy() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.kt"); + } + + @Test + @TestMetadata("interdependentStarProjections.kt") + public void testInterdependentStarProjections() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.kt"); + } + + @Test + @TestMetadata("unsafeVarianceAndCovariantProjection.kt") + public void testUnsafeVarianceAndCovariantProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/unsafeVarianceAndCovariantProjection.kt"); + } + } } @Nested diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirUpperBoundViolatedHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirUpperBoundViolatedHelpers.kt index 2b481492c7d..9c29402d0cb 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirUpperBoundViolatedHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirUpperBoundViolatedHelpers.kt @@ -21,7 +21,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.types.AbstractTypeChecker -import org.jetbrains.kotlin.types.TypeApproximatorConfiguration import kotlin.reflect.KClass /** @@ -88,14 +87,6 @@ private class FE10LikeConeSubstitutor( projection.type!!.updateNullabilityIfNeeded(type)?.withCombinedAttributesFrom(type) ?: return null - if (type.isUnsafeVarianceType(useSiteSession)) { - useSiteSession.typeApproximator.approximateToSuperType( - result, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference - )?.let { - return it.withProjection(projection) - } - } - return result.withProjection(projection) } diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/resolve/scopes/JvmMappedScopes.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/resolve/scopes/JvmMappedScopes.kt index b49ce1f243a..4c52578b664 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/resolve/scopes/JvmMappedScopes.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/resolve/scopes/JvmMappedScopes.kt @@ -11,8 +11,11 @@ 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.declarations.utils.classId -import org.jetbrains.kotlin.fir.resolve.* +import org.jetbrains.kotlin.fir.resolve.ScopeSession +import org.jetbrains.kotlin.fir.resolve.createSubstitutionForScope +import org.jetbrains.kotlin.fir.resolve.defaultType import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider +import org.jetbrains.kotlin.fir.resolve.scopeSessionKey import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope import org.jetbrains.kotlin.fir.scopes.FirTypeScope @@ -82,7 +85,7 @@ private fun wrapSubstitutionScopeIfNeed( // is called on an external type, like MyMap, // to determine parameter types properly (e.g. String, String instead of K, V) val platformTypeParameters = platformClass.typeParameters - val platformSubstitution = createSubstitution(platformTypeParameters, declaration.defaultType(), session) + val platformSubstitution = createSubstitutionForScope(platformTypeParameters, declaration.defaultType(), session) val substitutor = substitutorByMap(platformSubstitution, session) FirClassSubstitutionScope( session, useSiteMemberScope, PLATFORM_TYPE_PARAMETERS_SUBSTITUTION_SCOPE_KEY, substitutor, @@ -94,4 +97,3 @@ private fun wrapSubstitutionScopeIfNeed( } private val PLATFORM_TYPE_PARAMETERS_SUBSTITUTION_SCOPE_KEY = scopeSessionKey, FirTypeScope>() - diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt index 797e2c63671..5b94f8cb37f 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/ScopeUtils.kt @@ -119,7 +119,7 @@ private fun ConeClassLikeType.classScope( val substitutor = when { attributes.contains(CompilerConeAttributes.RawType) -> ConeRawScopeSubstitutor(useSiteSession) else -> substitutorByMap( - createSubstitution(fir.typeParameters, fullyExpandedType, useSiteSession), + createSubstitutionForScope(fir.typeParameters, fullyExpandedType, useSiteSession), useSiteSession, ) } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt index 4aa7b22895c..12c6846b401 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt @@ -18,10 +18,11 @@ import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.FirTypeScope import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag -import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase import org.jetbrains.kotlin.fir.types.* -import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.types.TypeApproximatorConfiguration +import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.model.CaptureStatus import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartSet @@ -178,29 +179,54 @@ inline fun scopeSessionKey(): ScopeSes val USE_SITE = scopeSessionKey, FirTypeScope>() /* TODO REMOVE */ -fun createSubstitution( +fun createSubstitutionForScope( typeParameters: List, // TODO: or really declared? type: ConeClassLikeType, session: FirSession ): Map { val capturedOrType = session.typeContext.captureFromArguments(type, CaptureStatus.FROM_EXPRESSION) ?: type - val typeArguments = (capturedOrType as ConeClassLikeType).typeArguments - return typeParameters.zip(typeArguments) { typeParameter, typeArgument -> - val typeParameterSymbol = typeParameter.symbol - typeParameterSymbol to when (typeArgument) { - is ConeKotlinTypeProjection -> { - typeArgument.type - } - else /* StarProjection */ -> { - ConeTypeIntersector.intersectTypes( - session.typeContext, - typeParameterSymbol.resolvedBounds.map { it.coneType } - ) - } + val capturedTypeArguments = (capturedOrType as ConeClassLikeType).typeArguments + + return typeParameters.withIndex().mapNotNull { (index, typeParameter) -> + val capturedTypeArgument = capturedTypeArguments.getOrNull(index) ?: return@mapNotNull null + require(capturedTypeArgument is ConeKotlinType) { + "There should left no projections after capture conversion, but $capturedTypeArgument found at $index" } + val originalTypeArgument = type.typeArguments.getOrNull(index) ?: return@mapNotNull null + + val typeParameterSymbol = typeParameter.symbol + val resultingArgument = + computeNonTrivialTypeArgumentForScopeSubstitutor(typeParameterSymbol, originalTypeArgument, session, capturedTypeArgument) + ?: capturedTypeArgument + + typeParameterSymbol to resultingArgument }.toMap() } +/** + * Returns null if `capturedTypeArgument` should be used + */ +private fun computeNonTrivialTypeArgumentForScopeSubstitutor( + typeParameterSymbol: FirTypeParameterSymbol, + originalTypeArgument: ConeTypeProjection, + session: FirSession, + capturedTypeArgument: ConeKotlinType +): ConeKotlinType? { + // We don't do anything for contravariant parameters (IN), because their UnsafeVariance usages are mostly return type. + // And if we continue using captured types for them, they will just be approximated (as return types) as they've been before. + if (typeParameterSymbol.variance != Variance.OUT_VARIANCE) return null + + return when (originalTypeArgument.kind) { + // Out is the same as Out + ProjectionKind.OUT -> originalTypeArgument.type!! + // Out<*> is the same as Out (i.e. Out) + ProjectionKind.STAR -> session.typeApproximator.approximateToSuperType( + capturedTypeArgument, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference + ) + else -> null + } +} + private fun ConeClassLikeType.computePartialExpansion( useSiteSession: FirSession, supertypeSupplier: SupertypeSupplier diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt index 54b9c67037f..a86b23bf840 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/resolve/substitution/Substitutors.kt @@ -16,7 +16,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.name.StandardClassIds -import org.jetbrains.kotlin.types.TypeApproximatorConfiguration import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.types.model.TypeVariableMarker @@ -204,16 +203,9 @@ class ConeSubstitutorByMap( override fun substituteType(type: ConeKotlinType): ConeKotlinType? { if (type !is ConeTypeParameterType) return null - val result = - substitution[type.lookupTag.symbol].updateNullabilityIfNeeded(type) - ?.withCombinedAttributesFrom(type) - ?: return null - if (type.isUnsafeVarianceType(useSiteSession)) { - return useSiteSession.typeApproximator.approximateToSuperType( - result, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference - ) ?: result - } - return result + return substitution[type.lookupTag.symbol].updateNullabilityIfNeeded(type) + ?.withCombinedAttributesFrom(type) + ?: return null } override fun equals(other: Any?): Boolean { diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirKotlinScopeProvider.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirKotlinScopeProvider.kt index 34a1bd7cf05..dd32065dd2d 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirKotlinScopeProvider.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/FirKotlinScopeProvider.kt @@ -159,7 +159,7 @@ fun ConeKotlinType.scopeForSupertype( private fun substitutor(symbol: FirRegularClassSymbol, type: ConeClassLikeType, useSiteSession: FirSession): ConeSubstitutor { if (type.typeArguments.isEmpty()) return ConeSubstitutor.Empty - val originalSubstitution = createSubstitution(symbol.fir.typeParameters, type, useSiteSession) + val originalSubstitution = createSubstitutionForScope(symbol.fir.typeParameters, type, useSiteSession) return substitutorByMap(originalSubstitution, useSiteSession) } 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 4710d95d69a..7b86f774b07 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 @@ -241,11 +241,6 @@ fun FirTypeRef.isExtensionFunctionType(session: FirSession): Boolean { return coneTypeSafe()?.isExtensionFunctionType(session) == true } -fun ConeKotlinType.isUnsafeVarianceType(session: FirSession): Boolean { - val type = this.coneLowerBoundIfFlexible().fullyExpandedType(session) - return type.attributes.unsafeVarianceType != null -} - fun ConeKotlinType.toSymbol(session: FirSession): FirClassifierSymbol<*>? { return (this as? ConeLookupTagBasedType)?.lookupTag?.toSymbol(session) } diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/unsafeVarianceInAliasedFunctionalType.fir.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/unsafeVarianceInAliasedFunctionalType.fir.kt deleted file mode 100644 index ca0d9acc14c..00000000000 --- a/compiler/testData/diagnostics/tests/generics/projectionsScope/unsafeVarianceInAliasedFunctionalType.fir.kt +++ /dev/null @@ -1,11 +0,0 @@ -class Foo(val baz: Baz) - -class Bar { - val foo: Foo<*> = TODO() - - fun bar(): Baz { - return foo.baz - } -} - -typealias Baz = (@UnsafeVariance T) -> Unit diff --git a/compiler/testData/diagnostics/tests/generics/projectionsScope/unsafeVarianceInAliasedFunctionalType.kt b/compiler/testData/diagnostics/tests/generics/projectionsScope/unsafeVarianceInAliasedFunctionalType.kt index 05ad642e149..5bdf285f2ea 100644 --- a/compiler/testData/diagnostics/tests/generics/projectionsScope/unsafeVarianceInAliasedFunctionalType.kt +++ b/compiler/testData/diagnostics/tests/generics/projectionsScope/unsafeVarianceInAliasedFunctionalType.kt @@ -1,3 +1,5 @@ +// FIR_IDENTICAL +// ISSUE: KT-54894 class Foo(val baz: Baz) class Bar { diff --git a/compiler/testData/diagnostics/tests/inference/capturedTypes/capturedTypesSubstitutionIntoAbbreviation.fir.kt b/compiler/testData/diagnostics/tests/inference/capturedTypes/capturedTypesSubstitutionIntoAbbreviation.fir.kt index d7561d83ff1..80876c7b852 100644 --- a/compiler/testData/diagnostics/tests/inference/capturedTypes/capturedTypesSubstitutionIntoAbbreviation.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/capturedTypes/capturedTypesSubstitutionIntoAbbreviation.fir.kt @@ -25,7 +25,7 @@ fun main() { ")!>getTag3().action ")!>getTag4().action ")!>getTag5().action - ")!>getTag6().action - ")!>getTag7().action - ")!>getTag8().action + ")!>getTag6().action + ")!>getTag7().action + ")!>getTag8().action } diff --git a/compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.fir.kt b/compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.fir.kt new file mode 100644 index 00000000000..1c3571725fc --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.fir.kt @@ -0,0 +1,13 @@ +// ISSUE: KT-54764 + +data class Out(val prop: T) + +fun foo(b: Out<*>) { + b.copy("") // error in K1, OK in K2 +} + +fun foo(a: Any) { + if (a is Out<*>) { + a.copy("") + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.kt b/compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.kt new file mode 100644 index 00000000000..315b2e2f7b4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.kt @@ -0,0 +1,13 @@ +// ISSUE: KT-54764 + +data class Out(val prop: T) + +fun foo(b: Out<*>) { + b.copy("") // error in K1, OK in K2 +} + +fun foo(a: Any) { + if (a is Out<*>) { + a.copy("") + } +} diff --git a/compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.fir.kt b/compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.fir.kt new file mode 100644 index 00000000000..0b49a5bc7cc --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.fir.kt @@ -0,0 +1,13 @@ +// ISSUE: KT-54764 + +interface A, F, out E : B> { + fun copy(t: @UnsafeVariance T, f: F, e: @UnsafeVariance E): A +} + +interface B + +fun foo(a: A<*, String, B<*, String>>, b1: B<*, String>, b2: B<*, *>, b3: B>) { + >, kotlin.String, B<*, kotlin.String>>")!>a.copy(b2, "", b1) + >, kotlin.String, B<*, kotlin.String>>")!>a.copy(b1, "", b1) + >, kotlin.String, B<*, kotlin.String>>")!>a.copy(b3, "", b1) +} diff --git a/compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.kt b/compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.kt new file mode 100644 index 00000000000..a1f52a6e034 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.kt @@ -0,0 +1,13 @@ +// ISSUE: KT-54764 + +interface A, F, out E : B> { + fun copy(t: @UnsafeVariance T, f: F, e: @UnsafeVariance E): A +} + +interface B + +fun foo(a: A<*, String, B<*, String>>, b1: B<*, String>, b2: B<*, *>, b3: B>) { + , kotlin.String, B>")!>a.copy(b2, "", b1) + , kotlin.String, B>")!>a.copy(b1, "", b1) + , kotlin.String, B>")!>a.copy(b3, "", b1) +} diff --git a/compiler/testData/diagnostics/tests/scopes/variantProjections/unsafeVarianceAndCovariantProjection.kt b/compiler/testData/diagnostics/tests/scopes/variantProjections/unsafeVarianceAndCovariantProjection.kt new file mode 100644 index 00000000000..0f96c0b4343 --- /dev/null +++ b/compiler/testData/diagnostics/tests/scopes/variantProjections/unsafeVarianceAndCovariantProjection.kt @@ -0,0 +1,32 @@ +// FIR_IDENTICAL +// ISSUE: KT-54764 + +interface Out1 { + fun copy(t: @UnsafeVariance T): Out1 +} + +fun foo1(o1: Out1<*>, o2: Out1<out Any?>, o3: Out1) { + ")!>o1.copy("") + ")!>o2.copy("") + ")!>o3.copy("") +} + +interface Out2 { + fun copy(t: @UnsafeVariance T): Out2 +} + +fun foo2(o1: Out2<*>, o2: Out2<out CharSequence>, o3: Out2) { + ")!>o1.copy("") + ")!>o2.copy("") + ")!>o3.copy("") +} + +interface Out3> { + fun copy(t: @UnsafeVariance T): Out3 +} + +fun foo3(o1: Out3<*>, o2: Out3<out Out3<*>>, o3: Out3>) { + >")!>o1.copy(o1) + >")!>o2.copy(o1) + >")!>o3.copy(o1) +} diff --git a/compiler/testData/ir/irText/types/typeAliasWithUnsafeVariance.fir.ir.txt b/compiler/testData/ir/irText/types/typeAliasWithUnsafeVariance.fir.ir.txt index 55c42ca5057..38f6289f35f 100644 --- a/compiler/testData/ir/irText/types/typeAliasWithUnsafeVariance.fir.ir.txt +++ b/compiler/testData/ir/irText/types/typeAliasWithUnsafeVariance.fir.ir.txt @@ -99,5 +99,5 @@ FILE fqName: fileName:/typeAliasWithUnsafeVariance.kt FUN name:doAction visibility:public modality:FINAL <> () returnType:kotlin.Unit BLOCK_BODY TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit - CALL 'public final fun (): kotlin.Function1.Tag, kotlin.Unit> declared in .Tag' type=kotlin.Function1 origin=GET_PROPERTY + CALL 'public final fun (): kotlin.Function1.Tag, kotlin.Unit> declared in .Tag' type=kotlin.Function1 origin=GET_PROPERTY $this: CALL 'public final fun getTag (): .Tag<*> declared in ' type=.Tag<*> origin=null 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 48c59bb8afe..a4555d1ac09 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 @@ -28909,6 +28909,34 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt"); } } + + @Nested + @TestMetadata("compiler/testData/diagnostics/tests/scopes/variantProjections") + @TestDataPath("$PROJECT_ROOT") + public class VariantProjections { + @Test + public void testAllFilesPresentInVariantProjections() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/scopes/variantProjections"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.(reversed|fir|ll)\\.kts?$"), true); + } + + @Test + @TestMetadata("dataClassCopy.kt") + public void testDataClassCopy() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/dataClassCopy.kt"); + } + + @Test + @TestMetadata("interdependentStarProjections.kt") + public void testInterdependentStarProjections() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/interdependentStarProjections.kt"); + } + + @Test + @TestMetadata("unsafeVarianceAndCovariantProjection.kt") + public void testUnsafeVarianceAndCovariantProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/scopes/variantProjections/unsafeVarianceAndCovariantProjection.kt"); + } + } } @Nested diff --git a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/9.fir.kt b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/9.fir.kt index ba61554b849..5108945f4a5 100644 --- a/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/9.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/notLinked/dfa/pos/9.fir.kt @@ -26,26 +26,26 @@ fun case_2(a: Out<out Out<out if (a != null) { val b = ?>?>?>?>?>? & Out?>?>?>?>?>")!>a.get() if (b != null) { - val c = ?>?>?>?>? & Out?>?>?>?>")!>b.get() + val c = ?>?>?>?>? & Out?>?>?>?>")!>b.get() if (c != null) { - val d = ?>?>?>? & Out?>?>?>")!>c.get() + val d = ?>?>?>? & Out?>?>?>")!>c.get() if (d != null) { - val e = ?>?>? & Out?>?>")!>d.get() + val e = ?>?>? & Out?>?>")!>d.get() if (e != null) { - val f = ?>? & Out?>")!>e.get() + val f = ?>? & Out?>")!>e.get() if (f != null) { - val g = ? & Out")!>f.get() + val g = ? & Out")!>f.get() if (g != null) { - g - g.equals(null) - g.propT - g.propAny - g.propNullableT - g.propNullableAny - g.funT() - g.funAny() - g.funNullableT() - g.funNullableAny() + g + g.equals(null) + g.propT + g.propAny + g.propNullableT + g.propNullableAny + g.funT() + g.funAny() + g.funNullableT() + g.funNullableAny() } } }