FIR: Weaken some UPPER_BOUND_VIOLATED restrictions
See test data at starProjectionInsteadOutCaptured.kt ^KT-49412 Fixed ^KT-50230 Relates ^KT-48044 Fixed
This commit is contained in:
+18
@@ -13881,6 +13881,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
|||||||
runTest("compiler/testData/diagnostics/tests/inference/underscoredTypeInForbiddenPositions.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/underscoredTypeInForbiddenPositions.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unsoundness1.kt")
|
||||||
|
public void testUnsoundness1() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/unsoundness1.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unsoundness2.kt")
|
||||||
|
public void testUnsoundness2() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/unsoundness2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("useFunctionLiteralsToInferType.kt")
|
@TestMetadata("useFunctionLiteralsToInferType.kt")
|
||||||
public void testUseFunctionLiteralsToInferType() throws Exception {
|
public void testUseFunctionLiteralsToInferType() throws Exception {
|
||||||
@@ -31083,6 +31095,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
|||||||
runTest("compiler/testData/diagnostics/tests/typeParameters/repeatedBound.kt");
|
runTest("compiler/testData/diagnostics/tests/typeParameters/repeatedBound.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("starProjectionInsteadOutCaptured.kt")
|
||||||
|
public void testStarProjectionInsteadOutCaptured() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/typeParameters/starProjectionInsteadOutCaptured.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("upperBoundCannotBeArray.kt")
|
@TestMetadata("upperBoundCannotBeArray.kt")
|
||||||
public void testUpperBoundCannotBeArray() throws Exception {
|
public void testUpperBoundCannotBeArray() throws Exception {
|
||||||
|
|||||||
+18
@@ -140,3 +140,21 @@ FILE: upperBoundViolated.kt
|
|||||||
|
|
||||||
}
|
}
|
||||||
public final typealias Alias<V1> = R|(Class<V1>) -> kotlin/Boolean|
|
public final typealias Alias<V1> = R|(Class<V1>) -> kotlin/Boolean|
|
||||||
|
public abstract class Base<T : R|Base<T>|> : R|kotlin/Any| {
|
||||||
|
public constructor<T : R|Base<T>|>(): R|Base<T>| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public final class DerivedOut<out O : R|Base<out O>|> : R|kotlin/Any| {
|
||||||
|
public constructor<out O : R|Base<out O>|>(): R|DerivedOut<O>| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public final class DerivedIn<in I : R|Base<in I>|> : R|kotlin/Any| {
|
||||||
|
public constructor<in I : R|Base<in I>|>(): R|DerivedIn<I>| {
|
||||||
|
super<R|kotlin/Any|>()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
+2
-3
@@ -55,7 +55,7 @@ class Test1<S1 : Test1<S1, K>, K : Any>
|
|||||||
class Test2<S2 : Test1<S2, *>>
|
class Test2<S2 : Test1<S2, *>>
|
||||||
|
|
||||||
class Test3<S3 : Test3<S3, in K>, K : Any>
|
class Test3<S3 : Test3<S3, in K>, K : Any>
|
||||||
class Test4<S4 : Test3<<!UPPER_BOUND_VIOLATED!>S4<!>, out Any>>
|
class Test4<S4 : Test3<S4, out Any>>
|
||||||
|
|
||||||
class Test5<S5 : Test5<S5, in K>, K : Any>
|
class Test5<S5 : Test5<S5, in K>, K : Any>
|
||||||
class Test6<S6 : Test5<S6, in Any>>
|
class Test6<S6 : Test5<S6, in Any>>
|
||||||
@@ -66,9 +66,8 @@ class Test8<S8 : Test7<S8, <!UPPER_BOUND_VIOLATED!>in Any<!>>>
|
|||||||
class Class<V : Any>
|
class Class<V : Any>
|
||||||
typealias Alias <V1> = (Class<V1>) -> Boolean
|
typealias Alias <V1> = (Class<V1>) -> Boolean
|
||||||
|
|
||||||
/* TODO: Should not be errors. Uncomment after fixing of https://youtrack.jetbrains.com/issue/KT-48044
|
|
||||||
abstract class Base<T : Base<T>> {}
|
abstract class Base<T : Base<T>> {}
|
||||||
class DerivedOut<out O : Base<out O>> {}
|
class DerivedOut<out O : Base<out O>> {}
|
||||||
class DerivedIn<in I : Base<in I>> {}*/
|
class DerivedIn<in I : Base<in I>> {}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+18
@@ -13881,6 +13881,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/inference/underscoredTypeInForbiddenPositions.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/underscoredTypeInForbiddenPositions.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unsoundness1.kt")
|
||||||
|
public void testUnsoundness1() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/unsoundness1.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unsoundness2.kt")
|
||||||
|
public void testUnsoundness2() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/unsoundness2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("useFunctionLiteralsToInferType.kt")
|
@TestMetadata("useFunctionLiteralsToInferType.kt")
|
||||||
public void testUseFunctionLiteralsToInferType() throws Exception {
|
public void testUseFunctionLiteralsToInferType() throws Exception {
|
||||||
@@ -31083,6 +31095,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/typeParameters/repeatedBound.kt");
|
runTest("compiler/testData/diagnostics/tests/typeParameters/repeatedBound.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("starProjectionInsteadOutCaptured.kt")
|
||||||
|
public void testStarProjectionInsteadOutCaptured() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/typeParameters/starProjectionInsteadOutCaptured.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("upperBoundCannotBeArray.kt")
|
@TestMetadata("upperBoundCannotBeArray.kt")
|
||||||
public void testUpperBoundCannotBeArray() throws Exception {
|
public void testUpperBoundCannotBeArray() throws Exception {
|
||||||
|
|||||||
+18
@@ -13881,6 +13881,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/inference/underscoredTypeInForbiddenPositions.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/underscoredTypeInForbiddenPositions.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unsoundness1.kt")
|
||||||
|
public void testUnsoundness1() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/unsoundness1.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unsoundness2.kt")
|
||||||
|
public void testUnsoundness2() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/unsoundness2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("useFunctionLiteralsToInferType.kt")
|
@TestMetadata("useFunctionLiteralsToInferType.kt")
|
||||||
public void testUseFunctionLiteralsToInferType() throws Exception {
|
public void testUseFunctionLiteralsToInferType() throws Exception {
|
||||||
@@ -31083,6 +31095,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/typeParameters/repeatedBound.kt");
|
runTest("compiler/testData/diagnostics/tests/typeParameters/repeatedBound.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("starProjectionInsteadOutCaptured.kt")
|
||||||
|
public void testStarProjectionInsteadOutCaptured() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/typeParameters/starProjectionInsteadOutCaptured.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("upperBoundCannotBeArray.kt")
|
@TestMetadata("upperBoundCannotBeArray.kt")
|
||||||
public void testUpperBoundCannotBeArray() throws Exception {
|
public void testUpperBoundCannotBeArray() throws Exception {
|
||||||
|
|||||||
+7
-3
@@ -6,10 +6,10 @@
|
|||||||
package org.jetbrains.kotlin.fir.analysis.checkers
|
package org.jetbrains.kotlin.fir.analysis.checkers
|
||||||
|
|
||||||
import org.jetbrains.kotlin.KtSourceElement
|
import org.jetbrains.kotlin.KtSourceElement
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
|
||||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.withSuppressedDiagnostics
|
||||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
|
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
|
||||||
@@ -70,7 +70,11 @@ private fun buildDeepSubstitutionMultimap(
|
|||||||
for (index in 0 until count) {
|
for (index in 0 until count) {
|
||||||
val typeArgument = typeArguments[index]
|
val typeArgument = typeArguments[index]
|
||||||
|
|
||||||
val substitutedArgument = ConeSubstitutorByMap(substitution, session).substituteArgument(typeArgument) ?: typeArgument
|
val substitutedArgument = ConeSubstitutorByMap(substitution, session).substituteArgument(
|
||||||
|
typeArgument,
|
||||||
|
classSymbol.toLookupTag(),
|
||||||
|
index
|
||||||
|
) ?: typeArgument
|
||||||
val substitutedType = substitutedArgument.type ?: continue
|
val substitutedType = substitutedArgument.type ?: continue
|
||||||
|
|
||||||
val typeParameterSymbol = typeParameterSymbols[index]
|
val typeParameterSymbol = typeParameterSymbols[index]
|
||||||
|
|||||||
+91
-17
@@ -8,16 +8,23 @@ package org.jetbrains.kotlin.fir.analysis.checkers
|
|||||||
import org.jetbrains.kotlin.KtSourceElement
|
import org.jetbrains.kotlin.KtSourceElement
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||||
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.withCombinedAttributesFrom
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
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.types.AbstractTypeChecker
|
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||||
|
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
|
||||||
|
import org.jetbrains.kotlin.types.Variance
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively analyzes type parameters and reports the diagnostic on the given source calculated using typeRef
|
* Recursively analyzes type parameters and reports the diagnostic on the given source calculated using typeRef
|
||||||
@@ -44,23 +51,9 @@ fun checkUpperBoundViolated(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val count = minOf(typeParameterSymbols.size, type.typeArguments.size)
|
val substitution = typeParameterSymbols.zip(type.typeArguments).toMap()
|
||||||
val substitution = mutableMapOf<FirTypeParameterSymbol, ConeKotlinType>()
|
val substitutor = FE10LikeConeSubstitutor(substitution, context.session)
|
||||||
|
|
||||||
for (index in 0 until count) {
|
|
||||||
val typeArgument = type.typeArguments[index]
|
|
||||||
val typeParameterSymbol = typeParameterSymbols[index]
|
|
||||||
|
|
||||||
val typeArgumentType = typeArgument.type
|
|
||||||
if (typeArgumentType != null) {
|
|
||||||
substitution[typeParameterSymbol] = typeArgumentType
|
|
||||||
} else {
|
|
||||||
substitution[typeParameterSymbol] =
|
|
||||||
ConeStubTypeForTypeVariableInSubtyping(ConeTypeVariable("", typeParameterSymbol.toLookupTag()), ConeNullability.NOT_NULL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val substitutor = substitutorByMap(substitution, context.session)
|
|
||||||
val typeRefAndSourcesForArguments = extractArgumentsTypeRefAndSource(typeRef) ?: return
|
val typeRefAndSourcesForArguments = extractArgumentsTypeRefAndSource(typeRef) ?: return
|
||||||
val typeArgumentsWithSourceInfo = type.typeArguments.withIndex().map { (index, projection) ->
|
val typeArgumentsWithSourceInfo = type.typeArguments.withIndex().map { (index, projection) ->
|
||||||
val (argTypeRef, source) =
|
val (argTypeRef, source) =
|
||||||
@@ -80,6 +73,87 @@ fun checkUpperBoundViolated(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class FE10LikeConeSubstitutor(
|
||||||
|
private val substitution: Map<FirTypeParameterSymbol, ConeTypeProjection>,
|
||||||
|
private val useSiteSession: FirSession
|
||||||
|
) : AbstractConeSubstitutor(useSiteSession.typeContext) {
|
||||||
|
override fun substituteType(type: ConeKotlinType): ConeKotlinType? {
|
||||||
|
if (type !is ConeTypeParameterType) return null
|
||||||
|
val projection = substitution[type.lookupTag.symbol] ?: return null
|
||||||
|
|
||||||
|
if (projection.isStarProjection) {
|
||||||
|
return StandardClassIds.Any.constructClassLikeType(emptyArray(), isNullable = true).withProjection(projection)
|
||||||
|
}
|
||||||
|
|
||||||
|
val result =
|
||||||
|
projection.type!!.updateNullabilityIfNeeded(type)
|
||||||
|
?.withCombinedAttributesFrom(type, useSiteSession.typeContext)
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
if (type.isUnsafeVarianceType(useSiteSession)) {
|
||||||
|
useSiteSession.typeApproximator.approximateToSuperType(
|
||||||
|
result, TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference
|
||||||
|
)?.let {
|
||||||
|
return it.withProjection(projection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.withProjection(projection)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ConeKotlinType.withProjection(projection: ConeTypeProjection): ConeKotlinType {
|
||||||
|
if (projection.kind == ProjectionKind.INVARIANT) return this
|
||||||
|
return withAttributes(ConeAttributes.create(listOf(OriginalProjectionTypeAttribute(projection))), useSiteSession.typeContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun substituteArgument(projection: ConeTypeProjection, lookupTag: ConeClassLikeLookupTag, index: Int): ConeTypeProjection? {
|
||||||
|
val substitutedProjection = super.substituteArgument(projection, lookupTag, index) ?: return null
|
||||||
|
if (substitutedProjection.isStarProjection) return null
|
||||||
|
|
||||||
|
val type = substitutedProjection.type!!
|
||||||
|
|
||||||
|
val projectionFromType = type.attributes.originalProjection?.data ?: type
|
||||||
|
val projectionKindFromType = projectionFromType.kind
|
||||||
|
|
||||||
|
if (projectionKindFromType == ProjectionKind.STAR) return ConeStarProjection
|
||||||
|
|
||||||
|
if (projectionKindFromType == ProjectionKind.INVARIANT || projectionKindFromType == projection.kind) {
|
||||||
|
return substitutedProjection
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projection.kind == ProjectionKind.INVARIANT) {
|
||||||
|
return wrapProjection(projectionFromType, type)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConeStarProjection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OriginalProjectionTypeAttribute(val data: ConeTypeProjection) : ConeAttribute<OriginalProjectionTypeAttribute>() {
|
||||||
|
override fun union(other: OriginalProjectionTypeAttribute?): OriginalProjectionTypeAttribute? {
|
||||||
|
return other
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun intersect(other: OriginalProjectionTypeAttribute?): OriginalProjectionTypeAttribute? {
|
||||||
|
return other
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun add(other: OriginalProjectionTypeAttribute?): OriginalProjectionTypeAttribute? {
|
||||||
|
return other
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isSubtypeOf(other: OriginalProjectionTypeAttribute?): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString() = "OriginalProjectionTypeAttribute: $data"
|
||||||
|
|
||||||
|
override val key: KClass<out OriginalProjectionTypeAttribute>
|
||||||
|
get() = OriginalProjectionTypeAttribute::class
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ConeAttributes.originalProjection: OriginalProjectionTypeAttribute? by ConeAttributes.attributeAccessor<OriginalProjectionTypeAttribute>()
|
||||||
|
|
||||||
class TypeArgumentWithSourceInfo(
|
class TypeArgumentWithSourceInfo(
|
||||||
val coneTypeProjection: ConeTypeProjection,
|
val coneTypeProjection: ConeTypeProjection,
|
||||||
val typeRef: FirTypeRef?,
|
val typeRef: FirTypeRef?,
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
|
|||||||
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
|
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType
|
import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor
|
import org.jetbrains.kotlin.fir.resolve.substitution.AbstractConeSubstitutor
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||||
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
import org.jetbrains.kotlin.fir.symbols.ensureResolved
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
@@ -115,10 +116,18 @@ private fun mapTypeAliasArguments(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun substituteArgument(projection: ConeTypeProjection): ConeTypeProjection? {
|
override fun substituteArgument(
|
||||||
|
projection: ConeTypeProjection,
|
||||||
|
lookupTag: ConeClassLikeLookupTag,
|
||||||
|
index: Int
|
||||||
|
): ConeTypeProjection? {
|
||||||
val type = (projection as? ConeKotlinTypeProjection)?.type ?: return null
|
val type = (projection as? ConeKotlinTypeProjection)?.type ?: return null
|
||||||
val symbol = (type as? ConeTypeParameterType)?.lookupTag?.symbol ?: return super.substituteArgument(projection)
|
val symbol = (type as? ConeTypeParameterType)?.lookupTag?.symbol ?: return super.substituteArgument(
|
||||||
val mappedProjection = typeAliasMap[symbol] ?: return super.substituteArgument(projection)
|
projection,
|
||||||
|
lookupTag,
|
||||||
|
index
|
||||||
|
)
|
||||||
|
val mappedProjection = typeAliasMap[symbol] ?: return super.substituteArgument(projection, lookupTag, index)
|
||||||
var mappedType = (mappedProjection as? ConeKotlinTypeProjection)?.type.updateNullabilityIfNeeded(type)
|
var mappedType = (mappedProjection as? ConeKotlinTypeProjection)?.type.updateNullabilityIfNeeded(type)
|
||||||
mappedType = when (mappedType) {
|
mappedType = when (mappedType) {
|
||||||
is ConeClassErrorType,
|
is ConeClassErrorType,
|
||||||
|
|||||||
+8
-5
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.resolve.substitution
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.resolve.withCombinedAttributesFrom
|
import org.jetbrains.kotlin.fir.resolve.withCombinedAttributesFrom
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
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
|
||||||
@@ -17,7 +18,7 @@ import org.jetbrains.kotlin.types.model.TypeVariableMarker
|
|||||||
import org.jetbrains.kotlin.types.model.typeConstructor
|
import org.jetbrains.kotlin.types.model.typeConstructor
|
||||||
|
|
||||||
abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContext) : ConeSubstitutor() {
|
abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContext) : ConeSubstitutor() {
|
||||||
private fun wrapProjection(old: ConeTypeProjection, newType: ConeKotlinType): ConeTypeProjection {
|
protected fun wrapProjection(old: ConeTypeProjection, newType: ConeKotlinType): ConeTypeProjection {
|
||||||
return when (old) {
|
return when (old) {
|
||||||
is ConeStarProjection -> old
|
is ConeStarProjection -> old
|
||||||
is ConeKotlinTypeProjectionIn -> ConeKotlinTypeProjectionIn(newType)
|
is ConeKotlinTypeProjectionIn -> ConeKotlinTypeProjectionIn(newType)
|
||||||
@@ -29,7 +30,7 @@ abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContex
|
|||||||
}
|
}
|
||||||
|
|
||||||
abstract fun substituteType(type: ConeKotlinType): ConeKotlinType?
|
abstract fun substituteType(type: ConeKotlinType): ConeKotlinType?
|
||||||
open fun substituteArgument(projection: ConeTypeProjection): ConeTypeProjection? {
|
open fun substituteArgument(projection: ConeTypeProjection, lookupTag: ConeClassLikeLookupTag, index: Int): ConeTypeProjection? {
|
||||||
val type = (projection as? ConeKotlinTypeProjection)?.type ?: return null
|
val type = (projection as? ConeKotlinTypeProjection)?.type ?: return null
|
||||||
val newType = substituteOrNull(type) ?: return null
|
val newType = substituteOrNull(type) ?: return null
|
||||||
return wrapProjection(projection, newType)
|
return wrapProjection(projection, newType)
|
||||||
@@ -124,8 +125,11 @@ abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContex
|
|||||||
private fun ConeKotlinType.substituteArguments(): ConeKotlinType? {
|
private fun ConeKotlinType.substituteArguments(): ConeKotlinType? {
|
||||||
val newArguments by lazy { arrayOfNulls<ConeTypeProjection>(typeArguments.size) }
|
val newArguments by lazy { arrayOfNulls<ConeTypeProjection>(typeArguments.size) }
|
||||||
var initialized = false
|
var initialized = false
|
||||||
|
|
||||||
|
require(this is ConeClassLikeType) { "Unknown type to substitute: $this, ${this::class}" }
|
||||||
|
|
||||||
for ((index, typeArgument) in this.typeArguments.withIndex()) {
|
for ((index, typeArgument) in this.typeArguments.withIndex()) {
|
||||||
newArguments[index] = substituteArgument(typeArgument)?.also {
|
newArguments[index] = substituteArgument(typeArgument, lookupTag, index)?.also {
|
||||||
initialized = true
|
initialized = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,8 +148,7 @@ abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContex
|
|||||||
nullability.isNullable,
|
nullability.isNullable,
|
||||||
attributes
|
attributes
|
||||||
)
|
)
|
||||||
is ConeClassLikeType -> error("Unknown class-like type to substitute: $this, ${this::class}")
|
else -> error("Unknown class-like type to substitute: $this, ${this::class}")
|
||||||
else -> error("Unknown type to substitute: $this, ${this::class}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
|||||||
+1
-3
@@ -1,6 +1,4 @@
|
|||||||
// IGNORE_BACKEND: JVM
|
// IGNORE_BACKEND: JVM
|
||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
// FIR status: UPPER_BOUND_VIOLATED at Service<out T>
|
|
||||||
|
|
||||||
inline fun <
|
inline fun <
|
||||||
reified TService : Service<TService, TEvent>,
|
reified TService : Service<TService, TEvent>,
|
||||||
@@ -25,4 +23,4 @@ class SomeService : Service<SomeService, SomeService.SomeEvent> {
|
|||||||
fun box(): String {
|
fun box(): String {
|
||||||
event { someEvent: SomeService.SomeEvent -> } // REIFIED_TYPE_FORBIDDEN_SUBSTITUTION
|
event { someEvent: SomeService.SomeEvent -> } // REIFIED_TYPE_FORBIDDEN_SUBSTITUTION
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// SKIP_TXT
|
||||||
|
// !LANGUAGE: +ProperTypeInferenceConstraintsProcessing
|
||||||
|
|
||||||
|
class A<T, F : T>
|
||||||
|
fun foo(a: A<*, in CharSequence>) {}
|
||||||
|
fun <T, U> coerce(t: T): U {
|
||||||
|
val constrain: Constrain<U, *, in T>? = null
|
||||||
|
val bind = Bind(constrain)
|
||||||
|
return bind.upcast(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Constrain<A, B : A, C : B>
|
||||||
|
|
||||||
|
class Bind<A, B : A, C : B>(val constrain: Constrain<A, B, C>?) {
|
||||||
|
fun upcast(c: C): A = c
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T, U> coerce2(t: T): U {
|
||||||
|
// We might report an error on unsound type reference Constrain<U, *, T>?, too
|
||||||
|
val constrain: Constrain<U, *, T>? = null
|
||||||
|
val bind = Bind(<!ARGUMENT_TYPE_MISMATCH!>constrain<!>) // WARNING: Type mismatch: inferred type is T but U was expected
|
||||||
|
return bind.upcast(t)
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// SKIP_TXT
|
||||||
|
// !LANGUAGE: +ProperTypeInferenceConstraintsProcessing
|
||||||
|
|
||||||
|
class A<T, F : T>
|
||||||
|
fun foo(a: A<*, in CharSequence>) {}
|
||||||
|
fun <T, U> coerce(t: T): U {
|
||||||
|
val constrain: Constrain<U, *, in T>? = null
|
||||||
|
val bind = Bind(constrain)
|
||||||
|
return bind.upcast(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Constrain<A, B : A, C : B>
|
||||||
|
|
||||||
|
class Bind<A, B : A, C : B>(val constrain: Constrain<A, B, C>?) {
|
||||||
|
fun upcast(c: C): A = c
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T, U> coerce2(t: T): U {
|
||||||
|
// We might report an error on unsound type reference Constrain<U, *, T>?, too
|
||||||
|
val constrain: Constrain<U, *, T>? = null
|
||||||
|
val bind = Bind(<!TYPE_MISMATCH, TYPE_MISMATCH!>constrain<!>) // WARNING: Type mismatch: inferred type is T but U was expected
|
||||||
|
return bind.upcast(t)
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
|
// SKIP_TXT
|
||||||
|
// !LANGUAGE: +ProperTypeInferenceConstraintsProcessing
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
val zero = coerce<Int, String>(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T, U> coerce(t: T): U {
|
||||||
|
// Should be an error somewhere because this code leads to unsoundness
|
||||||
|
// We may report that `Constrain<U, *, in T>?` type definition is unsound or the call `Bind(constrain)`
|
||||||
|
// See KT-50230
|
||||||
|
val constrain: Constrain<U, *, in T>? = null
|
||||||
|
val bind = Bind(constrain)
|
||||||
|
return bind.upcast(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Constrain<A, B : A, C : B>
|
||||||
|
|
||||||
|
class Bind<A, B : A, C : B>(val constrain: Constrain<A, B, C>?) {
|
||||||
|
fun upcast(c: C): A = c
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
|
// SKIP_TXT
|
||||||
|
interface A
|
||||||
|
interface B<X : A>
|
||||||
|
interface C<E : A, F : B<E>>
|
||||||
|
|
||||||
|
fun foo1(c: C<out A, out B<*>>) {}
|
||||||
|
|
||||||
|
fun foo2(c: C<*, B<*>>) {}
|
||||||
|
fun <T : B<*>> foo3(c: C<*, T>) {}
|
||||||
|
fun <T : B<*>> foo4(c: C<out A, T>) {}
|
||||||
Generated
+18
@@ -13887,6 +13887,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/inference/underscoredTypeInForbiddenPositions.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/underscoredTypeInForbiddenPositions.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unsoundness1.kt")
|
||||||
|
public void testUnsoundness1() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/unsoundness1.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("unsoundness2.kt")
|
||||||
|
public void testUnsoundness2() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/unsoundness2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("useFunctionLiteralsToInferType.kt")
|
@TestMetadata("useFunctionLiteralsToInferType.kt")
|
||||||
public void testUseFunctionLiteralsToInferType() throws Exception {
|
public void testUseFunctionLiteralsToInferType() throws Exception {
|
||||||
@@ -31173,6 +31185,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/typeParameters/repeatedBound.kt");
|
runTest("compiler/testData/diagnostics/tests/typeParameters/repeatedBound.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("starProjectionInsteadOutCaptured.kt")
|
||||||
|
public void testStarProjectionInsteadOutCaptured() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/typeParameters/starProjectionInsteadOutCaptured.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("upperBoundCannotBeArray.kt")
|
@TestMetadata("upperBoundCannotBeArray.kt")
|
||||||
public void testUpperBoundCannotBeArray() throws Exception {
|
public void testUpperBoundCannotBeArray() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user