[FIR] Check type arguments in inner/outer local classes as in K1
^KT-59922 ^KT-59951
This commit is contained in:
committed by
Space Team
parent
cfa48b5cc0
commit
9383d0bfa4
+12
@@ -7,9 +7,14 @@ package org.jetbrains.kotlin.fir.analysis.checkers
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
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.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.defaultType
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.getClassAndItsOuterClassesWhenLocal
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutorByMap
|
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.scopes.platformClassMapper
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
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
|
else if (subtype is ConeTypeParameterType) return false
|
||||||
|
|
||||||
val regularClassSymbol = subtype.toRegularClassSymbol(context.session) ?: return true
|
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)
|
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>
|
// 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.FirSession
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||||
import org.jetbrains.kotlin.fir.declarations.getAnnotationsByClassId
|
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.buildAnnotation
|
||||||
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationArgumentMapping
|
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationArgumentMapping
|
||||||
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
|
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.FirSymbolProvider
|
||||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||||
import org.jetbrains.kotlin.fir.symbols.*
|
import org.jetbrains.kotlin.fir.symbols.*
|
||||||
@@ -64,6 +67,12 @@ fun ConeClassLikeLookupTag.toSymbol(useSiteSession: FirSession): FirClassLikeSym
|
|||||||
fun ConeClassLikeLookupTag.toFirRegularClassSymbol(session: FirSession): FirRegularClassSymbol? =
|
fun ConeClassLikeLookupTag.toFirRegularClassSymbol(session: FirSession): FirRegularClassSymbol? =
|
||||||
toSymbol(session) as? 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)
|
@OptIn(LookupTagInternals::class)
|
||||||
fun ConeClassLikeLookupTagImpl.bindSymbolToLookupTag(session: FirSession, symbol: FirClassLikeSymbol<*>?) {
|
fun ConeClassLikeLookupTagImpl.bindSymbolToLookupTag(session: FirSession, symbol: FirClassLikeSymbol<*>?) {
|
||||||
boundSymbol = WeakPair(session, symbol)
|
boundSymbol = WeakPair(session, symbol)
|
||||||
|
|||||||
+16
-7
@@ -12,9 +12,14 @@ import org.jetbrains.kotlin.fakeElement
|
|||||||
import org.jetbrains.kotlin.fir.*
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isExternal
|
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.declarations.utils.isLocal
|
||||||
import org.jetbrains.kotlin.fir.diagnostics.*
|
import org.jetbrains.kotlin.fir.diagnostics.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.*
|
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.builder.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
|
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
|
||||||
import org.jetbrains.kotlin.fir.expressions.impl.toAnnotationArgumentMapping
|
import org.jetbrains.kotlin.fir.expressions.impl.toAnnotationArgumentMapping
|
||||||
@@ -842,8 +847,12 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
|
|||||||
if (firClass.typeParameters.isEmpty()) return this
|
if (firClass.typeParameters.isEmpty()) return this
|
||||||
|
|
||||||
val originalType = argument.unwrapExpression().resolvedType
|
val originalType = argument.unwrapExpression().resolvedType
|
||||||
|
val outerClasses by lazy(LazyThreadSafetyMode.NONE) { firClass.symbol.getClassAndItsOuterClassesWhenLocal(session) }
|
||||||
val newType = components.computeRepresentativeTypeForBareType(type, originalType)
|
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()
|
(firClass as FirClass).defaultType()
|
||||||
} else return buildErrorTypeRef {
|
} else return buildErrorTypeRef {
|
||||||
source = this@withTypeArgumentsForBareType.source
|
source = this@withTypeArgumentsForBareType.source
|
||||||
@@ -857,7 +866,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
|
|||||||
data: ResolutionMode,
|
data: ResolutionMode,
|
||||||
): FirStatement {
|
): FirStatement {
|
||||||
val resolved = components.typeResolverTransformer.withBareTypes {
|
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 {
|
components.typeResolverTransformer.withIsOperandOfIsOperator {
|
||||||
typeOperatorCall.transformConversionTypeRef(transformer, ResolutionMode.ContextIndependent)
|
typeOperatorCall.transformConversionTypeRef(transformer, ResolutionMode.ContextIndependent)
|
||||||
}
|
}
|
||||||
@@ -882,13 +891,13 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
|
|||||||
}, null)
|
}, null)
|
||||||
|
|
||||||
when (resolved.operation) {
|
when (resolved.operation) {
|
||||||
FirOperation.IS, FirOperation.NOT_IS -> {
|
IS, NOT_IS -> {
|
||||||
resolved.resultType = session.builtinTypes.booleanType.type
|
resolved.resultType = session.builtinTypes.booleanType.type
|
||||||
}
|
}
|
||||||
FirOperation.AS -> {
|
AS -> {
|
||||||
resolved.resultType = conversionTypeRef.coneType
|
resolved.resultType = conversionTypeRef.coneType
|
||||||
}
|
}
|
||||||
FirOperation.SAFE_AS -> {
|
SAFE_AS -> {
|
||||||
resolved.resultType = conversionTypeRef.coneType.withNullability(
|
resolved.resultType = conversionTypeRef.coneType.withNullability(
|
||||||
ConeNullability.NULLABLE, session.typeContext,
|
ConeNullability.NULLABLE, session.typeContext,
|
||||||
)
|
)
|
||||||
@@ -900,7 +909,7 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun FirTypeOperatorCall.transformTypeOperatorCallChildren(): FirTypeOperatorCall {
|
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()}")
|
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
|
// 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.typeArguments.isNotEmpty() ||
|
||||||
(it.lookupTag.toSymbol(session)?.fir as? FirTypeParameterRefsOwner)?.typeParameters?.isEmpty() == true
|
(it.lookupTag.toSymbol(session)?.fir as? FirTypeParameterRefsOwner)?.typeParameters?.isEmpty() == true
|
||||||
}?.let {
|
}?.let {
|
||||||
if (operation == FirOperation.SAFE_AS)
|
if (operation == SAFE_AS)
|
||||||
it.withNullability(ConeNullability.NULLABLE, session.typeContext)
|
it.withNullability(ConeNullability.NULLABLE, session.typeContext)
|
||||||
else
|
else
|
||||||
it
|
it
|
||||||
|
|||||||
+17
-6
@@ -1,16 +1,16 @@
|
|||||||
fun <E> foo(x: Any, y: Any) : Any {
|
fun <E> foo(x: Any, y: Any) : Any {
|
||||||
class C
|
class C
|
||||||
// without E?
|
// without E?
|
||||||
if(x is <!NO_TYPE_ARGUMENTS_ON_RHS!>C<!>) {
|
if (x is <!CANNOT_CHECK_FOR_ERASED!>C<!>) {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
if (1 == 2) {
|
if (1 == 2) {
|
||||||
x as C
|
x <!UNCHECKED_CAST!>as C<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
if (2 == 3) {
|
if (2 == 3) {
|
||||||
x as? C
|
x <!UNCHECKED_CAST!>as? C<!>
|
||||||
}
|
}
|
||||||
|
|
||||||
class Outer<F> {
|
class Outer<F> {
|
||||||
@@ -18,16 +18,27 @@ fun <E> foo(x: Any, y: Any) : Any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// bare type
|
// 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<!>) {
|
if (y is <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer.Inner<!>) {
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
|
||||||
if (y is Outer<*>.Inner) {
|
if (y is <!CANNOT_CHECK_FOR_ERASED!>Outer<*>.Inner<!>) {
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
|
||||||
y as Outer<*>.Inner
|
y <!UNCHECKED_CAST!>as Outer<*><!>
|
||||||
y as Outer.Inner
|
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()
|
return C()
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-1
@@ -1,7 +1,7 @@
|
|||||||
fun <E> foo(x: Any, y: Any) : Any {
|
fun <E> foo(x: Any, y: Any) : Any {
|
||||||
class C
|
class C
|
||||||
// without E?
|
// without E?
|
||||||
if(x is <!CANNOT_CHECK_FOR_ERASED!>C<!>) {
|
if (x is <!CANNOT_CHECK_FOR_ERASED!>C<!>) {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,6 +18,14 @@ fun <E> foo(x: Any, y: Any) : Any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// bare type
|
// 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<!>) {
|
if (y is <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer.Inner<!>) {
|
||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
@@ -26,6 +34,9 @@ fun <E> foo(x: Any, y: Any) : Any {
|
|||||||
return y
|
return y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
y <!UNCHECKED_CAST!>as Outer<*><!>
|
||||||
|
y <!USELESS_CAST!>as <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer<!><!>
|
||||||
|
|
||||||
y <!UNCHECKED_CAST!>as Outer<*>.Inner<!>
|
y <!UNCHECKED_CAST!>as Outer<*>.Inner<!>
|
||||||
y <!USELESS_CAST!>as <!NO_TYPE_ARGUMENTS_ON_RHS!>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) {
|
fun <T> testLocal(x: Any) {
|
||||||
class C
|
class C
|
||||||
typealias CA = 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<!>) {}
|
if (x is <!UNRESOLVED_REFERENCE!>CA<!>) {}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user