Allow augmented assignments on dynamic receivers
This commit is contained in:
+6
-5
@@ -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))
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user