[FIR] Check type arguments in inner/outer local classes as in K1

^KT-59922 ^KT-59951
This commit is contained in:
Evgeniy.Zhelenskiy
2023-11-20 20:04:45 +00:00
committed by Space Team
parent cfa48b5cc0
commit 9383d0bfa4
6 changed files with 67 additions and 15 deletions
@@ -7,9 +7,14 @@ package org.jetbrains.kotlin.fir.analysis.checkers
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.getContainingClassLookupTag
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.getClassAndItsOuterClassesWhenLocal
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
import org.jetbrains.kotlin.fir.scopes.platformClassMapper
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
@@ -169,6 +174,13 @@ fun isCastErased(supertype: ConeKotlinType, subtype: ConeKotlinType, context: Ch
else if (subtype is ConeTypeParameterType) return false
val regularClassSymbol = subtype.toRegularClassSymbol(context.session) ?: return true
val outerClasses = regularClassSymbol.getClassAndItsOuterClassesWhenLocal(context.session)
if (regularClassSymbol.isLocal && regularClassSymbol.typeParameterSymbols.any { it.containingDeclarationSymbol !in outerClasses }) {
return true
}
val staticallyKnownSubtype = findStaticallyKnownSubtype(supertype, regularClassSymbol, context)
// If the substitution failed, it means that the result is an impossible type, e.g. something like Out<in Foo>
@@ -12,9 +12,12 @@ import org.jetbrains.kotlin.fir.FirFunctionTypeParameter
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.getAnnotationsByClassId
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotation
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationArgumentMapping
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
import org.jetbrains.kotlin.fir.getContainingClassLookupTag
import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.symbols.*
@@ -64,6 +67,12 @@ fun ConeClassLikeLookupTag.toSymbol(useSiteSession: FirSession): FirClassLikeSym
fun ConeClassLikeLookupTag.toFirRegularClassSymbol(session: FirSession): FirRegularClassSymbol? =
toSymbol(session) as? FirRegularClassSymbol
fun FirClassLikeSymbol<*>.getClassAndItsOuterClassesWhenLocal(session: FirSession): Set<FirClassLikeSymbol<*>> =
generateSequence(this.takeIf { it.isLocal }) {
if (it.isInner) it.getContainingClassLookupTag()?.toFirRegularClassSymbol(session) else null
}.toSet()
@OptIn(LookupTagInternals::class)
fun ConeClassLikeLookupTagImpl.bindSymbolToLookupTag(session: FirSession, symbol: FirClassLikeSymbol<*>?) {
boundSymbol = WeakPair(session, symbol)
@@ -12,9 +12,14 @@ import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isExternal
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.diagnostics.*
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.FirOperation.AS
import org.jetbrains.kotlin.fir.expressions.FirOperation.IS
import org.jetbrains.kotlin.fir.expressions.FirOperation.NOT_IS
import org.jetbrains.kotlin.fir.expressions.FirOperation.SAFE_AS
import org.jetbrains.kotlin.fir.expressions.builder.*
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
import org.jetbrains.kotlin.fir.expressions.impl.toAnnotationArgumentMapping
@@ -842,8 +847,12 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
if (firClass.typeParameters.isEmpty()) return this
val originalType = argument.unwrapExpression().resolvedType
val outerClasses by lazy(LazyThreadSafetyMode.NONE) { firClass.symbol.getClassAndItsOuterClassesWhenLocal(session) }
val newType = components.computeRepresentativeTypeForBareType(type, originalType)
?: if (firClass.isLocal && (operation == FirOperation.AS || operation == FirOperation.SAFE_AS)) {
?: if (
firClass.isLocal && firClass.typeParameters.none { it.symbol.containingDeclarationSymbol in outerClasses } &&
(operation == NOT_IS || operation == IS || operation == AS || operation == SAFE_AS)
) {
(firClass as FirClass).defaultType()
} else return buildErrorTypeRef {
source = this@withTypeArgumentsForBareType.source
@@ -857,7 +866,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
data: ResolutionMode,
): FirStatement {
val resolved = components.typeResolverTransformer.withBareTypes {
if (typeOperatorCall.operation == FirOperation.IS || typeOperatorCall.operation == FirOperation.NOT_IS) {
if (typeOperatorCall.operation == IS || typeOperatorCall.operation == NOT_IS) {
components.typeResolverTransformer.withIsOperandOfIsOperator {
typeOperatorCall.transformConversionTypeRef(transformer, ResolutionMode.ContextIndependent)
}
@@ -882,13 +891,13 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
}, null)
when (resolved.operation) {
FirOperation.IS, FirOperation.NOT_IS -> {
IS, NOT_IS -> {
resolved.resultType = session.builtinTypes.booleanType.type
}
FirOperation.AS -> {
AS -> {
resolved.resultType = conversionTypeRef.coneType
}
FirOperation.SAFE_AS -> {
SAFE_AS -> {
resolved.resultType = conversionTypeRef.coneType.withNullability(
ConeNullability.NULLABLE, session.typeContext,
)
@@ -900,7 +909,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
}
private fun FirTypeOperatorCall.transformTypeOperatorCallChildren(): FirTypeOperatorCall {
if (operation == FirOperation.AS || operation == FirOperation.SAFE_AS) {
if (operation == AS || operation == SAFE_AS) {
val argument = argumentList.arguments.singleOrNull() ?: error("Not a single argument: ${this.render()}")
// For calls in the form of (materialize() as MyClass) we've got a special rule that adds expect type to the `materialize()` call
@@ -914,7 +923,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
it.typeArguments.isNotEmpty() ||
(it.lookupTag.toSymbol(session)?.fir as? FirTypeParameterRefsOwner)?.typeParameters?.isEmpty() == true
}?.let {
if (operation == FirOperation.SAFE_AS)
if (operation == SAFE_AS)
it.withNullability(ConeNullability.NULLABLE, session.typeContext)
else
it
@@ -1,16 +1,16 @@
fun <E> foo(x: Any, y: Any) : Any {
class C
// without E?
if(x is <!NO_TYPE_ARGUMENTS_ON_RHS!>C<!>) {
if (x is <!CANNOT_CHECK_FOR_ERASED!>C<!>) {
return x
}
if (1 == 2) {
x as C
x <!UNCHECKED_CAST!>as C<!>
}
if (2 == 3) {
x as? C
x <!UNCHECKED_CAST!>as? C<!>
}
class Outer<F> {
@@ -18,16 +18,27 @@ fun <E> foo(x: Any, y: Any) : Any {
}
// bare type
if (y is <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer<!>) {
return y
}
if (y is <!CANNOT_CHECK_FOR_ERASED!>Outer<*><!>) {
return y
}
if (y is <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer.Inner<!>) {
return y
}
if (y is Outer<*>.Inner) {
if (y is <!CANNOT_CHECK_FOR_ERASED!>Outer<*>.Inner<!>) {
return y
}
y as Outer<*>.Inner
y as Outer.Inner
y <!UNCHECKED_CAST!>as Outer<*><!>
y as <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer<!>
y <!UNCHECKED_CAST!>as Outer<*>.Inner<!>
y as <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer.Inner<!>
return C()
}
@@ -1,7 +1,7 @@
fun <E> foo(x: Any, y: Any) : Any {
class C
// without E?
if(x is <!CANNOT_CHECK_FOR_ERASED!>C<!>) {
if (x is <!CANNOT_CHECK_FOR_ERASED!>C<!>) {
return x
}
@@ -18,6 +18,14 @@ fun <E> foo(x: Any, y: Any) : Any {
}
// bare type
if (y is <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer<!>) {
return y
}
if (y is <!CANNOT_CHECK_FOR_ERASED!>Outer<*><!>) {
return y
}
if (y is <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer.Inner<!>) {
return y
}
@@ -26,6 +34,9 @@ fun <E> foo(x: Any, y: Any) : Any {
return y
}
y <!UNCHECKED_CAST!>as Outer<*><!>
y <!USELESS_CAST!>as <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer<!><!>
y <!UNCHECKED_CAST!>as Outer<*>.Inner<!>
y <!USELESS_CAST!>as <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer.Inner<!><!>
@@ -30,6 +30,6 @@ fun testWrong4(x: List<Any>) = x is <!NO_TYPE_ARGUMENTS_ON_RHS!>ReadableList<!>
fun <T> testLocal(x: Any) {
class C
typealias CA = C
if (x is <!NO_TYPE_ARGUMENTS_ON_RHS!>C<!>) {}
if (x is <!CANNOT_CHECK_FOR_ERASED!>C<!>) {}
if (x is <!UNRESOLVED_REFERENCE!>CA<!>) {}
}