FIR: reuse visibility checker when determining immutable property reference
This commit is contained in:
committed by
Denis Zharkov
parent
463d53ee5c
commit
67604551c5
+17
-2
@@ -999,11 +999,23 @@ class DeclarationsConverter(
|
|||||||
|
|
||||||
val convertedAccessors = accessors.map { convertGetterOrSetter(it, returnType, propertyVisibility, modifiers) }
|
val convertedAccessors = accessors.map { convertGetterOrSetter(it, returnType, propertyVisibility, modifiers) }
|
||||||
this.getter = convertedAccessors.find { it.isGetter }
|
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 =
|
this.setter =
|
||||||
if (isVar) {
|
if (isVar) {
|
||||||
convertedAccessors.find { it.isSetter }
|
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
|
} else null
|
||||||
|
|
||||||
// Upward propagation of `inline` and `external` modifiers (from accessors to property)
|
// Upward propagation of `inline` and `external` modifiers (from accessors to property)
|
||||||
@@ -1173,6 +1185,9 @@ class DeclarationsConverter(
|
|||||||
context.firFunctionTargets.removeLast()
|
context.firFunctionTargets.removeLast()
|
||||||
}.also {
|
}.also {
|
||||||
target.bind(it)
|
target.bind(it)
|
||||||
|
currentDispatchReceiverType()?.lookupTag?.let { lookupTag ->
|
||||||
|
it.containingClassAttr = lookupTag
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -350,6 +350,9 @@ class RawFirBuilder(
|
|||||||
it.extractAnnotationsFrom(this)
|
it.extractAnnotationsFrom(this)
|
||||||
}
|
}
|
||||||
it.status = status
|
it.status = status
|
||||||
|
currentDispatchReceiverType()?.lookupTag?.let { lookupTag ->
|
||||||
|
it.containingClassAttr = lookupTag
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val source = this.toFirSourceElement()
|
val source = this.toFirSourceElement()
|
||||||
@@ -386,6 +389,9 @@ class RawFirBuilder(
|
|||||||
this.contractDescription = it
|
this.contractDescription = it
|
||||||
}
|
}
|
||||||
}.also {
|
}.also {
|
||||||
|
currentDispatchReceiverType()?.lookupTag?.let { lookupTag ->
|
||||||
|
it.containingClassAttr = lookupTag
|
||||||
|
}
|
||||||
accessorTarget.bind(it)
|
accessorTarget.bind(it)
|
||||||
this@RawFirBuilder.context.firFunctionTargets.removeLast()
|
this@RawFirBuilder.context.firFunctionTargets.removeLast()
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-65
@@ -5,10 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.resolve.calls
|
package org.jetbrains.kotlin.fir.resolve.calls
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.FirElement
|
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
|
||||||
import org.jetbrains.kotlin.fir.FirSourceElement
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
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.extractInputOutputTypesFromCallableReferenceExpectedType
|
||||||
import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType
|
import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType
|
||||||
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
|
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.symbols.impl.FirCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.typeCheckerContext
|
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
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.FirTransformer
|
||||||
import org.jetbrains.kotlin.fir.visitors.FirVisitor
|
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.components.SuspendConversionStrategy
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
|
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
|
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
|
||||||
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
|
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
|
||||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
|
||||||
import org.jetbrains.kotlin.types.expressions.CoercionStrategy
|
import org.jetbrains.kotlin.types.expressions.CoercionStrategy
|
||||||
|
|
||||||
|
|
||||||
@@ -51,7 +43,7 @@ internal object CheckCallableReferenceExpectedType : CheckerStage() {
|
|||||||
|
|
||||||
val fir: FirCallableDeclaration<*> = candidate.symbol.fir
|
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)
|
val resultingType = candidate.substitutor.substituteOrSelf(rawResultingType)
|
||||||
|
|
||||||
if (callableReferenceAdaptation.needCompatibilityResolveForCallableReference()) {
|
if (callableReferenceAdaptation.needCompatibilityResolveForCallableReference()) {
|
||||||
@@ -98,7 +90,7 @@ internal object CheckCallableReferenceExpectedType : CheckerStage() {
|
|||||||
private fun buildReflectionType(
|
private fun buildReflectionType(
|
||||||
fir: FirCallableDeclaration<*>,
|
fir: FirCallableDeclaration<*>,
|
||||||
receiverType: ConeKotlinType?,
|
receiverType: ConeKotlinType?,
|
||||||
callInfo: CallInfo,
|
candidate: Candidate,
|
||||||
context: ResolutionContext
|
context: ResolutionContext
|
||||||
): Pair<ConeKotlinType, CallableReferenceAdaptation?> {
|
): Pair<ConeKotlinType, CallableReferenceAdaptation?> {
|
||||||
val returnTypeRef = context.bodyResolveComponents.returnTypeCalculator.tryCalculateReturnType(fir)
|
val returnTypeRef = context.bodyResolveComponents.returnTypeCalculator.tryCalculateReturnType(fir)
|
||||||
@@ -107,7 +99,7 @@ private fun buildReflectionType(
|
|||||||
val unboundReferenceTarget = if (receiverType != null) 1 else 0
|
val unboundReferenceTarget = if (receiverType != null) 1 else 0
|
||||||
val callableReferenceAdaptation =
|
val callableReferenceAdaptation =
|
||||||
context.bodyResolveComponents
|
context.bodyResolveComponents
|
||||||
.getCallableReferenceAdaptation(context.session, fir, callInfo.expectedType, unboundReferenceTarget)
|
.getCallableReferenceAdaptation(context.session, fir, candidate.callInfo.expectedType, unboundReferenceTarget)
|
||||||
|
|
||||||
val parameters = mutableListOf<ConeKotlinType>()
|
val parameters = mutableListOf<ConeKotlinType>()
|
||||||
|
|
||||||
@@ -132,7 +124,7 @@ private fun buildReflectionType(
|
|||||||
isSuspend = isSuspend
|
isSuspend = isSuspend
|
||||||
) to callableReferenceAdaptation
|
) 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
|
else -> ConeClassErrorType(ConeUnsupportedCallableReferenceTarget(fir)) to null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -396,66 +388,19 @@ private fun createKPropertyType(
|
|||||||
propertyOrField: FirVariable<*>,
|
propertyOrField: FirVariable<*>,
|
||||||
receiverType: ConeKotlinType?,
|
receiverType: ConeKotlinType?,
|
||||||
returnTypeRef: FirResolvedTypeRef,
|
returnTypeRef: FirResolvedTypeRef,
|
||||||
callInfo: CallInfo,
|
candidate: Candidate,
|
||||||
): ConeKotlinType {
|
): ConeKotlinType {
|
||||||
val propertyType = returnTypeRef.type
|
val propertyType = returnTypeRef.type
|
||||||
return org.jetbrains.kotlin.fir.resolve.createKPropertyType(
|
return org.jetbrains.kotlin.fir.resolve.createKPropertyType(
|
||||||
receiverType,
|
receiverType,
|
||||||
propertyType,
|
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 (!isVar) return false
|
||||||
if (this is FirField) return true
|
if (this is FirField) return true
|
||||||
if (this is FirProperty && this.hasJvmFieldAnnotation) return true
|
return source?.kind == FirFakeSourceElementKind.PropertyFromParameter ||
|
||||||
return setter == null || setter!!.isVisible(this, callInfo)
|
(setter is FirMemberDeclaration && candidate.callInfo.session.visibilityChecker.isVisible(setter!!, candidate))
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -121,7 +121,10 @@ class FirProviderImpl(val session: FirSession, val kotlinScopeProvider: KotlinSc
|
|||||||
state.classifierContainerFileMap[classId] = file
|
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 symbol = callableDeclaration.symbol
|
||||||
val callableId = symbol.callableId
|
val callableId = symbol.callableId
|
||||||
val (state, file) = data
|
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>) {
|
override fun visitProperty(property: FirProperty, data: Pair<State, FirFile>) {
|
||||||
visitCallableDeclaration(property, data)
|
visitCallableDeclaration(property, data)
|
||||||
|
property.getter?.let { visitCallableDeclaration(it, data) }
|
||||||
|
property.setter?.let { visitCallableDeclaration(it, data) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitEnumEntry(enumEntry: FirEnumEntry, data: Pair<State, FirFile>) {
|
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.ConeFlexibleType
|
||||||
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef
|
||||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
|
||||||
|
|
||||||
fun FirTypeParameterBuilder.addDefaultBoundIfNecessary(isFlexible: Boolean = false) {
|
fun FirTypeParameterBuilder.addDefaultBoundIfNecessary(isFlexible: Boolean = false) {
|
||||||
if (bounds.isEmpty()) {
|
if (bounds.isEmpty()) {
|
||||||
@@ -86,9 +85,6 @@ val FirTypeAlias.expandedConeType: ConeClassLikeType? get() = expandedTypeRef.co
|
|||||||
|
|
||||||
val FirClass<*>.classId get() = symbol.classId
|
val FirClass<*>.classId get() = symbol.classId
|
||||||
|
|
||||||
inline val FirDeclaration.ownerClassId: ClassId?
|
|
||||||
get() = (this as? FirCallableMemberDeclaration<*>)?.symbol?.callableId?.classId
|
|
||||||
|
|
||||||
val FirClassSymbol<*>.superConeTypes
|
val FirClassSymbol<*>.superConeTypes
|
||||||
get() = when (this) {
|
get() = when (this) {
|
||||||
is FirRegularClassSymbol -> fir.superConeTypes
|
is FirRegularClassSymbol -> fir.superConeTypes
|
||||||
|
|||||||
Reference in New Issue
Block a user