[FIR] Implement bare type modification with known type arguments
This commit is contained in:
+34
-8
@@ -37,9 +37,7 @@ import org.jetbrains.kotlin.fir.symbols.invoke
|
|||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.fir.types.builder.*
|
import org.jetbrains.kotlin.fir.types.builder.*
|
||||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||||
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
|
import org.jetbrains.kotlin.fir.visitors.*
|
||||||
import org.jetbrains.kotlin.fir.visitors.compose
|
|
||||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
@@ -282,31 +280,59 @@ class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransformer) :
|
|||||||
throw IllegalArgumentException(operatorCall.render())
|
throw IllegalArgumentException(operatorCall.render())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun FirTypeRef.withTypeArgumentsForBareType(argument: FirExpression): FirTypeRef {
|
||||||
|
val baseTypeArguments = argument.typeRef.coneTypeSafe<ConeKotlinType>()?.typeArguments
|
||||||
|
val type = coneTypeSafe<ConeKotlinType>()
|
||||||
|
return if (type?.typeArguments?.isEmpty() != true ||
|
||||||
|
type is ConeTypeParameterType ||
|
||||||
|
baseTypeArguments?.isEmpty() != false ||
|
||||||
|
(type is ConeClassLikeType &&
|
||||||
|
(type.lookupTag.toSymbol(session)?.fir as? FirTypeParametersOwner)?.typeParameters?.isEmpty() == true)
|
||||||
|
) {
|
||||||
|
this
|
||||||
|
} else {
|
||||||
|
withReplacedConeType(type.withArguments(baseTypeArguments))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun transformTypeOperatorCall(
|
override fun transformTypeOperatorCall(
|
||||||
typeOperatorCall: FirTypeOperatorCall,
|
typeOperatorCall: FirTypeOperatorCall,
|
||||||
data: ResolutionMode,
|
data: ResolutionMode,
|
||||||
): CompositeTransformResult<FirStatement> {
|
): CompositeTransformResult<FirStatement> {
|
||||||
val symbolProvider = session.firSymbolProvider
|
|
||||||
val resolved = (transformExpression(typeOperatorCall, data).single as FirTypeOperatorCall)
|
val resolved = (transformExpression(typeOperatorCall, data).single as FirTypeOperatorCall)
|
||||||
.transformArguments(integerLiteralTypeApproximator, null)
|
.transformArguments(integerLiteralTypeApproximator, null)
|
||||||
|
val conversionTypeRef = resolved.conversionTypeRef.withTypeArgumentsForBareType(resolved.argument)
|
||||||
|
resolved.transformChildren(object : FirDefaultTransformer<Nothing?>() {
|
||||||
|
override fun <E : FirElement> transformElement(element: E, data: Nothing?): CompositeTransformResult<E> {
|
||||||
|
return element.compose()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun transformTypeRef(typeRef: FirTypeRef, data: Nothing?): CompositeTransformResult<FirTypeRef> {
|
||||||
|
return if (typeRef === resolved.conversionTypeRef) {
|
||||||
|
conversionTypeRef.compose()
|
||||||
|
} else {
|
||||||
|
typeRef.compose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, null)
|
||||||
when (resolved.operation) {
|
when (resolved.operation) {
|
||||||
FirOperation.IS, FirOperation.NOT_IS -> {
|
FirOperation.IS, FirOperation.NOT_IS -> {
|
||||||
resolved.resultType = session.builtinTypes.booleanType
|
resolved.resultType = session.builtinTypes.booleanType
|
||||||
}
|
}
|
||||||
FirOperation.AS -> {
|
FirOperation.AS -> {
|
||||||
resolved.resultType = resolved.conversionTypeRef
|
resolved.resultType = conversionTypeRef
|
||||||
}
|
}
|
||||||
FirOperation.SAFE_AS -> {
|
FirOperation.SAFE_AS -> {
|
||||||
resolved.resultType =
|
resolved.resultType =
|
||||||
resolved.conversionTypeRef.withReplacedConeType(
|
conversionTypeRef.withReplacedConeType(
|
||||||
resolved.conversionTypeRef.coneTypeUnsafe<ConeKotlinType>().withNullability(
|
conversionTypeRef.coneTypeUnsafe<ConeKotlinType>().withNullability(
|
||||||
ConeNullability.NULLABLE, session.inferenceContext,
|
ConeNullability.NULLABLE, session.inferenceContext,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else -> error("Unknown type operator")
|
else -> error("Unknown type operator")
|
||||||
}
|
}
|
||||||
dataFlowAnalyzer.exitTypeOperatorCall(typeOperatorCall)
|
dataFlowAnalyzer.exitTypeOperatorCall(resolved)
|
||||||
return resolved.transform(integerLiteralTypeApproximator, null)
|
return resolved.transform(integerLiteralTypeApproximator, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-1
@@ -4,6 +4,20 @@ interface MutableA<T> : A<T> {
|
|||||||
fun add(x: T)
|
fun add(x: T)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface MutableString : MutableA<String>
|
||||||
|
|
||||||
fun test(a: A<String>) {
|
fun test(a: A<String>) {
|
||||||
(a as MutableA).<!INAPPLICABLE_CANDIDATE!>add<!>("")
|
(a as? MutableA)?.add("")
|
||||||
|
(a as MutableA).add("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test2(a: A<String>) {
|
||||||
|
val b = a as MutableString
|
||||||
|
b.add("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test3(a: A<String>) {
|
||||||
|
if (a is MutableA) {
|
||||||
|
a.add("")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,21 @@ FILE: bareTypes.kt
|
|||||||
public abstract fun add(x: R|T|): R|kotlin/Unit|
|
public abstract fun add(x: R|T|): R|kotlin/Unit|
|
||||||
|
|
||||||
}
|
}
|
||||||
public final fun test(a: R|A<kotlin/String>|): R|kotlin/Unit| {
|
public abstract interface MutableString : R|MutableA<kotlin/String>| {
|
||||||
(R|<local>/a| as R|MutableA|).<Inapplicable(INAPPLICABLE): [/MutableA.add]>#(String())
|
}
|
||||||
|
public final fun test(a: R|A<kotlin/String>|): R|kotlin/Unit| {
|
||||||
|
(R|<local>/a| as? R|MutableA<kotlin/String>|)?.R|FakeOverride</MutableA.add: R|kotlin/Unit|>|(String())
|
||||||
|
(R|<local>/a| as R|MutableA<kotlin/String>|).R|FakeOverride</MutableA.add: R|kotlin/Unit|>|(String())
|
||||||
|
}
|
||||||
|
public final fun test2(a: R|A<kotlin/String>|): R|kotlin/Unit| {
|
||||||
|
lval b: R|MutableString| = (R|<local>/a| as R|MutableString|)
|
||||||
|
R|<local>/b|.R|FakeOverride</MutableA.add: R|kotlin/Unit|>|(String())
|
||||||
|
}
|
||||||
|
public final fun test3(a: R|A<kotlin/String>|): R|kotlin/Unit| {
|
||||||
|
when () {
|
||||||
|
(R|<local>/a| is R|MutableA<kotlin/String>|) -> {
|
||||||
|
R|<local>/a|.R|FakeOverride</MutableA.add: R|kotlin/Unit|>|(String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -29,11 +29,11 @@ FILE: recursiveCallOnWhenWithSealedClass.kt
|
|||||||
|
|
||||||
public final fun unwrap(): R|T| {
|
public final fun unwrap(): R|T| {
|
||||||
^unwrap when (this@R|/Maybe|) {
|
^unwrap when (this@R|/Maybe|) {
|
||||||
($subj$ is R|Maybe.Nope|) -> {
|
($subj$ is R|Maybe.Nope<T>|) -> {
|
||||||
throw R|java/lang/Exception.Exception|(String())
|
throw R|java/lang/Exception.Exception|(String())
|
||||||
}
|
}
|
||||||
($subj$ is R|Maybe.Yeah|) -> {
|
($subj$ is R|Maybe.Yeah<T>|) -> {
|
||||||
this@R|/Maybe|.R|/Maybe.Yeah.meat|
|
this@R|/Maybe|.R|FakeOverride</Maybe.Yeah.meat: R|T|>|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ interface G<T> : Tr<T>
|
|||||||
fun test(tr: Tr<String>) {
|
fun test(tr: Tr<String>) {
|
||||||
val v = tr as G?
|
val v = tr as G?
|
||||||
// If v is not nullable, there will be a warning on this line:
|
// If v is not nullable, there will be a warning on this line:
|
||||||
<!INAPPLICABLE_CANDIDATE!>checkSubtype<!><G<String>>(v!!)
|
checkSubtype<G<String>>(v!!)
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ class C2(val v2: Int)
|
|||||||
|
|
||||||
fun _as_left(e: Either<C1, C2>): Any {
|
fun _as_left(e: Either<C1, C2>): Any {
|
||||||
val v = e as Left
|
val v = e as Left
|
||||||
return <!INAPPLICABLE_CANDIDATE!>checkSubtype<!><Left<C1>>(v)
|
return checkSubtype<Left<C1>>(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun _as_right(e: Either<C1, C2>): Any {
|
fun _as_right(e: Either<C1, C2>): Any {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class C2(val v2: Int)
|
|||||||
|
|
||||||
fun _is_l(e: Either<C1, C2>): Any {
|
fun _is_l(e: Either<C1, C2>): Any {
|
||||||
if (e is Left) {
|
if (e is Left) {
|
||||||
return e.value.<!UNRESOLVED_REFERENCE!>v1<!>
|
return e.value.v1
|
||||||
}
|
}
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ fun _is_l(e: Either<C1, C2>): Any {
|
|||||||
if (e !is Left) {
|
if (e !is Left) {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
return e.value.<!UNRESOLVED_REFERENCE!>v1<!>
|
return e.value.v1
|
||||||
}
|
}
|
||||||
|
|
||||||
fun _is_r(e: Either<C1, C2>): Any {
|
fun _is_r(e: Either<C1, C2>): Any {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ class C2(val v2: Int)
|
|||||||
|
|
||||||
fun _as_left(e: Either<C1, C2>): Any? {
|
fun _as_left(e: Either<C1, C2>): Any? {
|
||||||
val v = e as? Left
|
val v = e as? Left
|
||||||
return <!INAPPLICABLE_CANDIDATE!>checkSubtype<!><Left<C1>?>(v)
|
return checkSubtype<Left<C1>?>(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun _as_right(e: Either<C1, C2>): Any? {
|
fun _as_right(e: Either<C1, C2>): Any? {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class C2(val v2: Int)
|
|||||||
|
|
||||||
fun _when(e: Either<C1, C2>): Any {
|
fun _when(e: Either<C1, C2>): Any {
|
||||||
return when (e) {
|
return when (e) {
|
||||||
is Left -> e.value.<!UNRESOLVED_REFERENCE!>v1<!>
|
is Left -> e.value.v1
|
||||||
is Right -> e.value.<!UNRESOLVED_REFERENCE!>v2<!>
|
is Right -> e.value.<!UNRESOLVED_REFERENCE!>v2<!>
|
||||||
else -> e
|
else -> e
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ interface G<T> : Tr<T>
|
|||||||
|
|
||||||
fun test(tr: Tr<String>?) {
|
fun test(tr: Tr<String>?) {
|
||||||
val v = tr as G
|
val v = tr as G
|
||||||
<!INAPPLICABLE_CANDIDATE!>checkSubtype<!><G<String>>(v)
|
checkSubtype<G<String>>(v)
|
||||||
}
|
}
|
||||||
@@ -6,5 +6,5 @@ interface G<T> : Tr<T>
|
|||||||
fun test(tr: Tr<String>?) {
|
fun test(tr: Tr<String>?) {
|
||||||
val v = tr as G?
|
val v = tr as G?
|
||||||
// If v is not nullable, there will be a warning on this line:
|
// If v is not nullable, there will be a warning on this line:
|
||||||
<!INAPPLICABLE_CANDIDATE!>checkSubtype<!><G<String>>(v!!)
|
checkSubtype<G<String>>(v!!)
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// !WITH_NEW_INFERENCE
|
// !WITH_NEW_INFERENCE
|
||||||
|
|
||||||
fun <A, B> Either<A, B>.recover(f: (A) -> B): Either<A, B> = when (this) {
|
fun <A, B> Either<A, B>.recover(f: (A) -> B): Either<A, B> = when (this) {
|
||||||
is Either.Left -> <!INAPPLICABLE_CANDIDATE!>f<!>(this.a).<!INAPPLICABLE_CANDIDATE!>right<!>()
|
is Either.Left -> f(this.a).right()
|
||||||
is Either.Right -> this
|
is Either.Right -> this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -33,6 +33,6 @@ import p.*
|
|||||||
|
|
||||||
fun test(b: B<Tr>?) {
|
fun test(b: B<Tr>?) {
|
||||||
if (b is C) {
|
if (b is C) {
|
||||||
b?.<!AMBIGUITY!>foo<!>(null)
|
b?.foo(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user