FIR: copy constructor for typealias'ed inner/nested class

This commit is contained in:
Jinseong Jeon
2020-10-06 12:02:58 -07:00
committed by Denis Zharkov
parent 5e5712afbb
commit 8c88670185
7 changed files with 83 additions and 16 deletions
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildConstructedClassTypeParameterRef
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.resolve.*
import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor
@@ -42,7 +43,7 @@ internal fun FirScope.processConstructorsByName(
substitutor,
processor,
session,
bodyResolveComponents.scopeSession,
bodyResolveComponents,
includeInnerConstructors
)
@@ -135,7 +136,7 @@ private fun processConstructors(
substitutor: ConeSubstitutor,
processor: (FirFunctionSymbol<*>) -> Unit,
session: FirSession,
scopeSession: ScopeSession,
bodyResolveComponents: BodyResolveComponents,
includeInnerConstructors: Boolean
) {
try {
@@ -144,7 +145,7 @@ private fun processConstructors(
is FirTypeAliasSymbol -> {
matchedSymbol.ensureResolved(FirResolvePhase.TYPES, 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()) {
prepareSubstitutingScopeForTypeAliasConstructors(
@@ -154,14 +155,16 @@ private fun processConstructors(
}
is FirClassSymbol ->
(matchedSymbol.fir as FirClass<*>).scopeForClass(
substitutor, session, scopeSession
substitutor, session, bodyResolveComponents.scopeSession
)
}
//TODO: why don't we use declared member scope at this point?
scope?.processDeclaredConstructors {
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)
}
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
}
@@ -85,3 +85,26 @@ inline fun buildConstructor(init: FirConstructorBuilder.() -> Unit): FirConstruc
}
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()
}
@@ -81,6 +81,7 @@ object BuilderConfigurator : AbstractBuilderConfigurator<FirTreeBuilder>(FirTree
builder(constructor, "FirConstructorImpl") {
openBuilder()
withCopy()
}
builder(field) {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
class Outer(val x: String) {
inner class Inner(val y: String) {
val z = x + y
@@ -7,7 +7,7 @@ class Outer {
typealias OI = Outer.Inner
fun test1(x: Outer) = x.<!UNRESOLVED_REFERENCE!>OI<!>()
fun test1(x: Outer) = x.OI()
class Generic<T> {
@@ -17,8 +17,8 @@ class Generic<T> {
typealias GI<T> = Generic<T>.Inner
typealias GIntI = Generic<Int>.Inner
fun test2(x: Generic<Int>) = x.<!UNRESOLVED_REFERENCE!>GI<!>()
fun <T> test3(x: Generic<T>) = x.<!UNRESOLVED_REFERENCE!>GI<!>()
fun <T> test4(x: Generic<List<T>>) = x.<!UNRESOLVED_REFERENCE!>GI<!>()
fun <T> test5(x: Generic<T>) = x.<!UNRESOLVED_REFERENCE!>GIntI<!>()
fun test2(x: Generic<Int>) = x.GI()
fun <T> test3(x: Generic<T>) = x.GI()
fun <T> test4(x: Generic<List<T>>) = x.GI()
fun <T> test5(x: Generic<T>) = x.<!INAPPLICABLE_CANDIDATE!>GIntI<!>()
fun Generic<Int>.test6() = GIntI()
@@ -26,9 +26,9 @@ class Outer {
}
typealias Test5 = Outer.Inner
val test5 = Test5()
val test5 = <!UNRESOLVED_REFERENCE!>Test5<!>()
val test5a = Outer.<!UNRESOLVED_REFERENCE!>Inner<!>()
val test5b = Outer.<!UNRESOLVED_REFERENCE!>TestInner<!>()
val test5c = Outer().<!UNRESOLVED_REFERENCE!>TestInner<!>()
val test5d = Outer().Inner()
val test5e = Outer().<!UNRESOLVED_REFERENCE!>Test5<!>()
val test5e = Outer().Test5()
+4 -3
View File
@@ -81,7 +81,8 @@ FILE fqName:<root> fileName:/kt16905.kt
overridden:
public open fun toString (): kotlin.String declared in 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
RETURN type=kotlin.Nothing from='public final fun test (): IrErrorType declared in <root>'
ERROR_CALL 'Unresolved reference: <Unresolved name: OI>#' type=IrErrorType
RETURN type=kotlin.Nothing from='public final fun test (): <root>.Outer.Inner declared in <root>'
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