diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 10b0e76e758..6635071b166 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -3090,7 +3090,7 @@ public class ExpressionCodegen extends KtVisitor impleme PropertyReferenceCodegen codegen = new PropertyReferenceCodegen( state, parentCodegen, context.intoAnonymousClass(classDescriptor, this, OwnerKind.IMPLEMENTATION), - element, classBuilder, target, dispatchReceiver, receiverAsmType + element, classBuilder, variableDescriptor, target, dispatchReceiver, receiverAsmType ); codegen.generate(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.java index 381b9815106..da22a422ac3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.java @@ -140,14 +140,16 @@ public class JvmRuntimeTypes { } @NotNull - public KotlinType getSupertypeForPropertyReference(@NotNull VariableDescriptorWithAccessors descriptor, boolean isBound) { + public KotlinType getSupertypeForPropertyReference( + @NotNull VariableDescriptorWithAccessors descriptor, boolean isMutable, boolean isBound + ) { if (descriptor instanceof LocalVariableDescriptor) { - return (descriptor.isVar() ? mutableLocalVariableReference : localVariableReference).getDefaultType(); + return (isMutable ? mutableLocalVariableReference : localVariableReference).getDefaultType(); } int arity = (descriptor.getExtensionReceiverParameter() != null ? 1 : 0) + (descriptor.getDispatchReceiverParameter() != null ? 1 : 0) - (isBound ? 1 : 0); - return (descriptor.isVar() ? mutablePropertyReferences : propertyReferences).get(arity).getDefaultType(); + return (isMutable ? mutablePropertyReferences : propertyReferences).get(arity).getDefaultType(); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt index c061f2842d2..5a4a1a8f74f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyReferenceCodegen.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.codegen +import org.jetbrains.kotlin.builtins.ReflectionTypes import org.jetbrains.kotlin.codegen.AsmUtil.method import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.context.ClassContext @@ -46,6 +47,7 @@ class PropertyReferenceCodegen( context: ClassContext, expression: KtElement, classBuilder: ClassBuilder, + private val localVariableDescriptorForReference: VariableDescriptor, private val target: VariableDescriptor, dispatchReceiver: ReceiverValue?, private val receiverType: Type? // non-null for bound references @@ -177,7 +179,7 @@ class PropertyReferenceCodegen( value.put(OBJECT_TYPE, this) } - if (!target.isVar) return + if (!ReflectionTypes.isKMutablePropertyType(localVariableDescriptorForReference.type)) return val setterParameters = (getterParameters + arrayOf(OBJECT_TYPE)) generateAccessor(method("set", Type.VOID_TYPE, *setterParameters)) { value -> diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java index 4a4cd5a3b0c..f030d1e4197 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java @@ -301,8 +301,13 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { callableDescriptor = bindingContext.get(VARIABLE, expression); if (callableDescriptor == null) return; + //noinspection ConstantConditions supertypes = Collections.singleton( - runtimeTypes.getSupertypeForPropertyReference((PropertyDescriptor) target, receiverType != null) + runtimeTypes.getSupertypeForPropertyReference( + (PropertyDescriptor) target, + ReflectionTypes.Companion.isKMutablePropertyType(callableDescriptor.getReturnType()), + receiverType != null + ) ); } else { @@ -380,7 +385,8 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { if (delegate != null && descriptor instanceof VariableDescriptorWithAccessors) { VariableDescriptorWithAccessors variableDescriptor = (VariableDescriptorWithAccessors) descriptor; String name = inventAnonymousClassName(); - KotlinType supertype = runtimeTypes.getSupertypeForPropertyReference(variableDescriptor, /* bound = */ false); + KotlinType supertype = + runtimeTypes.getSupertypeForPropertyReference(variableDescriptor, variableDescriptor.isVar(), /* bound = */ false); ClassDescriptor classDescriptor = recordClassForCallable(delegate, variableDescriptor, Collections.singleton(supertype), name); recordClosure(classDescriptor, name); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt index 7e7060cc5b5..a633c09e521 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/callableReferences/CallableReferencesResolutionUtils.kt @@ -180,7 +180,8 @@ private fun createReflectionTypeForCallableDescriptor( reflectionTypes: ReflectionTypes, trace: BindingTrace?, reportOn: KtExpression?, - ignoreReceiver: Boolean + ignoreReceiver: Boolean, + scopeOwnerDescriptor: DeclarationDescriptor ): KotlinType? { if (descriptor is CallableMemberDescriptor && isMemberExtension(descriptor)) { if (reportOn != null) { @@ -209,7 +210,12 @@ private fun createReflectionTypeForCallableDescriptor( return reflectionTypes.getKFunctionType(Annotations.EMPTY, receiverType, valueParametersTypes, returnType) } is PropertyDescriptor -> { - reflectionTypes.getKPropertyType(Annotations.EMPTY, receiverType, descriptor.type, descriptor.isVar) + val mutable = descriptor.isVar && run { + val setter = descriptor.setter + // TODO: support private-to-this + setter == null || Visibilities.isVisible(null, setter, scopeOwnerDescriptor) + } + reflectionTypes.getKPropertyType(Annotations.EMPTY, receiverType, descriptor.type, mutable) } is VariableDescriptor -> { if (reportOn != null) { @@ -230,9 +236,10 @@ private fun isMemberExtension(descriptor: CallableMemberDescriptor): Boolean { fun getReflectionTypeForCandidateDescriptor( descriptor: CallableDescriptor, reflectionTypes: ReflectionTypes, - ignoreReceiver: Boolean + ignoreReceiver: Boolean, + scopeOwnerDescriptor: DeclarationDescriptor ): KotlinType? = - createReflectionTypeForCallableDescriptor(descriptor, null, reflectionTypes, null, null, ignoreReceiver) + createReflectionTypeForCallableDescriptor(descriptor, null, reflectionTypes, null, null, ignoreReceiver, scopeOwnerDescriptor) fun createReflectionTypeForResolvedCallableReference( reference: KtCallableReferenceExpression, @@ -243,7 +250,7 @@ fun createReflectionTypeForResolvedCallableReference( reflectionTypes: ReflectionTypes ): KotlinType? { val type = createReflectionTypeForCallableDescriptor( - descriptor, lhsType, reflectionTypes, context.trace, reference.callableReference, ignoreReceiver + descriptor, lhsType, reflectionTypes, context.trace, reference.callableReference, ignoreReceiver, context.scope.ownerDescriptor ) ?: return null when (descriptor) { is FunctionDescriptor -> { @@ -272,7 +279,8 @@ fun getResolvedCallableReferenceShapeType( overloadResolutionResults.isSingleResult -> OverloadResolutionResultsUtil.getResultingCall(overloadResolutionResults, context.contextDependency)?.let { call -> createReflectionTypeForCallableDescriptor( - call.resultingDescriptor, lhsType, reflectionTypes, context.trace, reference, reference.isEmptyLHS + call.resultingDescriptor, lhsType, reflectionTypes, context.trace, reference, reference.isEmptyLHS, + context.scope.ownerDescriptor ) } expectedTypeUnknown /* && overload resolution was ambiguous */ -> diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt index 62eade94064..60ebad81fe5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CandidateResolver.kt @@ -161,7 +161,8 @@ class CandidateResolver( val candidate = candidateCall.candidateDescriptor val candidateReflectionType = getReflectionTypeForCandidateDescriptor( candidate, reflectionTypes, - call.callElement.parent.let { it is KtCallableReferenceExpression && it.isEmptyLHS } + call.callElement.parent.let { it is KtCallableReferenceExpression && it.isEmptyLHS }, + scope.ownerDescriptor ) if (candidateReflectionType != null) { if (!KotlinTypeChecker.DEFAULT.isSubtypeOf(candidateReflectionType, expectedType)) { diff --git a/compiler/testData/codegen/box/callableReference/property/privateSetterInsideClass.kt b/compiler/testData/codegen/box/callableReference/property/privateSetterInsideClass.kt new file mode 100644 index 00000000000..8674d12a3a8 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/property/privateSetterInsideClass.kt @@ -0,0 +1,18 @@ +import kotlin.reflect.KMutableProperty + +class Bar(name: String) { + var foo: String = name + private set + + fun test() { + val p = Bar::foo + if (p !is KMutableProperty<*>) throw AssertionError("Fail: p is not a KMutableProperty") + p.set(this, "OK") + } +} + +fun box(): String { + val bar = Bar("Fail") + bar.test() + return bar.foo +} diff --git a/compiler/testData/codegen/box/callableReference/property/privateSetterOutsideClass.kt b/compiler/testData/codegen/box/callableReference/property/privateSetterOutsideClass.kt new file mode 100644 index 00000000000..3c8b5f8de62 --- /dev/null +++ b/compiler/testData/codegen/box/callableReference/property/privateSetterOutsideClass.kt @@ -0,0 +1,26 @@ +// See KT-12337 Reference to property with invisible setter should not be a KMutableProperty + +import kotlin.reflect.KProperty1 +import kotlin.reflect.KMutableProperty + +open class Bar(name: String) { + var foo: String = name + private set +} + +class Baz : Bar("") { + fun ref() = Bar::foo +} + +fun box(): String { + val p1: KProperty1 = Bar::foo + if (p1 is KMutableProperty<*>) return "Fail: p1 is a KMutableProperty" + + val p2 = Baz().ref() + if (p2 is KMutableProperty<*>) return "Fail: p2 is a KMutableProperty" + + val p3 = Bar("")::foo + if (p3 is KMutableProperty<*>) return "Fail: p3 is a KMutableProperty" + + return p1.get(Bar("OK")) +} diff --git a/compiler/testData/codegen/box/delegatedProperty/privateSetterKPropertyIsNotMutable.kt b/compiler/testData/codegen/box/delegatedProperty/privateSetterKPropertyIsNotMutable.kt new file mode 100644 index 00000000000..8b659237a76 --- /dev/null +++ b/compiler/testData/codegen/box/delegatedProperty/privateSetterKPropertyIsNotMutable.kt @@ -0,0 +1,34 @@ +// WITH_REFLECT + +import kotlin.reflect.* +import kotlin.reflect.jvm.* + +object Delegate { + operator fun getValue(thiz: My, property: KProperty<*>): String { + if (property !is KMutableProperty<*>) return "Fail: property is not a KMutableProperty" + property as KMutableProperty1 + + try { + property.set(thiz, "") + return "Fail: property.set should cause IllegalCallableAccessException" + } + catch (e: IllegalCallableAccessException) { + // OK + } + + property.accessible = true + property.set(thiz, "") + + return "OK" + } + + operator fun setValue(thiz: My, property: KProperty<*>, value: String) { + } +} + +class My { + var delegate: String by Delegate + private set +} + +fun box() = My().delegate diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 333ba83dc4e..82dad63426c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -1956,6 +1956,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("privateSetterInsideClass.kt") + public void testPrivateSetterInsideClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/property/privateSetterInsideClass.kt"); + doTest(fileName); + } + + @TestMetadata("privateSetterOutsideClass.kt") + public void testPrivateSetterOutsideClass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/property/privateSetterOutsideClass.kt"); + doTest(fileName); + } + @TestMetadata("simpleExtension.kt") public void testSimpleExtension() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/callableReference/property/simpleExtension.kt"); @@ -5116,6 +5128,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("privateSetterKPropertyIsNotMutable.kt") + public void testPrivateSetterKPropertyIsNotMutable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/privateSetterKPropertyIsNotMutable.kt"); + doTest(fileName); + } + @TestMetadata("privateVar.kt") public void testPrivateVar() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/delegatedProperty/privateVar.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 1bafe5e861c..4abbe3034cc 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -216,6 +216,9 @@ public abstract class KotlinBuiltIns { public final FqNameUnsafe kClass = reflect("KClass"); public final FqNameUnsafe kCallable = reflect("KCallable"); + public final FqNameUnsafe kMutableProperty0 = reflect("KMutableProperty0"); + public final FqNameUnsafe kMutableProperty1 = reflect("KMutableProperty1"); + public final FqNameUnsafe kMutableProperty2 = reflect("KMutableProperty2"); public final ClassId kProperty = ClassId.topLevel(reflect("KProperty").toSafe()); public final Map fqNameToPrimitiveType; diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt index b8f48d98ec7..b7ed08cf6ae 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/ReflectionTypes.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.scopes.MemberScope @@ -120,12 +121,19 @@ class ReflectionTypes(module: ModuleDescriptor) { type.isFunctionTypeOrSubtype || isKCallableType(type) private fun isKCallableType(type: KotlinType): Boolean = - isExactKCallableType(type) || + hasFqName(type.constructor, KotlinBuiltIns.FQ_NAMES.kCallable) || type.constructor.supertypes.any { isKCallableType(it) } - private fun isExactKCallableType(type: KotlinType): Boolean { - val descriptor = type.constructor.declarationDescriptor - return descriptor is ClassDescriptor && DescriptorUtils.getFqName(descriptor) == KotlinBuiltIns.FQ_NAMES.kCallable + fun isKMutablePropertyType(type: KotlinType): Boolean { + val typeConstructor = type.constructor + return hasFqName(typeConstructor, KotlinBuiltIns.FQ_NAMES.kMutableProperty0) || + hasFqName(typeConstructor, KotlinBuiltIns.FQ_NAMES.kMutableProperty1) || + hasFqName(typeConstructor, KotlinBuiltIns.FQ_NAMES.kMutableProperty2) + } + + private fun hasFqName(typeConstructor: TypeConstructor, fqName: FqNameUnsafe): Boolean { + val descriptor = typeConstructor.declarationDescriptor as? ClassDescriptor ?: return false + return descriptor.name == fqName.shortName() && DescriptorUtils.getFqName(descriptor) == fqName } fun createKPropertyStarType(module: ModuleDescriptor): KotlinType? { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt index 0cf6038525a..454561a2172 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt @@ -249,7 +249,7 @@ private fun MutableCollection.addLookupElementsForNullable(factor fun CallableDescriptor.callableReferenceType(resolutionFacade: ResolutionFacade): FuzzyType? { if (!CallType.CALLABLE_REFERENCE.descriptorKindFilter.accepts(this)) return null // not supported by callable references return getReflectionTypeForCandidateDescriptor( - this, resolutionFacade.getFrontendService(ReflectionTypes::class.java), false + this, resolutionFacade.getFrontendService(ReflectionTypes::class.java), false, resolutionFacade.moduleDescriptor )?.toFuzzyType(emptyList()) }