NI: avoid creating useless captured types during incorporation

^KT-37546 Fixed
This commit is contained in:
Victor Petukhov
2020-03-17 12:17:22 +03:00
parent e34dd27a34
commit ea59ea8aa2
7 changed files with 2337 additions and 31 deletions

View File

@@ -6,5 +6,5 @@ FILE: definetelyNotNullForTypeParameter.kt
}
public final fun <F : R|kotlin/Any|> foo(computable: R|Out<F?>|): R|kotlin/Unit|
public final fun <T : R|kotlin/Any|> bar(computable: R|Out<T?>|): R|kotlin/Unit| {
R|/foo|<R|T?!!|>(R|/id|<R|T?|>(R|<local>/computable|))
R|/foo|<R|T|>(R|/id|<R|T?|>(R|<local>/computable|))
}

View File

@@ -10278,6 +10278,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
runTest("compiler/testData/diagnostics/tests/inference/capturedTypes/approximateBeforeFixation.kt");
}
@TestMetadata("avoidCreatingUselessCapturedTypes.kt")
public void testAvoidCreatingUselessCapturedTypes() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/capturedTypes/avoidCreatingUselessCapturedTypes.kt");
}
@TestMetadata("cannotCaptureInProjection.kt")
public void testCannotCaptureInProjection() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/capturedTypes/cannotCaptureInProjection.kt");

View File

