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<Captured(*)> <: Out<Supertype> and when we've got @UnsafeVariance
value parameters at Out, it's ok to allow passing Supertype there.

^KT-57602 Fixed
^KT-54894 Fixed
This commit is contained in:
Denis.Zharkov
2023-03-24 17:21:03 +01:00
committed by Space Team
parent 1cd040fd6a
commit 55a58e54fe
25 changed files with 300 additions and 79 deletions
@@ -28149,6 +28149,34 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
runTest("compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt"); 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 @Nested
@@ -28149,6 +28149,34 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
runTest("compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt"); 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 @Nested
@@ -185,7 +185,7 @@ digraph complex_kt {
color=blue color=blue
55 [label="Enter block"]; 55 [label="Enter block"];
56 [label="Access variable this@R|/firstIsInstanceOrNull|"]; 56 [label="Access variable this@R|/firstIsInstanceOrNull|"];
57 [label="Function call: this@R|/firstIsInstanceOrNull|.R|SubstitutionOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<CapturedType(*)>|>|()" style="filled" fillcolor=yellow]; 57 [label="Function call: this@R|/firstIsInstanceOrNull|.R|SubstitutionOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<kotlin/Any?>|>|()" style="filled" fillcolor=yellow];
58 [label="Variable declaration: lval <iterator>: R|kotlin/collections/Iterator<kotlin/Any?>|"]; 58 [label="Variable declaration: lval <iterator>: R|kotlin/collections/Iterator<kotlin/Any?>|"];
subgraph cluster_19 { subgraph cluster_19 {
color=blue color=blue
@@ -24,7 +24,7 @@ FILE: complex.kt
} }
public final inline fun <reified T : R|kotlin/Any|> R|kotlin/collections/List<*>|.firstIsInstanceOrNull(): R|T?| { public final inline fun <reified T : R|kotlin/Any|> R|kotlin/collections/List<*>|.firstIsInstanceOrNull(): R|T?| {
{ {
lval <iterator>: R|kotlin/collections/Iterator<kotlin/Any?>| = this@R|/firstIsInstanceOrNull|.R|SubstitutionOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<CapturedType(*)>|>|() lval <iterator>: R|kotlin/collections/Iterator<kotlin/Any?>| = this@R|/firstIsInstanceOrNull|.R|SubstitutionOverride<kotlin/collections/List.iterator: R|kotlin/collections/Iterator<kotlin/Any?>|>|()
while(R|<local>/<iterator>|.R|SubstitutionOverride<kotlin/collections/Iterator.hasNext: R|kotlin/Boolean|>|()) { while(R|<local>/<iterator>|.R|SubstitutionOverride<kotlin/collections/Iterator.hasNext: R|kotlin/Boolean|>|()) {
lval element: R|kotlin/Any?| = R|<local>/<iterator>|.R|SubstitutionOverride<kotlin/collections/Iterator.next: R|kotlin/Any?|>|() lval element: R|kotlin/Any?| = R|<local>/<iterator>|.R|SubstitutionOverride<kotlin/collections/Iterator.next: R|kotlin/Any?|>|()
{ {
@@ -77,7 +77,7 @@ FILE: typeAliasWithTypeArguments.kt
public final fun test_6(a: R|A|, out1: R|Out1<A>|, out2: R|Out1<in A>|, out3: R|Out1<out A>|): R|kotlin/Unit| { public final fun test_6(a: R|A|, out1: R|Out1<A>|, out2: R|Out1<in A>|, out3: R|Out1<out A>|): R|kotlin/Unit| {
R|<local>/out1|.R|SubstitutionOverride</Out.value: R|A|>|().R|/A.foo|() R|<local>/out1|.R|SubstitutionOverride</Out.value: R|A|>|().R|/A.foo|()
R|<local>/out2|.<Unresolved name: value>#().<Unresolved name: foo>#() R|<local>/out2|.<Unresolved name: value>#().<Unresolved name: foo>#()
R|<local>/out3|.R|SubstitutionOverride</Out.value: R|CapturedType(out A)|>|().R|/A.foo|() R|<local>/out3|.R|SubstitutionOverride</Out.value: R|A|>|().R|/A.foo|()
} }
public final fun test_7(a: R|A|, inv1: R|Invariant1<A>|, inv2: R|Invariant1<in A>|, inv3: R|Invariant1<out A>|): R|kotlin/Unit| { public final fun test_7(a: R|A|, inv1: R|Invariant1<A>|, inv2: R|Invariant1<in A>|, inv3: R|Invariant1<out A>|): R|kotlin/Unit| {
R|<local>/inv1|.R|SubstitutionOverride</Invariant.value: R|A|>|().R|/A.foo|() R|<local>/inv1|.R|SubstitutionOverride</Invariant.value: R|A|>|().R|/A.foo|()
@@ -28149,6 +28149,34 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
runTest("compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt"); 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 @Nested
@@ -28161,6 +28161,34 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
runTest("compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt"); 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 @Nested
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
@@ -88,14 +87,6 @@ private class FE10LikeConeSubstitutor(
projection.type!!.updateNullabilityIfNeeded(type)?.withCombinedAttributesFrom(type) projection.type!!.updateNullabilityIfNeeded(type)?.withCombinedAttributesFrom(type)
?: return null ?: return null
if (type.isUnsafeVarianceType(useSiteSession)) {
useSiteSession.typeApproximator.approximateToSuperType(
result, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference
)?.let {
return it.withProjection(projection)
}
}
return result.withProjection(projection) return result.withProjection(projection)
} }
@@ -11,8 +11,11 @@ import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirRegularClass import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.utils.classId 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.providers.symbolProvider
import org.jetbrains.kotlin.fir.resolve.scopeSessionKey
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope import org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScope
import org.jetbrains.kotlin.fir.scopes.FirTypeScope import org.jetbrains.kotlin.fir.scopes.FirTypeScope
@@ -82,7 +85,7 @@ private fun wrapSubstitutionScopeIfNeed(
// is called on an external type, like MyMap<String, String>, // is called on an external type, like MyMap<String, String>,
// to determine parameter types properly (e.g. String, String instead of K, V) // to determine parameter types properly (e.g. String, String instead of K, V)
val platformTypeParameters = platformClass.typeParameters val platformTypeParameters = platformClass.typeParameters
val platformSubstitution = createSubstitution(platformTypeParameters, declaration.defaultType(), session) val platformSubstitution = createSubstitutionForScope(platformTypeParameters, declaration.defaultType(), session)
val substitutor = substitutorByMap(platformSubstitution, session) val substitutor = substitutorByMap(platformSubstitution, session)
FirClassSubstitutionScope( FirClassSubstitutionScope(
session, useSiteMemberScope, PLATFORM_TYPE_PARAMETERS_SUBSTITUTION_SCOPE_KEY, substitutor, 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<FirClassSymbol<*>, FirTypeScope>() private val PLATFORM_TYPE_PARAMETERS_SUBSTITUTION_SCOPE_KEY = scopeSessionKey<FirClassSymbol<*>, FirTypeScope>()
@@ -119,7 +119,7 @@ private fun ConeClassLikeType.classScope(
val substitutor = when { val substitutor = when {
attributes.contains(CompilerConeAttributes.RawType) -> ConeRawScopeSubstitutor(useSiteSession) attributes.contains(CompilerConeAttributes.RawType) -> ConeRawScopeSubstitutor(useSiteSession)
else -> substitutorByMap( else -> substitutorByMap(
createSubstitution(fir.typeParameters, fullyExpandedType, useSiteSession), createSubstitutionForScope(fir.typeParameters, fullyExpandedType, useSiteSession),
useSiteSession, useSiteSession,
) )
} }
@@ -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.FirScope
import org.jetbrains.kotlin.fir.scopes.FirTypeScope import org.jetbrains.kotlin.fir.scopes.FirTypeScope
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag 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.impl.*
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.* 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.types.model.CaptureStatus
import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.SmartSet import org.jetbrains.kotlin.utils.SmartSet
@@ -178,29 +179,54 @@ inline fun <reified ID : Any, reified FS : FirScope> scopeSessionKey(): ScopeSes
val USE_SITE = scopeSessionKey<FirClassSymbol<*>, FirTypeScope>() val USE_SITE = scopeSessionKey<FirClassSymbol<*>, FirTypeScope>()
/* TODO REMOVE */ /* TODO REMOVE */
fun createSubstitution( fun createSubstitutionForScope(
typeParameters: List<FirTypeParameterRef>, // TODO: or really declared? typeParameters: List<FirTypeParameterRef>, // TODO: or really declared?
type: ConeClassLikeType, type: ConeClassLikeType,
session: FirSession session: FirSession
): Map<FirTypeParameterSymbol, ConeKotlinType> { ): Map<FirTypeParameterSymbol, ConeKotlinType> {
val capturedOrType = session.typeContext.captureFromArguments(type, CaptureStatus.FROM_EXPRESSION) ?: type val capturedOrType = session.typeContext.captureFromArguments(type, CaptureStatus.FROM_EXPRESSION) ?: type
val typeArguments = (capturedOrType as ConeClassLikeType).typeArguments val capturedTypeArguments = (capturedOrType as ConeClassLikeType).typeArguments
return typeParameters.zip(typeArguments) { typeParameter, typeArgument ->
val typeParameterSymbol = typeParameter.symbol return typeParameters.withIndex().mapNotNull { (index, typeParameter) ->
typeParameterSymbol to when (typeArgument) { val capturedTypeArgument = capturedTypeArguments.getOrNull(index) ?: return@mapNotNull null
is ConeKotlinTypeProjection -> { require(capturedTypeArgument is ConeKotlinType) {
typeArgument.type "There should left no projections after capture conversion, but $capturedTypeArgument found at $index"
}
else /* StarProjection */ -> {
ConeTypeIntersector.intersectTypes(
session.typeContext,
typeParameterSymbol.resolvedBounds.map { it.coneType }
)
}
} }
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() }.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<out T> is the same as Out<T>
ProjectionKind.OUT -> originalTypeArgument.type!!
// Out<*> is the same as Out<SubstitutedUpperBounds> (i.e. Out<Supertype(CapturedType(*))>)
ProjectionKind.STAR -> session.typeApproximator.approximateToSuperType(
capturedTypeArgument, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference
)
else -> null
}
}
private fun ConeClassLikeType.computePartialExpansion( private fun ConeClassLikeType.computePartialExpansion(
useSiteSession: FirSession, useSiteSession: FirSession,
supertypeSupplier: SupertypeSupplier supertypeSupplier: SupertypeSupplier
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.name.StandardClassIds 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.TypeConstructorMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
import org.jetbrains.kotlin.types.model.TypeVariableMarker import org.jetbrains.kotlin.types.model.TypeVariableMarker
@@ -204,16 +203,9 @@ class ConeSubstitutorByMap(
override fun substituteType(type: ConeKotlinType): ConeKotlinType? { override fun substituteType(type: ConeKotlinType): ConeKotlinType? {
if (type !is ConeTypeParameterType) return null if (type !is ConeTypeParameterType) return null
val result = return substitution[type.lookupTag.symbol].updateNullabilityIfNeeded(type)
substitution[type.lookupTag.symbol].updateNullabilityIfNeeded(type) ?.withCombinedAttributesFrom(type)
?.withCombinedAttributesFrom(type) ?: return null
?: return null
if (type.isUnsafeVarianceType(useSiteSession)) {
return useSiteSession.typeApproximator.approximateToSuperType(
result, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference
) ?: result
}
return result
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@@ -159,7 +159,7 @@ fun ConeKotlinType.scopeForSupertype(
private fun substitutor(symbol: FirRegularClassSymbol, type: ConeClassLikeType, useSiteSession: FirSession): ConeSubstitutor { private fun substitutor(symbol: FirRegularClassSymbol, type: ConeClassLikeType, useSiteSession: FirSession): ConeSubstitutor {
if (type.typeArguments.isEmpty()) return ConeSubstitutor.Empty 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) return substitutorByMap(originalSubstitution, useSiteSession)
} }
@@ -241,11 +241,6 @@ fun FirTypeRef.isExtensionFunctionType(session: FirSession): Boolean {
return coneTypeSafe<ConeKotlinType>()?.isExtensionFunctionType(session) == true return coneTypeSafe<ConeKotlinType>()?.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<*>? { fun ConeKotlinType.toSymbol(session: FirSession): FirClassifierSymbol<*>? {
return (this as? ConeLookupTagBasedType)?.lookupTag?.toSymbol(session) return (this as? ConeLookupTagBasedType)?.lookupTag?.toSymbol(session)
} }
@@ -1,11 +0,0 @@
class Foo<out T>(val baz: Baz<T>)
class Bar {
val foo: Foo<*> = TODO()
fun <T> bar(): Baz<T> {
return <!RETURN_TYPE_MISMATCH!>foo.baz<!>
}
}
typealias Baz<T> = (@UnsafeVariance T) -> Unit
@@ -1,3 +1,5 @@
// FIR_IDENTICAL
// ISSUE: KT-54894
class Foo<out T>(val baz: Baz<T>) class Foo<out T>(val baz: Baz<T>)
class Bar { class Bar {
@@ -25,7 +25,7 @@ fun main() {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.Nothing, kotlin.Unit>")!>getTag3().action<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.Nothing, kotlin.Unit>")!>getTag3().action<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.Nothing, kotlin.Unit>")!>getTag4().action<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.Nothing, kotlin.Unit>")!>getTag4().action<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.Nothing, kotlin.Any?>")!>getTag5().action<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.Nothing, kotlin.Any?>")!>getTag5().action<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.Nothing, kotlin.Unit>")!>getTag6().action<!> <!DEBUG_INFO_EXPRESSION_TYPE("Action<in kotlin.Any?>")!>getTag6().action<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.Nothing, kotlin.Unit>")!>getTag7().action<!> <!DEBUG_INFO_EXPRESSION_TYPE("Action<kotlin.Any?>")!>getTag7().action<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Function1<kotlin.Nothing, kotlin.Any?>")!>getTag8().action<!> <!DEBUG_INFO_EXPRESSION_TYPE("Action2<kotlin.Any?>")!>getTag8().action<!>
} }
@@ -0,0 +1,13 @@
// ISSUE: KT-54764
data class Out<out T>(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("")
}
}
@@ -0,0 +1,13 @@
// ISSUE: KT-54764
data class Out<out T>(val prop: T)
fun foo(b: Out<*>) {
b.copy(<!TYPE_MISMATCH!>""<!>) // error in K1, OK in K2
}
fun foo(a: Any) {
if (a is Out<*>) {
<!DEBUG_INFO_SMARTCAST!>a<!>.copy("")
}
}
@@ -0,0 +1,13 @@
// ISSUE: KT-54764
interface A<out T : B<F, E>, F, out E : B<T, F>> {
fun copy(t: @UnsafeVariance T, f: F, e: @UnsafeVariance E): A<T, F, E>
}
interface B<out X, out Y>
fun foo(a: A<*, String, B<*, String>>, b1: B<*, String>, b2: B<*, *>, b3: B<String, B<*, String>>) {
<!DEBUG_INFO_EXPRESSION_TYPE("A<B<kotlin.String, B<*, kotlin.String>>, kotlin.String, B<*, kotlin.String>>")!>a.copy(<!ARGUMENT_TYPE_MISMATCH!>b2<!>, "", b1)<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<B<kotlin.String, B<*, kotlin.String>>, kotlin.String, B<*, kotlin.String>>")!>a.copy(<!ARGUMENT_TYPE_MISMATCH!>b1<!>, "", b1)<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<B<kotlin.String, B<*, kotlin.String>>, kotlin.String, B<*, kotlin.String>>")!>a.copy(b3, "", b1)<!>
}
@@ -0,0 +1,13 @@
// ISSUE: KT-54764
interface A<out T : B<F, E>, F, out E : B<T, F>> {
fun copy(t: @UnsafeVariance T, f: F, e: @UnsafeVariance E): A<T, F, E>
}
interface B<out X, out Y>
fun foo(a: A<*, String, B<*, String>>, b1: B<*, String>, b2: B<*, *>, b3: B<String, B<*, String>>) {
<!DEBUG_INFO_EXPRESSION_TYPE("A<B<*, *>, kotlin.String, B<kotlin.Any?, kotlin.String>>")!>a.copy(b2, "", b1)<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<B<*, *>, kotlin.String, B<kotlin.Any?, kotlin.String>>")!>a.copy(b1, "", b1)<!>
<!DEBUG_INFO_EXPRESSION_TYPE("A<B<*, *>, kotlin.String, B<kotlin.Any?, kotlin.String>>")!>a.copy(b3, "", b1)<!>
}
@@ -0,0 +1,32 @@
// FIR_IDENTICAL
// ISSUE: KT-54764
interface Out1<out T> {
fun copy(t: @UnsafeVariance T): Out1<T>
}
fun foo1(o1: Out1<*>, o2: Out1<<!REDUNDANT_PROJECTION!>out<!> Any?>, o3: Out1<Any?>) {
<!DEBUG_INFO_EXPRESSION_TYPE("Out1<kotlin.Any?>")!>o1.copy("")<!>
<!DEBUG_INFO_EXPRESSION_TYPE("Out1<kotlin.Any?>")!>o2.copy("")<!>
<!DEBUG_INFO_EXPRESSION_TYPE("Out1<kotlin.Any?>")!>o3.copy("")<!>
}
interface Out2<out T : CharSequence> {
fun copy(t: @UnsafeVariance T): Out2<T>
}
fun foo2(o1: Out2<*>, o2: Out2<<!REDUNDANT_PROJECTION!>out<!> CharSequence>, o3: Out2<CharSequence>) {
<!DEBUG_INFO_EXPRESSION_TYPE("Out2<kotlin.CharSequence>")!>o1.copy("")<!>
<!DEBUG_INFO_EXPRESSION_TYPE("Out2<kotlin.CharSequence>")!>o2.copy("")<!>
<!DEBUG_INFO_EXPRESSION_TYPE("Out2<kotlin.CharSequence>")!>o3.copy("")<!>
}
interface Out3<out T : Out3<T>> {
fun copy(t: @UnsafeVariance T): Out3<T>
}
fun foo3(o1: Out3<*>, o2: Out3<<!REDUNDANT_PROJECTION!>out<!> Out3<*>>, o3: Out3<Out3<*>>) {
<!DEBUG_INFO_EXPRESSION_TYPE("Out3<Out3<*>>")!>o1.copy(o1)<!>
<!DEBUG_INFO_EXPRESSION_TYPE("Out3<Out3<*>>")!>o2.copy(o1)<!>
<!DEBUG_INFO_EXPRESSION_TYPE("Out3<Out3<*>>")!>o3.copy(o1)<!>
}
@@ -99,5 +99,5 @@ FILE fqName:<root> fileName:/typeAliasWithUnsafeVariance.kt
FUN name:doAction visibility:public modality:FINAL <> () returnType:kotlin.Unit FUN name:doAction visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY BLOCK_BODY
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
CALL 'public final fun <get-action> (): kotlin.Function1<RenderingT of <root>.Tag, kotlin.Unit> declared in <root>.Tag' type=kotlin.Function1<kotlin.Nothing, kotlin.Unit> origin=GET_PROPERTY CALL 'public final fun <get-action> (): kotlin.Function1<RenderingT of <root>.Tag, kotlin.Unit> declared in <root>.Tag' type=kotlin.Function1<kotlin.Any?, kotlin.Unit> origin=GET_PROPERTY
$this: CALL 'public final fun getTag (): <root>.Tag<*> declared in <root>' type=<root>.Tag<*> origin=null $this: CALL 'public final fun getTag (): <root>.Tag<*> declared in <root>' type=<root>.Tag<*> origin=null
@@ -28909,6 +28909,34 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/scopes/protectedVisibility/withSmartcast.kt"); 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 @Nested
@@ -26,26 +26,26 @@ fun case_2(a: Out<<!REDUNDANT_PROJECTION!>out<!> Out<<!REDUNDANT_PROJECTION!>out
if (a != null) { if (a != null) {
val b = <!DEBUG_INFO_EXPRESSION_TYPE("Out<out Out<out Out<out Out<out Out<out Out<out kotlin.Int?>?>?>?>?>?>? & Out<out Out<out Out<out Out<out Out<out Out<out kotlin.Int?>?>?>?>?>?>")!>a<!>.get() val b = <!DEBUG_INFO_EXPRESSION_TYPE("Out<out Out<out Out<out Out<out Out<out Out<out kotlin.Int?>?>?>?>?>?>? & Out<out Out<out Out<out Out<out Out<out Out<out kotlin.Int?>?>?>?>?>?>")!>a<!>.get()
if (b != null) { if (b != null) {
val c = <!DEBUG_INFO_EXPRESSION_TYPE("Out<Out<Out<Out<Out<kotlin.Any?>?>?>?>?>? & Out<Out<Out<Out<Out<kotlin.Any?>?>?>?>?>")!>b<!>.get() val c = <!DEBUG_INFO_EXPRESSION_TYPE("Out<out Out<out Out<out Out<out Out<out kotlin.Int?>?>?>?>?>? & Out<out Out<out Out<out Out<out Out<out kotlin.Int?>?>?>?>?>")!>b<!>.get()
if (c != null) { if (c != null) {
val d = <!DEBUG_INFO_EXPRESSION_TYPE("Out<Out<Out<Out<kotlin.Any?>?>?>?>? & Out<Out<Out<Out<kotlin.Any?>?>?>?>")!>c<!>.get() val d = <!DEBUG_INFO_EXPRESSION_TYPE("Out<out Out<out Out<out Out<out kotlin.Int?>?>?>?>? & Out<out Out<out Out<out Out<out kotlin.Int?>?>?>?>")!>c<!>.get()
if (d != null) { if (d != null) {
val e = <!DEBUG_INFO_EXPRESSION_TYPE("Out<Out<Out<kotlin.Any?>?>?>? & Out<Out<Out<kotlin.Any?>?>?>")!>d<!>.get() val e = <!DEBUG_INFO_EXPRESSION_TYPE("Out<out Out<out Out<out kotlin.Int?>?>?>? & Out<out Out<out Out<out kotlin.Int?>?>?>")!>d<!>.get()
if (e != null) { if (e != null) {
val f = <!DEBUG_INFO_EXPRESSION_TYPE("Out<Out<kotlin.Any?>?>? & Out<Out<kotlin.Any?>?>")!>e<!>.get() val f = <!DEBUG_INFO_EXPRESSION_TYPE("Out<out Out<out kotlin.Int?>?>? & Out<out Out<out kotlin.Int?>?>")!>e<!>.get()
if (f != null) { if (f != null) {
val g = <!DEBUG_INFO_EXPRESSION_TYPE("Out<kotlin.Any?>? & Out<kotlin.Any?>")!>f<!>.get() val g = <!DEBUG_INFO_EXPRESSION_TYPE("Out<out kotlin.Int?>? & Out<out kotlin.Int?>")!>f<!>.get()
if (g != null) { if (g != null) {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>g<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>g<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>g<!>.equals(null) <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>g<!>.equals(null)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>g<!>.propT <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>g<!>.propT
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>g<!>.propAny <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>g<!>.propAny
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>g<!>.propNullableT <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>g<!>.propNullableT
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>g<!>.propNullableAny <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>g<!>.propNullableAny
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>g<!>.funT() <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>g<!>.funT()
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>g<!>.funAny() <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>g<!>.funAny()
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>g<!>.funNullableT() <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>g<!>.funNullableT()
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>g<!>.funNullableAny() <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int? & kotlin.Int")!>g<!>.funNullableAny()
} }
} }
} }