Allow augmented assignments on dynamic receivers

This commit is contained in:
Andrey Breslav
2014-11-27 09:19:50 +03:00
parent cc210c2950
commit 53bbf20b0d
5 changed files with 47 additions and 21 deletions
@@ -147,9 +147,12 @@ public class TaskPrioritizer {
) { ) {
addMembers(explicitReceiver, c, /*static=*/false, isExplicit); addMembers(explicitReceiver, c, /*static=*/false, isExplicit);
addCandidatesForDynamicReceiver(explicitReceiver, implicitReceivers, c, isExplicit); if (TypesPackage.isDynamic(explicitReceiver.getType())) {
addCandidatesForDynamicReceiver(explicitReceiver, implicitReceivers, c, isExplicit);
addExtensionCandidates(explicitReceiver, implicitReceivers, c, isExplicit); }
else {
addExtensionCandidates(explicitReceiver, implicitReceivers, c, isExplicit);
}
} }
private static <D extends CallableDescriptor, F extends D> void addExtensionCandidates( private static <D extends CallableDescriptor, F extends D> void addExtensionCandidates(
@@ -208,8 +211,6 @@ public class TaskPrioritizer {
@NotNull final TaskPrioritizerContext<D, F> c, @NotNull final TaskPrioritizerContext<D, F> c,
boolean isExplicit boolean isExplicit
) { ) {
if (!TypesPackage.isDynamic(explicitReceiver.getType())) return;
TaskPrioritizerContext<D, F> onlyDynamicReceivers = c.replaceCollectors(TasksPackage.onlyDynamicReceivers(c.callableDescriptorCollectors)); TaskPrioritizerContext<D, F> onlyDynamicReceivers = c.replaceCollectors(TasksPackage.onlyDynamicReceivers(c.callableDescriptorCollectors));
addExtensionCandidates(explicitReceiver, implicitReceivers, onlyDynamicReceivers, isExplicit); addExtensionCandidates(explicitReceiver, implicitReceivers, onlyDynamicReceivers, isExplicit);
@@ -18,7 +18,6 @@ package org.jetbrains.jet.lang.resolve.calls.tasks
import org.jetbrains.jet.lang.psi.Call import org.jetbrains.jet.lang.psi.Call
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.jet.lang.descriptors.annotations.Annotations import org.jetbrains.jet.lang.descriptors.annotations.Annotations
import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor
@@ -43,14 +42,13 @@ import org.jetbrains.jet.lang.resolve.calls.tasks.collectors.CallableDescriptorC
import org.jetbrains.jet.lang.resolve.scopes.JetScope import org.jetbrains.jet.lang.resolve.scopes.JetScope
import org.jetbrains.jet.lang.resolve.BindingTrace import org.jetbrains.jet.lang.resolve.BindingTrace
import org.jetbrains.jet.lang.resolve.calls.tasks.collectors.CallableDescriptorCollectors import org.jetbrains.jet.lang.resolve.calls.tasks.collectors.CallableDescriptorCollectors
import org.jetbrains.jet.lexer.JetToken
import org.jetbrains.jet.lang.psi.JetOperationReferenceExpression
import org.jetbrains.jet.lang.psi.JetArrayAccessExpression
import java.util.ArrayList
import org.jetbrains.jet.lang.resolve.scopes.JetScopeImpl import org.jetbrains.jet.lang.resolve.scopes.JetScopeImpl
import org.jetbrains.jet.utils.Printer import org.jetbrains.jet.utils.Printer
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.descriptors.VariableDescriptor import org.jetbrains.jet.lang.descriptors.VariableDescriptor
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.jet.lang.types.expressions.OperatorConventions
import org.jetbrains.jet.lang.psi.JetOperationReferenceExpression
object DynamicCallableDescriptors { object DynamicCallableDescriptors {
@@ -62,9 +60,27 @@ object DynamicCallableDescriptors {
} }
override fun getFunctions(name: Name): Collection<FunctionDescriptor> { override fun getFunctions(name: Name): Collection<FunctionDescriptor> {
if (isAugmentedAssignmentConvention(name)) return listOf()
return listOf(createDynamicFunction(owner, name, call)) return listOf(createDynamicFunction(owner, name, call))
} }
/*
* Detects the case when name "plusAssign" is requested for "+=" call,
* since both "plus" and "plusAssign" are resolvable on dynamic receivers,
* we have to prefer ne of them, and prefer "plusAssign" for generality:
* it may be called even on a val
*/
private fun isAugmentedAssignmentConvention(name: Name): Boolean {
val callee = call.getCalleeExpression()
if (callee is JetOperationReferenceExpression) {
val token = callee.getReferencedNameElementType()
if (token in JetTokens.AUGMENTED_ASSIGNMENTS && OperatorConventions.ASSIGNMENT_OPERATIONS[token] != name) {
return true
}
}
return false
}
override fun getProperties(name: Name): Collection<VariableDescriptor> { override fun getProperties(name: Name): Collection<VariableDescriptor> {
return if (call.getValueArgumentList() == null && call.getValueArguments().isEmpty()) { return if (call.getValueArgumentList() == null && call.getValueArguments().isEmpty()) {
listOf(createDynamicProperty(owner, name, call)) listOf(createDynamicProperty(owner, name, call))
@@ -36,6 +36,7 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.JetTypeInfo; import org.jetbrains.jet.lang.types.JetTypeInfo;
import org.jetbrains.jet.lang.types.TypeUtils; import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.lexer.JetTokens;
@@ -284,7 +285,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
else if (assignmentOperationType != null && (assignmentOperationDescriptors.isSuccess() || !binaryOperationDescriptors.isSuccess())) { else if (assignmentOperationType != null && (assignmentOperationDescriptors.isSuccess() || !binaryOperationDescriptors.isSuccess())) {
// There's 'plusAssign()', so we do a.plusAssign(b) // There's 'plusAssign()', so we do a.plusAssign(b)
temporaryForAssignmentOperation.commit(); temporaryForAssignmentOperation.commit();
if (!KotlinBuiltIns.getInstance().isUnit(assignmentOperationType)) { if (!JetTypeChecker.DEFAULT.equalTypes(KotlinBuiltIns.getInstance().getUnitType(), assignmentOperationType)) {
context.trace.report(ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT.on(operationSign, assignmentOperationDescriptors.getResultingDescriptor(), operationSign)); context.trace.report(ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT.on(operationSign, assignmentOperationDescriptors.getResultingDescriptor(), operationSign));
} }
} }
@@ -72,17 +72,23 @@ fun test(d: dynamic) {
dVar<!DEBUG_INFO_DYNAMIC!>--<!> dVar<!DEBUG_INFO_DYNAMIC!>--<!>
<!DEBUG_INFO_DYNAMIC!>--<!>dVar <!DEBUG_INFO_DYNAMIC!>--<!>dVar
// dVar += 1 dVar <!DEBUG_INFO_DYNAMIC!>+=<!> 1
// dVar -= 1 dVar <!DEBUG_INFO_DYNAMIC!>-=<!> 1
// dVar *= 1 dVar <!DEBUG_INFO_DYNAMIC!>*=<!> 1
// dVar /= 1 dVar <!DEBUG_INFO_DYNAMIC!>/=<!> 1
// dVar %= 1 dVar <!DEBUG_INFO_DYNAMIC!>%=<!> 1
// d[1] += 1 d <!DEBUG_INFO_DYNAMIC!>+=<!> 1
// d[1] -= 1 d <!DEBUG_INFO_DYNAMIC!>-=<!> 1
// d[1] *= 1 d <!DEBUG_INFO_DYNAMIC!>*=<!> 1
// d[1] /= 1 d <!DEBUG_INFO_DYNAMIC!>/=<!> 1
// d[1] %= 1 d <!DEBUG_INFO_DYNAMIC!>%=<!> 1
<!DEBUG_INFO_DYNAMIC!>d[1]<!> <!DEBUG_INFO_DYNAMIC!>+=<!> 1
<!DEBUG_INFO_DYNAMIC!>d[1]<!> <!DEBUG_INFO_DYNAMIC!>-=<!> 1
<!DEBUG_INFO_DYNAMIC!>d[1]<!> <!DEBUG_INFO_DYNAMIC!>*=<!> 1
<!DEBUG_INFO_DYNAMIC!>d[1]<!> <!DEBUG_INFO_DYNAMIC!>/=<!> 1
<!DEBUG_INFO_DYNAMIC!>d[1]<!> <!DEBUG_INFO_DYNAMIC!>%=<!> 1
} }
val dyn: dynamic = null val dyn: dynamic = null
+2
View File
@@ -70,6 +70,8 @@ Internally, `dynamic` is represented as a flexible type `Nothing..Any?`, with th
the code may change its semantics just because somebody added some extension in another file. the code may change its semantics just because somebody added some extension in another file.
- This means that an extension to a normal, non-dynamic type **can not** be called on a `dynamic` receiver. - This means that an extension to a normal, non-dynamic type **can not** be called on a `dynamic` receiver.
If needed, one can force a call to an extension by casting the receiver to a static type: `(d as Foo).bar()` If needed, one can force a call to an extension by casting the receiver to a static type: `(d as Foo).bar()`
- Augmented assignments on dynamic receivers (e.g. `dyn += foo`) are resolved to `plusAssign()` function, not `plus`, for generality:
this permits calling them on vals (e.g. those holding collection-like objects)
## Type Argument Inference ## Type Argument Inference