[FIR] Implement bare type modification with known type arguments

This commit is contained in:
Mikhail Glukhikh
2020-02-10 10:56:59 +03:00
parent 28332933ce
commit e3721ed406
14 changed files with 79 additions and 24 deletions
@@ -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
View File
@@ -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())
}
}
} }
@@ -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
} }
@@ -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)
} }
} }