[FIR] Add ir support

This commit is contained in:
Nikolay Lunyak
2021-08-16 20:53:29 +03:00
committed by TeamCityServer
parent 8b918e12b2
commit 79ceaf03bf
11 changed files with 137 additions and 92 deletions
@@ -699,6 +699,7 @@ class Fir2IrDeclarationStorage(
private val FirProperty.fieldVisibility: Visibility
get() = when {
hasExplicitBackingField -> backingField?.visibility ?: status.visibility
isLateInit -> setter?.visibility ?: status.visibility
isConst -> status.visibility
hasJvmFieldAnnotation -> status.visibility
@@ -798,7 +799,6 @@ class Fir2IrDeclarationStorage(
parent = irParent
}
val type = property.returnTypeRef.toIrType()
val initializer = property.initializer
val delegate = property.delegate
val getter = property.getter
val setter = property.setter
@@ -810,14 +810,21 @@ class Fir2IrDeclarationStorage(
Name.identifier("${property.name}\$delegate"), true, delegate
)
} else {
val initializer = property.backingField?.initializer ?: property.initializer
// There are cases when we get here for properties
// that have no backing field. For example, in the
// funExpression.kt test there's an attempt
// to access the `javaClass` property of the `foo0`'s
// `block` argument
val typeToUse = property.backingField?.returnTypeRef?.toIrType() ?: type
createBackingField(
property, IrDeclarationOrigin.PROPERTY_BACKING_FIELD,
components.visibilityConverter.convertToDescriptorVisibility(property.fieldVisibility),
property.name, property.isVal, initializer, type
property.name, property.isVal, initializer, typeToUse
).also { field ->
if (initializer is FirConstExpression<*>) {
// TODO: Normally we shouldn't have error type here
val constType = initializer.typeRef.toIrType().takeIf { it !is IrErrorType } ?: type
val constType = initializer.typeRef.toIrType().takeIf { it !is IrErrorType } ?: typeToUse
field.initializer = factory.createExpressionBody(initializer.toIrConst(constType))
}
}
@@ -158,7 +158,7 @@ internal class ClassMemberGenerator(
}
fun convertPropertyContent(irProperty: IrProperty, property: FirProperty, containingClass: FirClass?): IrProperty {
val initializer = property.initializer
val initializer = property.backingField?.initializer ?: property.initializer
val delegate = property.delegate
val propertyType = property.returnTypeRef.toIrType()
irProperty.initializeBackingField(property, initializerExpression = initializer ?: delegate)
@@ -29,6 +29,8 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
import org.jetbrains.kotlin.fir.backend.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
class Fir2IrLazyProperty(
@@ -88,6 +90,16 @@ class Fir2IrLazyProperty(
with(typeConverter) { fir.returnTypeRef.toIrType() }
}
private fun toIrInitializer(initializer: FirExpression?): IrExpressionBody? {
return if (initializer is FirConstExpression<*>) {
// TODO: Normally we shouldn't have error type here
val constType = with(typeConverter) { initializer.typeRef.toIrType().takeIf { it !is IrErrorType } ?: type }
factory.createExpressionBody(initializer.toIrConst(constType))
} else {
null
}
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
override var backingField: IrField? by lazyVar(lock) {
// TODO: this checks are very preliminary, FIR resolve should determine backing field presence itself
@@ -96,6 +108,26 @@ class Fir2IrLazyProperty(
!fir.isConst && (fir.modality == Modality.ABSTRACT || parent is IrClass && parent.isInterface) -> {
null
}
fir.hasExplicitBackingField -> {
with(declarationStorage) {
val backingFieldType = with(typeConverter) {
fir.backingField?.returnTypeRef?.toIrType()
}
val initializer = fir.backingField?.initializer ?: fir.initializer
val visibility = fir.backingField?.visibility ?: fir.visibility
createBackingField(
fir,
IrDeclarationOrigin.PROPERTY_BACKING_FIELD,
components.visibilityConverter.convertToDescriptorVisibility(visibility),
fir.name,
fir.isVal,
initializer,
backingFieldType
).also { field ->
field.initializer = toIrInitializer(initializer)
}
}
}
fir.initializer != null || fir.getter is FirDefaultPropertyGetter || fir.isVar && fir.setter is FirDefaultPropertySetter -> {
with(declarationStorage) {
createBackingField(
@@ -103,12 +135,7 @@ class Fir2IrLazyProperty(
components.visibilityConverter.convertToDescriptorVisibility(fir.visibility), fir.name, fir.isVal, fir.initializer,
type
).also { field ->
val initializer = fir.initializer
if (initializer is FirConstExpression<*>) {
// TODO: Normally we shouldn't have error type here
val constType = with(typeConverter) { initializer.typeRef.toIrType().takeIf { it !is IrErrorType } ?: type }
field.initializer = factory.createExpressionBody(initializer.toIrConst(constType))
}
field.initializer = toIrInitializer(fir.initializer)
}
}
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.hasExplicitBackingField
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.declarations.utils.isReferredViaField
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
@@ -44,6 +45,7 @@ import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class FirCallResolver(
private val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents,
@@ -727,6 +729,11 @@ class FirCallResolver(
resolvedSymbol = coneSymbol
}
}
if (coneSymbol.safeAs<FirPropertySymbol>()?.hasExplicitBackingField == true) {
return FirPropertyWithExplicitBackingFieldResolvedNamedReference(
source, name, candidate.symbol, candidate.hasVisibleBackingField
)
}
/*
* This `if` is an optimization for local variables and properties without type parameters
* Since they have no type variables, so we can don't run completion on them at all and create
@@ -18,11 +18,13 @@ import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol
import org.jetbrains.kotlin.fir.resolve.calls.ReceiverValue
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
abstract class FirModuleVisibilityChecker : FirSessionComponent {
abstract fun <T> isInFriendModule(declaration: T): Boolean where T : FirMemberDeclaration, T : FirDeclaration
@@ -51,6 +53,23 @@ abstract class FirVisibilityChecker : FirSessionComponent {
}
}
private fun FirMemberDeclaration.getBackingFieldIfApplicable(): FirBackingField? {
val field = this.safeAs<FirProperty>()?.getExplicitBackingField()
?: return null
// This check prevents resolving protected and
// public fields.
if (
field.visibility == Visibilities.PrivateToThis ||
field.visibility == Visibilities.Private ||
field.visibility == Visibilities.Internal
) {
return field
}
return null
}
fun isVisible(
declaration: FirMemberDeclaration,
candidate: Candidate
@@ -71,7 +90,20 @@ abstract class FirVisibilityChecker : FirSessionComponent {
return true
}
return isVisible(declaration, session, useSiteFile, containingDeclarations, candidate.dispatchReceiverValue)
val visible = isVisible(declaration, session, useSiteFile, containingDeclarations, candidate.dispatchReceiverValue)
val backingField = declaration.getBackingFieldIfApplicable()
if (visible && backingField != null) {
candidate.hasVisibleBackingField = isVisible(
backingField,
session,
useSiteFile,
containingDeclarations,
candidate.dispatchReceiverValue,
)
}
return visible
}
fun isVisible(
@@ -277,6 +309,7 @@ val FirSession.visibilityChecker: FirVisibilityChecker by FirSession.sessionComp
fun FirBasedSymbol<*>.getOwnerLookupTag(): ConeClassLikeLookupTag? {
return when (this) {
is FirBackingFieldSymbol -> fir.propertySymbol.getOwnerLookupTag()
is FirClassLikeSymbol<*> -> {
if (classId.isLocal) {
(fir as? FirRegularClass)?.containingClassForLocal()
@@ -11,7 +11,9 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.canNarrowDownGetterType
import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
@@ -187,6 +189,30 @@ internal fun typeForQualifierByDeclaration(declaration: FirDeclaration, resultTy
return null
}
private fun FirPropertyWithExplicitBackingFieldResolvedNamedReference.getNarrowedDownSymbol(): FirBasedSymbol<*> {
val propertyReceiver = resolvedSymbol.safeAs<FirPropertySymbol>()
?: return resolvedSymbol
// This can happen in case of 2 properties referencing
// each other recursively. See: Jet81.fir.kt
if (
propertyReceiver.fir.returnTypeRef is FirImplicitTypeRef ||
propertyReceiver.fir.backingField?.returnTypeRef is FirImplicitTypeRef
) {
return resolvedSymbol
}
if (
propertyReceiver.isFinal &&
hasVisibleBackingField &&
propertyReceiver.canNarrowDownGetterType
) {
return propertyReceiver.fir.backingField?.symbol ?: resolvedSymbol
}
return resolvedSymbol
}
fun <T : FirResolvable> BodyResolveComponents.typeFromCallee(access: T): FirResolvedTypeRef {
return when (val newCallee = access.calleeReference) {
is FirErrorNamedReference ->
@@ -198,16 +224,8 @@ fun <T : FirResolvable> BodyResolveComponents.typeFromCallee(access: T): FirReso
typeFromSymbol(newCallee.candidateSymbol, false)
}
is FirPropertyWithExplicitBackingFieldResolvedNamedReference -> {
val backingField = newCallee.resolvedSymbol
.safeAs<FirPropertySymbol>()
?.fir
?.backingField
if (newCallee.hasVisibleBackingField && backingField != null) {
typeFromSymbol(backingField.symbol, false)
} else {
typeFromSymbol(newCallee.resolvedSymbol, false)
}
val symbol = newCallee.getNarrowedDownSymbol()
typeFromSymbol(symbol, false)
}
is FirResolvedNamedReference -> {
typeFromSymbol(newCallee.resolvedSymbol, false)
@@ -145,6 +145,8 @@ class Candidate(
fun extensionReceiverExpression(): FirExpression =
extensionReceiverValue?.receiverExpression?.takeIf { it !is FirExpressionStub } ?: FirNoReceiverExpression
var hasVisibleBackingField = false
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
@@ -8,8 +8,11 @@ package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.fir.FirImplementationDetail
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.name.Name
@OptIn(FirImplementationDetail::class)
@@ -32,3 +35,18 @@ class FirErrorReferenceWithCandidate(
) : FirNamedReferenceWithCandidate(source, name, candidate) {
override val isError: Boolean get() = true
}
class FirPropertyWithExplicitBackingFieldResolvedNamedReference(
override val source: FirSourceElement?,
override val name: Name,
override val resolvedSymbol: FirBasedSymbol<*>,
val hasVisibleBackingField: Boolean,
) : FirResolvedNamedReference() {
override val candidateSymbol: FirBasedSymbol<*>? get() = null
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {}
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirPropertyWithExplicitBackingFieldResolvedNamedReference {
return this
}
}
@@ -69,11 +69,6 @@ open class ExpressionReceiverValue(
}
}
class NarrowedExpressionReceiverValue(
explicitReceiver: FirExpression,
override val type: ConeKotlinType,
) : ExpressionReceiverValue(explicitReceiver)
sealed class ImplicitReceiverValue<S : FirBasedSymbol<*>>(
val boundSymbol: S,
type: ConeKotlinType,
@@ -5,34 +5,21 @@
package org.jetbrains.kotlin.fir.resolve.calls.tower
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.asReversedFrozen
import org.jetbrains.kotlin.fir.containingClass
import org.jetbrains.kotlin.fir.declarations.utils.canNarrowDownGetterType
import org.jetbrains.kotlin.fir.declarations.utils.isFinal
import org.jetbrains.kotlin.fir.declarations.utils.visibility
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
import org.jetbrains.kotlin.fir.expressions.builder.buildExpressionStub
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.DoubleColonLHS
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirImplicitTypeRef
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.fir.types.impl.FirImplicitBuiltinTypeRef
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal class TowerDataElementsForName(
name: Name,
@@ -236,64 +223,12 @@ internal open class FirTowerResolveTask(
)
}
private fun FirPropertySymbol.hasAccessibleBackingField(info: CallInfo): Boolean {
val receiverContainer = containingClass()?.toSymbol(info.session)
if (!this.isFinal) {
return false
}
return when (fir.backingField?.visibility) {
Visibilities.Private -> {
info.containingDeclarations.any {
it.symbol == receiverContainer
}
}
Visibilities.Internal -> {
receiverContainer?.moduleData == info.containingFile.moduleData
}
else -> {
false
}
}
}
private fun FirExpression.getNarrowedDownReturnType(info: CallInfo): ConeKotlinType? {
val propertyReceiver = this.safeAs<FirQualifiedAccessExpression>()
?.calleeReference
?.safeAs<FirResolvedNamedReference>()
?.resolvedSymbol
?.safeAs<FirPropertySymbol>()
?: return null
// This can happen in case of 2 properties referencing
// each other recursively. See: Jet81.fir.kt
if (
propertyReceiver.fir.returnTypeRef is FirImplicitTypeRef ||
propertyReceiver.fir.backingField?.returnTypeRef is FirImplicitTypeRef
) {
return null
}
if (propertyReceiver.hasAccessibleBackingField(info) && propertyReceiver.canNarrowDownGetterType) {
return propertyReceiver.fir.backingField?.returnTypeRef?.coneType
}
return null
}
suspend fun runResolverForExpressionReceiver(
info: CallInfo,
receiver: FirExpression,
parentGroup: TowerGroup = TowerGroup.EmptyRoot
) {
val narrowedType = receiver.getNarrowedDownReturnType(info)
val explicitReceiverValue = if (narrowedType != null) {
NarrowedExpressionReceiverValue(receiver, narrowedType)
} else {
ExpressionReceiverValue(receiver)
}
val explicitReceiverValue = ExpressionReceiverValue(receiver)
processExtensionsThatHideMembers(info, explicitReceiverValue, parentGroup)
@@ -29,6 +29,9 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: FirKotli
symbol.originalIfFakeOverride()?.let {
return getFirCallableContainerFile(it)
}
if (symbol is FirBackingFieldSymbol) {
return getFirCallableContainerFile(symbol.fir.propertySymbol)
}
if (symbol is FirAccessorSymbol) {
val fir = symbol.fir
if (fir is FirSyntheticProperty) {