FIR: reuse visibility checker when determining immutable property reference

This commit is contained in:
Jinseong Jeon
2020-11-17 07:11:13 -08:00
committed by Denis Zharkov
parent 463d53ee5c
commit 67604551c5
5 changed files with 39 additions and 72 deletions
@@ -999,11 +999,23 @@ class DeclarationsConverter(
val convertedAccessors = accessors.map { convertGetterOrSetter(it, returnType, propertyVisibility, modifiers) }
this.getter = convertedAccessors.find { it.isGetter }
?: FirDefaultPropertyGetter(null, session, FirDeclarationOrigin.Source, returnType, propertyVisibility)
?: FirDefaultPropertyGetter(
null, session, FirDeclarationOrigin.Source, returnType, propertyVisibility
).also {
currentDispatchReceiverType()?.lookupTag?.let { lookupTag ->
it.containingClassAttr = lookupTag
}
}
this.setter =
if (isVar) {
convertedAccessors.find { it.isSetter }
?: FirDefaultPropertySetter(null, session, FirDeclarationOrigin.Source, returnType, propertyVisibility)
?: FirDefaultPropertySetter(
null, session, FirDeclarationOrigin.Source, returnType, propertyVisibility
).also {
currentDispatchReceiverType()?.lookupTag?.let { lookupTag ->
it.containingClassAttr = lookupTag
}
}
} else null
// Upward propagation of `inline` and `external` modifiers (from accessors to property)
@@ -1173,6 +1185,9 @@ class DeclarationsConverter(
context.firFunctionTargets.removeLast()
}.also {
target.bind(it)
currentDispatchReceiverType()?.lookupTag?.let { lookupTag ->
it.containingClassAttr = lookupTag
}
}
}
@@ -350,6 +350,9 @@ class RawFirBuilder(
it.extractAnnotationsFrom(this)
}
it.status = status
currentDispatchReceiverType()?.lookupTag?.let { lookupTag ->
it.containingClassAttr = lookupTag
}
}
}
val source = this.toFirSourceElement()
@@ -386,6 +389,9 @@ class RawFirBuilder(
this.contractDescription = it
}
}.also {
currentDispatchReceiverType()?.lookupTag?.let { lookupTag ->
it.containingClassAttr = lookupTag
}
accessorTarget.bind(it)
this@RawFirBuilder.context.firFunctionTargets.removeLast()
}
@@ -5,10 +5,7 @@
package org.jetbrains.kotlin.fir.resolve.calls
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.FirSourceElement
import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirExpression
@@ -22,19 +19,14 @@ import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnsupportedCallableRefer
import org.jetbrains.kotlin.fir.resolve.inference.extractInputOutputTypesFromCallableReferenceExpectedType
import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.typeCheckerContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.calls.components.SuspendConversionStrategy
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.expressions.CoercionStrategy
@@ -51,7 +43,7 @@ internal object CheckCallableReferenceExpectedType : CheckerStage() {
val fir: FirCallableDeclaration<*> = candidate.symbol.fir
val (rawResultingType, callableReferenceAdaptation) = buildReflectionType(fir, resultingReceiverType, callInfo, context)
val (rawResultingType, callableReferenceAdaptation) = buildReflectionType(fir, resultingReceiverType, candidate, context)
val resultingType = candidate.substitutor.substituteOrSelf(rawResultingType)
if (callableReferenceAdaptation.needCompatibilityResolveForCallableReference()) {
@@ -98,7 +90,7 @@ internal object CheckCallableReferenceExpectedType : CheckerStage() {
private fun buildReflectionType(
fir: FirCallableDeclaration<*>,
receiverType: ConeKotlinType?,
callInfo: CallInfo,
candidate: Candidate,
context: ResolutionContext
): Pair<ConeKotlinType, CallableReferenceAdaptation?> {
val returnTypeRef = context.bodyResolveComponents.returnTypeCalculator.tryCalculateReturnType(fir)
@@ -107,7 +99,7 @@ private fun buildReflectionType(
val unboundReferenceTarget = if (receiverType != null) 1 else 0
val callableReferenceAdaptation =
context.bodyResolveComponents
.getCallableReferenceAdaptation(context.session, fir, callInfo.expectedType, unboundReferenceTarget)
.getCallableReferenceAdaptation(context.session, fir, candidate.callInfo.expectedType, unboundReferenceTarget)
val parameters = mutableListOf<ConeKotlinType>()
@@ -132,7 +124,7 @@ private fun buildReflectionType(
isSuspend = isSuspend
) to callableReferenceAdaptation
}
is FirVariable -> createKPropertyType(fir, receiverType, returnTypeRef, callInfo) to null
is FirVariable -> createKPropertyType(fir, receiverType, returnTypeRef, candidate) to null
else -> ConeClassErrorType(ConeUnsupportedCallableReferenceTarget(fir)) to null
}
}
@@ -396,66 +388,19 @@ private fun createKPropertyType(
propertyOrField: FirVariable<*>,
receiverType: ConeKotlinType?,
returnTypeRef: FirResolvedTypeRef,
callInfo: CallInfo,
candidate: Candidate,
): ConeKotlinType {
val propertyType = returnTypeRef.type
return org.jetbrains.kotlin.fir.resolve.createKPropertyType(
receiverType,
propertyType,
isMutable = propertyOrField.canBeMutableReference(callInfo)
isMutable = propertyOrField.canBeMutableReference(candidate)
)
}
private fun FirVariable<*>.canBeMutableReference(callInfo: CallInfo): Boolean {
private fun FirVariable<*>.canBeMutableReference(candidate: Candidate): Boolean {
if (!isVar) return false
if (this is FirField) return true
if (this is FirProperty && this.hasJvmFieldAnnotation) return true
return setter == null || setter!!.isVisible(this, callInfo)
}
private fun FirPropertyAccessor.isVisible(variable: FirVariable<*>, callInfo: CallInfo): Boolean {
if (visibility == Visibilities.Public) {
return true
}
val variableOwnerClassId = variable.ownerClassId
var containingDeclarationOwnerClassId = callInfo.containingDeclarations.last().ownerClassId
// For a (nested) local class, find a declaration, which contains that local class
while (containingDeclarationOwnerClassId?.isLocal == true) {
var last: FirDeclaration? = null
var localContainer: FirDeclaration? = null
for (containingDeclaration in callInfo.containingDeclarations.asReversed()) {
if (last != null && last is FirClass<*> && last.classId == containingDeclarationOwnerClassId) {
localContainer = containingDeclaration
break
}
last = containingDeclaration
}
if (localContainer == null || last == callInfo.containingDeclarations.first()) {
break
}
containingDeclarationOwnerClassId = localContainer.ownerClassId
}
return when (visibility) {
is Visibilities.Protected -> {
// Check if containing declaration's owner class is a subtype of this accessor's owner class
fun ClassId.toClassLikeType() = ConeClassLikeTypeImpl(ConeClassLikeLookupTagImpl(this), emptyArray(), isNullable = false)
val variableOwner = variableOwnerClassId?.toClassLikeType() ?: return false
val containingDeclarationOwner = containingDeclarationOwnerClassId?.toClassLikeType() ?: return false
AbstractTypeChecker.isSubtypeOf(
session.typeCheckerContext, containingDeclarationOwner, variableOwner, isFromNullabilityConstraint = false
)
}
is Visibilities.Internal -> {
// TODO: Check if containing declaration's owner class is in the same module of this accessor's owner class
true
}
is Visibilities.Private, is Visibilities.PrivateToThis -> {
// Check if containing declaration's owner class is...
// 1) ...same as the owner class of this variable
// 2) ...inner class of the owner class of this variable
variableOwnerClassId == containingDeclarationOwnerClassId ||
variableOwnerClassId == containingDeclarationOwnerClassId?.outerClassId
}
else -> false
}
return source?.kind == FirFakeSourceElementKind.PropertyFromParameter ||
(setter is FirMemberDeclaration && candidate.callInfo.session.visibilityChecker.isVisible(setter!!, candidate))
}
@@ -121,7 +121,10 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: KotlinSc
state.classifierContainerFileMap[classId] = file
}
override fun <F : FirCallableDeclaration<F>> visitCallableDeclaration(callableDeclaration: FirCallableDeclaration<F>, data: Pair<State, FirFile>) {
override fun <F : FirCallableDeclaration<F>> visitCallableDeclaration(
callableDeclaration: FirCallableDeclaration<F>,
data: Pair<State, FirFile>
) {
val symbol = callableDeclaration.symbol
val callableId = symbol.callableId
val (state, file) = data
@@ -139,6 +142,8 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: KotlinSc
override fun visitProperty(property: FirProperty, data: Pair<State, FirFile>) {
visitCallableDeclaration(property, data)
property.getter?.let { visitCallableDeclaration(it, data) }
property.setter?.let { visitCallableDeclaration(it, data) }
}
override fun visitEnumEntry(enumEntry: FirEnumEntry, data: Pair<State, FirFile>) {
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.ConeFlexibleType
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.name.ClassId
fun FirTypeParameterBuilder.addDefaultBoundIfNecessary(isFlexible: Boolean = false) {
if (bounds.isEmpty()) {
@@ -86,9 +85,6 @@ val FirTypeAlias.expandedConeType: ConeClassLikeType? get() = expandedTypeRef.co
val FirClass<*>.classId get() = symbol.classId
inline val FirDeclaration.ownerClassId: ClassId?
get() = (this as? FirCallableMemberDeclaration<*>)?.symbol?.callableId?.classId
val FirClassSymbol<*>.superConeTypes
get() = when (this) {
is FirRegularClassSymbol -> fir.superConeTypes