diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index fc5b0ecc297..afafb1b3378 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -141,6 +141,8 @@ public interface BindingContext { WritableSlice> DELEGATED_PROPERTY_RESOLVED_CALL = Slices.createSimpleSlice(); WritableSlice DELEGATED_PROPERTY_CALL = Slices.createSimpleSlice(); + WritableSlice> CREATE_DELEGATE_RESOLVED_CALL = Slices.createSimpleSlice(); + WritableSlice CREATE_DELEGATE_CALL = Slices.createSimpleSlice(); WritableSlice> DELEGATED_PROPERTY_PD_RESOLVED_CALL = Slices.createSimpleSlice(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatedPropertyResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatedPropertyResolver.kt index 85ab32d0f5a..c51a2b57692 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatedPropertyResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DelegatedPropertyResolver.kt @@ -18,6 +18,8 @@ package org.jetbrains.kotlin.resolve import com.google.common.collect.Lists import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors @@ -51,7 +53,8 @@ import org.jetbrains.kotlin.util.OperatorNameConventions class DelegatedPropertyResolver( private val builtIns: KotlinBuiltIns, private val fakeCallResolver: FakeCallResolver, - private val expressionTypingServices: ExpressionTypingServices + private val expressionTypingServices: ExpressionTypingServices, + private val languageVersionSettings: LanguageVersionSettings ) { fun resolvePropertyDelegate( @@ -81,31 +84,53 @@ class DelegatedPropertyResolver( delegateFunctionsScope = initializerScope } - val delegateType = resolveDelegateExpression(delegateExpression, property, variableDescriptor, initializerScope, trace, outerDataFlowInfo) - resolveDelegatedPropertyGetMethod(variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, outerDataFlowInfo) + val byExpressionType = resolveDelegateExpression(delegateExpression, property, variableDescriptor, initializerScope, trace, outerDataFlowInfo) + + resolveCreateDelegateMethod(variableDescriptor, delegateExpression, byExpressionType, trace, delegateFunctionsScope, outerDataFlowInfo) + val delegateType = getResolvedDelegateType(variableDescriptor, delegateExpression, byExpressionType, trace) + + resolveGetValueMethod(variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, outerDataFlowInfo) if (property.isVar) { - resolveDelegatedPropertySetMethod(variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, outerDataFlowInfo) + resolveSetValueMethod(variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, outerDataFlowInfo) } - resolveDelegatedPropertyPDMethod(variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, outerDataFlowInfo) + resolvePropertyDelegatedMethod(variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, outerDataFlowInfo) } - fun getDelegatedPropertyGetMethodReturnType( + private fun getResolvedDelegateType( variableDescriptor: VariableDescriptorWithAccessors, delegateExpression: KtExpression, - delegateType: KotlinType, + byExpressionType: KotlinType, + trace: BindingTrace + ): KotlinType { + val createDelegateResolvedCall = trace.bindingContext.get(CREATE_DELEGATE_RESOLVED_CALL, variableDescriptor) + if (createDelegateResolvedCall != null) { + return createDelegateResolvedCall.resultingDescriptor.returnType + ?: throw AssertionError("No return type fore 'createDelegate' of ${delegateExpression.text}") + } + return byExpressionType + } + + fun getGetValueMethodReturnType( + variableDescriptor: VariableDescriptorWithAccessors, + delegateExpression: KtExpression, + byExpressionType: KotlinType, trace: BindingTrace, delegateFunctionsScope: LexicalScope, dataFlowInfo: DataFlowInfo ): KotlinType? { - resolveDelegatedPropertyConventionMethod( - variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, dataFlowInfo, true - ) + resolveCreateDelegateMethod(variableDescriptor, delegateExpression, byExpressionType, trace, delegateFunctionsScope, dataFlowInfo) + val delegateType = getResolvedDelegateType(variableDescriptor, delegateExpression, byExpressionType, trace) + resolveGetSetValueMethod(variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, dataFlowInfo, true) + val resolvedCall = trace.bindingContext.get(DELEGATED_PROPERTY_RESOLVED_CALL, variableDescriptor.getter) return if (resolvedCall != null) resolvedCall.resultingDescriptor.returnType else null } - private fun resolveDelegatedPropertyGetMethod( + private val isOperatorCreateDelegateSupported: Boolean + get() = languageVersionSettings.supportsFeature(LanguageFeature.OperatorCreateDelegate) + + private fun resolveGetValueMethod( variableDescriptor: VariableDescriptorWithAccessors, delegateExpression: KtExpression, delegateType: KotlinType, @@ -113,9 +138,7 @@ class DelegatedPropertyResolver( delegateFunctionsScope: LexicalScope, dataFlowInfo: DataFlowInfo ) { - val returnType = getDelegatedPropertyGetMethodReturnType( - variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, dataFlowInfo - ) + val returnType = getGetValueMethodReturnType(variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, dataFlowInfo) val propertyType = variableDescriptor.type /* Do not check return type of get() method of delegate for properties with DeferredType because property type is taken from it */ @@ -127,7 +150,7 @@ class DelegatedPropertyResolver( } } - private fun resolveDelegatedPropertySetMethod( + private fun resolveSetValueMethod( variableDescriptor: VariableDescriptorWithAccessors, delegateExpression: KtExpression, delegateType: KotlinType, @@ -135,15 +158,15 @@ class DelegatedPropertyResolver( delegateFunctionsScope: LexicalScope, dataFlowInfo: DataFlowInfo ) { - resolveDelegatedPropertyConventionMethod(variableDescriptor, delegateExpression, delegateType, trace, - delegateFunctionsScope, dataFlowInfo, false) + resolveGetSetValueMethod(variableDescriptor, delegateExpression, delegateType, trace, + delegateFunctionsScope, dataFlowInfo, false) } - private fun createExpressionForProperty(psiFactory: KtPsiFactory): KtExpression { - return psiFactory.createExpression("null as ${KotlinBuiltIns.FQ_NAMES.kProperty.asSingleFqName().asString()}<*>") + private fun KtPsiFactory.createExpressionForProperty(): KtExpression { + return createExpression("null as ${KotlinBuiltIns.FQ_NAMES.kProperty.asSingleFqName().asString()}<*>") } - private fun resolveDelegatedPropertyPDMethod( + private fun resolvePropertyDelegatedMethod( variableDescriptor: VariableDescriptorWithAccessors, delegateExpression: KtExpression, delegateType: KotlinType, @@ -155,7 +178,7 @@ class DelegatedPropertyResolver( val context = ExpressionTypingContext.newContext(traceToResolvePDMethod, delegateFunctionsScope, dataFlowInfo, TypeUtils.NO_EXPECTED_TYPE) val psiFactory = KtPsiFactory(delegateExpression) - val arguments = listOf(createExpressionForProperty(psiFactory)) + val arguments = listOf(psiFactory.createExpressionForProperty()) val receiver = ExpressionReceiver.create(delegateExpression, delegateType, trace.bindingContext) val resolutionResult = fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, @@ -180,7 +203,7 @@ class DelegatedPropertyResolver( } /* Resolve getValue() or setValue() methods from delegate */ - private fun resolveDelegatedPropertyConventionMethod( + private fun resolveGetSetValueMethod( propertyDescriptor: VariableDescriptorWithAccessors, delegateExpression: KtExpression, delegateType: KotlinType, @@ -194,34 +217,26 @@ class DelegatedPropertyResolver( if (trace.bindingContext.get(DELEGATED_PROPERTY_CALL, accessor) != null) return - val functionResults = getDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, - delegateFunctionsScope, dataFlowInfo, isGet, true) - val call = trace.bindingContext.get(DELEGATED_PROPERTY_CALL, accessor) - ?: throw AssertionError("'getDelegatedPropertyConventionMethod' didn't record a call") + val functionResults = getGetSetValueMethod( + propertyDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, dataFlowInfo, + isGet = isGet, isComplete = true + ) if (!functionResults.isSuccess) { - val expectedFunction = renderCall(call, trace.bindingContext) - when { - functionResults.isSingleResult || - functionResults.isIncomplete || - functionResults.resultCode == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES -> - trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE.on(delegateExpression, expectedFunction, functionResults.resultingCalls)) - functionResults.isAmbiguity -> - trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY.on(delegateExpression, expectedFunction, functionResults.resultingCalls)) - else -> - trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType)) - } + val call = trace.bindingContext.get(DELEGATED_PROPERTY_CALL, accessor) + ?: throw AssertionError("'getDelegatedPropertyConventionMethod' didn't record a call") + reportDelegateOperatorResolutionError(trace, call, functionResults, delegateExpression, delegateType) return } val resultingDescriptor = functionResults.resultingDescriptor - val resultingCall = functionResults.resultingCall - val declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor) - if (declaration is KtProperty) { - val delegate = declaration.delegate - if (delegate != null) { - if (!resultingDescriptor.isOperator) { + + if (!resultingDescriptor.isOperator) { + val declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor) + if (declaration is KtProperty) { + val delegate = declaration.delegate + if (delegate != null) { val byKeyword = delegate.byKeywordNode.psi OperatorCallChecker.report(byKeyword, resultingDescriptor, trace) } @@ -231,8 +246,61 @@ class DelegatedPropertyResolver( trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, resultingCall) } + private fun reportDelegateOperatorResolutionError( + trace: BindingTrace, + delegateOperatorCall: Call, + delegateOperatorResults: OverloadResolutionResults, + delegateExpression: KtExpression, + delegateType: KotlinType, + operatorRequired: Boolean = true + ) { + val expectedFunction = renderCall(delegateOperatorCall, trace.bindingContext) + + when { + delegateOperatorResults.isSingleResult || + delegateOperatorResults.isIncomplete || + delegateOperatorResults.resultCode == OverloadResolutionResults.Code.MANY_FAILED_CANDIDATES -> + trace.report(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE.on(delegateExpression, expectedFunction, delegateOperatorResults.resultingCalls)) + delegateOperatorResults.isAmbiguity -> + trace.report(DELEGATE_SPECIAL_FUNCTION_AMBIGUITY.on(delegateExpression, expectedFunction, delegateOperatorResults.resultingCalls)) + else -> + if (operatorRequired) trace.report(DELEGATE_SPECIAL_FUNCTION_MISSING.on(delegateExpression, expectedFunction, delegateType)) + } + } + + private fun resolveCreateDelegateMethod( + propertyDescriptor: VariableDescriptorWithAccessors, + byExpression: KtExpression, + byExpressionType: KotlinType, + trace: BindingTrace, + delegateFunctionsScope: LexicalScope, + dataFlowInfo: DataFlowInfo + ) { + if (!isOperatorCreateDelegateSupported) return + if (trace.bindingContext.get(BindingContext.CREATE_DELEGATE_CALL, propertyDescriptor) != null) return + + val createDelegateResults = getCreateDelegateMethod(propertyDescriptor, byExpression, byExpressionType, + trace, delegateFunctionsScope, dataFlowInfo) + if (!createDelegateResults.isSuccess) { + val call = trace.bindingContext.get(BindingContext.CREATE_DELEGATE_CALL, propertyDescriptor) + ?: throw AssertionError("'getDelegatedPropertyConventionMethod' didn't record a call") + reportDelegateOperatorResolutionError(trace, call, createDelegateResults, byExpression, byExpressionType, + operatorRequired = false) + return + } + + val resultingDescriptor = createDelegateResults.resultingDescriptor + if (!resultingDescriptor.isOperator) { + // TODO resolved 'createDelegate' function, which is not an operator - warning? + return + } + + val resultingCall = createDelegateResults.resultingCall + trace.record(CREATE_DELEGATE_RESOLVED_CALL, propertyDescriptor, resultingCall) + } + /* Resolve getValue() or setValue() methods from delegate */ - private fun getDelegatedPropertyConventionMethod( + private fun getGetSetValueMethod( propertyDescriptor: VariableDescriptorWithAccessors, delegateExpression: KtExpression, delegateType: KotlinType, @@ -257,7 +325,7 @@ class DelegatedPropertyResolver( val arguments = Lists.newArrayList() val psiFactory = KtPsiFactory(delegateExpression) arguments.add(psiFactory.createExpression(if (hasThis) "this" else "null")) - arguments.add(createExpressionForProperty(psiFactory)) + arguments.add(psiFactory.createExpressionForProperty()) if (!isGet) { val fakeArgument = createFakeExpressionOfType(delegateExpression.project, trace, @@ -277,6 +345,34 @@ class DelegatedPropertyResolver( return resolutionResult.second } + private fun getCreateDelegateMethod( + propertyDescriptor: VariableDescriptorWithAccessors, + delegateExpression: KtExpression, + delegateExpressionType: KotlinType, + trace: BindingTrace, + delegateFunctionsScope: LexicalScope, + dataFlowInfo: DataFlowInfo + ): OverloadResolutionResults { + val expectedType = TypeUtils.NO_EXPECTED_TYPE + val context = ExpressionTypingContext.newContext(trace, delegateFunctionsScope, dataFlowInfo, expectedType) + val propertyHasReceiver = propertyDescriptor.extensionReceiverParameter != null || + propertyDescriptor.dispatchReceiverParameter != null + val arguments = KtPsiFactory(delegateExpression).run { + listOf( + createExpression(if (propertyHasReceiver) "this" else "null"), + createExpressionForProperty() + ) + } + val functionName = OperatorNameConventions.CREATE_DELEGATE + val receiver = ExpressionReceiver.create(delegateExpression, delegateExpressionType, trace.bindingContext) + + val (createDelegateCall, createDelegateResolutionResult) = + fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, functionName, delegateExpression) + trace.record(BindingContext.CREATE_DELEGATE_CALL, propertyDescriptor, createDelegateCall) + + return createDelegateResolutionResult + } + //TODO: diagnostics rendering does not belong here private fun renderCall(call: Call, context: BindingContext): String { val calleeExpression = call.calleeExpression @@ -303,15 +399,16 @@ class DelegatedPropertyResolver( ): KotlinType { val traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property") val calleeExpression = delegateExpression.getCalleeExpressionIfAny() - val completer = createConstraintSystemCompleter( - property, variableDescriptor, delegateExpression, scopeForDelegate, trace, dataFlowInfo - ) - if (calleeExpression != null) { - traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, calleeExpression, completer) + val completer = createConstraintSystemCompleter(property, variableDescriptor, delegateExpression, scopeForDelegate, trace, dataFlowInfo) + + calleeExpression?.let { + traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, it, completer) } - val delegateType = expressionTypingServices.safeGetType(scopeForDelegate, delegateExpression, NO_EXPECTED_TYPE, - dataFlowInfo, traceToResolveDelegatedProperty) - traceToResolveDelegatedProperty.commit({ slice, key -> slice !== CONSTRAINT_SYSTEM_COMPLETER }, true) + + val delegateType = expressionTypingServices.safeGetType(scopeForDelegate, delegateExpression, NO_EXPECTED_TYPE, dataFlowInfo, traceToResolveDelegatedProperty) + + traceToResolveDelegatedProperty.commit({ slice, _ -> slice !== CONSTRAINT_SYSTEM_COMPLETER }, true) + return delegateType } @@ -334,21 +431,23 @@ class DelegatedPropertyResolver( ?: throw AssertionError("No substitutor in the system for call: " + resolvedCall.call) val traceToResolveConventionMethods = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property convention methods") - val getMethodResults = getDelegatedPropertyConventionMethod( - variableDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, delegateFunctionsScope, - dataFlowInfo, true, false - ) - if (conventionMethodFound(getMethodResults)) { - val descriptor = getMethodResults.resultingDescriptor - val returnTypeOfGetMethod = descriptor.returnType - if (returnTypeOfGetMethod != null && !TypeUtils.noExpectedType(expectedType)) { - val returnTypeInSystem = typeVariableSubstitutor.substitute(returnTypeOfGetMethod, Variance.INVARIANT) + val delegateType = getDelegateType(returnType, constraintSystem, typeVariableSubstitutor, traceToResolveConventionMethods) + + val getValueResults = getGetSetValueMethod( + variableDescriptor, delegateExpression, delegateType, traceToResolveConventionMethods, delegateFunctionsScope, dataFlowInfo, + isGet = true, isComplete = false + ) + if (conventionMethodFound(getValueResults)) { + val getValueDescriptor = getValueResults.resultingDescriptor + val getValueReturnType = getValueDescriptor.returnType + if (getValueReturnType != null && !TypeUtils.noExpectedType(expectedType)) { + val returnTypeInSystem = typeVariableSubstitutor.substitute(getValueReturnType, Variance.INVARIANT) if (returnTypeInSystem != null) { constraintSystem.addSubtypeConstraint(returnTypeInSystem, expectedType, FROM_COMPLETER.position()) } } - addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, descriptor) + addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, getValueDescriptor) } if (!variableDescriptor.isVar) return @@ -357,29 +456,49 @@ class DelegatedPropertyResolver( // But if the type isn't known yet, the constraint shouldn't be added (we try to infer the type of 'v' here as well). if (variableDescriptor.returnType is DeferredType) return - val setMethodResults = getDelegatedPropertyConventionMethod( - variableDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, delegateFunctionsScope, - dataFlowInfo, false, false + val setValueResults = getGetSetValueMethod( + variableDescriptor, delegateExpression, delegateType, traceToResolveConventionMethods, delegateFunctionsScope, dataFlowInfo, + isGet = false, isComplete = false ) - - if (conventionMethodFound(setMethodResults)) { - val descriptor = setMethodResults.resultingDescriptor - val valueParameters = descriptor.valueParameters - if (valueParameters.size == 3) { - val valueParameterForThis = valueParameters[2] - + if (conventionMethodFound(setValueResults)) { + val setValueDescriptor = setValueResults.resultingDescriptor + val setValueParameters = setValueDescriptor.valueParameters + if (setValueParameters.size == 3) { if (!noExpectedType(expectedType)) { - constraintSystem.addSubtypeConstraint( - expectedType, - typeVariableSubstitutor.substitute(valueParameterForThis.type, Variance.INVARIANT), - FROM_COMPLETER.position() - ) + val thisParameterType = setValueParameters[2].type + val substitutedThisParameterType = typeVariableSubstitutor.substitute(thisParameterType, Variance.INVARIANT) + constraintSystem.addSubtypeConstraint(expectedType, substitutedThisParameterType, FROM_COMPLETER.position()) } - addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, descriptor) + addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, setValueDescriptor) } } } + private fun getDelegateType( + byExpressionType: KotlinType, + constraintSystem: ConstraintSystem.Builder, + typeVariableSubstitutor: TypeSubstitutor, + traceToResolveConventionMethods: TemporaryBindingTrace + ): KotlinType { + if (isOperatorCreateDelegateSupported) { + val createDelegateResults = getCreateDelegateMethod( + variableDescriptor, delegateExpression, byExpressionType, + traceToResolveConventionMethods, delegateFunctionsScope, dataFlowInfo + ) + if (conventionMethodFound(createDelegateResults)) { + val createDelegateDescriptor = createDelegateResults.resultingDescriptor + val createDelegateReturnType = createDelegateDescriptor.returnType + if (createDelegateDescriptor.isOperator) { + addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, createDelegateDescriptor) + return createDelegateReturnType + ?: throw AssertionError("No return type fore 'createDelegate' of ${delegateExpression.text}") + } + } + } + + return byExpressionType + } + private fun conventionMethodFound(results: OverloadResolutionResults): Boolean = results.isSuccess || results.isSingleResult && results.resultCode == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OperatorModifierChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OperatorModifierChecker.kt index 7596e3d5c0f..13da0f3a6ee 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/OperatorModifierChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/OperatorModifierChecker.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.resolve +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.DeclarationDescriptor @@ -42,22 +43,17 @@ object OperatorModifierChecker { val checkResult = OperatorChecks.check(functionDescriptor) if (checkResult.isSuccess) { - val shouldUseOperatorRem = languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem) when (functionDescriptor.name) { - in COROUTINE_OPERATOR_NAMES -> { - if (!languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) { - diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, LanguageFeature.Coroutines)) - } - } - - in REM_TO_MOD_OPERATION_NAMES.keys -> { - if (!shouldUseOperatorRem) { - diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, LanguageFeature.OperatorRem)) - } - } + in COROUTINE_OPERATOR_NAMES -> + checkSupportsFeature(LanguageFeature.Coroutines, languageVersionSettings, diagnosticHolder, modifier) + in REM_TO_MOD_OPERATION_NAMES.keys -> + checkSupportsFeature(LanguageFeature.OperatorRem, languageVersionSettings, diagnosticHolder, modifier) + OperatorNameConventions.CREATE_DELEGATE -> + checkSupportsFeature(LanguageFeature.OperatorCreateDelegate, languageVersionSettings, diagnosticHolder, modifier) } - if (functionDescriptor.name in REM_TO_MOD_OPERATION_NAMES.values && shouldUseOperatorRem) { + if (functionDescriptor.name in REM_TO_MOD_OPERATION_NAMES.values + && languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem)) { val newNameConvention = REM_TO_MOD_OPERATION_NAMES.inverse()[functionDescriptor.name] diagnosticHolder.report(Errors.DEPRECATED_BINARY_MOD.on(modifier, functionDescriptor, newNameConvention!!.asString())) } @@ -69,6 +65,12 @@ object OperatorModifierChecker { diagnosticHolder.report(Errors.INAPPLICABLE_OPERATOR_MODIFIER.on(modifier, errorDescription)) } + + private fun checkSupportsFeature(feature: LanguageFeature, languageVersionSettings: LanguageVersionSettings, diagnosticHolder: DiagnosticSink, modifier: PsiElement) { + if (!languageVersionSettings.supportsFeature(feature)) { + diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, feature)) + } + } } private val COROUTINE_OPERATOR_NAMES = diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeAndInitializerResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeAndInitializerResolver.kt index 7da76b59a2b..9836b42dab6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeAndInitializerResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/VariableTypeAndInitializerResolver.kt @@ -140,7 +140,7 @@ class VariableTypeAndInitializerResolver( delegateExpression, property, variableDescriptor, scopeForInitializer, trace, dataFlowInfo) val delegateFunctionsScope = ScopeUtils.makeScopeForDelegateConventionFunctions(scopeForInitializer, variableDescriptor) - val getterReturnType = delegatedPropertyResolver.getDelegatedPropertyGetMethodReturnType( + val getterReturnType = delegatedPropertyResolver.getGetValueMethodReturnType( variableDescriptor, delegateExpression, type, trace, delegateFunctionsScope, dataFlowInfo ) diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/genericCreateDelegate.kt b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/genericCreateDelegate.kt new file mode 100644 index 00000000000..bf40701f3ad --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/genericCreateDelegate.kt @@ -0,0 +1,13 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class Cell(val value: V) + +class GenericDelegate(val value: V) + +operator fun T.createDelegate(a: Any?, p: Any?) = GenericDelegate(this) + +operator fun GenericDelegate.getValue(a: Any?, p: Any?) = Cell(value) + +val test1: Cell by "OK" +val test2: Cell by "OK" +val test3 by "OK" \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/genericCreateDelegate.txt b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/genericCreateDelegate.txt new file mode 100644 index 00000000000..a27190dbbc6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/genericCreateDelegate.txt @@ -0,0 +1,23 @@ +package + +public val test1: Cell +public val test2: Cell +public val test3: Cell +public operator fun T.createDelegate(/*0*/ a: kotlin.Any?, /*1*/ p: kotlin.Any?): GenericDelegate +public operator fun GenericDelegate.getValue(/*0*/ a: kotlin.Any?, /*1*/ p: kotlin.Any?): Cell + +public final class Cell { + public constructor Cell(/*0*/ value: V) + public final val value: V + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class GenericDelegate { + public constructor GenericDelegate(/*0*/ value: V) + public final val value: V + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/noOperatorModifierOnCreateDelegate.kt b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/noOperatorModifierOnCreateDelegate.kt new file mode 100644 index 00000000000..ad88012409e --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/noOperatorModifierOnCreateDelegate.kt @@ -0,0 +1,17 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +import kotlin.reflect.KProperty + +class StringDelegate(val s: String) { + operator fun getValue(a: Any?, p: KProperty<*>): Int = 42 +} + +// NB no operator +fun String.createDelegate(a: Any?, p: KProperty<*>) = StringDelegate(this) + +operator fun String.getValue(a: Any?, p: KProperty<*>) = this + +val test1: String by "OK" +val test2: Int by "OK" +val test3 by "OK" + diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/noOperatorModifierOnCreateDelegate.txt b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/noOperatorModifierOnCreateDelegate.txt new file mode 100644 index 00000000000..1a8513e6904 --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/noOperatorModifierOnCreateDelegate.txt @@ -0,0 +1,16 @@ +package + +public val test1: kotlin.String +public val test2: kotlin.Int +public val test3: kotlin.String +public fun kotlin.String.createDelegate(/*0*/ a: kotlin.Any?, /*1*/ p: kotlin.reflect.KProperty<*>): StringDelegate +public operator fun kotlin.String.getValue(/*0*/ a: kotlin.Any?, /*1*/ p: kotlin.reflect.KProperty<*>): kotlin.String + +public final class StringDelegate { + public constructor StringDelegate(/*0*/ s: kotlin.String) + public final val s: kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ a: kotlin.Any?, /*1*/ p: kotlin.reflect.KProperty<*>): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/simpleCreateDelegate.kt b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/simpleCreateDelegate.kt new file mode 100644 index 00000000000..d3adf675432 --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/simpleCreateDelegate.kt @@ -0,0 +1,10 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +import kotlin.reflect.KProperty + +operator fun String.createDelegate(a: Any?, p: KProperty<*>) = this +operator fun String.getValue(a: Any?, p: KProperty<*>) = this + +val test1: String by "OK" + +val test2 by "OK" \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/simpleCreateDelegate.txt b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/simpleCreateDelegate.txt new file mode 100644 index 00000000000..14fdf89b94d --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/simpleCreateDelegate.txt @@ -0,0 +1,6 @@ +package + +public val test1: kotlin.String +public val test2: kotlin.String +public operator fun kotlin.String.createDelegate(/*0*/ a: kotlin.Any?, /*1*/ p: kotlin.reflect.KProperty<*>): kotlin.String +public operator fun kotlin.String.getValue(/*0*/ a: kotlin.Any?, /*1*/ p: kotlin.reflect.KProperty<*>): kotlin.String diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/unsupportedOperatorCreateDelegate.kt b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/unsupportedOperatorCreateDelegate.kt new file mode 100644 index 00000000000..a920d0e2685 --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/unsupportedOperatorCreateDelegate.kt @@ -0,0 +1,14 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// !LANGUAGE: -OperatorCreateDelegate + +class WrongDelegate(val x: Int) { + operator fun getValue(thisRef: Any?, prop: Any): Int = x +} + +operator fun String.createDelegate(thisRef: Any?, prop: Any) = WrongDelegate(this.length) + +operator fun String.getValue(thisRef: Any?, prop: Any) = this + +val test1: String by "OK" +val test2: Int by "OK" +val test3 by "OK" \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/unsupportedOperatorCreateDelegate.txt b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/unsupportedOperatorCreateDelegate.txt new file mode 100644 index 00000000000..1b5cc65f256 --- /dev/null +++ b/compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/unsupportedOperatorCreateDelegate.txt @@ -0,0 +1,16 @@ +package + +public val test1: kotlin.String +public val test2: kotlin.Int +public val test3: kotlin.String +public operator fun kotlin.String.createDelegate(/*0*/ thisRef: kotlin.Any?, /*1*/ prop: kotlin.Any): WrongDelegate +public operator fun kotlin.String.getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ prop: kotlin.Any): kotlin.String + +public final class WrongDelegate { + public constructor WrongDelegate(/*0*/ x: kotlin.Int) + public final val x: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ thisRef: kotlin.Any?, /*1*/ prop: kotlin.Any): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 4ba5ac7d595..d745d8c64b0 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -5758,6 +5758,39 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("compiler/testData/diagnostics/tests/delegatedProperty/createDelegate") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CreateDelegate extends AbstractDiagnosticsTest { + public void testAllFilesPresentInCreateDelegate() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/delegatedProperty/createDelegate"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("genericCreateDelegate.kt") + public void testGenericCreateDelegate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/genericCreateDelegate.kt"); + doTest(fileName); + } + + @TestMetadata("noOperatorModifierOnCreateDelegate.kt") + public void testNoOperatorModifierOnCreateDelegate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/noOperatorModifierOnCreateDelegate.kt"); + doTest(fileName); + } + + @TestMetadata("simpleCreateDelegate.kt") + public void testSimpleCreateDelegate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/simpleCreateDelegate.kt"); + doTest(fileName); + } + + @TestMetadata("unsupportedOperatorCreateDelegate.kt") + public void testUnsupportedOperatorCreateDelegate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/delegatedProperty/createDelegate/unsupportedOperatorCreateDelegate.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/tests/delegatedProperty/inference") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 38385f7647a..a85ba7ad07a 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -36,6 +36,7 @@ enum class LanguageFeature(val sinceVersion: LanguageVersion?) { DivisionByZeroInConstantExpressions(KOTLIN_1_1), InlineConstVals(KOTLIN_1_1), OperatorRem(KOTLIN_1_1), + OperatorCreateDelegate(KOTLIN_1_1), // Experimental features MultiPlatformProjects(null),