FIR: Avoid potentially dangerous checks at makesSenseToBeDefinitelyNotNull
Sometimes, it might be called before type parameter bounds are initialized or even before the symbols are bound to FIR In such cases, we just assume it makes sense to create DNN there
This commit is contained in:
@@ -7,15 +7,12 @@ package org.jetbrains.kotlin.fir.java
|
||||
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeIntermediateDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
|
||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.java.enhancement.readOnlyToMutable
|
||||
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
|
||||
@@ -109,7 +106,7 @@ private fun JavaType?.toConeTypeProjection(
|
||||
lowerBound, session.typeContext,
|
||||
// Upper bounds might be not initialized properly yet, so we force creating DefinitelyNotNullType
|
||||
// It should not affect semantics, since it would be still a valid type anyway
|
||||
forceWithoutCheck = true,
|
||||
avoidComprehensiveCheck = true,
|
||||
) ?: lowerBound
|
||||
else -> lowerBound
|
||||
}
|
||||
|
||||
+4
-2
@@ -47,7 +47,7 @@ abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContex
|
||||
override fun substituteOrNull(type: ConeKotlinType): ConeKotlinType? {
|
||||
val newType = substituteType(type)
|
||||
if (newType != null && type is ConeDefinitelyNotNullType) {
|
||||
return newType.makeConeTypeDefinitelyNotNullOrNotNull(typeContext)
|
||||
return newType.makeConeTypeDefinitelyNotNullOrNotNull(typeContext, avoidComprehensiveCheck = false)
|
||||
}
|
||||
return (newType ?: type.substituteRecursive())
|
||||
}
|
||||
@@ -109,7 +109,9 @@ abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContex
|
||||
typeContext,
|
||||
substitutedOriginal.attributes.add(original.attributes)
|
||||
)
|
||||
return ConeDefinitelyNotNullType.create(substituted, typeContext) ?: substituted
|
||||
return ConeDefinitelyNotNullType.create(
|
||||
substituted, typeContext, avoidComprehensiveCheck = true,
|
||||
) ?: substituted
|
||||
}
|
||||
|
||||
private fun ConeFlexibleType.substituteBounds(): ConeFlexibleType? {
|
||||
|
||||
@@ -58,13 +58,16 @@ fun ConeInferenceContext.intersectTypesOrNull(types: List<ConeKotlinType>): Cone
|
||||
fun TypeCheckerProviderContext.equalTypes(a: ConeKotlinType, b: ConeKotlinType): Boolean =
|
||||
AbstractTypeChecker.equalTypes(this, a, b)
|
||||
|
||||
private fun ConeTypeContext.makesSenseToBeDefinitelyNotNull(originalType: ConeKotlinType): Boolean {
|
||||
return when (val type = originalType.lowerBoundIfFlexible()) {
|
||||
is ConeTypeParameterType -> !type.lookupTag.symbol.isInitializedFir || type.isNullableType()
|
||||
private fun ConeTypeContext.makesSenseToBeDefinitelyNotNull(
|
||||
type: ConeSimpleKotlinType,
|
||||
avoidComprehensiveCheck: Boolean,
|
||||
): Boolean {
|
||||
return when (type) {
|
||||
is ConeTypeParameterType -> avoidComprehensiveCheck || type.isNullableType()
|
||||
// Actually, this branch should work for type parameters as well, but it breaks some cases. See KT-40114.
|
||||
// Basically, if we have `T : X..X?`, then `T <: Any` but we still have `T` != `T & Any`.
|
||||
is ConeTypeVariableType, is ConeCapturedType -> {
|
||||
!AbstractNullabilityChecker.isSubtypeOfAny(
|
||||
avoidComprehensiveCheck || !AbstractNullabilityChecker.isSubtypeOfAny(
|
||||
newTypeCheckerState(errorTypesEqualToAnything = false, stubTypesEqualToAnything = false), type
|
||||
)
|
||||
}
|
||||
@@ -76,22 +79,32 @@ private fun ConeTypeContext.makesSenseToBeDefinitelyNotNull(originalType: ConeKo
|
||||
fun ConeDefinitelyNotNullType.Companion.create(
|
||||
original: ConeKotlinType,
|
||||
typeContext: ConeTypeContext,
|
||||
forceWithoutCheck: Boolean = false,
|
||||
// Sometimes, it might be called before type parameter bounds are initialized
|
||||
// or even before the symbols are bound to FIR
|
||||
// In such cases, we just assume it makes sense to create DNN there
|
||||
// NB: `makesSenseToBeDefinitelyNotNull` is mostly an optimization, it should not affect semantics
|
||||
avoidComprehensiveCheck: Boolean = false,
|
||||
): ConeDefinitelyNotNullType? {
|
||||
return when (original) {
|
||||
is ConeDefinitelyNotNullType -> original
|
||||
is ConeFlexibleType -> create(original.lowerBound, typeContext)
|
||||
is ConeSimpleKotlinType -> runIf(forceWithoutCheck || typeContext.makesSenseToBeDefinitelyNotNull(original)) {
|
||||
is ConeFlexibleType -> create(original.lowerBound, typeContext, avoidComprehensiveCheck)
|
||||
is ConeSimpleKotlinType -> runIf(typeContext.makesSenseToBeDefinitelyNotNull(original, avoidComprehensiveCheck)) {
|
||||
ConeDefinitelyNotNullType(original.coneLowerBoundIfFlexible())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ConeKotlinType.makeConeTypeDefinitelyNotNullOrNotNull(typeContext: ConeTypeContext): ConeKotlinType {
|
||||
fun ConeKotlinType.makeConeTypeDefinitelyNotNullOrNotNull(
|
||||
typeContext: ConeTypeContext,
|
||||
avoidComprehensiveCheck: Boolean = false,
|
||||
): ConeKotlinType {
|
||||
if (this is ConeIntersectionType) {
|
||||
return ConeIntersectionType(intersectedTypes.map { it.makeConeTypeDefinitelyNotNullOrNotNull(typeContext) })
|
||||
return ConeIntersectionType(intersectedTypes.map {
|
||||
it.makeConeTypeDefinitelyNotNullOrNotNull(typeContext, avoidComprehensiveCheck)
|
||||
})
|
||||
}
|
||||
return ConeDefinitelyNotNullType.create(this, typeContext) ?: this.withNullability(ConeNullability.NOT_NULL, typeContext)
|
||||
return ConeDefinitelyNotNullType.create(this, typeContext, avoidComprehensiveCheck)
|
||||
?: this.withNullability(ConeNullability.NOT_NULL, typeContext)
|
||||
}
|
||||
|
||||
fun <T : ConeKotlinType> T.withArguments(arguments: Array<out ConeTypeProjection>): T {
|
||||
|
||||
@@ -89,7 +89,7 @@ fun main4() {
|
||||
|
||||
fun takeStarFoo3(x: Foo3<*>) {
|
||||
x.value = <!ASSIGNMENT_TYPE_MISMATCH!>"test"<!>
|
||||
x.value <!NONE_APPLICABLE!>+=<!> "test"
|
||||
x.value <!UNRESOLVED_REFERENCE!>+=<!> "test"
|
||||
}
|
||||
|
||||
fun main5() {
|
||||
|
||||
+2
-2
@@ -89,7 +89,7 @@ fun main4() {
|
||||
|
||||
fun takeStarFoo3(x: Foo3<*>) {
|
||||
x.value = <!ASSIGNMENT_TYPE_MISMATCH!>"test"<!>
|
||||
x.value <!NONE_APPLICABLE!>+=<!> "test"
|
||||
x.value <!UNRESOLVED_REFERENCE!>+=<!> "test"
|
||||
}
|
||||
|
||||
fun main5() {
|
||||
@@ -115,4 +115,4 @@ fun main6() {
|
||||
bar.value = 1
|
||||
takeStarBar3(bar)
|
||||
println(bar.value) // CCE: String cannot be cast to Number
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -26,10 +26,10 @@ fun test() {
|
||||
}
|
||||
|
||||
fun testJava(appender: JavaWriterAppender) {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("JavaWriterAppender.Builder1<*>..JavaWriterAppender.Builder1<*>?!")!>appender.newBuilder()<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("JavaWriterAppender.Builder1<out JavaWriterAppender.Builder1<*>..JavaWriterAppender.Builder1<*>?!>")!>appender.Builder1()<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("JavaWriterAppender.Builder1<*>..JavaWriterAppender.Builder1<*>?!"), NEW_INFERENCE_ERROR!>appender.newBuilder()<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("JavaWriterAppender.Builder1<out JavaWriterAppender.Builder1<*>..JavaWriterAppender.Builder1<*>?!>"), NEW_INFERENCE_ERROR!>appender.Builder1()<!>
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("JavaWriterAppender.Builder1<*>..JavaWriterAppender.Builder1<*>?! & JavaWriterAppender.Builder2<*>..JavaWriterAppender.Builder2<*>?!..JavaWriterAppender.Builder1<*>? & JavaWriterAppender.Builder2<*>?")!>appender.intersectTwoSelfTypes()<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("JavaWriterAppender.Builder1<*> & JavaWriterAppender.Builder2<*>..JavaWriterAppender.Builder1<*>? & JavaWriterAppender.Builder2<*>?"), NEW_INFERENCE_ERROR!>appender.intersectTwoSelfTypes()<!>
|
||||
}
|
||||
|
||||
object WriterAppender {
|
||||
|
||||
Reference in New Issue
Block a user