FIR: handle reference to property with invisible setter

This commit is contained in:
Jinseong Jeon
2020-11-09 16:31:37 -08:00
committed by Denis Zharkov
parent b2b2629e79
commit 463d53ee5c
7 changed files with 105 additions and 32 deletions
@@ -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
}
}
@@ -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())
}