@@ -113,50 +113,94 @@ class ConstraintIncorporator(
}
}
private fun Context.approximateIfNeededAndAddNewConstraint(
baseConstraint: Constraint,
type: KotlinTypeMarker,
targetVariable: TypeVariableMarker,
otherVariable: TypeVariableMarker,
otherConstraint: Constraint,
needApproximation: Boolean = true
) {
val typeWithSubstitution = baseConstraint.type.substitute(this, otherVariable, type)
val prepareType = { toSuper: Boolean ->
if (needApproximation) approximateCapturedTypes(typeWithSubstitution, toSuper) else typeWithSubstitution
}
when (baseConstraint.kind) {
ConstraintKind.EQUALITY -> {
addNewConstraint(targetVariable, baseConstraint, otherVariable, otherConstraint, typeWithSubstitution, isSubtype = false)
addNewConstraint(targetVariable, baseConstraint, otherVariable, otherConstraint, typeWithSubstitution, isSubtype = true)
}
ConstraintKind.UPPER -> {
addNewConstraint(targetVariable, baseConstraint, otherVariable, otherConstraint, prepareType(true), isSubtype = false)
}
ConstraintKind.LOWER -> {
addNewConstraint(targetVariable, baseConstraint, otherVariable, otherConstraint, prepareType(false), isSubtype = true)
}
}
}
private fun Context.generateNewConstraint(
targetVariable: TypeVariableMarker,
baseConstraint: Constraint,
otherVariable: TypeVariableMarker,
otherConstraint: Constraint
) {
val typeWithSubstitution = when (otherConstraint.kind) {
val isBaseGenericType = baseConstraint.type.argumentsCount() != 0
val isOtherCapturedType = otherConstraint.type.isCapturedType()
val (type, needApproximation) = when (otherConstraint.kind) {
ConstraintKind.EQUALITY -> {
baseConstraint.type.substitute(this, otherVariable, otherConstraint.type)
otherConstraint.type to true
}
ConstraintKind.UPPER -> {
val temporaryCapturedType = createCapturedType(
createTypeArgument(otherConstraint.type, TypeVariance.OUT),
listOf(otherConstraint.type),
null,
CaptureStatus.FOR_INCORPORATION
)
baseConstraint.type.substitute(this, otherVariable, temporaryCapturedType)
/*
* Creating a captured type isn't needed due to its future approximation to `Nothing` or itself
* Example:
* targetVariable = TypeVariable(A)
* baseConstraint = LOWER(TypeVariable(B))
* otherConstraint = UPPER(Number)
* incorporatedConstraint = Approx(CapturedType(out Number)) <: TypeVariable(A) => Nothing <: TypeVariable(A)
* TODO: implement this for generics and captured types
*/
if (baseConstraint.kind == ConstraintKind.LOWER && !isBaseGenericType && !isOtherCapturedType) {
nothingType() to false
} else if (baseConstraint.kind == ConstraintKind.UPPER && !isBaseGenericType && !isOtherCapturedType) {
otherConstraint.type to false
} else {
createCapturedType(
createTypeArgument(otherConstraint.type, TypeVariance.OUT),
listOf(otherConstraint.type),
null,
CaptureStatus.FOR_INCORPORATION
) to true
}
}
ConstraintKind.LOWER -> {
val temporaryCapturedType = createCapturedType(
createTypeArgument(otherConstraint.type, TypeVariance.IN),
emptyList(),
otherConstraint.type,
CaptureStatus.FOR_INCORPORATION
)
baseConstraint.type.substitute(this, otherVariable, temporaryCapturedType)
/*
* Creating a captured type isn't needed due to its future approximation to `Any?` or itself
* Example:
* targetVariable = TypeVariable(A)
* baseConstraint = UPPER(TypeVariable(B))
* otherConstraint = LOWER(Number)
* incorporatedConstraint = TypeVariable(A) <: Approx(CapturedType(in Number)) => TypeVariable(A) <: Any?
* TODO: implement this for generics and captured types
*/
if (baseConstraint.kind == ConstraintKind.UPPER && !isBaseGenericType && !isOtherCapturedType) {
nullableAnyType() to false
} else if (baseConstraint.kind == ConstraintKind.LOWER && !isBaseGenericType && !isOtherCapturedType) {
otherConstraint.type to false
} else {
createCapturedType(
createTypeArgument(otherConstraint.type, TypeVariance.IN),
emptyList(),
otherConstraint.type,
CaptureStatus.FOR_INCORPORATION
) to true
}
}
}
when (baseConstraint.kind) {
ConstraintKind.EQUALITY -> {
addNewConstraint(targetVariable, baseConstraint, otherVariable, otherConstraint, typeWithSubstitution, isSubtype = true)
addNewConstraint(targetVariable, baseConstraint, otherVariable, otherConstraint, typeWithSubstitution, isSubtype = false)
}
ConstraintKind.UPPER -> {
val generatedConstraintType = approximateCapturedTypes(typeWithSubstitution, toSuper = true)
addNewConstraint(targetVariable, baseConstraint, otherVariable, otherConstraint, generatedConstraintType, isSubtype = false)
}
ConstraintKind.LOWER -> {
val generatedConstraintType = approximateCapturedTypes(typeWithSubstitution, toSuper = false)
addNewConstraint(targetVariable, baseConstraint, otherVariable, otherConstraint, generatedConstraintType, isSubtype = true)
}
}
approximateIfNeededAndAddNewConstraint(baseConstraint, type, targetVariable, otherVariable, otherConstraint, needApproximation)
}
private fun Context.addNewConstraint(

View File

@@ -0,0 +1,9 @@
package
public object Entities {
private constructor Entities()
public final val map: kotlin.collections.Map<kotlin.String, kotlin.Int>
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}

View File

@@ -10285,6 +10285,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTestWithFirVali
runTest("compiler/testData/diagnostics/tests/inference/capturedTypes/approximateBeforeFixation.kt");
}
@TestMetadata("avoidCreatingUselessCapturedTypes.kt")
public void testAvoidCreatingUselessCapturedTypes() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/capturedTypes/avoidCreatingUselessCapturedTypes.kt");
}
@TestMetadata("cannotCaptureInProjection.kt")
public void testCannotCaptureInProjection() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/capturedTypes/cannotCaptureInProjection.kt");

View File

@@ -10280,6 +10280,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/inference/capturedTypes/approximateBeforeFixation.kt");
}
@TestMetadata("avoidCreatingUselessCapturedTypes.kt")
public void testAvoidCreatingUselessCapturedTypes() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/capturedTypes/avoidCreatingUselessCapturedTypes.kt");
}
@TestMetadata("cannotCaptureInProjection.kt")
public void testCannotCaptureInProjection() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/capturedTypes/cannotCaptureInProjection.kt");