Implement fake override mapping for functions

Related to KT-29636
This commit is contained in:
Simon Ogorodnik
2019-01-31 19:34:52 +03:00
committed by Mikhail Glukhikh
parent 1f5e89cd32
commit 0e3fecf614
11 changed files with 330 additions and 63 deletions
@@ -14,15 +14,17 @@ import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReferenceImpl
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.ProcessorAction.NEXT
import org.jetbrains.kotlin.fir.scopes.impl.FirClassDeclaredMemberScope
import org.jetbrains.kotlin.fir.scopes.impl.FirClassUseSiteScope
import org.jetbrains.kotlin.fir.scopes.impl.FirCompositeScope
import org.jetbrains.kotlin.fir.scopes.impl.FirTopLevelDeclaredMemberScope
import org.jetbrains.kotlin.fir.scopes.impl.*
import org.jetbrains.kotlin.fir.symbols.ConeCallableSymbol
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.types.ConeClassErrorType
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypedProjection
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
import org.jetbrains.kotlin.fir.visitors.compose
@@ -35,6 +37,21 @@ class FirAccessResolveTransformer : FirAbstractTreeTransformerWithSuperTypes(rev
}
}
private fun ConeClassLikeType.buildSubstitutionScope(
useSiteSession: FirSession,
unsubstituted: FirScope,
regularClass: FirRegularClass
): FirClassSubstitutionScope? {
if (this.typeArguments.isEmpty()) return null
@Suppress("UNCHECKED_CAST")
val substitution = regularClass.typeParameters.zip(this.typeArguments) { typeParameter, typeArgument ->
typeParameter.symbol to (typeArgument as? ConeTypedProjection)?.type
}.filter { (_, type) -> type != null }.toMap() as Map<ConeTypeParameterSymbol, ConeKotlinType>
return FirClassSubstitutionScope(useSiteSession, unsubstituted, substitution, true)
}
private fun FirRegularClass.buildUseSiteScope(useSiteSession: FirSession = session): FirClassUseSiteScope {
val superTypeScope = FirCompositeScope(mutableListOf())
val declaredScope = FirClassDeclaredMemberScope(this, useSiteSession)
@@ -43,7 +60,8 @@ class FirAccessResolveTransformer : FirAbstractTreeTransformerWithSuperTypes(rev
if (useSiteSuperType is ConeClassErrorType) return@mapNotNullTo null
val symbol = useSiteSuperType.symbol
if (symbol is FirClassSymbol) {
symbol.fir.buildUseSiteScope(useSiteSession)
val scope = symbol.fir.buildUseSiteScope(useSiteSession)
useSiteSuperType.buildSubstitutionScope(useSiteSession, scope, symbol.fir) ?: scope
} else {
null
}
@@ -58,10 +76,10 @@ class FirAccessResolveTransformer : FirAbstractTreeTransformerWithSuperTypes(rev
}
}
var lookupFunctions = false
var lookupProperties = false
private var lookupFunctions = false
private var lookupProperties = false
inline fun <T> withNewSettings(block: () -> T): T {
private inline fun <T> withNewSettings(block: () -> T): T {
val prevFunctions = lookupFunctions
val prevProperties = lookupProperties
val result = block()
@@ -0,0 +1,149 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.scopes.impl
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirNamedFunction
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
import org.jetbrains.kotlin.fir.declarations.impl.FirMemberFunctionImpl
import org.jetbrains.kotlin.fir.declarations.impl.FirValueParameterImpl
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.symbols.*
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeAbbreviatedTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeImpl
import org.jetbrains.kotlin.name.Name
class FirClassSubstitutionScope(
session: FirSession,
private val unsubstituted: FirScope,
private val substitution: Map<ConeTypeParameterSymbol, ConeKotlinType>,
lookupInFir: Boolean
) : FirAbstractProviderBasedScope(session, lookupInFir) {
val fakeOverrides = mutableMapOf<ConeCallableSymbol, ConeCallableSymbol>()
private fun wrapProjection(old: ConeKotlinTypeProjection, newType: ConeKotlinType): ConeKotlinTypeProjection {
return when (old) {
is StarProjection -> old
is ConeKotlinTypeProjectionIn -> ConeKotlinTypeProjectionIn(newType)
is ConeKotlinTypeProjectionOut -> ConeKotlinTypeProjectionOut(newType)
is ConeKotlinType -> newType
else -> old
}
}
private fun ConeKotlinType.substitute(): ConeKotlinType? {
if (this is ConeTypeParameterType) return substitution[this]
val newArguments by lazy { arrayOfNulls<ConeKotlinTypeProjection>(typeArguments.size) }
var initialized = false
for ((index, typeArgument) in this.typeArguments.withIndex()) {
val type = (typeArgument as? ConeTypedProjection)?.type ?: continue
val newType = type.substitute()
if (newType != null) {
initialized = true
newArguments[index] = wrapProjection(typeArgument, newType)
}
}
if (initialized) {
for ((index, typeArgument) in this.typeArguments.withIndex()) {
if (newArguments[index] == null) {
newArguments[index] = typeArgument
}
}
@Suppress("UNCHECKED_CAST")
return when (this) {
is ConeKotlinErrorType -> error("Trying to substitute arguments for error type")
is ConeTypeParameterType -> error("Trying to substitute arguments for type parameter")
is ConeClassTypeImpl -> ConeClassTypeImpl(symbol, newArguments as Array<ConeKotlinTypeProjection>)
is ConeAbbreviatedTypeImpl -> ConeAbbreviatedTypeImpl(
abbreviationSymbol,
newArguments as Array<ConeKotlinTypeProjection>,
directExpansion.substitute() as? ConeClassLikeType ?: directExpansion
)
is ConeFunctionType -> TODO("Substitute function type properly")
is ConeClassLikeType -> error("Unknown class-like type to substitute: $this, ${this::class}")
}
}
return null
}
override fun processFunctionsByName(name: Name, processor: (ConeFunctionSymbol) -> ProcessorAction): ProcessorAction {
unsubstituted.processFunctionsByName(name) process@{ original ->
val function = fakeOverrides.getOrPut(original) { createFakeOverride(original, name) }
processor(function as ConeFunctionSymbol)
}
return super.processFunctionsByName(name, processor)
}
override fun processPropertiesByName(name: Name, processor: (ConePropertySymbol) -> ProcessorAction): ProcessorAction {
return unsubstituted.processPropertiesByName(name, processor)
}
private fun createFakeOverride(
original: ConeFunctionSymbol,
name: Name
): FirFunctionSymbol {
val member = (original as FirBasedSymbol<*>).fir as? FirNamedFunction ?: error("Can't fake override for $original")
val receiverType = member.receiverType?.coneTypeUnsafe()
val newReceiverType = receiverType?.substitute()
val returnType = member.returnType.coneTypeUnsafe()
val newReturnType = returnType.substitute()
val newParameterTypes = member.valueParameters.map {
it.returnType.coneTypeUnsafe().substitute()
}
val symbol = FirFunctionSymbol(original.callableId, true)
with(member) {
// TODO: consider using here some light-weight functions instead of pseudo-real FirMemberFunctionImpl
// As second alternative, we can invent some light-weight kind of FirRegularClass
FirMemberFunctionImpl(
session,
psi,
symbol,
name,
member.receiverType?.withReplacedConeType(newReceiverType),
member.returnType.withReplacedConeType(newReturnType)
).apply {
status = member.status as FirDeclarationStatusImpl
valueParameters += member.valueParameters.zip(newParameterTypes) { valueParameter, newType ->
with(valueParameter) {
FirValueParameterImpl(
session, psi,
name, this.returnType.withReplacedConeType(newType),
defaultValue, isCrossinline, isNoinline, isVararg
)
}
}
}
}
return symbol
}
}
fun FirType.withReplacedConeType(newType: ConeKotlinType?): FirResolvedType {
require(this is FirResolvedType)
if (newType == null) return this
return FirResolvedTypeImpl(
session, psi, newType,
isNullable,
annotations
)
}
@@ -0,0 +1,15 @@
open class A<T> {
fun foo(t: T): T {
return t
}
}
class Some
class B : A<Some>() {
fun test() {
foo(Some())
}
}
@@ -0,0 +1,21 @@
FILE: simpleFakeOverride.kt
<T> public open class A {
public constructor(): super<R|kotlin/Any|>()
public final function foo(t: R|T|): R|T| {
return@@@foo <Unresolved name: t>#
}
}
public final class Some {
public constructor(): super<R|kotlin/Any|>()
}
public final class B : R|A<Some>| {
public constructor(): super<R|A<Some>|>()
public final function test(): R|kotlin/Unit| {
R|FakeOverride</A.foo>|(<Unresolved name: Some>#())
}
}
@@ -259,6 +259,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase {
public void testSimple() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/overrides/simple.kt");
}
@TestMetadata("simpleFakeOverride.kt")
public void testSimpleFakeOverride() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/overrides/simpleFakeOverride.kt");
}
}
@TestMetadata("compiler/fir/resolve/testData/resolve/references")