'createDelegate' operator convention:

- resolution & inference
- additional diagnostic for language feature support
This commit is contained in:
Dmitry Petrov
2016-12-05 14:20:37 +03:00
committed by Stanislav Erokhin
parent d3ab0f066e
commit e2ba288323
14 changed files with 365 additions and 93 deletions
@@ -141,6 +141,8 @@ public interface BindingContext {
WritableSlice<VariableAccessorDescriptor, ResolvedCall<FunctionDescriptor>> DELEGATED_PROPERTY_RESOLVED_CALL = Slices.createSimpleSlice(); WritableSlice<VariableAccessorDescriptor, ResolvedCall<FunctionDescriptor>> DELEGATED_PROPERTY_RESOLVED_CALL = Slices.createSimpleSlice();
WritableSlice<VariableAccessorDescriptor, Call> DELEGATED_PROPERTY_CALL = Slices.createSimpleSlice(); WritableSlice<VariableAccessorDescriptor, Call> DELEGATED_PROPERTY_CALL = Slices.createSimpleSlice();
WritableSlice<VariableDescriptorWithAccessors, ResolvedCall<FunctionDescriptor>> CREATE_DELEGATE_RESOLVED_CALL = Slices.createSimpleSlice();
WritableSlice<VariableDescriptorWithAccessors, Call> CREATE_DELEGATE_CALL = Slices.createSimpleSlice();
WritableSlice<VariableDescriptorWithAccessors, ResolvedCall<FunctionDescriptor>> DELEGATED_PROPERTY_PD_RESOLVED_CALL = Slices.createSimpleSlice(); WritableSlice<VariableDescriptorWithAccessors, ResolvedCall<FunctionDescriptor>> DELEGATED_PROPERTY_PD_RESOLVED_CALL = Slices.createSimpleSlice();
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.resolve
import com.google.common.collect.Lists import com.google.common.collect.Lists
import org.jetbrains.kotlin.builtins.KotlinBuiltIns 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.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
@@ -51,7 +53,8 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
class DelegatedPropertyResolver( class DelegatedPropertyResolver(
private val builtIns: KotlinBuiltIns, private val builtIns: KotlinBuiltIns,
private val fakeCallResolver: FakeCallResolver, private val fakeCallResolver: FakeCallResolver,
private val expressionTypingServices: ExpressionTypingServices private val expressionTypingServices: ExpressionTypingServices,
private val languageVersionSettings: LanguageVersionSettings
) { ) {
fun resolvePropertyDelegate( fun resolvePropertyDelegate(
@@ -81,31 +84,53 @@ class DelegatedPropertyResolver(
delegateFunctionsScope = initializerScope delegateFunctionsScope = initializerScope
} }
val delegateType = resolveDelegateExpression(delegateExpression, property, variableDescriptor, initializerScope, trace, outerDataFlowInfo) val byExpressionType = resolveDelegateExpression(delegateExpression, property, variableDescriptor, initializerScope, trace, outerDataFlowInfo)
resolveDelegatedPropertyGetMethod(variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, 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) { 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, variableDescriptor: VariableDescriptorWithAccessors,
delegateExpression: KtExpression, 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, trace: BindingTrace,
delegateFunctionsScope: LexicalScope, delegateFunctionsScope: LexicalScope,
dataFlowInfo: DataFlowInfo dataFlowInfo: DataFlowInfo
): KotlinType? { ): KotlinType? {
resolveDelegatedPropertyConventionMethod( resolveCreateDelegateMethod(variableDescriptor, delegateExpression, byExpressionType, trace, delegateFunctionsScope, dataFlowInfo)
variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, dataFlowInfo, true 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) val resolvedCall = trace.bindingContext.get(DELEGATED_PROPERTY_RESOLVED_CALL, variableDescriptor.getter)
return if (resolvedCall != null) resolvedCall.resultingDescriptor.returnType else null 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, variableDescriptor: VariableDescriptorWithAccessors,
delegateExpression: KtExpression, delegateExpression: KtExpression,
delegateType: KotlinType, delegateType: KotlinType,
@@ -113,9 +138,7 @@ class DelegatedPropertyResolver(
delegateFunctionsScope: LexicalScope, delegateFunctionsScope: LexicalScope,
dataFlowInfo: DataFlowInfo dataFlowInfo: DataFlowInfo
) { ) {
val returnType = getDelegatedPropertyGetMethodReturnType( val returnType = getGetValueMethodReturnType(variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, dataFlowInfo)
variableDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, dataFlowInfo
)
val propertyType = variableDescriptor.type 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 */ /* 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, variableDescriptor: VariableDescriptorWithAccessors,
delegateExpression: KtExpression, delegateExpression: KtExpression,
delegateType: KotlinType, delegateType: KotlinType,
@@ -135,15 +158,15 @@ class DelegatedPropertyResolver(
delegateFunctionsScope: LexicalScope, delegateFunctionsScope: LexicalScope,
dataFlowInfo: DataFlowInfo dataFlowInfo: DataFlowInfo
) { ) {
resolveDelegatedPropertyConventionMethod(variableDescriptor, delegateExpression, delegateType, trace, resolveGetSetValueMethod(variableDescriptor, delegateExpression, delegateType, trace,
delegateFunctionsScope, dataFlowInfo, false) delegateFunctionsScope, dataFlowInfo, false)
} }
private fun createExpressionForProperty(psiFactory: KtPsiFactory): KtExpression { private fun KtPsiFactory.createExpressionForProperty(): KtExpression {
return psiFactory.createExpression("null as ${KotlinBuiltIns.FQ_NAMES.kProperty.asSingleFqName().asString()}<*>") return createExpression("null as ${KotlinBuiltIns.FQ_NAMES.kProperty.asSingleFqName().asString()}<*>")
} }
private fun resolveDelegatedPropertyPDMethod( private fun resolvePropertyDelegatedMethod(
variableDescriptor: VariableDescriptorWithAccessors, variableDescriptor: VariableDescriptorWithAccessors,
delegateExpression: KtExpression, delegateExpression: KtExpression,
delegateType: KotlinType, delegateType: KotlinType,
@@ -155,7 +178,7 @@ class DelegatedPropertyResolver(
val context = ExpressionTypingContext.newContext(traceToResolvePDMethod, delegateFunctionsScope, dataFlowInfo, TypeUtils.NO_EXPECTED_TYPE) val context = ExpressionTypingContext.newContext(traceToResolvePDMethod, delegateFunctionsScope, dataFlowInfo, TypeUtils.NO_EXPECTED_TYPE)
val psiFactory = KtPsiFactory(delegateExpression) val psiFactory = KtPsiFactory(delegateExpression)
val arguments = listOf(createExpressionForProperty(psiFactory)) val arguments = listOf(psiFactory.createExpressionForProperty())
val receiver = ExpressionReceiver.create(delegateExpression, delegateType, trace.bindingContext) val receiver = ExpressionReceiver.create(delegateExpression, delegateType, trace.bindingContext)
val resolutionResult = fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments, val resolutionResult = fakeCallResolver.makeAndResolveFakeCallInContext(receiver, context, arguments,
@@ -180,7 +203,7 @@ class DelegatedPropertyResolver(
} }
/* Resolve getValue() or setValue() methods from delegate */ /* Resolve getValue() or setValue() methods from delegate */
private fun resolveDelegatedPropertyConventionMethod( private fun resolveGetSetValueMethod(
propertyDescriptor: VariableDescriptorWithAccessors, propertyDescriptor: VariableDescriptorWithAccessors,
delegateExpression: KtExpression, delegateExpression: KtExpression,
delegateType: KotlinType, delegateType: KotlinType,
@@ -194,34 +217,26 @@ class DelegatedPropertyResolver(
if (trace.bindingContext.get(DELEGATED_PROPERTY_CALL, accessor) != null) return if (trace.bindingContext.get(DELEGATED_PROPERTY_CALL, accessor) != null) return
val functionResults = getDelegatedPropertyConventionMethod(propertyDescriptor, delegateExpression, delegateType, trace, val functionResults = getGetSetValueMethod(
delegateFunctionsScope, dataFlowInfo, isGet, true) propertyDescriptor, delegateExpression, delegateType, trace, delegateFunctionsScope, dataFlowInfo,
val call = trace.bindingContext.get(DELEGATED_PROPERTY_CALL, accessor) isGet = isGet, isComplete = true
?: throw AssertionError("'getDelegatedPropertyConventionMethod' didn't record a call") )
if (!functionResults.isSuccess) { if (!functionResults.isSuccess) {
val expectedFunction = renderCall(call, trace.bindingContext) val call = trace.bindingContext.get(DELEGATED_PROPERTY_CALL, accessor)
when { ?: throw AssertionError("'getDelegatedPropertyConventionMethod' didn't record a call")
functionResults.isSingleResult || reportDelegateOperatorResolutionError(trace, call, functionResults, delegateExpression, delegateType)
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))
}
return return
} }
val resultingDescriptor = functionResults.resultingDescriptor val resultingDescriptor = functionResults.resultingDescriptor
val resultingCall = functionResults.resultingCall val resultingCall = functionResults.resultingCall
val declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor)
if (declaration is KtProperty) { if (!resultingDescriptor.isOperator) {
val delegate = declaration.delegate val declaration = DescriptorToSourceUtils.descriptorToDeclaration(propertyDescriptor)
if (delegate != null) { if (declaration is KtProperty) {
if (!resultingDescriptor.isOperator) { val delegate = declaration.delegate
if (delegate != null) {
val byKeyword = delegate.byKeywordNode.psi val byKeyword = delegate.byKeywordNode.psi
OperatorCallChecker.report(byKeyword, resultingDescriptor, trace) OperatorCallChecker.report(byKeyword, resultingDescriptor, trace)
} }
@@ -231,8 +246,61 @@ class DelegatedPropertyResolver(
trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, resultingCall) trace.record(DELEGATED_PROPERTY_RESOLVED_CALL, accessor, resultingCall)
} }
private fun reportDelegateOperatorResolutionError(
trace: BindingTrace,
delegateOperatorCall: Call,
delegateOperatorResults: OverloadResolutionResults<FunctionDescriptor>,
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 */ /* Resolve getValue() or setValue() methods from delegate */
private fun getDelegatedPropertyConventionMethod( private fun getGetSetValueMethod(
propertyDescriptor: VariableDescriptorWithAccessors, propertyDescriptor: VariableDescriptorWithAccessors,
delegateExpression: KtExpression, delegateExpression: KtExpression,
delegateType: KotlinType, delegateType: KotlinType,
@@ -257,7 +325,7 @@ class DelegatedPropertyResolver(
val arguments = Lists.newArrayList<KtExpression>() val arguments = Lists.newArrayList<KtExpression>()
val psiFactory = KtPsiFactory(delegateExpression) val psiFactory = KtPsiFactory(delegateExpression)
arguments.add(psiFactory.createExpression(if (hasThis) "this" else "null")) arguments.add(psiFactory.createExpression(if (hasThis) "this" else "null"))
arguments.add(createExpressionForProperty(psiFactory)) arguments.add(psiFactory.createExpressionForProperty())
if (!isGet) { if (!isGet) {
val fakeArgument = createFakeExpressionOfType(delegateExpression.project, trace, val fakeArgument = createFakeExpressionOfType(delegateExpression.project, trace,
@@ -277,6 +345,34 @@ class DelegatedPropertyResolver(
return resolutionResult.second return resolutionResult.second
} }
private fun getCreateDelegateMethod(
propertyDescriptor: VariableDescriptorWithAccessors,
delegateExpression: KtExpression,
delegateExpressionType: KotlinType,
trace: BindingTrace,
delegateFunctionsScope: LexicalScope,
dataFlowInfo: DataFlowInfo
): OverloadResolutionResults<FunctionDescriptor> {
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 //TODO: diagnostics rendering does not belong here
private fun renderCall(call: Call, context: BindingContext): String { private fun renderCall(call: Call, context: BindingContext): String {
val calleeExpression = call.calleeExpression val calleeExpression = call.calleeExpression
@@ -303,15 +399,16 @@ class DelegatedPropertyResolver(
): KotlinType { ): KotlinType {
val traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property") val traceToResolveDelegatedProperty = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property")
val calleeExpression = delegateExpression.getCalleeExpressionIfAny() val calleeExpression = delegateExpression.getCalleeExpressionIfAny()
val completer = createConstraintSystemCompleter( val completer = createConstraintSystemCompleter(property, variableDescriptor, delegateExpression, scopeForDelegate, trace, dataFlowInfo)
property, variableDescriptor, delegateExpression, scopeForDelegate, trace, dataFlowInfo
) calleeExpression?.let {
if (calleeExpression != null) { traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, it, completer)
traceToResolveDelegatedProperty.record(CONSTRAINT_SYSTEM_COMPLETER, calleeExpression, completer)
} }
val delegateType = expressionTypingServices.safeGetType(scopeForDelegate, delegateExpression, NO_EXPECTED_TYPE,
dataFlowInfo, traceToResolveDelegatedProperty) val delegateType = expressionTypingServices.safeGetType(scopeForDelegate, delegateExpression, NO_EXPECTED_TYPE, dataFlowInfo, traceToResolveDelegatedProperty)
traceToResolveDelegatedProperty.commit({ slice, key -> slice !== CONSTRAINT_SYSTEM_COMPLETER }, true)
traceToResolveDelegatedProperty.commit({ slice, _ -> slice !== CONSTRAINT_SYSTEM_COMPLETER }, true)
return delegateType return delegateType
} }
@@ -334,21 +431,23 @@ class DelegatedPropertyResolver(
?: throw AssertionError("No substitutor in the system for call: " + resolvedCall.call) ?: throw AssertionError("No substitutor in the system for call: " + resolvedCall.call)
val traceToResolveConventionMethods = TemporaryBindingTrace.create(trace, "Trace to resolve delegated property convention methods") 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 delegateType = getDelegateType(returnType, constraintSystem, typeVariableSubstitutor, traceToResolveConventionMethods)
val descriptor = getMethodResults.resultingDescriptor
val returnTypeOfGetMethod = descriptor.returnType val getValueResults = getGetSetValueMethod(
if (returnTypeOfGetMethod != null && !TypeUtils.noExpectedType(expectedType)) { variableDescriptor, delegateExpression, delegateType, traceToResolveConventionMethods, delegateFunctionsScope, dataFlowInfo,
val returnTypeInSystem = typeVariableSubstitutor.substitute(returnTypeOfGetMethod, Variance.INVARIANT) 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) { if (returnTypeInSystem != null) {
constraintSystem.addSubtypeConstraint(returnTypeInSystem, expectedType, FROM_COMPLETER.position()) constraintSystem.addSubtypeConstraint(returnTypeInSystem, expectedType, FROM_COMPLETER.position())
} }
} }
addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, descriptor) addConstraintForThisValue(constraintSystem, typeVariableSubstitutor, getValueDescriptor)
} }
if (!variableDescriptor.isVar) return 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). // 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 if (variableDescriptor.returnType is DeferredType) return
val setMethodResults = getDelegatedPropertyConventionMethod( val setValueResults = getGetSetValueMethod(
variableDescriptor, delegateExpression, returnType, traceToResolveConventionMethods, delegateFunctionsScope, variableDescriptor, delegateExpression, delegateType, traceToResolveConventionMethods, delegateFunctionsScope, dataFlowInfo,
dataFlowInfo, false, false isGet = false, isComplete = false
) )
if (conventionMethodFound(setValueResults)) {
if (conventionMethodFound(setMethodResults)) { val setValueDescriptor = setValueResults.resultingDescriptor
val descriptor = setMethodResults.resultingDescriptor val setValueParameters = setValueDescriptor.valueParameters
val valueParameters = descriptor.valueParameters if (setValueParameters.size == 3) {
if (valueParameters.size == 3) {
val valueParameterForThis = valueParameters[2]
if (!noExpectedType(expectedType)) { if (!noExpectedType(expectedType)) {
constraintSystem.addSubtypeConstraint( val thisParameterType = setValueParameters[2].type
expectedType, val substitutedThisParameterType = typeVariableSubstitutor.substitute(thisParameterType, Variance.INVARIANT)
typeVariableSubstitutor.substitute(valueParameterForThis.type, Variance.INVARIANT), constraintSystem.addSubtypeConstraint(expectedType, substitutedThisParameterType, FROM_COMPLETER.position())
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<FunctionDescriptor>): Boolean = private fun conventionMethodFound(results: OverloadResolutionResults<FunctionDescriptor>): Boolean =
results.isSuccess || results.isSuccess ||
results.isSingleResult && results.resultCode == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH results.isSingleResult && results.resultCode == OverloadResolutionResults.Code.SINGLE_CANDIDATE_ARGUMENT_MISMATCH
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.resolve package org.jetbrains.kotlin.resolve
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -42,22 +43,17 @@ object OperatorModifierChecker {
val checkResult = OperatorChecks.check(functionDescriptor) val checkResult = OperatorChecks.check(functionDescriptor)
if (checkResult.isSuccess) { if (checkResult.isSuccess) {
val shouldUseOperatorRem = languageVersionSettings.supportsFeature(LanguageFeature.OperatorRem)
when (functionDescriptor.name) { when (functionDescriptor.name) {
in COROUTINE_OPERATOR_NAMES -> { in COROUTINE_OPERATOR_NAMES ->
if (!languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) { checkSupportsFeature(LanguageFeature.Coroutines, languageVersionSettings, diagnosticHolder, modifier)
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, LanguageFeature.Coroutines)) in REM_TO_MOD_OPERATION_NAMES.keys ->
} checkSupportsFeature(LanguageFeature.OperatorRem, languageVersionSettings, diagnosticHolder, modifier)
} OperatorNameConventions.CREATE_DELEGATE ->
checkSupportsFeature(LanguageFeature.OperatorCreateDelegate, languageVersionSettings, diagnosticHolder, modifier)
in REM_TO_MOD_OPERATION_NAMES.keys -> {
if (!shouldUseOperatorRem) {
diagnosticHolder.report(Errors.UNSUPPORTED_FEATURE.on(modifier, LanguageFeature.OperatorRem))
}
}
} }
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] val newNameConvention = REM_TO_MOD_OPERATION_NAMES.inverse()[functionDescriptor.name]
diagnosticHolder.report(Errors.DEPRECATED_BINARY_MOD.on(modifier, functionDescriptor, newNameConvention!!.asString())) 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)) 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 = private val COROUTINE_OPERATOR_NAMES =
@@ -140,7 +140,7 @@ class VariableTypeAndInitializerResolver(
delegateExpression, property, variableDescriptor, scopeForInitializer, trace, dataFlowInfo) delegateExpression, property, variableDescriptor, scopeForInitializer, trace, dataFlowInfo)
val delegateFunctionsScope = ScopeUtils.makeScopeForDelegateConventionFunctions(scopeForInitializer, variableDescriptor) val delegateFunctionsScope = ScopeUtils.makeScopeForDelegateConventionFunctions(scopeForInitializer, variableDescriptor)
val getterReturnType = delegatedPropertyResolver.getDelegatedPropertyGetMethodReturnType( val getterReturnType = delegatedPropertyResolver.getGetValueMethodReturnType(
variableDescriptor, delegateExpression, type, trace, delegateFunctionsScope, dataFlowInfo variableDescriptor, delegateExpression, type, trace, delegateFunctionsScope, dataFlowInfo
) )
@@ -0,0 +1,13 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class Cell<out V>(val value: V)
class GenericDelegate<V>(val value: V)
operator fun <T> T.createDelegate(a: Any?, p: Any?) = GenericDelegate(this)
operator fun <W> GenericDelegate<W>.getValue(a: Any?, p: Any?) = Cell(value)
val test1: Cell<String> by "OK"
val test2: Cell<Any> by "OK"
val test3 by "OK"
@@ -0,0 +1,23 @@
package
public val test1: Cell<kotlin.String>
public val test2: Cell<kotlin.Any>
public val test3: Cell<kotlin.String>
public operator fun </*0*/ T> T.createDelegate(/*0*/ a: kotlin.Any?, /*1*/ p: kotlin.Any?): GenericDelegate<T>
public operator fun </*0*/ W> GenericDelegate<W>.getValue(/*0*/ a: kotlin.Any?, /*1*/ p: kotlin.Any?): Cell<W>
public final class Cell</*0*/ out V> {
public constructor Cell</*0*/ out V>(/*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</*0*/ V> {
public constructor GenericDelegate</*0*/ V>(/*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
}
@@ -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 <!DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH!>"OK"<!>
val test3 by "OK"
@@ -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
}
@@ -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"
@@ -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
@@ -0,0 +1,14 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// !LANGUAGE: -OperatorCreateDelegate
class WrongDelegate(val x: Int) {
operator fun getValue(thisRef: Any?, prop: Any): Int = x
}
<!UNSUPPORTED_FEATURE(only available since Kotlin 1.1: operator createDelegate)!>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 <!DELEGATE_SPECIAL_FUNCTION_RETURN_TYPE_MISMATCH!>"OK"<!>
val test3 by "OK"
@@ -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
}
@@ -5758,6 +5758,39 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName); 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") @TestMetadata("compiler/testData/diagnostics/tests/delegatedProperty/inference")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -36,6 +36,7 @@ enum class LanguageFeature(val sinceVersion: LanguageVersion?) {
DivisionByZeroInConstantExpressions(KOTLIN_1_1), DivisionByZeroInConstantExpressions(KOTLIN_1_1),
InlineConstVals(KOTLIN_1_1), InlineConstVals(KOTLIN_1_1),
OperatorRem(KOTLIN_1_1), OperatorRem(KOTLIN_1_1),
OperatorCreateDelegate(KOTLIN_1_1),
// Experimental features // Experimental features
MultiPlatformProjects(null), MultiPlatformProjects(null),