K2: don't make T = Type! substitutions for constructors, including SAM

Without this commit we get some ABI changes and it looks bad.

Related to KT-65596
This commit is contained in:
Mikhail Glukhikh
2024-02-21 13:58:26 +01:00
committed by Space Team
parent 81414d758d
commit 78b6432ced
27 changed files with 159 additions and 76 deletions
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap
import org.jetbrains.kotlin.fir.scopes.impl.toConeType
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.StandardClassIds
@@ -82,12 +83,64 @@ internal object CreateFreshTypeVariableSubstitutorStage : ResolutionStage() {
}
}
/**
* This function provides a type for a newly created EQUALS constraint on a fresh type variable,
* for a situation when we have an explicit type argument and type parameter is a Java type parameter without known nullability.
*
* For a normal function call, like foo<T = SomeType>, we create a constraint T = SomeType!.
* This is an unsafe solution, however yet we have to keep it, otherwise a lot of code becomes red.
* Typical "strange" example:
*
* ```
* // Java
* public class Foo {
* static <T> T id(T foo) {
* return null;
* }
* }
*
* // Kotlin
* fun test(): String {
* return Foo.id<String?>(null) // OK...
* }
* ```
*
* We keep more sound constraint T = SomeType for regular and SAM constructor calls. Typical examples are:
*
* ```
* fun test1() = J1<Int>() // type should be J1<Int>, not J1<Int!>
* // J1.java
* public class J1<T1> {}
* ```
*
* or
*
* ```
* // Again, type should be J<String> and not J<String!>
* fun test1() = J<String> { x -> x }
*
*
* // FILE: J.java
* public interface J<T> {
* T foo(T x);
* }
* ```
*
* @return type which is chosen for EQUALS constraint
*/
private fun getTypePreservingFlexibilityWrtTypeVariable(
type: ConeKotlinType,
typeParameter: FirTypeParameterRef,
session: FirSession,
): ConeKotlinType {
return if (typeParameter.shouldBeFlexible(session.typeContext)) {
val containingDeclarationSymbol = typeParameter.symbol.containingDeclarationSymbol
// To remove constructors (they use class type parameters)
return if (
containingDeclarationSymbol is FirCallableSymbol &&
// To remove SAMs
containingDeclarationSymbol !is FirSyntheticFunctionSymbol &&
typeParameter.shouldBeFlexible(session.typeContext)
) {
val notNullType = type.withNullability(ConeNullability.NOT_NULL, session.typeContext) as ConeSimpleKotlinType
ConeFlexibleType(notNullType, notNullType.withNullability(ConeNullability.NULLABLE, session.typeContext))
} else {