K2: Fix generic inference case with !! synthetic call
Result of the `checkNotNull` calls should always be a non-nullable values. The simplest idea how to acheive it is adding not-nullable Any bound to the type parameter declaration. Existing comment stating about impossibility of such bound seems to be not 100% correct because it doesn't take into account presence of definitely-non-nullable X & Any types that allow described case with nullable generic. ^KT-55804 Fixed
This commit is contained in:
committed by
Space Team
parent
352316ba9f
commit
56557fb8ff
+6
@@ -14366,6 +14366,12 @@ public class DiagnosticCompilerTestFE10TestdataTestGenerated extends AbstractDia
|
|||||||
runTest("compiler/testData/diagnostics/tests/inference/errorsOnImplicitInvokeInSimpleCall.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/errorsOnImplicitInvokeInSimpleCall.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("exclExclInference.kt")
|
||||||
|
public void testExclExclInference() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/exclExclInference.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("expectedTypeAdditionalTest.kt")
|
@TestMetadata("expectedTypeAdditionalTest.kt")
|
||||||
public void testExpectedTypeAdditionalTest() throws Exception {
|
public void testExpectedTypeAdditionalTest() throws Exception {
|
||||||
|
|||||||
+6
@@ -14366,6 +14366,12 @@ public class LLFirPreresolvedReversedDiagnosticCompilerFE10TestDataTestGenerated
|
|||||||
runTest("compiler/testData/diagnostics/tests/inference/errorsOnImplicitInvokeInSimpleCall.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/errorsOnImplicitInvokeInSimpleCall.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("exclExclInference.kt")
|
||||||
|
public void testExclExclInference() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/exclExclInference.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("expectedTypeAdditionalTest.kt")
|
@TestMetadata("expectedTypeAdditionalTest.kt")
|
||||||
public void testExpectedTypeAdditionalTest() throws Exception {
|
public void testExpectedTypeAdditionalTest() throws Exception {
|
||||||
|
|||||||
+6
@@ -14366,6 +14366,12 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
|
|||||||
runTest("compiler/testData/diagnostics/tests/inference/errorsOnImplicitInvokeInSimpleCall.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/errorsOnImplicitInvokeInSimpleCall.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("exclExclInference.kt")
|
||||||
|
public void testExclExclInference() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/exclExclInference.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("expectedTypeAdditionalTest.kt")
|
@TestMetadata("expectedTypeAdditionalTest.kt")
|
||||||
public void testExpectedTypeAdditionalTest() throws Exception {
|
public void testExpectedTypeAdditionalTest() throws Exception {
|
||||||
|
|||||||
+6
@@ -14372,6 +14372,12 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
|
|||||||
runTest("compiler/testData/diagnostics/tests/inference/errorsOnImplicitInvokeInSimpleCall.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/errorsOnImplicitInvokeInSimpleCall.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("exclExclInference.kt")
|
||||||
|
public void testExclExclInference() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/exclExclInference.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("expectedTypeAdditionalTest.kt")
|
@TestMetadata("expectedTypeAdditionalTest.kt")
|
||||||
public void testExpectedTypeAdditionalTest() throws Exception {
|
public void testExpectedTypeAdditionalTest() throws Exception {
|
||||||
|
|||||||
+12
-8
@@ -239,7 +239,10 @@ class FirSyntheticCallGenerator(
|
|||||||
containingDeclarations = components.containingDeclarations
|
containingDeclarations = components.containingDeclarations
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun generateSyntheticSelectTypeParameter(functionSymbol: FirSyntheticFunctionSymbol): Pair<FirTypeParameter, FirResolvedTypeRef> {
|
private fun generateSyntheticSelectTypeParameter(
|
||||||
|
functionSymbol: FirSyntheticFunctionSymbol,
|
||||||
|
isNullableBound: Boolean = true,
|
||||||
|
): Pair<FirTypeParameter, FirResolvedTypeRef> {
|
||||||
val typeParameterSymbol = FirTypeParameterSymbol()
|
val typeParameterSymbol = FirTypeParameterSymbol()
|
||||||
val typeParameter =
|
val typeParameter =
|
||||||
buildTypeParameter {
|
buildTypeParameter {
|
||||||
@@ -251,7 +254,12 @@ class FirSyntheticCallGenerator(
|
|||||||
containingDeclarationSymbol = functionSymbol
|
containingDeclarationSymbol = functionSymbol
|
||||||
variance = Variance.INVARIANT
|
variance = Variance.INVARIANT
|
||||||
isReified = false
|
isReified = false
|
||||||
addDefaultBoundIfNecessary()
|
|
||||||
|
if (!isNullableBound) {
|
||||||
|
bounds += moduleData.session.builtinTypes.anyType
|
||||||
|
} else {
|
||||||
|
addDefaultBoundIfNecessary()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val typeParameterTypeRef = buildResolvedTypeRef { type = ConeTypeParameterTypeImpl(typeParameterSymbol.toLookupTag(), false) }
|
val typeParameterTypeRef = buildResolvedTypeRef { type = ConeTypeParameterTypeImpl(typeParameterSymbol.toLookupTag(), false) }
|
||||||
@@ -280,13 +288,9 @@ class FirSyntheticCallGenerator(
|
|||||||
|
|
||||||
private fun generateSyntheticCheckNotNullFunction(): FirSimpleFunction {
|
private fun generateSyntheticCheckNotNullFunction(): FirSimpleFunction {
|
||||||
// Synthetic function signature:
|
// Synthetic function signature:
|
||||||
// fun <K> checkNotNull(arg: K?): K
|
// fun <K : Any> checkNotNull(arg: K?): K
|
||||||
//
|
|
||||||
// Note: The upper bound of `K` cannot be `Any` because of the following case:
|
|
||||||
// fun <X> test(a: X) = a!!
|
|
||||||
// `X` is not a subtype of `Any` and hence cannot satisfy `K` if it had an upper bound of `Any`.
|
|
||||||
val functionSymbol = FirSyntheticFunctionSymbol(SyntheticCallableId.CHECK_NOT_NULL)
|
val functionSymbol = FirSyntheticFunctionSymbol(SyntheticCallableId.CHECK_NOT_NULL)
|
||||||
val (typeParameter, returnType) = generateSyntheticSelectTypeParameter(functionSymbol)
|
val (typeParameter, returnType) = generateSyntheticSelectTypeParameter(functionSymbol, isNullableBound = false)
|
||||||
|
|
||||||
val argumentType = buildResolvedTypeRef {
|
val argumentType = buildResolvedTypeRef {
|
||||||
type = returnType.type.withNullability(ConeNullability.NULLABLE, session.typeContext)
|
type = returnType.type.withNullability(ConeNullability.NULLABLE, session.typeContext)
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
fun main() {
|
fun main() {
|
||||||
<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!><!UNSUPPORTED!>[]<!><!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>!!<!>
|
<!UNSUPPORTED!>[]<!><!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!><!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// FIR_DUMP
|
||||||
|
// ISSUE: KT-55804
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val x: String?
|
||||||
|
x = materialize()!! // Should be treated as non-nullable assignment
|
||||||
|
x.length // Should be allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <E> materialize(): E = TODO()
|
||||||
|
|
||||||
|
fun <F> test(f: F) = f!!
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
test<String>("").length
|
||||||
|
test<String?>(null).length // `.length` should be allowed because return type of "test" should be inferred to `F & Any`
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
FILE: exclExclInference.fir.kt
|
||||||
|
public final fun foo(): R|kotlin/Unit| {
|
||||||
|
lval x: R|kotlin/String?|
|
||||||
|
R|<local>/x| = R|/materialize|<R|kotlin/String?|>()!!
|
||||||
|
R|<local>/x|.R|kotlin/String.length|
|
||||||
|
}
|
||||||
|
public final fun <E> materialize(): R|E| {
|
||||||
|
^materialize R|kotlin/TODO|()
|
||||||
|
}
|
||||||
|
public final fun <F> test(f: R|F|): R|F & Any| {
|
||||||
|
^test R|<local>/f|!!
|
||||||
|
}
|
||||||
|
public final fun main(): R|kotlin/Unit| {
|
||||||
|
R|/test|<R|kotlin/String|>(String()).R|kotlin/String.length|
|
||||||
|
R|/test|<R|kotlin/String?|>(Null(null)).R|kotlin/String.length|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// FIR_DUMP
|
||||||
|
// ISSUE: KT-55804
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
val x: String?
|
||||||
|
x = materialize()!! // Should be treated as non-nullable assignment
|
||||||
|
<!DEBUG_INFO_SMARTCAST!>x<!>.length // Should be allowed
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <E> materialize(): E = TODO()
|
||||||
|
|
||||||
|
fun <F> test(f: F) = f!!
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
test<String>("").length
|
||||||
|
test<String?>(null).length // `.length` should be allowed because return type of "test" should be inferred to `F & Any`
|
||||||
|
}
|
||||||
@@ -4,8 +4,8 @@ import java.lang.Exception
|
|||||||
fun <K> id(arg: K): K = arg
|
fun <K> id(arg: K): K = arg
|
||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>id(<!UNRESOLVED_REFERENCE!>unresolved<!>)!!<!>
|
id(<!UNRESOLVED_REFERENCE!>unresolved<!>)!!
|
||||||
<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!><!UNRESOLVED_REFERENCE!>unresolved<!>!!!!<!>
|
<!UNRESOLVED_REFERENCE!>unresolved<!>!!<!UNNECESSARY_NOT_NULL_ASSERTION!>!!<!>
|
||||||
<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>try {
|
<!NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER, NEW_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>try {
|
||||||
id(<!UNRESOLVED_REFERENCE!>unresolved<!>)
|
id(<!UNRESOLVED_REFERENCE!>unresolved<!>)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
Generated
+6
@@ -14372,6 +14372,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/inference/errorsOnImplicitInvokeInSimpleCall.kt");
|
runTest("compiler/testData/diagnostics/tests/inference/errorsOnImplicitInvokeInSimpleCall.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("exclExclInference.kt")
|
||||||
|
public void testExclExclInference() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/inference/exclExclInference.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("expectedTypeAdditionalTest.kt")
|
@TestMetadata("expectedTypeAdditionalTest.kt")
|
||||||
public void testExpectedTypeAdditionalTest() throws Exception {
|
public void testExpectedTypeAdditionalTest() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user