[FE 1.0] Don't fail candidates which were marked with compatibility resolve previously

^KT-52431 Fixed
^KT-52393 Fixed
This commit is contained in:
Victor Petukhov
2022-05-20 14:09:22 +02:00
committed by teamcity
parent 73d676d9de
commit 02a430875f
17 changed files with 198 additions and 23 deletions
@@ -15593,6 +15593,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52364.kt"); runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52364.kt");
} }
@Test
@TestMetadata("kt52393.kt")
public void testKt52393() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52393.kt");
}
@Test
@TestMetadata("kt52431.kt")
public void testKt52431() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52431.kt");
}
@Test @Test
@TestMetadata("nullableEmptyIntersection.kt") @TestMetadata("nullableEmptyIntersection.kt")
public void testNullableEmptyIntersection() throws Exception { public void testNullableEmptyIntersection() throws Exception {
@@ -15593,6 +15593,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52364.kt"); runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52364.kt");
} }
@Test
@TestMetadata("kt52393.kt")
public void testKt52393() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52393.kt");
}
@Test
@TestMetadata("kt52431.kt")
public void testKt52431() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52431.kt");
}
@Test @Test
@TestMetadata("nullableEmptyIntersection.kt") @TestMetadata("nullableEmptyIntersection.kt")
public void testNullableEmptyIntersection() throws Exception { public void testNullableEmptyIntersection() throws Exception {
@@ -15593,6 +15593,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52364.kt"); runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52364.kt");
} }
@Test
@TestMetadata("kt52393.kt")
public void testKt52393() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52393.kt");
}
@Test
@TestMetadata("kt52431.kt")
public void testKt52431() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52431.kt");
}
@Test @Test
@TestMetadata("nullableEmptyIntersection.kt") @TestMetadata("nullableEmptyIntersection.kt")
public void testNullableEmptyIntersection() throws Exception { public void testNullableEmptyIntersection() throws Exception {
@@ -581,10 +581,10 @@ internal object CheckIncompatibleTypeVariableUpperBounds : ResolutionStage() {
val upperTypes = variableWithConstraints.constraints.extractUpperTypesToCheckIntersectionEmptiness() val upperTypes = variableWithConstraints.constraints.extractUpperTypesToCheckIntersectionEmptiness()
// TODO: consider reporting errors on bounded type variables by incompatible types but with other lower constraints // TODO: consider reporting errors on bounded type variables by incompatible types but with other lower constraints
if ( if (upperTypes.size <= 1 || variableWithConstraints.constraints.any { it.kind.isLower() })
variableWithConstraints.constraints.none { it.kind.isLower() } continue
&& upperTypes.computeEmptyIntersectionTypeKind().isDefinitelyEmpty()
) { if (upperTypes.computeEmptyIntersectionTypeKind().isDefinitelyEmpty()) {
sink.yieldDiagnostic( sink.yieldDiagnostic(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
InferredEmptyIntersectionDiagnostic( InferredEmptyIntersectionDiagnostic(
@@ -152,7 +152,8 @@ class InferredEmptyIntersectionError(
class OnlyInputTypesDiagnostic(val typeVariable: TypeVariableMarker) : ConstraintSystemError(INAPPLICABLE) class OnlyInputTypesDiagnostic(val typeVariable: TypeVariableMarker) : ConstraintSystemError(INAPPLICABLE)
object LowerPriorityToPreserveCompatibility : ConstraintSystemError(RESOLVED_NEED_PRESERVE_COMPATIBILITY) class LowerPriorityToPreserveCompatibility(val needToReportWarning: Boolean) :
ConstraintSystemError(RESOLVED_NEED_PRESERVE_COMPATIBILITY)
fun Constraint.isExpectedTypePosition() = fun Constraint.isExpectedTypePosition() =
position.from is ExpectedTypeConstraintPosition<*> || position.from is DelegatedPropertyConstraintPosition<*> position.from is ExpectedTypeConstraintPosition<*> || position.from is DelegatedPropertyConstraintPosition<*>
@@ -450,9 +450,13 @@ class NewConstraintSystemImpl(
languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection) languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection)
val upperTypes = intersectionTypeConstructor.supertypes() val upperTypes = intersectionTypeConstructor.supertypes()
// Diagnostic with these incompatible types has already been reported at the resolution stage
if (upperTypes.size <= 1 || storage.errors.any { it is InferredEmptyIntersection && it.incompatibleTypes == upperTypes })
return
if (upperTypes.computeEmptyIntersectionTypeKind().isDefinitelyEmpty()) { if (upperTypes.computeEmptyIntersectionTypeKind().isDefinitelyEmpty()) {
// Remove existing errors from resolution stage because a completion error is more precise // Remove existing errors from resolution stage because a completion error is more precise
storage.errors.removeIf { it is InferredEmptyIntersectionError || it is InferredEmptyIntersectionWarning } storage.errors.removeIf { it is InferredEmptyIntersection }
val errorFactory = val errorFactory =
if (isInferredEmptyIntersectionForbidden) ::InferredEmptyIntersectionError else ::InferredEmptyIntersectionWarning if (isInferredEmptyIntersectionForbidden) ::InferredEmptyIntersectionError else ::InferredEmptyIntersectionWarning
addError(errorFactory(upperTypes, variable)) addError(errorFactory(upperTypes, variable))
@@ -892,18 +892,23 @@ internal object CheckIncompatibleTypeVariableUpperBounds : ResolutionPart() {
for (variableWithConstraints in getSystem().getBuilder().currentStorage().notFixedTypeVariables.values) { for (variableWithConstraints in getSystem().getBuilder().currentStorage().notFixedTypeVariables.values) {
val upperTypes = variableWithConstraints.constraints.extractUpperTypesToCheckIntersectionEmptiness() val upperTypes = variableWithConstraints.constraints.extractUpperTypesToCheckIntersectionEmptiness()
// TODO: consider reporting errors on bounded type variables by incompatible types but with other lower constraints when {
if ( // TODO: consider reporting errors on bounded type variables by incompatible types but with other lower constraints
variableWithConstraints.constraints.none { it.kind.isLower() } upperTypes.size <= 1 || variableWithConstraints.constraints.any { it.kind.isLower() } ->
&& upperTypes.computeEmptyIntersectionTypeKind().isDefinitelyEmpty() continue
) { wasPreviouslyDiscriminated(upperTypes) -> {
val isInferredEmptyIntersectionForbidden = markCandidateForCompatibilityResolve(needToReportWarning = false)
callComponents.languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection) continue
}
upperTypes.computeEmptyIntersectionTypeKind().isDefinitelyEmpty() -> {
val isInferredEmptyIntersectionForbidden =
callComponents.languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection)
val errorFactory = if (isInferredEmptyIntersectionForbidden || wasPreviouslyDiscriminated(upperTypes)) val errorFactory = if (isInferredEmptyIntersectionForbidden)
::InferredEmptyIntersectionError ::InferredEmptyIntersectionError
else ::InferredEmptyIntersectionWarning else ::InferredEmptyIntersectionWarning
addError(errorFactory(upperTypes, variableWithConstraints.typeVariable)) addError(errorFactory(upperTypes, variableWithConstraints.typeVariable))
}
} }
} }
} }
@@ -160,12 +160,15 @@ open class MutableResolvedCallAtom(
override fun toString(): String = "$atom, candidate = $candidateDescriptor" override fun toString(): String = "$atom, candidate = $candidateDescriptor"
} }
fun ResolutionCandidate.markCandidateForCompatibilityResolve() { fun ResolutionCandidate.markCandidateForCompatibilityResolve(needToReportWarning: Boolean = true) {
if (callComponents.languageVersionSettings.supportsFeature(LanguageFeature.DisableCompatibilityModeForNewInference)) return if (callComponents.languageVersionSettings.supportsFeature(LanguageFeature.DisableCompatibilityModeForNewInference)) return
addDiagnostic(LowerPriorityToPreserveCompatibility.asDiagnostic()) addDiagnostic(LowerPriorityToPreserveCompatibility(needToReportWarning).asDiagnostic())
} }
fun CallableReferencesCandidateFactory.markCandidateForCompatibilityResolve(diagnostics: SmartList<KotlinCallDiagnostic>) { fun CallableReferencesCandidateFactory.markCandidateForCompatibilityResolve(
diagnostics: SmartList<KotlinCallDiagnostic>,
needToReportWarning: Boolean = true,
) {
if (callComponents.languageVersionSettings.supportsFeature(LanguageFeature.DisableCompatibilityModeForNewInference)) return if (callComponents.languageVersionSettings.supportsFeature(LanguageFeature.DisableCompatibilityModeForNewInference)) return
diagnostics.add(LowerPriorityToPreserveCompatibility.asDiagnostic()) diagnostics.add(LowerPriorityToPreserveCompatibility(needToReportWarning).asDiagnostic())
} }
@@ -18,6 +18,9 @@ package org.jetbrains.kotlin.resolve.calls.tower
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus
import org.jetbrains.kotlin.resolve.calls.components.candidate.ResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.inference.model.LowerPriorityToPreserveCompatibility
import org.jetbrains.kotlin.resolve.calls.model.constraintSystemError
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST
import org.jetbrains.kotlin.resolve.scopes.HierarchicalScope import org.jetbrains.kotlin.resolve.scopes.HierarchicalScope
@@ -437,13 +440,21 @@ class TowerResolver {
} }
if (firstGroupWithResolved == null) return null if (firstGroupWithResolved == null) return null
if (compatibilityCandidate != null && compatibilityGroup !== firstGroupWithResolved) { if (compatibilityCandidate != null
&& compatibilityGroup !== firstGroupWithResolved
&& needToReportCompatibilityWarning(compatibilityCandidate)
) {
firstGroupWithResolved.forEach { it.addCompatibilityWarning(compatibilityCandidate) } firstGroupWithResolved.forEach { it.addCompatibilityWarning(compatibilityCandidate) }
} }
return firstGroupWithResolved.filter(::isSuccessfulCandidate) return firstGroupWithResolved.filter(::isSuccessfulCandidate)
} }
private fun needToReportCompatibilityWarning(candidate: C) = candidate is ResolutionCandidate &&
candidate.diagnostics.any {
(it.constraintSystemError as? LowerPriorityToPreserveCompatibility)?.needToReportWarning == true
}
private fun isSuccessfulCandidate(candidate: C): Boolean { private fun isSuccessfulCandidate(candidate: C): Boolean {
return candidate.resultingApplicability == CandidateApplicability.RESOLVED return candidate.resultingApplicability == CandidateApplicability.RESOLVED
|| candidate.resultingApplicability == CandidateApplicability.RESOLVED_WITH_ERROR || candidate.resultingApplicability == CandidateApplicability.RESOLVED_WITH_ERROR
@@ -0,0 +1,17 @@
class Wrapper<T>
class WrapperFunctions {
infix fun <T : Comparable<T>, S : T?> Wrapper<in S>.greaterEq(t: T): Unit = Unit
infix fun <T : Comparable<T>, S : T?> Wrapper<in S>.greaterEq(other: Wrapper<in S>): Unit = Unit // if this function is removed, it also works
}
fun main() {
val wrapper = Wrapper<Long>()
val number: Int = 5 // doesn't work
// val number: Long = 5 // works
with (WrapperFunctions()) {
<!ARGUMENT_TYPE_MISMATCH!>wrapper <!NONE_APPLICABLE!>greaterEq<!> number<!>
}
}
@@ -0,0 +1,17 @@
class Wrapper<T>
class WrapperFunctions {
infix fun <T : Comparable<T>, S : T?> Wrapper<in S>.greaterEq(t: T): Unit = Unit
infix fun <T : Comparable<T>, S : T?> Wrapper<in S>.greaterEq(other: Wrapper<in S>): Unit = Unit // if this function is removed, it also works
}
fun main() {
val wrapper = Wrapper<Long>()
val number: Int = 5 // doesn't work
// val number: Long = 5 // works
with (WrapperFunctions()) {
wrapper <!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>greaterEq<!> number
}
}
@@ -0,0 +1,19 @@
package
public fun main(): kotlin.Unit
public final class Wrapper</*0*/ T> {
public constructor Wrapper</*0*/ T>()
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
}
public final class WrapperFunctions {
public constructor WrapperFunctions()
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
public final infix fun </*0*/ T : kotlin.Comparable<T>, /*1*/ S : T?> Wrapper<in S>.greaterEq(/*0*/ t: T): kotlin.Unit
public final infix fun </*0*/ T : kotlin.Comparable<T>, /*1*/ S : T?> Wrapper<in S>.greaterEq(/*0*/ other: Wrapper<in S>): kotlin.Unit
}
@@ -0,0 +1,13 @@
interface Expression<K>
interface ExpressionWithColumnType<K> : Expression<K>
class Column<T>: ExpressionWithColumnType<T>
infix fun <T : Comparable<T>, S : T?> ExpressionWithColumnType<in S>.less(t: T) {}
infix fun <T : Comparable<T>, S : T?> Expression<in S>.less(other: Expression<in S>) {}
fun main(x: Column<Long?>, y: Double) {
x <!NONE_APPLICABLE!>less<!> y // error in 1.7.20, no error in 1.7.0
}
@@ -0,0 +1,13 @@
interface Expression<K>
interface ExpressionWithColumnType<K> : Expression<K>
class Column<T>: ExpressionWithColumnType<T>
infix fun <T : Comparable<T>, S : T?> ExpressionWithColumnType<in S>.less(t: T) {}
infix fun <T : Comparable<T>, S : T?> Expression<in S>.less(other: Expression<in S>) {}
fun main(x: Column<Long?>, y: Double) {
x <!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>less<!> y // error in 1.7.20, no error in 1.7.0
}
@@ -0,0 +1,24 @@
package
public fun main(/*0*/ x: Column<kotlin.Long?>, /*1*/ y: kotlin.Double): kotlin.Unit
public infix fun </*0*/ T : kotlin.Comparable<T>, /*1*/ S : T?> Expression<in S>.less(/*0*/ other: Expression<in S>): kotlin.Unit
public infix fun </*0*/ T : kotlin.Comparable<T>, /*1*/ S : T?> ExpressionWithColumnType<in S>.less(/*0*/ t: T): kotlin.Unit
public final class Column</*0*/ T> : ExpressionWithColumnType<T> {
public constructor Column</*0*/ T>()
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
}
public interface Expression</*0*/ K> {
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
}
public interface ExpressionWithColumnType</*0*/ K> : Expression<K> {
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
}
+1 -1
View File
@@ -8,5 +8,5 @@ fun <T : Comparable<T>, S : T?> Expression<in S>.greater(other: T): GreaterOp =
fun foo(countExpr: Expression<Long>) { fun foo(countExpr: Expression<Long>) {
countExpr.greater(0) countExpr.greater(0)
countExpr.<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>greater<!>("0") countExpr.<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>greater<!>("0")
countExpr.<!INFERRED_TYPE_VARIABLE_INTO_EMPTY_INTERSECTION_WARNING!>greater<!><String, Nothing>("0") countExpr.greater<String, Nothing>("0")
} }
@@ -15599,6 +15599,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52364.kt"); runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52364.kt");
} }
@Test
@TestMetadata("kt52393.kt")
public void testKt52393() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52393.kt");
}
@Test
@TestMetadata("kt52431.kt")
public void testKt52431() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/emptyIntersectionTypes/kt52431.kt");
}
@Test @Test
@TestMetadata("nullableEmptyIntersection.kt") @TestMetadata("nullableEmptyIntersection.kt")
public void testNullableEmptyIntersection() throws Exception { public void testNullableEmptyIntersection() throws Exception {