diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt index 0ea08f5a9c6..562e8cceb0b 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt @@ -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)) diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt index 32870ee9f78..e647039c01f 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/CallAndReferenceGenerator.kt @@ -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() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallableReferenceResolution.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallableReferenceResolution.kt index 4bb43f4a416..217611780bd 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallableReferenceResolution.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallableReferenceResolution.kt @@ -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 + } +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/InferenceUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/InferenceUtils.kt index 20f9490b99a..dc9e4f15eda 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/InferenceUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/InferenceUtils.kt @@ -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()) } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationUtil.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationUtil.kt index 8b70b7bb7e3..9ace088f2e5 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationUtil.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationUtil.kt @@ -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 diff --git a/compiler/testData/codegen/box/callableReference/property/privateSetterOutsideClass.kt b/compiler/testData/codegen/box/callableReference/property/privateSetterOutsideClass.kt index d9dbdf71ca3..dc5f6e97bb3 100644 --- a/compiler/testData/codegen/box/callableReference/property/privateSetterOutsideClass.kt +++ b/compiler/testData/codegen/box/callableReference/property/privateSetterOutsideClass.kt @@ -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 diff --git a/compiler/testData/ir/irText/expressions/propertyReferences.fir.txt b/compiler/testData/ir/irText/expressions/propertyReferences.fir.txt index ab06764a079..03377753661 100644 --- a/compiler/testData/ir/irText/expressions/propertyReferences.fir.txt +++ b/compiler/testData/ir/irText/expressions/propertyReferences.fir.txt @@ -278,20 +278,20 @@ FILE fqName: fileName:/propertyReferences.kt RETURN type=kotlin.Nothing from='public final fun (): kotlin.reflect.KMutableProperty0 declared in ' GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test_J_nonConst type:kotlin.reflect.KMutableProperty0 visibility:private [final,static]' type=kotlin.reflect.KMutableProperty0 origin=null PROPERTY name:test_varWithPrivateSet visibility:public modality:FINAL [val] - FIELD PROPERTY_BACKING_FIELD name:test_varWithPrivateSet type:kotlin.reflect.KMutableProperty1<.C, kotlin.Int> visibility:private [final,static] + FIELD PROPERTY_BACKING_FIELD name:test_varWithPrivateSet type:kotlin.reflect.KProperty1<.C, kotlin.Int> visibility:private [final,static] EXPRESSION_BODY - PROPERTY_REFERENCE 'public final varWithPrivateSet: kotlin.Int [var]' field=null getter='public final fun (): kotlin.Int declared in .C' setter='private final fun (: kotlin.Int): kotlin.Unit declared in .C' type=kotlin.reflect.KMutableProperty1<.C, kotlin.Int> origin=null - FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> () returnType:kotlin.reflect.KMutableProperty1<.C, kotlin.Int> + PROPERTY_REFERENCE 'public final varWithPrivateSet: kotlin.Int [var]' field=null getter='public final fun (): kotlin.Int declared in .C' setter=null type=kotlin.reflect.KProperty1<.C, kotlin.Int> origin=null + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> () returnType:kotlin.reflect.KProperty1<.C, kotlin.Int> correspondingProperty: PROPERTY name:test_varWithPrivateSet visibility:public modality:FINAL [val] BLOCK_BODY - RETURN type=kotlin.Nothing from='public final fun (): kotlin.reflect.KMutableProperty1<.C, kotlin.Int> declared in ' - GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test_varWithPrivateSet type:kotlin.reflect.KMutableProperty1<.C, kotlin.Int> visibility:private [final,static]' type=kotlin.reflect.KMutableProperty1<.C, kotlin.Int> origin=null + RETURN type=kotlin.Nothing from='public final fun (): kotlin.reflect.KProperty1<.C, kotlin.Int> declared in ' + GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test_varWithPrivateSet type:kotlin.reflect.KProperty1<.C, kotlin.Int> visibility:private [final,static]' type=kotlin.reflect.KProperty1<.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<.C, kotlin.Int> visibility:private [final,static] + FIELD PROPERTY_BACKING_FIELD name:test_varWithProtectedSet type:kotlin.reflect.KProperty1<.C, kotlin.Int> visibility:private [final,static] EXPRESSION_BODY - PROPERTY_REFERENCE 'public final varWithProtectedSet: kotlin.Int [var]' field=null getter='public final fun (): kotlin.Int declared in .C' setter='protected final fun (: kotlin.Int): kotlin.Unit declared in .C' type=kotlin.reflect.KMutableProperty1<.C, kotlin.Int> origin=null - FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> () returnType:kotlin.reflect.KMutableProperty1<.C, kotlin.Int> + PROPERTY_REFERENCE 'public final varWithProtectedSet: kotlin.Int [var]' field=null getter='public final fun (): kotlin.Int declared in .C' setter=null type=kotlin.reflect.KProperty1<.C, kotlin.Int> origin=null + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:FINAL <> () returnType:kotlin.reflect.KProperty1<.C, kotlin.Int> correspondingProperty: PROPERTY name:test_varWithProtectedSet visibility:public modality:FINAL [val] BLOCK_BODY - RETURN type=kotlin.Nothing from='public final fun (): kotlin.reflect.KMutableProperty1<.C, kotlin.Int> declared in ' - GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test_varWithProtectedSet type:kotlin.reflect.KMutableProperty1<.C, kotlin.Int> visibility:private [final,static]' type=kotlin.reflect.KMutableProperty1<.C, kotlin.Int> origin=null + RETURN type=kotlin.Nothing from='public final fun (): kotlin.reflect.KProperty1<.C, kotlin.Int> declared in ' + GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test_varWithProtectedSet type:kotlin.reflect.KProperty1<.C, kotlin.Int> visibility:private [final,static]' type=kotlin.reflect.KProperty1<.C, kotlin.Int> origin=null