FIR: handle reference to property with invisible setter
This commit is contained in:
committed by
Denis Zharkov
parent
b2b2629e79
commit
463d53ee5c
+66
-10
@@ -5,6 +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
|
||||
@@ -21,14 +22,19 @@ 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
|
||||
|
||||
|
||||
@@ -89,13 +95,6 @@ internal object CheckCallableReferenceExpectedType : CheckerStage() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
val resultingReceiverType = when (callInfo.lhs) {
|
||||
is DoubleColonLHS.Type -> callInfo.lhs.type.takeIf { callInfo.explicitReceiver !is FirResolvedQualifier }
|
||||
else -> null
|
||||
}
|
||||
*/
|
||||
private fun buildReflectionType(
|
||||
fir: FirCallableDeclaration<*>,
|
||||
receiverType: ConeKotlinType?,
|
||||
@@ -133,7 +132,7 @@ private fun buildReflectionType(
|
||||
isSuspend = isSuspend
|
||||
) to callableReferenceAdaptation
|
||||
}
|
||||
is FirVariable -> createKPropertyType(fir, receiverType, returnTypeRef) to null
|
||||
is FirVariable -> createKPropertyType(fir, receiverType, returnTypeRef, callInfo) to null
|
||||
else -> ConeClassErrorType(ConeUnsupportedCallableReferenceTarget(fir)) to null
|
||||
}
|
||||
}
|
||||
@@ -396,10 +395,67 @@ fun ConeKotlinType.isKCallableType(): Boolean {
|
||||
private fun createKPropertyType(
|
||||
propertyOrField: FirVariable<*>,
|
||||
receiverType: ConeKotlinType?,
|
||||
returnTypeRef: FirResolvedTypeRef
|
||||
returnTypeRef: FirResolvedTypeRef,
|
||||
callInfo: CallInfo,
|
||||
): ConeKotlinType {
|
||||
val propertyType = returnTypeRef.type
|
||||
return org.jetbrains.kotlin.fir.resolve.createKPropertyType(
|
||||
receiverType, propertyType, isMutable = propertyOrField.isVar
|
||||
receiverType,
|
||||
propertyType,
|
||||
isMutable = propertyOrField.canBeMutableReference(callInfo)
|
||||
)
|
||||
}
|
||||
|
||||
private fun FirVariable<*>.canBeMutableReference(callInfo: CallInfo): 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
|
||||
}
|
||||
}
|
||||
|
||||
+13
-3
@@ -29,12 +29,22 @@ import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
private fun ConeKotlinType.functionClassKind(session: FirSession): FunctionClassKind? {
|
||||
private fun ConeKotlinType.classId(session: FirSession): ClassId? {
|
||||
contract {
|
||||
returns(true) implies (this@functionClassKind is ConeClassLikeType)
|
||||
returns(true) implies (this@classId is ConeClassLikeType)
|
||||
}
|
||||
if (this !is ConeClassLikeType) return null
|
||||
val classId = fullyExpandedType(session).lookupTag.classId
|
||||
return fullyExpandedType(session).lookupTag.classId
|
||||
}
|
||||
|
||||
fun ConeKotlinType.isKMutableProperty(session: FirSession): Boolean {
|
||||
val classId = classId(session) ?: return false
|
||||
return classId.packageFqName == StandardClassIds.BASE_REFLECT_PACKAGE &&
|
||||
classId.shortClassName.identifier.startsWith("KMutableProperty")
|
||||
}
|
||||
|
||||
private fun ConeKotlinType.functionClassKind(session: FirSession): FunctionClassKind? {
|
||||
val classId = classId(session) ?: return null
|
||||
return FunctionClassKind.byClassNamePrefix(classId.packageFqName, classId.relativeClassName.asString())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user