FIR: handle reference to property with invisible setter
This commit is contained in:
committed by
Denis Zharkov
parent
b2b2629e79
commit
463d53ee5c
@@ -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))
|
||||
|
||||
+11
-7
@@ -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()
|
||||
|
||||
+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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user