FIR: copy constructor for typealias'ed inner/nested class
This commit is contained in:
committed by
Denis Zharkov
parent
5e5712afbb
commit
8c88670185
+48
-5
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.FirSession
|
|||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.builder.buildConstructedClassTypeParameterRef
|
import org.jetbrains.kotlin.fir.declarations.builder.buildConstructedClassTypeParameterRef
|
||||||
import org.jetbrains.kotlin.fir.declarations.builder.buildConstructor
|
import org.jetbrains.kotlin.fir.declarations.builder.buildConstructor
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.builder.buildConstructorCopy
|
||||||
import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
|
import org.jetbrains.kotlin.fir.declarations.builder.buildValueParameter
|
||||||
import org.jetbrains.kotlin.fir.resolve.*
|
import org.jetbrains.kotlin.fir.resolve.*
|
||||||
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
|
||||||
@@ -42,7 +43,7 @@ internal fun FirScope.processConstructorsByName(
|
|||||||
substitutor,
|
substitutor,
|
||||||
processor,
|
processor,
|
||||||
session,
|
session,
|
||||||
bodyResolveComponents.scopeSession,
|
bodyResolveComponents,
|
||||||
includeInnerConstructors
|
includeInnerConstructors
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -135,7 +136,7 @@ private fun processConstructors(
|
|||||||
substitutor: ConeSubstitutor,
|
substitutor: ConeSubstitutor,
|
||||||
processor: (FirFunctionSymbol<*>) -> Unit,
|
processor: (FirFunctionSymbol<*>) -> Unit,
|
||||||
session: FirSession,
|
session: FirSession,
|
||||||
scopeSession: ScopeSession,
|
bodyResolveComponents: BodyResolveComponents,
|
||||||
includeInnerConstructors: Boolean
|
includeInnerConstructors: Boolean
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
@@ -144,7 +145,7 @@ private fun processConstructors(
|
|||||||
is FirTypeAliasSymbol -> {
|
is FirTypeAliasSymbol -> {
|
||||||
matchedSymbol.ensureResolved(FirResolvePhase.TYPES, session)
|
matchedSymbol.ensureResolved(FirResolvePhase.TYPES, session)
|
||||||
val type = matchedSymbol.fir.expandedTypeRef.coneTypeUnsafe<ConeClassLikeType>().fullyExpandedType(session)
|
val type = matchedSymbol.fir.expandedTypeRef.coneTypeUnsafe<ConeClassLikeType>().fullyExpandedType(session)
|
||||||
val basicScope = type.scope(session, scopeSession)
|
val basicScope = type.scope(session, bodyResolveComponents.scopeSession)
|
||||||
|
|
||||||
if (basicScope != null && type.typeArguments.isNotEmpty()) {
|
if (basicScope != null && type.typeArguments.isNotEmpty()) {
|
||||||
prepareSubstitutingScopeForTypeAliasConstructors(
|
prepareSubstitutingScopeForTypeAliasConstructors(
|
||||||
@@ -154,14 +155,16 @@ private fun processConstructors(
|
|||||||
}
|
}
|
||||||
is FirClassSymbol ->
|
is FirClassSymbol ->
|
||||||
(matchedSymbol.fir as FirClass<*>).scopeForClass(
|
(matchedSymbol.fir as FirClass<*>).scopeForClass(
|
||||||
substitutor, session, scopeSession
|
substitutor, session, bodyResolveComponents.scopeSession
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: why don't we use declared member scope at this point?
|
//TODO: why don't we use declared member scope at this point?
|
||||||
scope?.processDeclaredConstructors {
|
scope?.processDeclaredConstructors {
|
||||||
if (includeInnerConstructors || !it.fir.isInner) {
|
if (includeInnerConstructors || !it.fir.isInner) {
|
||||||
processor(it)
|
val constructorSymbolToProcess =
|
||||||
|
prepareCopyConstructorForTypealiasNestedClass(matchedSymbol, it, session, bodyResolveComponents) ?: it
|
||||||
|
processor(constructorSymbolToProcess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,3 +292,43 @@ private fun <F : FirFunction<F>> prepareSubstitutorForTypeAliasConstructors(
|
|||||||
|
|
||||||
return TypeAliasConstructorsSubstitutor(typeAliasSymbol, substitutor, copyFactory)
|
return TypeAliasConstructorsSubstitutor(typeAliasSymbol, substitutor, copyFactory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun prepareCopyConstructorForTypealiasNestedClass(
|
||||||
|
matchedSymbol: FirClassLikeSymbol<*>,
|
||||||
|
originalSymbol: FirConstructorSymbol,
|
||||||
|
session: FirSession,
|
||||||
|
bodyResolveComponents: BodyResolveComponents,
|
||||||
|
): FirConstructorSymbol? {
|
||||||
|
// If the matched symbol is a type alias, and the expanded type is a nested class, e.g.,
|
||||||
|
//
|
||||||
|
// class Outer {
|
||||||
|
// inner class Inner
|
||||||
|
// }
|
||||||
|
// typealias OI = Outer.Inner
|
||||||
|
// fun foo() { Outer().OI() }
|
||||||
|
//
|
||||||
|
// the chances are that `processor` belongs to [ScopeTowerLevel] (to resolve type aliases at top-level), which treats
|
||||||
|
// the explicit receiver (`Outer()`) as an extension receiver, whereas the constructor of the nested class may regard
|
||||||
|
// the same explicit receiver as a dispatch receiver (hence inconsistent receiver).
|
||||||
|
// Here, we add a copy of the nested class constructor, along with the outer type as an extension receiver, so that it
|
||||||
|
// can be seen as if resolving:
|
||||||
|
//
|
||||||
|
// fun Outer.OI(): OI = ...
|
||||||
|
//
|
||||||
|
if (originalSymbol.callableId.classId?.isNestedClass == true && matchedSymbol is FirTypeAliasSymbol) {
|
||||||
|
val innerTypeRef = originalSymbol.fir.returnTypeRef
|
||||||
|
val innerType = innerTypeRef.coneType.fullyExpandedType(session) as? ConeClassLikeType
|
||||||
|
if (innerType != null) {
|
||||||
|
val outerType = bodyResolveComponents.outerClassManager.outerType(innerType)
|
||||||
|
if (outerType != null) {
|
||||||
|
val extCopy = buildConstructorCopy(originalSymbol.fir) {
|
||||||
|
origin = FirDeclarationOrigin.Synthetic
|
||||||
|
receiverTypeRef = innerTypeRef.withReplacedConeType(outerType)
|
||||||
|
symbol = FirConstructorSymbol(originalSymbol.callableId, overriddenSymbol = originalSymbol)
|
||||||
|
}
|
||||||
|
return extCopy.symbol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|||||||
+23
@@ -85,3 +85,26 @@ inline fun buildConstructor(init: FirConstructorBuilder.() -> Unit): FirConstruc
|
|||||||
}
|
}
|
||||||
return FirConstructorBuilder().apply(init).build()
|
return FirConstructorBuilder().apply(init).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalContracts::class)
|
||||||
|
inline fun buildConstructorCopy(original: FirConstructor, init: FirConstructorBuilder.() -> Unit): FirConstructor {
|
||||||
|
contract {
|
||||||
|
callsInPlace(init, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
|
||||||
|
}
|
||||||
|
val copyBuilder = FirConstructorBuilder()
|
||||||
|
copyBuilder.source = original.source
|
||||||
|
copyBuilder.session = original.session
|
||||||
|
copyBuilder.resolvePhase = original.resolvePhase
|
||||||
|
copyBuilder.origin = original.origin
|
||||||
|
copyBuilder.returnTypeRef = original.returnTypeRef
|
||||||
|
copyBuilder.receiverTypeRef = original.receiverTypeRef
|
||||||
|
copyBuilder.typeParameters.addAll(original.typeParameters)
|
||||||
|
copyBuilder.valueParameters.addAll(original.valueParameters)
|
||||||
|
copyBuilder.status = original.status
|
||||||
|
copyBuilder.containerSource = original.containerSource
|
||||||
|
copyBuilder.annotations.addAll(original.annotations)
|
||||||
|
copyBuilder.symbol = original.symbol
|
||||||
|
copyBuilder.delegatedConstructor = original.delegatedConstructor
|
||||||
|
copyBuilder.body = original.body
|
||||||
|
return copyBuilder.apply(init).build()
|
||||||
|
}
|
||||||
|
|||||||
+1
@@ -81,6 +81,7 @@ object BuilderConfigurator : AbstractBuilderConfigurator<FirTreeBuilder>(FirTree
|
|||||||
|
|
||||||
builder(constructor, "FirConstructorImpl") {
|
builder(constructor, "FirConstructorImpl") {
|
||||||
openBuilder()
|
openBuilder()
|
||||||
|
withCopy()
|
||||||
}
|
}
|
||||||
|
|
||||||
builder(field) {
|
builder(field) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
class Outer(val x: String) {
|
class Outer(val x: String) {
|
||||||
inner class Inner(val y: String) {
|
inner class Inner(val y: String) {
|
||||||
val z = x + y
|
val z = x + y
|
||||||
|
|||||||
+5
-5
@@ -7,7 +7,7 @@ class Outer {
|
|||||||
|
|
||||||
typealias OI = Outer.Inner
|
typealias OI = Outer.Inner
|
||||||
|
|
||||||
fun test1(x: Outer) = x.<!UNRESOLVED_REFERENCE!>OI<!>()
|
fun test1(x: Outer) = x.OI()
|
||||||
|
|
||||||
|
|
||||||
class Generic<T> {
|
class Generic<T> {
|
||||||
@@ -17,8 +17,8 @@ class Generic<T> {
|
|||||||
typealias GI<T> = Generic<T>.Inner
|
typealias GI<T> = Generic<T>.Inner
|
||||||
typealias GIntI = Generic<Int>.Inner
|
typealias GIntI = Generic<Int>.Inner
|
||||||
|
|
||||||
fun test2(x: Generic<Int>) = x.<!UNRESOLVED_REFERENCE!>GI<!>()
|
fun test2(x: Generic<Int>) = x.GI()
|
||||||
fun <T> test3(x: Generic<T>) = x.<!UNRESOLVED_REFERENCE!>GI<!>()
|
fun <T> test3(x: Generic<T>) = x.GI()
|
||||||
fun <T> test4(x: Generic<List<T>>) = x.<!UNRESOLVED_REFERENCE!>GI<!>()
|
fun <T> test4(x: Generic<List<T>>) = x.GI()
|
||||||
fun <T> test5(x: Generic<T>) = x.<!UNRESOLVED_REFERENCE!>GIntI<!>()
|
fun <T> test5(x: Generic<T>) = x.<!INAPPLICABLE_CANDIDATE!>GIntI<!>()
|
||||||
fun Generic<Int>.test6() = GIntI()
|
fun Generic<Int>.test6() = GIntI()
|
||||||
+2
-2
@@ -26,9 +26,9 @@ class Outer {
|
|||||||
}
|
}
|
||||||
typealias Test5 = Outer.Inner
|
typealias Test5 = Outer.Inner
|
||||||
|
|
||||||
val test5 = Test5()
|
val test5 = <!UNRESOLVED_REFERENCE!>Test5<!>()
|
||||||
val test5a = Outer.<!UNRESOLVED_REFERENCE!>Inner<!>()
|
val test5a = Outer.<!UNRESOLVED_REFERENCE!>Inner<!>()
|
||||||
val test5b = Outer.<!UNRESOLVED_REFERENCE!>TestInner<!>()
|
val test5b = Outer.<!UNRESOLVED_REFERENCE!>TestInner<!>()
|
||||||
val test5c = Outer().<!UNRESOLVED_REFERENCE!>TestInner<!>()
|
val test5c = Outer().<!UNRESOLVED_REFERENCE!>TestInner<!>()
|
||||||
val test5d = Outer().Inner()
|
val test5d = Outer().Inner()
|
||||||
val test5e = Outer().<!UNRESOLVED_REFERENCE!>Test5<!>()
|
val test5e = Outer().Test5()
|
||||||
|
|||||||
+4
-3
@@ -81,7 +81,8 @@ FILE fqName:<root> fileName:/kt16905.kt
|
|||||||
overridden:
|
overridden:
|
||||||
public open fun toString (): kotlin.String declared in kotlin.Any
|
public open fun toString (): kotlin.String declared in kotlin.Any
|
||||||
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
|
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
|
||||||
FUN name:test visibility:public modality:FINAL <> () returnType:IrErrorType
|
FUN name:test visibility:public modality:FINAL <> () returnType:<root>.Outer.Inner
|
||||||
BLOCK_BODY
|
BLOCK_BODY
|
||||||
RETURN type=kotlin.Nothing from='public final fun test (): IrErrorType declared in <root>'
|
RETURN type=kotlin.Nothing from='public final fun test (): <root>.Outer.Inner declared in <root>'
|
||||||
ERROR_CALL 'Unresolved reference: <Unresolved name: OI>#' type=IrErrorType
|
CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.Outer.Inner' type=<root>.Outer.Inner origin=null
|
||||||
|
$outer: CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in <root>.Outer' type=<root>.Outer origin=null
|
||||||
|
|||||||
Reference in New Issue
Block a user