NI: Fix regression for star-projections approximation

See the test added

^KT-35703 Fixed
This commit is contained in:
Denis Zharkov
2020-01-10 16:06:15 +03:00
parent 7255ee0a5b
commit 4202c9c1a4
13 changed files with 92 additions and 13 deletions
@@ -144,7 +144,8 @@ fun ConeKotlinType.lowerBoundIfFlexible() = (this as? ConeFlexibleType)?.lowerBo
class ConeCapturedTypeConstructor( class ConeCapturedTypeConstructor(
val projection: ConeKotlinTypeProjection, val projection: ConeKotlinTypeProjection,
var supertypes: List<ConeKotlinType>? = null var supertypes: List<ConeKotlinType>? = null,
val typeParameterMarker: TypeParameterMarker? = null
) : CapturedTypeConstructorMarker ) : CapturedTypeConstructorMarker
class ConeCapturedType( class ConeCapturedType(
@@ -153,11 +154,15 @@ class ConeCapturedType(
override val nullability: ConeNullability = ConeNullability.NOT_NULL, override val nullability: ConeNullability = ConeNullability.NOT_NULL,
val constructor: ConeCapturedTypeConstructor val constructor: ConeCapturedTypeConstructor
) : ConeSimpleKotlinType(), CapturedTypeMarker { ) : ConeSimpleKotlinType(), CapturedTypeMarker {
constructor(captureStatus: CaptureStatus, lowerType: ConeKotlinType?, projection: ConeKotlinTypeProjection) : this( constructor(
captureStatus: CaptureStatus, lowerType: ConeKotlinType?, projection: ConeKotlinTypeProjection,
typeParameterMarker: TypeParameterMarker
) : this(
captureStatus, captureStatus,
lowerType, lowerType,
constructor = ConeCapturedTypeConstructor( constructor = ConeCapturedTypeConstructor(
projection projection,
typeParameterMarker = typeParameterMarker
) )
) )
@@ -257,6 +257,11 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
return this.constructor.projection return this.constructor.projection
} }
override fun CapturedTypeMarker.typeParameter(): TypeParameterMarker? {
require(this is ConeCapturedType)
return this.constructor.typeParameterMarker
}
override fun DefinitelyNotNullTypeMarker.original(): SimpleTypeMarker { override fun DefinitelyNotNullTypeMarker.original(): SimpleTypeMarker {
require(this is ConeDefinitelyNotNullType) require(this is ConeDefinitelyNotNullType)
return this.original as SimpleTypeMarker return this.original as SimpleTypeMarker
@@ -319,7 +319,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
null null
} }
ConeCapturedType(status, lowerType, argument as ConeKotlinTypeProjection) ConeCapturedType(status, lowerType, argument, typeConstructor.getParameter(index))
} }
for (index in 0 until argumentsCount) { for (index in 0 until argumentsCount) {
@@ -10058,6 +10058,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
runTest("compiler/testData/diagnostics/tests/inference/starApproximationFlexible.kt"); runTest("compiler/testData/diagnostics/tests/inference/starApproximationFlexible.kt");
} }
@TestMetadata("starApproximationFromDifferentTypeParameter.kt")
public void testStarApproximationFromDifferentTypeParameter() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/starApproximationFromDifferentTypeParameter.kt");
}
@TestMetadata("tooEagerSmartcast.kt") @TestMetadata("tooEagerSmartcast.kt")
public void testTooEagerSmartcast() throws Exception { public void testTooEagerSmartcast() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.kt"); runTest("compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.kt");
@@ -83,7 +83,10 @@ interface NewTypeSubstitutor: TypeSubstitutorMarker {
if (innerType is StubType || substitutedInnerType is StubType) { if (innerType is StubType || substitutedInnerType is StubType) {
return NewCapturedType( return NewCapturedType(
capturedType.captureStatus, capturedType.captureStatus,
NewCapturedTypeConstructor(TypeProjectionImpl(typeConstructor.projection.projectionKind, substitutedInnerType)), NewCapturedTypeConstructor(
TypeProjectionImpl(typeConstructor.projection.projectionKind, substitutedInnerType),
typeParameter = typeConstructor.typeParameter
),
lowerType = if (capturedType.lowerType != null) substitutedInnerType else null lowerType = if (capturedType.lowerType != null) substitutedInnerType else null
) )
} else { } else {
@@ -485,12 +485,14 @@ abstract class AbstractTypeApproximator(val ctx: TypeSystemInferenceExtensionCon
val argumentType = newArguments[index]?.getType() ?: argument.getType() val argumentType = newArguments[index]?.getType() ?: argument.getType()
val capturedType = argumentType.lowerBoundIfFlexible().asCapturedType()
val capturedStarProjectionOrNull = val capturedStarProjectionOrNull =
argumentType.lowerBoundIfFlexible().asCapturedType()?.typeConstructorProjection()?.takeIf { it.isStarProjection() } capturedType?.typeConstructorProjection()?.takeIf { it.isStarProjection() }
if (capturedStarProjectionOrNull != null && if (capturedStarProjectionOrNull != null &&
(effectiveVariance == TypeVariance.OUT || effectiveVariance == TypeVariance.INV) && (effectiveVariance == TypeVariance.OUT || effectiveVariance == TypeVariance.INV) &&
toSuper toSuper &&
capturedType.typeParameter() == parameter
) { ) {
newArguments[index] = capturedStarProjectionOrNull newArguments[index] = capturedStarProjectionOrNull
continue@loop continue@loop
@@ -0,0 +1,20 @@
// SKIP_TXT
// !LANGUAGE: +NewInference
// See KT-14453 and KT-35703
val <T : Any> KClass1<T>.primaryConstructor: KFunction1<T>? get() = null!!
interface KClass1<F : Any>
interface KFunction1<out R>
fun f(type: KClass1<*>): KFunction1<Any>? =
// What happens here:
// 1. Create captured type for type argument T (it's built upon star-projection from `KClass<*>` that's argument is <: Any)
// 2. Approximate resulting descriptor, now it 'KClass1<*>.primaryConstructor: KFunction1<*>'
// Note that star-projection in 'KFunction1<*>' is obtained from KClass and its `projectionType` is Any (not-nullable).
// Of course that situation is already strange
// 3. When checking subtyping after call completion we get that 'KFunction1<*>' is not a subtype of 'KFunction1<Any>' in new type checker.
// The answer is correct, but this old type checker used `projectionType` for this and it was resulting in KFunction1<*> <: KFunction1<Any>
//
// So to fix the issue we use 'out starProjectionType' instead of star-projection itself in approximation
// Note that in new inference there should be no approximation for IN-position types (they must remain captured)
type.primaryConstructor
@@ -0,0 +1,20 @@
// SKIP_TXT
// !LANGUAGE: +NewInference
// See KT-14453 and KT-35703
val <T : Any> KClass1<T>.primaryConstructor: KFunction1<T>? get() = null!!
interface KClass1<F : Any>
interface KFunction1<out R>
fun f(type: KClass1<*>): KFunction1<Any>? =
// What happens here:
// 1. Create captured type for type argument T (it's built upon star-projection from `KClass<*>` that's argument is <: Any)
// 2. Approximate resulting descriptor, now it 'KClass1<*>.primaryConstructor: KFunction1<*>'
// Note that star-projection in 'KFunction1<*>' is obtained from KClass and its `projectionType` is Any (not-nullable).
// Of course that situation is already strange
// 3. When checking subtyping after call completion we get that 'KFunction1<*>' is not a subtype of 'KFunction1<Any>' in new type checker.
// The answer is correct, but this old type checker used `projectionType` for this and it was resulting in KFunction1<*> <: KFunction1<Any>
//
// So to fix the issue we use 'out starProjectionType' instead of star-projection itself in approximation
// Note that in new inference there should be no approximation for IN-position types (they must remain captured)
type.primaryConstructor
@@ -10065,6 +10065,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
runTest("compiler/testData/diagnostics/tests/inference/starApproximationFlexible.kt"); runTest("compiler/testData/diagnostics/tests/inference/starApproximationFlexible.kt");
} }
@TestMetadata("starApproximationFromDifferentTypeParameter.kt")
public void testStarApproximationFromDifferentTypeParameter() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/starApproximationFromDifferentTypeParameter.kt");
}
@TestMetadata("tooEagerSmartcast.kt") @TestMetadata("tooEagerSmartcast.kt")
public void testTooEagerSmartcast() throws Exception { public void testTooEagerSmartcast() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.kt"); runTest("compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.kt");
@@ -10060,6 +10060,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/inference/starApproximationFlexible.kt"); runTest("compiler/testData/diagnostics/tests/inference/starApproximationFlexible.kt");
} }
@TestMetadata("starApproximationFromDifferentTypeParameter.kt")
public void testStarApproximationFromDifferentTypeParameter() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/starApproximationFromDifferentTypeParameter.kt");
}
@TestMetadata("tooEagerSmartcast.kt") @TestMetadata("tooEagerSmartcast.kt")
public void testTooEagerSmartcast() throws Exception { public void testTooEagerSmartcast() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.kt"); runTest("compiler/testData/diagnostics/tests/inference/tooEagerSmartcast.kt");
@@ -404,6 +404,11 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy
return this.constructor.projection return this.constructor.projection
} }
override fun CapturedTypeMarker.typeParameter(): TypeParameterMarker? {
require(this is NewCapturedType, this::errorMessage)
return this.constructor.typeParameter
}
override fun CapturedTypeMarker.captureStatus(): CaptureStatus { override fun CapturedTypeMarker.captureStatus(): CaptureStatus {
require(this is NewCapturedType, this::errorMessage) require(this is NewCapturedType, this::errorMessage)
return this.captureStatus return this.captureStatus
@@ -63,7 +63,7 @@ internal fun captureFromArguments(
val arguments = type.arguments val arguments = type.arguments
if (arguments.all { it.projectionKind == Variance.INVARIANT }) return null if (arguments.all { it.projectionKind == Variance.INVARIANT }) return null
val capturedArguments = arguments.map { projection -> val capturedArguments = arguments.zip(type.constructor.parameters).map { (projection, parameter) ->
if (projection.projectionKind == Variance.INVARIANT) return@map projection if (projection.projectionKind == Variance.INVARIANT) return@map projection
val lowerType = val lowerType =
@@ -73,7 +73,7 @@ internal fun captureFromArguments(
null null
} }
NewCapturedType(status, lowerType, projection).asTypeProjection() // todo optimization: do not create type projection NewCapturedType(status, lowerType, projection, parameter).asTypeProjection() // todo optimization: do not create type projection
} }
val substitutor = TypeConstructorSubstitution.create(type.constructor, capturedArguments).buildSubstitutor() val substitutor = TypeConstructorSubstitution.create(type.constructor, capturedArguments).buildSubstitutor()
@@ -112,8 +112,9 @@ class NewCapturedType(
override val annotations: Annotations = Annotations.EMPTY, override val annotations: Annotations = Annotations.EMPTY,
override val isMarkedNullable: Boolean = false override val isMarkedNullable: Boolean = false
) : SimpleType(), CapturedTypeMarker { ) : SimpleType(), CapturedTypeMarker {
internal constructor(captureStatus: CaptureStatus, lowerType: UnwrappedType?, projection: TypeProjection) : internal constructor(
this(captureStatus, NewCapturedTypeConstructor(projection), lowerType) captureStatus: CaptureStatus, lowerType: UnwrappedType?, projection: TypeProjection, typeParameter: TypeParameterDescriptor
) : this(captureStatus, NewCapturedTypeConstructor(projection, typeParameter = typeParameter), lowerType)
override val arguments: List<TypeProjection> get() = listOf() override val arguments: List<TypeProjection> get() = listOf()
@@ -140,7 +141,8 @@ class NewCapturedType(
class NewCapturedTypeConstructor( class NewCapturedTypeConstructor(
override val projection: TypeProjection, override val projection: TypeProjection,
private var supertypesComputation: (() -> List<UnwrappedType>)? = null, private var supertypesComputation: (() -> List<UnwrappedType>)? = null,
private val original: NewCapturedTypeConstructor? = null private val original: NewCapturedTypeConstructor? = null,
val typeParameter: TypeParameterDescriptor? = null
) : CapturedTypeConstructor { ) : CapturedTypeConstructor {
constructor( constructor(
@@ -177,7 +179,8 @@ class NewCapturedTypeConstructor(
supertypes.map { it.refine(kotlinTypeRefiner) } supertypes.map { it.refine(kotlinTypeRefiner) }
} }
}, },
original ?: this original ?: this,
typeParameter = typeParameter
) )
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@@ -153,6 +153,7 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui
fun KotlinTypeMarker.mayBeTypeVariable(): Boolean fun KotlinTypeMarker.mayBeTypeVariable(): Boolean
fun CapturedTypeMarker.typeConstructorProjection(): TypeArgumentMarker fun CapturedTypeMarker.typeConstructorProjection(): TypeArgumentMarker
fun CapturedTypeMarker.typeParameter(): TypeParameterMarker?
fun DefinitelyNotNullTypeMarker.original(): SimpleTypeMarker fun DefinitelyNotNullTypeMarker.original(): SimpleTypeMarker