From 8c88670185d1aa01df409ba84f1724b9aa94ec73 Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Tue, 6 Oct 2020 12:02:58 -0700 Subject: [PATCH] FIR: copy constructor for typealias'ed inner/nested class --- .../resolve/calls/ConstructorProcessing.kt | 53 +++++++++++++++++-- .../builder/FirConstructorBuilder.kt | 23 ++++++++ .../fir/tree/generator/BuilderConfigurator.kt | 1 + .../innerClassTypeAliasConstructor.kt | 1 - .../innerClassTypeAliasConstructor.fir.kt | 10 ++-- .../typeAliasConstructorWrongClass.fir.kt | 4 +- .../ir/irText/expressions/kt16905.fir.txt | 7 +-- 7 files changed, 83 insertions(+), 16 deletions(-) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConstructorProcessing.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConstructorProcessing.kt index d063280487d..54326503fa2 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConstructorProcessing.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConstructorProcessing.kt @@ -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().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 > 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 +} diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirConstructorBuilder.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirConstructorBuilder.kt index d0228e82f39..8025e552fac 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirConstructorBuilder.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/declarations/builder/FirConstructorBuilder.kt @@ -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() +} diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt index f0603093b9a..366ee25c400 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/BuilderConfigurator.kt @@ -81,6 +81,7 @@ object BuilderConfigurator : AbstractBuilderConfigurator(FirTree builder(constructor, "FirConstructorImpl") { openBuilder() + withCopy() } builder(field) { diff --git a/compiler/testData/codegen/box/typealias/innerClassTypeAliasConstructor.kt b/compiler/testData/codegen/box/typealias/innerClassTypeAliasConstructor.kt index 2b3b380b7c7..6193b148048 100644 --- a/compiler/testData/codegen/box/typealias/innerClassTypeAliasConstructor.kt +++ b/compiler/testData/codegen/box/typealias/innerClassTypeAliasConstructor.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR class Outer(val x: String) { inner class Inner(val y: String) { val z = x + y diff --git a/compiler/testData/diagnostics/tests/typealias/innerClassTypeAliasConstructor.fir.kt b/compiler/testData/diagnostics/tests/typealias/innerClassTypeAliasConstructor.fir.kt index 3f044a11226..33217c3fb62 100644 --- a/compiler/testData/diagnostics/tests/typealias/innerClassTypeAliasConstructor.fir.kt +++ b/compiler/testData/diagnostics/tests/typealias/innerClassTypeAliasConstructor.fir.kt @@ -7,7 +7,7 @@ class Outer { typealias OI = Outer.Inner -fun test1(x: Outer) = x.OI() +fun test1(x: Outer) = x.OI() class Generic { @@ -17,8 +17,8 @@ class Generic { typealias GI = Generic.Inner typealias GIntI = Generic.Inner -fun test2(x: Generic) = x.GI() -fun test3(x: Generic) = x.GI() -fun test4(x: Generic>) = x.GI() -fun test5(x: Generic) = x.GIntI() +fun test2(x: Generic) = x.GI() +fun test3(x: Generic) = x.GI() +fun test4(x: Generic>) = x.GI() +fun test5(x: Generic) = x.GIntI() fun Generic.test6() = GIntI() \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorWrongClass.fir.kt b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorWrongClass.fir.kt index e9ddaa6a736..66c2df6df98 100644 --- a/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorWrongClass.fir.kt +++ b/compiler/testData/diagnostics/tests/typealias/typeAliasConstructorWrongClass.fir.kt @@ -26,9 +26,9 @@ class Outer { } typealias Test5 = Outer.Inner -val test5 = Test5() +val test5 = Test5() val test5a = Outer.Inner() val test5b = Outer.TestInner() val test5c = Outer().TestInner() val test5d = Outer().Inner() -val test5e = Outer().Test5() +val test5e = Outer().Test5() diff --git a/compiler/testData/ir/irText/expressions/kt16905.fir.txt b/compiler/testData/ir/irText/expressions/kt16905.fir.txt index 80f57083ee6..1777070f720 100644 --- a/compiler/testData/ir/irText/expressions/kt16905.fir.txt +++ b/compiler/testData/ir/irText/expressions/kt16905.fir.txt @@ -81,7 +81,8 @@ FILE fqName: fileName:/kt16905.kt overridden: public open fun toString (): kotlin.String declared in kotlin.Any $this: VALUE_PARAMETER name: type:kotlin.Any - FUN name:test visibility:public modality:FINAL <> () returnType:IrErrorType + FUN name:test visibility:public modality:FINAL <> () returnType:.Outer.Inner BLOCK_BODY - RETURN type=kotlin.Nothing from='public final fun test (): IrErrorType declared in ' - ERROR_CALL 'Unresolved reference: #' type=IrErrorType + RETURN type=kotlin.Nothing from='public final fun test (): .Outer.Inner declared in ' + CONSTRUCTOR_CALL 'public constructor () [primary] declared in .Outer.Inner' type=.Outer.Inner origin=null + $outer: CONSTRUCTOR_CALL 'public constructor () [primary] declared in .Outer' type=.Outer origin=null