[NI] Soften restictions on using Nothing as proper constraint for full call completion
Consider lower `Nothing` constraint non-proper only if there is a dependant not analyzed postponed atom. Early completion to `Nothing` provides data flow info for smart casts. KT-35668 Fixed
This commit is contained in:
Generated
+10
@@ -10582,6 +10582,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
|
||||
runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaWithVariableAndNothing.kt")
|
||||
public void testLambdaWithVariableAndNothing() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("partialForIlt.kt")
|
||||
public void testPartialForIlt() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt");
|
||||
@@ -17846,6 +17851,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
|
||||
runTest("compiler/testData/diagnostics/tests/regressions/kt3535.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt35668.kt")
|
||||
public void testKt35668() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/regressions/kt35668.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt3647.kt")
|
||||
public void testKt3647() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/regressions/kt3647.kt");
|
||||
|
||||
+23
-3
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.TrivialConstraint
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
|
||||
import org.jetbrains.kotlin.resolve.calls.model.KotlinResolutionCandidate
|
||||
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtom
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
@@ -39,11 +40,14 @@ class CompletionModeCalculator {
|
||||
if (csBuilder.isProperType(returnType)) return ConstraintSystemCompletionMode.FULL
|
||||
|
||||
// For nested call with variables in return type check possibility of full completion
|
||||
return CalculatorForNestedCall(returnType, csCompleterContext, trivialConstraintTypeInferenceOracle).computeCompletionMode()
|
||||
return CalculatorForNestedCall(
|
||||
candidate, returnType, csCompleterContext, trivialConstraintTypeInferenceOracle
|
||||
).computeCompletionMode()
|
||||
}
|
||||
}
|
||||
|
||||
private class CalculatorForNestedCall(
|
||||
private val candidate: KotlinResolutionCandidate,
|
||||
private val returnType: UnwrappedType?,
|
||||
private val csCompleterContext: CsCompleterContext,
|
||||
private val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle
|
||||
@@ -57,6 +61,10 @@ class CompletionModeCalculator {
|
||||
private val variablesWithQueuedConstraints = mutableSetOf<TypeVariableMarker>()
|
||||
private val typesToProcess: Queue<KotlinTypeMarker> = ArrayDeque()
|
||||
|
||||
private val postponedAtoms: List<PostponedResolvedAtom> by lazy {
|
||||
KotlinConstraintSystemCompleter.getOrderedNotAnalyzedPostponedArguments(listOf(candidate.resolvedCall))
|
||||
}
|
||||
|
||||
fun computeCompletionMode(): ConstraintSystemCompletionMode = with(csCompleterContext) {
|
||||
// Add fixation directions for variables based on effective variance in type
|
||||
typesToProcess.add(returnType)
|
||||
@@ -163,13 +171,14 @@ class CompletionModeCalculator {
|
||||
direction: FixationDirection
|
||||
): Boolean {
|
||||
val constraints = variableWithConstraints.constraints
|
||||
val variable = variableWithConstraints.typeVariable
|
||||
|
||||
// todo check correctness for @Exact
|
||||
return constraints.isNotEmpty() && constraints.any { constraint ->
|
||||
constraint.hasRequiredKind(direction)
|
||||
&& !constraint.type.typeConstructor().isIntegerLiteralTypeConstructor()
|
||||
&& isProperType(constraint.type)
|
||||
&& trivialConstraintTypeInferenceOracle.isSuitableResultedType(constraint.type)
|
||||
&& !constraint.type.typeConstructor().isIntegerLiteralTypeConstructor()
|
||||
&& !isNothingConstraintForPartiallyAnalyzedVariable(constraint, variable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,5 +186,16 @@ class CompletionModeCalculator {
|
||||
FixationDirection.TO_SUBTYPE -> kind.isLower() || kind.isEqual()
|
||||
FixationDirection.EQUALITY -> kind.isEqual()
|
||||
}
|
||||
|
||||
private fun CsCompleterContext.isNothingConstraintForPartiallyAnalyzedVariable(
|
||||
constraint: Constraint,
|
||||
variable: TypeVariableMarker
|
||||
): Boolean {
|
||||
if (trivialConstraintTypeInferenceOracle.isSuitableResultedType(constraint.type) || !constraint.kind.isLower())
|
||||
return false
|
||||
return postponedAtoms.any { atom ->
|
||||
atom.expectedType?.contains { type -> variable.defaultType() == type } ?: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-19
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
|
||||
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype
|
||||
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionStatelessCallbacks
|
||||
import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.*
|
||||
@@ -23,7 +22,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
class KotlinConstraintSystemCompleter(
|
||||
private val resultTypeResolver: ResultTypeResolver,
|
||||
val variableFixationFinder: VariableFixationFinder,
|
||||
private val statelessCallbacks: KotlinResolutionStatelessCallbacks
|
||||
) {
|
||||
enum class ConstraintSystemCompletionMode {
|
||||
FULL,
|
||||
@@ -214,23 +212,6 @@ class KotlinConstraintSystemCompleter(
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List<ResolvedAtom>): List<PostponedResolvedAtom> {
|
||||
fun ResolvedAtom.process(to: MutableList<PostponedResolvedAtom>) {
|
||||
to.addIfNotNull(this.safeAs<PostponedResolvedAtom>()?.takeUnless { it.analyzed })
|
||||
|
||||
if (analyzed) {
|
||||
subResolvedAtoms?.forEach { it.process(to) }
|
||||
}
|
||||
}
|
||||
|
||||
val notAnalyzedArguments = arrayListOf<PostponedResolvedAtom>()
|
||||
for (primitive in topLevelAtoms) {
|
||||
primitive.process(notAnalyzedArguments)
|
||||
}
|
||||
|
||||
return notAnalyzedArguments
|
||||
}
|
||||
|
||||
private fun getOrderedAllTypeVariables(
|
||||
c: Context,
|
||||
collectVariablesFromContext: Boolean,
|
||||
@@ -352,4 +333,23 @@ class KotlinConstraintSystemCompleter(
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List<ResolvedAtom>): List<PostponedResolvedAtom> {
|
||||
fun ResolvedAtom.process(to: MutableList<PostponedResolvedAtom>) {
|
||||
to.addIfNotNull(this.safeAs<PostponedResolvedAtom>()?.takeUnless { it.analyzed })
|
||||
|
||||
if (analyzed) {
|
||||
subResolvedAtoms?.forEach { it.process(to) }
|
||||
}
|
||||
}
|
||||
|
||||
val notAnalyzedArguments = arrayListOf<PostponedResolvedAtom>()
|
||||
for (primitive in topLevelAtoms) {
|
||||
primitive.process(notAnalyzedArguments)
|
||||
}
|
||||
|
||||
return notAnalyzedArguments
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -15,7 +15,7 @@ val ww = if (true) {
|
||||
<!OI;TYPE_MISMATCH!>{ true }<!> <!USELESS_ELVIS!>?: null!!<!>
|
||||
}
|
||||
else if (true) {
|
||||
<!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, TYPE_MISMATCH!>{ <!NI;CONSTANT_EXPECTED_TYPE_MISMATCH!>true<!> }<!> <!USELESS_ELVIS!>?: null!!<!>
|
||||
<!OI;TYPE_MISMATCH!>{ true }<!> <!USELESS_ELVIS!>?: null!!<!>
|
||||
}
|
||||
else {
|
||||
null!!
|
||||
|
||||
Vendored
+52
@@ -0,0 +1,52 @@
|
||||
// !LANGUAGE: +NewInference
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
fun <K> id(it: K) = it
|
||||
|
||||
fun <E> smartCast(arg: E?, fn: () -> Any?): E = TODO()
|
||||
fun <E1> noSmartCast1(arg: E1?, fn: () -> E1): E1 = TODO()
|
||||
fun <E2> noSmartCast2(arg: E2?, fn: E2): E2 = TODO()
|
||||
fun <E3, F : E3> noSmartCast3(arg: E3?, fn: () -> F): E3 = TODO()
|
||||
fun <E4, F : E4> noSmartCast4(arg: E4?, fn: F): E4 = TODO()
|
||||
|
||||
|
||||
fun testSmartCast(s: String?) {
|
||||
id(
|
||||
if (s != null) ""
|
||||
else smartCast(null) { "" }
|
||||
)
|
||||
s.length
|
||||
}
|
||||
|
||||
fun testNoSmartCast1(s: String?) {
|
||||
id(
|
||||
if (s != null) ""
|
||||
else noSmartCast1(null) { "" }
|
||||
)
|
||||
s.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
|
||||
fun testNoSmartCast2(s: String?) {
|
||||
id(
|
||||
if (s != null) ( {""} )
|
||||
else noSmartCast2(null) { "" }
|
||||
)
|
||||
s.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
|
||||
fun testNoSmartCast3(s: String?) {
|
||||
id(
|
||||
if (s != null) ""
|
||||
else noSmartCast3(null) { "" }
|
||||
)
|
||||
s.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
|
||||
// KT-36069
|
||||
fun testNoSmartCast4(s: String?) {
|
||||
id(
|
||||
if (s != null) ( {""} )
|
||||
else noSmartCast4(null) { "" }
|
||||
)
|
||||
s.<!INAPPLICABLE_CANDIDATE!>length<!>
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// !LANGUAGE: +NewInference
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
fun <K> id(it: K) = it
|
||||
|
||||
fun <E> smartCast(arg: E?, fn: () -> Any?): E = TODO()
|
||||
fun <E1> noSmartCast1(arg: E1?, fn: () -> E1): E1 = TODO()
|
||||
fun <E2> noSmartCast2(arg: E2?, fn: E2): E2 = TODO()
|
||||
fun <E3, F : E3> noSmartCast3(arg: E3?, fn: () -> F): E3 = TODO()
|
||||
fun <E4, F : E4> noSmartCast4(arg: E4?, fn: F): E4 = TODO()
|
||||
|
||||
|
||||
fun testSmartCast(s: String?) {
|
||||
id(
|
||||
if (s != null) ""
|
||||
else <!IMPLICIT_NOTHING_AS_TYPE_PARAMETER!>smartCast<!>(null) { "" }
|
||||
)
|
||||
<!DEBUG_INFO_SMARTCAST!>s<!>.length
|
||||
}
|
||||
|
||||
fun testNoSmartCast1(s: String?) {
|
||||
id(
|
||||
if (s != null) ""
|
||||
else noSmartCast1(null) { "" }
|
||||
)
|
||||
s<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
|
||||
fun testNoSmartCast2(s: String?) {
|
||||
id(
|
||||
if (s != null) ( {""} )
|
||||
else noSmartCast2(null) { "" }
|
||||
)
|
||||
s<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
|
||||
fun testNoSmartCast3(s: String?) {
|
||||
id(
|
||||
if (s != null) ""
|
||||
else noSmartCast3(null) { "" }
|
||||
)
|
||||
s<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
|
||||
// KT-36069
|
||||
fun testNoSmartCast4(s: String?) {
|
||||
id(
|
||||
if (s != null) ( {""} )
|
||||
else <!IMPLICIT_NOTHING_AS_TYPE_PARAMETER, IMPLICIT_NOTHING_AS_TYPE_PARAMETER!>noSmartCast4<!>(null) <!TYPE_MISMATCH!>{ <!TYPE_MISMATCH!>""<!> }<!>
|
||||
)
|
||||
s<!UNSAFE_CALL!>.<!>length
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package
|
||||
|
||||
public fun </*0*/ K> id(/*0*/ it: K): K
|
||||
public fun </*0*/ E1> noSmartCast1(/*0*/ arg: E1?, /*1*/ fn: () -> E1): E1
|
||||
public fun </*0*/ E2> noSmartCast2(/*0*/ arg: E2?, /*1*/ fn: E2): E2
|
||||
public fun </*0*/ E3, /*1*/ F : E3> noSmartCast3(/*0*/ arg: E3?, /*1*/ fn: () -> F): E3
|
||||
public fun </*0*/ E4, /*1*/ F : E4> noSmartCast4(/*0*/ arg: E4?, /*1*/ fn: F): E4
|
||||
public fun </*0*/ E> smartCast(/*0*/ arg: E?, /*1*/ fn: () -> kotlin.Any?): E
|
||||
public fun testNoSmartCast1(/*0*/ s: kotlin.String?): kotlin.Unit
|
||||
public fun testNoSmartCast2(/*0*/ s: kotlin.String?): kotlin.Unit
|
||||
public fun testNoSmartCast3(/*0*/ s: kotlin.String?): kotlin.Unit
|
||||
public fun testNoSmartCast4(/*0*/ s: kotlin.String?): kotlin.Unit
|
||||
public fun testSmartCast(/*0*/ s: kotlin.String?): kotlin.Unit
|
||||
@@ -0,0 +1,20 @@
|
||||
// !LANGUAGE: +NewInference
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
fun main() {
|
||||
val baseDir: String? = ""
|
||||
val networkParameters: String? = ""
|
||||
if (baseDir != null) {
|
||||
if (networkParameters != null) {
|
||||
Unit
|
||||
} else if (true){
|
||||
return
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
networkParameters.length // unsafe call
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// !LANGUAGE: +NewInference
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
fun main() {
|
||||
val baseDir: String? = ""
|
||||
val networkParameters: String? = ""
|
||||
if (baseDir != null) {
|
||||
if (networkParameters != null) {
|
||||
Unit
|
||||
} else if (true){
|
||||
return
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_SMARTCAST!>networkParameters<!>.length // unsafe call
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public fun main(): kotlin.Unit
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// !WITH_NEW_INFERENCE
|
||||
val test: Int = <!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>if (true) {
|
||||
val test: Int = <!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH!>if (true) {
|
||||
when (2) {
|
||||
1 -> 1
|
||||
else -> <!OI;NULL_FOR_NONNULL_TYPE!>null<!>
|
||||
|
||||
@@ -10589,6 +10589,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaWithVariableAndNothing.kt")
|
||||
public void testLambdaWithVariableAndNothing() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("partialForIlt.kt")
|
||||
public void testPartialForIlt() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt");
|
||||
@@ -17858,6 +17863,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
runTest("compiler/testData/diagnostics/tests/regressions/kt3535.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt35668.kt")
|
||||
public void testKt35668() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/regressions/kt35668.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt3647.kt")
|
||||
public void testKt3647() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/regressions/kt3647.kt");
|
||||
|
||||
Generated
+10
@@ -10584,6 +10584,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
||||
runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaWithVariableAndNothing.kt")
|
||||
public void testLambdaWithVariableAndNothing() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/completion/lambdaWithVariableAndNothing.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("partialForIlt.kt")
|
||||
public void testPartialForIlt() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt");
|
||||
@@ -17848,6 +17853,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
||||
runTest("compiler/testData/diagnostics/tests/regressions/kt3535.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt35668.kt")
|
||||
public void testKt35668() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/regressions/kt35668.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt3647.kt")
|
||||
public void testKt3647() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/regressions/kt3647.kt");
|
||||
|
||||
Reference in New Issue
Block a user