From 3b7ad066fc631ddd278c8a09e0ccd09b475c08c1 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 25 Nov 2019 17:47:41 +0300 Subject: [PATCH] FIR: Substitute type alias constructors properly --- .../kotlin/fir/resolve/SupertypeUtils.kt | 7 -- .../kotlin/fir/resolve/calls/TowerLevels.kt | 102 +++++++++++++++++- .../resolve/stdlib/hashMapTypeAlias.kt | 9 ++ .../resolve/stdlib/hashMapTypeAlias.txt | 10 ++ ...FirDiagnosticsWithStdlibTestGenerated.java | 5 + .../fir/symbols/impl/FirFunctionSymbol.kt | 5 +- .../ir/irText/stubs/builtinMap.fir.txt | 2 +- 7 files changed, 126 insertions(+), 14 deletions(-) create mode 100644 compiler/fir/resolve/testData/resolve/stdlib/hashMapTypeAlias.kt create mode 100644 compiler/fir/resolve/testData/resolve/stdlib/hashMapTypeAlias.txt diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt index 9fdd1555ce2..4f68c0bb789 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/SupertypeUtils.kt @@ -73,13 +73,6 @@ fun FirClass<*>.buildUseSiteMemberScope(useSiteSession: FirSession, builder: Sco return symbolProvider.getClassUseSiteMemberScope(classId, useSiteSession, builder) } -fun FirTypeAlias.buildUseSiteMemberScope(useSiteSession: FirSession, builder: ScopeSession): FirScope? { - val type = expandedTypeRef.coneTypeUnsafe() - return type.scope(useSiteSession, builder)?.let { - type.wrapSubstitutionScopeIfNeed(useSiteSession, it, this, builder) - } -} - fun FirClass<*>.buildDefaultUseSiteMemberScope(useSiteSession: FirSession, builder: ScopeSession): FirScope { return builder.getOrBuild(symbol, USE_SITE) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt index 300776c7376..ba095eef50a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/TowerLevels.kt @@ -8,10 +8,14 @@ package org.jetbrains.kotlin.fir.resolve.calls import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.impl.FirConstructorImpl import org.jetbrains.kotlin.fir.declarations.impl.FirImportImpl import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedImportImpl +import org.jetbrains.kotlin.fir.declarations.impl.FirValueParameterImpl import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier import org.jetbrains.kotlin.fir.resolve.* +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculatorWithJump import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.ProcessorAction @@ -19,8 +23,8 @@ import org.jetbrains.kotlin.fir.scopes.impl.FirAbstractImportingScope import org.jetbrains.kotlin.fir.scopes.impl.FirExplicitSimpleImportingScope import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol import org.jetbrains.kotlin.fir.symbols.impl.* -import org.jetbrains.kotlin.fir.types.ConeClassLikeType -import org.jetbrains.kotlin.fir.types.coneTypeUnsafe +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.addToStdlib.cast @@ -357,11 +361,19 @@ private fun processConstructors( try { if (matchedSymbol != null) { val scope = when (matchedSymbol) { - is FirTypeAliasSymbol -> matchedSymbol.fir.buildUseSiteMemberScope(session, scopeSession) + is FirTypeAliasSymbol -> { + val type = matchedSymbol.fir.expandedTypeRef.coneTypeUnsafe().fullyExpandedType(session) + val basicScope = type.scope(session, scopeSession) + + if (basicScope != null && type.typeArguments.isNotEmpty()) { + prepareSubstitutingScopeForTypeAliasConstructors( + matchedSymbol, type, session, basicScope + ) ?: return ProcessorAction.STOP + } else basicScope + } is FirClassSymbol -> matchedSymbol.buildUseSiteMemberScope(session, scopeSession) } - val constructorName = when (matchedSymbol) { is FirTypeAliasSymbol -> finalExpansionName(matchedSymbol, session) ?: return ProcessorAction.NEXT is FirClassSymbol -> name @@ -381,3 +393,85 @@ private fun processConstructors( throw RuntimeException("While processing constructors", e) } } + +private class TypeAliasConstructorsSubstitutingScope( + private val typeAliasSymbol: FirTypeAliasSymbol, + private val substitutor: ConeSubstitutor, + private val delegatingScope: FirScope +) : FirScope() { + override fun processFunctionsByName(name: Name, processor: (FirFunctionSymbol<*>) -> ProcessorAction): ProcessorAction { + return delegatingScope.processFunctionsByName(name) { + val toProcess = if (it is FirConstructorSymbol) { + it.fir.createTypeAliasConstructor(substitutor, typeAliasSymbol.fir.typeParameters).symbol + } else { + it + } + + processor(toProcess) + } + } +} + +private fun prepareSubstitutingScopeForTypeAliasConstructors( + typeAliasSymbol: FirTypeAliasSymbol, + expandedType: ConeClassLikeType, + session: FirSession, + delegatingScope: FirScope +): FirScope? { + val expandedClass = expandedType.lookupTag.toSymbol(session)?.fir as? FirRegularClass ?: return null + + val resultingTypeArguments = expandedType.typeArguments.map { + // We don't know how to handle cases like yet + // typealias A = ArrayList<*>() + it as? ConeKotlinType ?: return null + } + + val substitutor = substitutorByMap( + expandedClass.typeParameters.map { it.symbol }.zip(resultingTypeArguments).toMap() + ); + + return TypeAliasConstructorsSubstitutingScope(typeAliasSymbol, substitutor, delegatingScope) +} + +private fun FirConstructor.createTypeAliasConstructor( + substitutor: ConeSubstitutor, + typeParameters: List +): FirConstructor { + val newReturnTypeRef = returnTypeRef.substitute(substitutor) + + val newParameterTypeRefs = valueParameters.map { valueParameter -> + valueParameter.returnTypeRef.substitute(substitutor) + } + + if (newReturnTypeRef == null && newParameterTypeRefs.all { it == null }) return this + + return FirConstructorImpl( + source, session, newReturnTypeRef ?: returnTypeRef, receiverTypeRef, status, + FirConstructorSymbol(symbol.callableId, overriddenSymbol = symbol) + ).apply { + resolvePhase = this@createTypeAliasConstructor.resolvePhase + valueParameters += this@createTypeAliasConstructor.valueParameters.zip( + newParameterTypeRefs + ) { valueParameter, newTypeRef -> + with(valueParameter) { + FirValueParameterImpl( + source, + session, + newTypeRef ?: returnTypeRef, + name, + FirVariableSymbol(valueParameter.symbol.callableId), + defaultValue, + isCrossinline, + isNoinline, + isVararg + ) + } + } + this.typeParameters += typeParameters + } +} + +private fun FirTypeRef.substitute(substitutor: ConeSubstitutor): FirResolvedTypeRef? = + coneTypeUnsafe() + .let(substitutor::substituteOrNull) + ?.let { FirResolvedTypeRefImpl(source, it) } diff --git a/compiler/fir/resolve/testData/resolve/stdlib/hashMapTypeAlias.kt b/compiler/fir/resolve/testData/resolve/stdlib/hashMapTypeAlias.kt new file mode 100644 index 00000000000..241b9898448 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/stdlib/hashMapTypeAlias.kt @@ -0,0 +1,9 @@ +typealias MyT = HashMap + +fun MyT.add(x: X, y: Int) {} + +fun main() { + MyT().apply { + add("1", 2) + } +} diff --git a/compiler/fir/resolve/testData/resolve/stdlib/hashMapTypeAlias.txt b/compiler/fir/resolve/testData/resolve/stdlib/hashMapTypeAlias.txt new file mode 100644 index 00000000000..9322eb4c238 --- /dev/null +++ b/compiler/fir/resolve/testData/resolve/stdlib/hashMapTypeAlias.txt @@ -0,0 +1,10 @@ +FILE: hashMapTypeAlias.kt + public final typealias MyT = R|kotlin/collections/HashMap| + public final fun R|MyT|.add(x: R|X|, y: R|kotlin/Int|): R|kotlin/Unit| { + } + public final fun main(): R|kotlin/Unit| { + R|java/util/HashMap.HashMap|().R|kotlin/apply||>( = apply@fun R|java/util/HashMap|.(): R|kotlin/Unit| { + this@R|special/anonymous|.R|/add|(String(1), Int(2)) + } + ) + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java index 271f4f6928f..3eeeae74f15 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java @@ -88,6 +88,11 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic runTest("compiler/fir/resolve/testData/resolve/stdlib/functionX.kt"); } + @TestMetadata("hashMapTypeAlias.kt") + public void testHashMapTypeAlias() throws Exception { + runTest("compiler/fir/resolve/testData/resolve/stdlib/hashMapTypeAlias.kt"); + } + @TestMetadata("hashSet.kt") public void testHashSet() throws Exception { runTest("compiler/fir/resolve/testData/resolve/stdlib/hashSet.kt"); diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirFunctionSymbol.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirFunctionSymbol.kt index cd5e33ebe4d..ece8cd57851 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirFunctionSymbol.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirFunctionSymbol.kt @@ -28,7 +28,8 @@ open class FirNamedFunctionSymbol( ) : FirFunctionSymbol(callableId) class FirConstructorSymbol( - callableId: CallableId + callableId: CallableId, + override val overriddenSymbol: FirConstructorSymbol? = null ) : FirFunctionSymbol(callableId) class FirAccessorSymbol( @@ -49,4 +50,4 @@ class FirAnonymousFunctionSymbol : FirFunctionWithoutNameSymbol(Name.identifier("accessor")) -class FirErrorFunctionSymbol : FirFunctionWithoutNameSymbol(Name.identifier("error")) \ No newline at end of file +class FirErrorFunctionSymbol : FirFunctionWithoutNameSymbol(Name.identifier("error")) diff --git a/compiler/testData/ir/irText/stubs/builtinMap.fir.txt b/compiler/testData/ir/irText/stubs/builtinMap.fir.txt index 77ef7cbddc2..10fdff54139 100644 --- a/compiler/testData/ir/irText/stubs/builtinMap.fir.txt +++ b/compiler/testData/ir/irText/stubs/builtinMap.fir.txt @@ -18,7 +18,7 @@ FILE fqName: fileName:/builtinMap.kt if: CONST Boolean type=kotlin.Boolean value=true then: CALL 'public final fun apply (block: kotlin.Function1): T of kotlin.apply [inline] declared in kotlin' type=java.util.LinkedHashMap.plus, V1 of .plus> origin=null : java.util.LinkedHashMap.plus, V1 of .plus> - $receiver: CONSTRUCTOR_CALL 'public constructor (p0: kotlin.collections.Map?) declared in java.util.LinkedHashMap' type=java.util.LinkedHashMap.plus, V1 of .plus> origin=null + $receiver: CONSTRUCTOR_CALL 'public constructor (p0: kotlin.collections.Map, out V of >?) declared in java.util.LinkedHashMap' type=java.util.LinkedHashMap.plus, V1 of .plus> origin=null : K1 of .plus : V1 of .plus p0: GET_VAR ': kotlin.collections.Map.plus, V1 of .plus> declared in .plus' type=kotlin.collections.Map.plus, V1 of .plus> origin=null