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
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.name.Name
object StandardClassIds {
private val BASE_KOTLIN_PACKAGE = FqName("kotlin")
private val BASE_REFLECT_PACKAGE = BASE_KOTLIN_PACKAGE.child(Name.identifier("reflect"))
val BASE_REFLECT_PACKAGE = BASE_KOTLIN_PACKAGE.child(Name.identifier("reflect"))
private fun String.baseId() = ClassId(BASE_KOTLIN_PACKAGE, Name.identifier(this))
private fun ClassId.unsignedId() = ClassId(BASE_KOTLIN_PACKAGE, Name.identifier("U" + shortClassName.identifier))
private fun String.reflectId() = ClassId(BASE_REFLECT_PACKAGE, Name.identifier(this))
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.resolve.calls.getExpectedTypeForSAMConversion
import org.jetbrains.kotlin.fir.resolve.calls.isFunctional
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
import org.jetbrains.kotlin.fir.resolve.inference.isKMutableProperty
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
import org.jetbrains.kotlin.fir.symbols.impl.*
@@ -68,6 +69,9 @@ class CallAndReferenceGenerator(
is IrPropertySymbol -> {
val referencedProperty = symbol.owner
val referencedPropertyGetter = referencedProperty.getter
val referencedPropertySetterSymbol =
if (callableReferenceAccess.typeRef.coneType.isKMutableProperty(session)) referencedProperty.setter?.symbol
else null
val backingFieldSymbol = when {
referencedPropertyGetter != null -> null
else -> referencedProperty.backingField?.symbol
@@ -75,18 +79,18 @@ class CallAndReferenceGenerator(
IrPropertyReferenceImpl(
startOffset, endOffset, type, symbol,
typeArgumentsCount = referencedPropertyGetter?.typeParameters?.size ?: 0,
backingFieldSymbol,
referencedPropertyGetter?.symbol,
referencedProperty.setter?.symbol,
field = backingFieldSymbol,
getter = referencedPropertyGetter?.symbol,
setter = referencedPropertySetterSymbol,
propertyOrigin()
)
}
is IrLocalDelegatedPropertySymbol -> {
IrLocalDelegatedPropertyReferenceImpl(
startOffset, endOffset, type, symbol,
symbol.owner.delegate.symbol,
symbol.owner.getter.symbol,
symbol.owner.setter?.symbol,
delegate = symbol.owner.delegate.symbol,
getter = symbol.owner.getter.symbol,
setter = symbol.owner.setter?.symbol,
IrStatementOrigin.PROPERTY_REFERENCE_FOR_DELEGATE
)
}
@@ -104,7 +108,7 @@ class CallAndReferenceGenerator(
startOffset, endOffset, type,
propertySymbol,
typeArgumentsCount = (type as? IrSimpleType)?.arguments?.size ?: 0,
symbol,
field = symbol,
getter = if (referencedField.isStatic) null else propertySymbol.owner.getter?.symbol,
setter = if (referencedField.isStatic) null else propertySymbol.owner.setter?.symbol,
propertyOrigin()
@@ -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())
}
@@ -20,6 +20,7 @@ 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()) {
@@ -85,6 +86,9 @@ 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
@@ -1,6 +1,5 @@
// DONT_TARGET_EXACT_BACKEND: WASM
// WASM_MUTE_REASON: PROPERTY_REFERENCES
// IGNORE_BACKEND_FIR: JVM_IR
// See KT-12337 Reference to property with invisible setter should not be a KMutableProperty
import kotlin.reflect.KProperty1
@@ -278,20 +278,20 @@ FILE fqName:<root> fileName:/propertyReferences.kt
RETURN type=kotlin.Nothing from='public final fun <get-test_J_nonConst> (): kotlin.reflect.KMutableProperty0<kotlin.Int> declared in <root>'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test_J_nonConst type:kotlin.reflect.KMutableProperty0<kotlin.Int> visibility:private [final,static]' type=kotlin.reflect.KMutableProperty0<kotlin.Int> origin=null
PROPERTY name:test_varWithPrivateSet visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:test_varWithPrivateSet type:kotlin.reflect.KMutableProperty1<<root>.C, kotlin.Int> visibility:private [final,static]
FIELD PROPERTY_BACKING_FIELD name:test_varWithPrivateSet type:kotlin.reflect.KProperty1<<root>.C, kotlin.Int> visibility:private [final,static]
EXPRESSION_BODY
PROPERTY_REFERENCE 'public final varWithPrivateSet: kotlin.Int [var]' field=null getter='public final fun <get-varWithPrivateSet> (): kotlin.Int declared in <root>.C' setter='private final fun <set-varWithPrivateSet> (<set-?>: kotlin.Int): kotlin.Unit declared in <root>.C' type=kotlin.reflect.KMutableProperty1<<root>.C, kotlin.Int> origin=null
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-test_varWithPrivateSet> visibility:public modality:FINAL <> () returnType:kotlin.reflect.KMutableProperty1<<root>.C, kotlin.Int>
PROPERTY_REFERENCE 'public final varWithPrivateSet: kotlin.Int [var]' field=null getter='public final fun <get-varWithPrivateSet> (): kotlin.Int declared in <root>.C' setter=null type=kotlin.reflect.KProperty1<<root>.C, kotlin.Int> origin=null
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-test_varWithPrivateSet> visibility:public modality:FINAL <> () returnType:kotlin.reflect.KProperty1<<root>.C, kotlin.Int>
correspondingProperty: PROPERTY name:test_varWithPrivateSet visibility:public modality:FINAL [val]
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-test_varWithPrivateSet> (): kotlin.reflect.KMutableProperty1<<root>.C, kotlin.Int> declared in <root>'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test_varWithPrivateSet type:kotlin.reflect.KMutableProperty1<<root>.C, kotlin.Int> visibility:private [final,static]' type=kotlin.reflect.KMutableProperty1<<root>.C, kotlin.Int> origin=null
RETURN type=kotlin.Nothing from='public final fun <get-test_varWithPrivateSet> (): kotlin.reflect.KProperty1<<root>.C, kotlin.Int> declared in <root>'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test_varWithPrivateSet type:kotlin.reflect.KProperty1<<root>.C, kotlin.Int> visibility:private [final,static]' type=kotlin.reflect.KProperty1<<root>.C, kotlin.Int> origin=null
PROPERTY name:test_varWithProtectedSet visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:test_varWithProtectedSet type:kotlin.reflect.KMutableProperty1<<root>.C, kotlin.Int> visibility:private [final,static]
FIELD PROPERTY_BACKING_FIELD name:test_varWithProtectedSet type:kotlin.reflect.KProperty1<<root>.C, kotlin.Int> visibility:private [final,static]
EXPRESSION_BODY
PROPERTY_REFERENCE 'public final varWithProtectedSet: kotlin.Int [var]' field=null getter='public final fun <get-varWithProtectedSet> (): kotlin.Int declared in <root>.C' setter='protected final fun <set-varWithProtectedSet> (<set-?>: kotlin.Int): kotlin.Unit declared in <root>.C' type=kotlin.reflect.KMutableProperty1<<root>.C, kotlin.Int> origin=null
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-test_varWithProtectedSet> visibility:public modality:FINAL <> () returnType:kotlin.reflect.KMutableProperty1<<root>.C, kotlin.Int>
PROPERTY_REFERENCE 'public final varWithProtectedSet: kotlin.Int [var]' field=null getter='public final fun <get-varWithProtectedSet> (): kotlin.Int declared in <root>.C' setter=null type=kotlin.reflect.KProperty1<<root>.C, kotlin.Int> origin=null
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-test_varWithProtectedSet> visibility:public modality:FINAL <> () returnType:kotlin.reflect.KProperty1<<root>.C, kotlin.Int>
correspondingProperty: PROPERTY name:test_varWithProtectedSet visibility:public modality:FINAL [val]
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-test_varWithProtectedSet> (): kotlin.reflect.KMutableProperty1<<root>.C, kotlin.Int> declared in <root>'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test_varWithProtectedSet type:kotlin.reflect.KMutableProperty1<<root>.C, kotlin.Int> visibility:private [final,static]' type=kotlin.reflect.KMutableProperty1<<root>.C, kotlin.Int> origin=null
RETURN type=kotlin.Nothing from='public final fun <get-test_varWithProtectedSet> (): kotlin.reflect.KProperty1<<root>.C, kotlin.Int> declared in <root>'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test_varWithProtectedSet type:kotlin.reflect.KProperty1<<root>.C, kotlin.Int> visibility:private [final,static]' type=kotlin.reflect.KProperty1<<root>.C, kotlin.Int> origin=null