FIR: Implement cycle avoidance in implicit type resolution

This commit is contained in:
Simon Ogorodnik
2019-03-12 18:46:44 +03:00
parent 334c42e8ab
commit 818701b8df
10 changed files with 239 additions and 82 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.fir.symbols.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.types.model.*
sealed class ConeKotlinTypeProjection : TypeArgumentMarker {
@@ -69,21 +70,11 @@ val ConeKotlinType.isNullable: Boolean get() = nullability != ConeNullability.NO
val ConeKotlinType.isMarkedNullable: Boolean get() = nullability == ConeNullability.NULLABLE
class ConeKotlinErrorType(val reason: String) : ConeKotlinType() {
override val typeArguments: Array<out ConeKotlinTypeProjection>
get() = EMPTY_ARRAY
override val nullability: ConeNullability
get() = ConeNullability.UNKNOWN
override fun toString(): String {
return "<ERROR TYPE: $reason>"
}
}
typealias ConeKotlinErrorType = ConeClassErrorType
class ConeClassErrorType(val reason: String) : ConeClassLikeType() {
override val lookupTag: ConeClassLikeLookupTag
get() = error("!")
get() = ConeClassLikeLookupTagImpl(ClassId.fromString("<error>"))
override val typeArguments: Array<out ConeKotlinTypeProjection>
get() = EMPTY_ARRAY
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.impl.FirCompositeScope
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
fun ConeKotlinType.scope(useSiteSession: FirSession): FirScope? {
return when (this) {
@@ -32,3 +34,16 @@ fun ConeKotlinType.scope(useSiteSession: FirSession): FirScope? {
fun FirRegularClass.defaultType(): ConeClassTypeImpl {
return ConeClassTypeImpl(
symbol.toLookupTag(),
typeParameters.map {
ConeTypeParameterTypeImpl(
it.symbol.toLookupTag(),
isNullable = false
)
}.toTypedArray(),
isNullable = false
)
}
@@ -9,6 +9,9 @@ import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirNamedFunction
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.scope
import org.jetbrains.kotlin.fir.resolve.transformers.ReturnTypeCalculator
import org.jetbrains.kotlin.fir.resolve.transformers.firUnsafe
@@ -16,9 +19,15 @@ import org.jetbrains.kotlin.fir.scopes.FirPosition
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.scopes.processClassifiersByNameWithAction
import org.jetbrains.kotlin.fir.service
import org.jetbrains.kotlin.fir.symbols.*
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeTypeCheckerContext
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.AbstractTypeChecker
import java.util.*
import kotlin.collections.LinkedHashSet
@@ -72,7 +81,9 @@ class CallResolver(val typeCalculator: ReturnTypeCalculator) {
enum class CandidateApplicability {
HIDDEN,
WRONG_RECEIVER,
PARAMETER_MAPPING_ERROR,
INAPPLICABLE,
SYNTHETIC_RESOLVED,
RESOLVED
}
@@ -98,15 +109,11 @@ abstract class ApplicabilityChecker {
currentApplicability = CandidateApplicability.HIDDEN
}
fun isSubtypeOf(superType: FirTypeRef?, subType: FirTypeRef?): Boolean {
if (superType == null && subType == null) return true
if (superType != null && subType != null) return true
return false
}
protected open fun getApplicability(group: Int, symbol: ConeSymbol): CandidateApplicability {
protected open fun getApplicability(
group: Int,
symbol: ConeSymbol,
resolver: CallResolver
): CandidateApplicability {
val declaration = (symbol as? FirBasedSymbol<*>)?.fir
?: return CandidateApplicability.HIDDEN
declaration as FirDeclaration
@@ -119,7 +126,7 @@ abstract class ApplicabilityChecker {
}
open fun consumeCandidate(group: Int, symbol: ConeSymbol, resolver: CallResolver) {
val applicability = getApplicability(group, symbol)
val applicability = getApplicability(group, symbol, resolver)
if (applicability > currentApplicability) {
groupNumbers.clear()
@@ -192,7 +199,11 @@ class VariableInvokeApplicabilityChecker(val variableName: Name) : FunctionAppli
return true //TODO: Actual type-check here
}
override fun getApplicability(group: Int, symbol: ConeSymbol): CandidateApplicability {
override fun getApplicability(
group: Int,
symbol: ConeSymbol,
resolver: CallResolver
): CandidateApplicability {
symbol as ConeCallableSymbol
val declaration = (symbol as? FirBasedSymbol<*>)?.fir
@@ -239,9 +250,10 @@ class VariableInvokeApplicabilityChecker(val variableName: Name) : FunctionAppli
val lastCandidate = variableChecker.candidates.lastOrNull()
if (variableChecker.currentApplicability == CandidateApplicability.RESOLVED && lastCandidate == symbol) {
val receiverScope =
resolver.typeCalculator.tryCalculateReturnType(lastCandidate.firUnsafe())?.type
?.scope(resolver.session)
resolver.typeCalculator.tryCalculateReturnType(lastCandidate.firUnsafe()).type
.scope(resolver.session)
lookupInvoke = true
@@ -294,18 +306,51 @@ open class FunctionApplicabilityChecker(val name: Name) : ApplicabilityChecker()
names.add(name)
}
override fun getApplicability(group: Int, symbol: ConeSymbol): CandidateApplicability {
val declaration = (symbol as FirBasedSymbol<*>).fir
lateinit var session: FirSession
if (declaration is FirFunction) {
if (declaration.valueParameters.size != parameterCount) return CandidateApplicability.PARAMETER_MAPPING_ERROR
override fun getApplicability(
group: Int,
symbol: ConeSymbol,
resolver: CallResolver
): CandidateApplicability {
val declaration = symbol.firUnsafe<FirCallableDeclaration>()
if (declaration is FirFunction && declaration.valueParameters.size != parameterCount) return CandidateApplicability.PARAMETER_MAPPING_ERROR
var extensionReceiver = declaration.receiverTypeRef?.coneTypeUnsafe()
var dispatchReceiver = declaration.dispatchReceiverType(session)
val explicitReceiverType = explicitReceiverType?.coneTypeUnsafe()
if (explicitReceiverType != null) {
if (dispatchReceiver != null && explicitReceiverType.isSubtypeOf(dispatchReceiver, session)) {
dispatchReceiver = null
}
if (extensionReceiver != null && explicitReceiverType.isSubtypeOf(extensionReceiver, session)) {
extensionReceiver = null
}
if (extensionReceiver != null || dispatchReceiver != null) return CandidateApplicability.WRONG_RECEIVER
}
return super.getApplicability(group, symbol)
return CandidateApplicability.RESOLVED
}
fun ConeKotlinType.isSubtypeOf(type: ConeKotlinType, session: FirSession): Boolean {
return AbstractTypeChecker.isSubtypeOf(ConeTypeCheckerContext(true, session), this, type)
}
override fun consumeCandidate(group: Int, symbol: ConeSymbol, resolver: CallResolver) {
if (symbol !is ConeFunctionSymbol) return
if (symbol.callableId.callableName != name) return
session = resolver.session
super.consumeCandidate(group, symbol, resolver)
}
}
fun FirCallableDeclaration.dispatchReceiverType(session: FirSession): ConeKotlinType? {
val id = (this.symbol as ConeCallableSymbol).callableId.classId ?: return null
val symbol = session.service<FirSymbolProvider>().getClassLikeSymbolByFqName(id) as? FirClassSymbol ?: return null
return symbol.fir.defaultType()
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.resolve.transformers
import com.google.common.collect.LinkedHashMultimap
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultSetterValueParameter
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirFunctionCallImpl
import org.jetbrains.kotlin.fir.expressions.impl.FirQualifiedAccessExpressionImpl
@@ -17,6 +18,7 @@ import org.jetbrains.kotlin.fir.references.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.resolve.FirProvider
import org.jetbrains.kotlin.fir.resolve.FirSymbolProvider
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.scope
import org.jetbrains.kotlin.fir.scopes.*
import org.jetbrains.kotlin.fir.scopes.impl.*
@@ -26,10 +28,7 @@ import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.compose
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.fir.types.impl.FirErrorTypeRefImpl
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
import org.jetbrains.kotlin.fir.types.impl.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.cast
@@ -67,6 +66,7 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn
override fun transformValueParameter(valueParameter: FirValueParameter, data: Any?): CompositeTransformResult<FirDeclaration> {
localScopes.lastOrNull()?.storeDeclaration(valueParameter)
if (valueParameter.returnTypeRef is FirImplicitTypeRef) return valueParameter.compose() // TODO
return super.transformValueParameter(valueParameter, data)
}
@@ -88,19 +88,6 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn
}
}
private fun FirRegularClass.defaultType(): ConeClassTypeImpl {
return ConeClassTypeImpl(
symbol.toLookupTag(),
typeParameters.map {
ConeTypeParameterTypeImpl(
it.symbol.toLookupTag(),
isNullable = false
)
}.toTypedArray(),
isNullable = false
)
}
protected inline fun <T> withScopeCleanup(scopes: MutableList<*>, crossinline l: () -> T): T {
val sizeBefore = scopes.size
@@ -119,7 +106,7 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn
val labels = LinkedHashMultimap.create<Name, ConeKotlinType>()
val jump = ReturnTypeCalculatorWithJump()
val jump = ReturnTypeCalculatorWithJump(session)
private fun runTowerResolver(
checkers: List<ApplicabilityChecker>
@@ -138,7 +125,7 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn
is FirErrorNamedReference ->
FirErrorTypeRefImpl(session, access.psi, newCallee.errorReason)
is FirResolvedCallableReference ->
jump.tryCalculateReturnType(newCallee.callableSymbol.firUnsafe())!!
jump.tryCalculateReturnType(newCallee.callableSymbol.firUnsafe())
else -> return
}
}
@@ -369,40 +356,74 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn
require(result.single === this)
}
inner class ReturnTypeCalculatorWithJump : ReturnTypeCalculator {
override fun tryCalculateReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef? {
val returnTypeRef = declaration.returnTypeRef
if (returnTypeRef is FirResolvedTypeRef) return returnTypeRef
require(declaration is FirCallableMemberDeclaration)
}
val id = (declaration.symbol as ConeCallableSymbol).callableId
val provider = session.service<FirProvider>()
val file = provider.getFirCallableContainerFile(id) ?: FirErrorTypeRefImpl(
session,
null,
"I don't know what todo"
)
val outerClasses = generateSequence(id.classId) { classId ->
classId.outerClassId
}.mapTo(mutableListOf()) { provider.getFirClassifierByFqName(it)!! }
class ReturnTypeCalculatorWithJump(val session: FirSession) : ReturnTypeCalculator {
val transformer = FirDesignatedBodyResolveTransformer(
(listOf(file) + outerClasses.asReversed() + listOf(declaration)).iterator(),
file.session
)
transformer.transformElement(file, null)
val newReturnTypeRef = declaration.returnTypeRef
require(newReturnTypeRef is FirResolvedTypeRef) { declaration.render() }
return newReturnTypeRef
val storeType = object : FirTransformer<FirTypeRef>() {
override fun <E : FirElement> transformElement(element: E, data: FirTypeRef): CompositeTransformResult<E> {
return element.compose()
}
override fun transformImplicitTypeRef(
implicitTypeRef: FirImplicitTypeRef,
data: FirTypeRef
): CompositeTransformResult<FirTypeRef> {
return data.compose()
}
}
private fun cycleErrorType(declaration: FirTypedDeclaration): FirResolvedTypeRef? {
if (declaration.returnTypeRef is FirComputingImplicitTypeRef) {
declaration.transformReturnTypeRef(storeType, FirErrorTypeRefImpl(session, null, "cycle"))
return declaration.returnTypeRef as FirResolvedTypeRef
}
return null
}
override fun tryCalculateReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef {
if (declaration is FirValueParameter && declaration.returnTypeRef is FirImplicitTypeRef) {
// TODO?
declaration.transformReturnTypeRef(storeType, FirErrorTypeRefImpl(session, null, "Unsupported: implicit VP type"))
}
val returnTypeRef = declaration.returnTypeRef
if (returnTypeRef is FirResolvedTypeRef) return returnTypeRef
cycleErrorType(declaration)?.let { return it }
require(declaration is FirCallableMemberDeclaration) { "${declaration::class}: ${declaration.render()}" }
val id = (declaration.symbol as ConeCallableSymbol).callableId
val provider = session.service<FirProvider>()
val file = provider.getFirCallableContainerFile(id) ?: FirErrorTypeRefImpl(
session,
null,
"I don't know what todo"
)
val outerClasses = generateSequence(id.classId) { classId ->
classId.outerClassId
}.mapTo(mutableListOf()) { provider.getFirClassifierByFqName(it)!! }
declaration.transformReturnTypeRef(storeType, FirComputingImplicitTypeRef)
val transformer = FirDesignatedBodyResolveTransformer(
(listOf(file) + outerClasses.asReversed() + listOf(declaration)).iterator(),
file.session
)
file.transform(transformer, null)
val newReturnTypeRef = declaration.returnTypeRef
cycleErrorType(declaration)?.let { return it }
require(newReturnTypeRef is FirResolvedTypeRef) { declaration.render() }
return newReturnTypeRef
}
}
@@ -446,12 +467,12 @@ class FirBodyResolveTransformerAdapter : FirTransformer<Nothing?>() {
}
inline fun <reified T> ConeSymbol.firUnsafe(): T {
inline fun <reified T : FirElement> ConeSymbol.firUnsafe(): T {
this as FirBasedSymbol<*>
return this.fir as T
}
interface ReturnTypeCalculator {
fun tryCalculateReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef?
fun tryCalculateReturnType(declaration: FirTypedDeclaration): FirResolvedTypeRef
}
@@ -10,6 +10,7 @@ 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.resolve.transformers.ReturnTypeCalculatorWithJump
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
import org.jetbrains.kotlin.fir.symbols.*
@@ -97,6 +98,8 @@ class FirClassSubstitutionScope(
return useSiteScope.processPropertiesByName(name, processor)
}
private val typeCalculator by lazy { ReturnTypeCalculatorWithJump(session) }
private fun createFakeOverride(
original: ConeFunctionSymbol,
name: Name
@@ -105,7 +108,7 @@ class FirClassSubstitutionScope(
val receiverType = member.receiverTypeRef?.coneTypeUnsafe()
val newReceiverType = receiverType?.substitute()
val returnType = member.returnTypeRef.coneTypeUnsafe()
val returnType = typeCalculator.tryCalculateReturnType(member).type
val newReturnType = returnType.substitute()
val newParameterTypes = member.valueParameters.map {
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.expandedConeType
import org.jetbrains.kotlin.fir.declarations.superConeTypes
@@ -22,7 +23,13 @@ import org.jetbrains.kotlin.fir.types.impl.ConeAbbreviatedTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeFunctionTypeImpl
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.resolve.calls.inference.CapturedType
import org.jetbrains.kotlin.types.AbstractTypeCheckerContext
import org.jetbrains.kotlin.types.DefinitelyNotNullType
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.checker.NewCapturedType
import org.jetbrains.kotlin.types.checker.convertVariance
import org.jetbrains.kotlin.types.isError
import org.jetbrains.kotlin.types.model.*
@@ -176,6 +183,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext {
}
override fun TypeConstructorMarker.supertypes(): Collection<KotlinTypeMarker> {
if (this is ErrorTypeConstructor) return emptyList()
require(this is ConeSymbol)
return when (this) {
is ConeTypeParameterSymbol -> emptyList()
@@ -235,7 +243,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext {
}
override fun captureFromArguments(type: SimpleTypeMarker, status: CaptureStatus): SimpleTypeMarker? {
TODO("not implemented")
return type //TODO
}
override fun SimpleTypeMarker.asArgumentList(): TypeArgumentListMarker {
@@ -264,7 +272,39 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext {
return typeConstructor().isNothingConstructor() && !this.nullability.isNullable
}
override fun SimpleTypeMarker.isSingleClassifierType(): Boolean {
TODO("not implemented")
require(this is ConeLookupTagBasedType)
val symbol = this.lookupTag.toSymbol(session)
return !isError() &&
(symbol is FirClassSymbol ||
symbol is FirTypeParameterSymbol)
// || TODO: (this is CapturedType || this is NewCapturedType || this is DefinitelyNotNullType)
}
}
class ConeTypeCheckerContext(override val isErrorTypeEqualsToAnything: Boolean, override val session: FirSession) :
AbstractTypeCheckerContext(), ConeTypeContext {
override fun substitutionSupertypePolicy(type: SimpleTypeMarker): SupertypesPolicy.DoCustomTransform {
return object : SupertypesPolicy.DoCustomTransform() {
override fun transformType(context: AbstractTypeCheckerContext, type: KotlinTypeMarker): SimpleTypeMarker {
return type.lowerBoundIfFlexible() //TODO
}
}
}
override fun areEqualTypeConstructors(a: TypeConstructorMarker, b: TypeConstructorMarker): Boolean {
return a == b
}
override fun intersectTypes(types: List<KotlinTypeMarker>): KotlinTypeMarker {
TODO("not implemented")
}
override val KotlinTypeMarker.isAllowedTypeVariable: Boolean
get() = false
}
@@ -0,0 +1,10 @@
interface Base {
fun check()
}
class My {
lateinit var delegate: Base
fun check() = delegate.check() // Should not resolve
}
@@ -0,0 +1,17 @@
FILE: dispatchReceiver.kt
public abstract interface Base {
public abstract fun check(): R|kotlin/Unit|
}
public final class My {
public constructor(): super<R|kotlin/Any|>()
public final lateinit var delegate: R|Base|
public get(): R|Base|
public set(value: R|Base|): R|kotlin/Unit|
public final fun check(): <ERROR TYPE: cycle> {
^check R|/My.delegate|.R|/My.check|()
}
}
@@ -159,6 +159,11 @@ public class FirResolveTestCaseGenerated extends AbstractFirResolveTestCase {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/resolve/expresssions"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("dispatchReceiver.kt")
public void testDispatchReceiver() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/expresssions/dispatchReceiver.kt");
}
@TestMetadata("localImplicitBodies.kt")
public void testLocalImplicitBodies() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/expresssions/localImplicitBodies.kt");
@@ -16,4 +16,14 @@ class FirImplicitTypeRefImpl(
) : FirImplicitTypeRef {
override val annotations: List<FirAnnotationCall>
get() = emptyList()
}
object FirComputingImplicitTypeRef : FirImplicitTypeRef {
override val psi: PsiElement?
get() = null
override val session: FirSession
get() = error("Session independent")
override val annotations: List<FirAnnotationCall>
get() = emptyList()
}