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);
addCandidatesForDynamicReceiver(explicitReceiver, implicitReceivers, c, isExplicit);
addExtensionCandidates(explicitReceiver, implicitReceivers, c, isExplicit);
if (TypesPackage.isDynamic(explicitReceiver.getType())) {
addCandidatesForDynamicReceiver(explicitReceiver, implicitReceivers, c, isExplicit);
}
else {
addExtensionCandidates(explicitReceiver, implicitReceivers, c, isExplicit);
}
}
private static <D extends CallableDescriptor, F extends D> void addExtensionCandidates(
@@ -208,8 +211,6 @@ public class TaskPrioritizer {
@NotNull final TaskPrioritizerContext<D, F> c,
boolean isExplicit
) {
if (!TypesPackage.isDynamic(explicitReceiver.getType())) return;
TaskPrioritizerContext<D, F> onlyDynamicReceivers = c.replaceCollectors(TasksPackage.onlyDynamicReceivers(c.callableDescriptorCollectors));
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.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.jet.lang.descriptors.annotations.Annotations
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.BindingTrace
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.utils.Printer
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
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 {
@@ -62,9 +60,27 @@ object DynamicCallableDescriptors {
}
override fun getFunctions(name: Name): Collection<FunctionDescriptor> {
if (isAugmentedAssignmentConvention(name)) return listOf()
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> {
return if (call.getValueArgumentList() == null && call.getValueArguments().isEmpty()) {
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.JetTypeInfo;
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.lexer.JetTokens;
@@ -284,7 +285,7 @@ public class ExpressionTypingVisitorForStatements extends ExpressionTypingVisito
else if (assignmentOperationType != null && (assignmentOperationDescriptors.isSuccess() || !binaryOperationDescriptors.isSuccess())) {
// There's 'plusAssign()', so we do a.plusAssign(b)
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));
}
}
@@ -72,17 +72,23 @@ fun test(d: dynamic) {
dVar<!DEBUG_INFO_DYNAMIC!>--<!>
<!DEBUG_INFO_DYNAMIC!>--<!>dVar
// dVar += 1
// dVar -= 1
// dVar *= 1
// dVar /= 1
// dVar %= 1
dVar <!DEBUG_INFO_DYNAMIC!>+=<!> 1
dVar <!DEBUG_INFO_DYNAMIC!>-=<!> 1
dVar <!DEBUG_INFO_DYNAMIC!>*=<!> 1
dVar <!DEBUG_INFO_DYNAMIC!>/=<!> 1
dVar <!DEBUG_INFO_DYNAMIC!>%=<!> 1
// d[1] += 1
// d[1] -= 1
// d[1] *= 1
// d[1] /= 1
// d[1] %= 1
d <!DEBUG_INFO_DYNAMIC!>+=<!> 1
d <!DEBUG_INFO_DYNAMIC!>-=<!> 1
d <!DEBUG_INFO_DYNAMIC!>*=<!> 1
d <!DEBUG_INFO_DYNAMIC!>/=<!> 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
+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.
- 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()`
- 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