diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index 1e699b0ca63..2be3d560873 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -224,6 +224,7 @@ public class CallResolver { FunctionDescriptorUtil.initializeFromFunctionType(functionDescriptor, calleeType, NO_RECEIVER); ResolutionCandidate resolutionCandidate = ResolutionCandidate.create(functionDescriptor); resolutionCandidate.setReceiverArgument(context.call.getExplicitReceiver()); + resolutionCandidate.setExplicitReceiverKind(ExplicitReceiverKind.RECEIVER_ARGUMENT); // strictly speaking, this is a hack: // we need to pass a reference, but there's no reference in the PSI, @@ -327,7 +328,7 @@ public class CallResolver { // Now, we try to remove this argument and see if it helps Collection> newCandidates = Lists.newArrayList(); for (ResolutionCandidate candidate : task.getCandidates()) { - newCandidates.add(ResolutionCandidate.create(candidate.getDescriptor())); + newCandidates.add(ResolutionCandidate.create(candidate.getDescriptor(), candidate.getExplicitReceiverKind())); } ResolutionTask newContext = new ResolutionTask(newCandidates, task.reference, TemporaryBindingTrace.create(task.trace), task.scope, new DelegatingCall(task.call) { @NotNull @@ -628,20 +629,35 @@ public class CallResolver { private ResolutionStatus checkAllValueArguments(CallResolutionContext context) { ResolutionStatus result = checkValueArgumentTypes(context); ResolvedCall candidateCall = context.candidateCall; - result = result.combine(checkReceiver(context, candidateCall.getResultingDescriptor().getReceiverParameter(), candidateCall.getReceiverArgument())); - result = result.combine(checkReceiver(context, candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject())); + + // Comment about a very special case. + // Call 'b.foo(1)' where class 'Foo' has an extension member 'fun B.invoke(Int)' should be checked two times for safe call (in 'checkReceiver'), because + // both 'b' (receiver) and 'foo' (this object) might be nullable. In the first case we mark dot, in the second 'foo'. + // Class 'CallForImplicitInvoke' helps up to recognise this case, and parameter 'implicitInvokeCheck' helps us to distinguish whether we check receiver or this object. + + result = result.combine(checkReceiver(context, candidateCall.getResultingDescriptor().getReceiverParameter(), candidateCall.getReceiverArgument(), + candidateCall.getExplicitReceiverKind().isReceiver(), false)); + + result = result.combine(checkReceiver(context, candidateCall.getResultingDescriptor().getExpectedThisObject(), candidateCall.getThisObject(), + candidateCall.getExplicitReceiverKind().isThisObject(), + // for the invocation 'foo(1)' where foo is a variable of function type we should mark 'foo' as callOperationNode if necessary + context.call instanceof CallTransformer.CallForImplicitInvoke)); return result; } - private ResolutionStatus checkReceiver(CallResolutionContext context, ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument) { + private ResolutionStatus checkReceiver(CallResolutionContext context, ReceiverDescriptor receiverParameter, ReceiverDescriptor receiverArgument, + boolean isExplicitReceiver, boolean implicitInvokeCheck) { + ResolutionStatus result = SUCCESS; if (receiverParameter.exists() && receiverArgument.exists()) { ASTNode callOperationNode = context.call.getCallOperationNode(); - boolean safeAccess = callOperationNode != null && callOperationNode.getElementType() == JetTokens.SAFE_ACCESS; + + boolean safeAccess = !implicitInvokeCheck && isExplicitReceiver && callOperationNode != null && callOperationNode.getElementType() == JetTokens.SAFE_ACCESS; JetType receiverArgumentType = receiverArgument.getType(); AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(context.dataFlowInfo, context.candidateCall.getTrace().getBindingContext()); if (!safeAccess && !receiverParameter.getType().isNullable() && !autoCastService.isNotNull(receiverArgument)) { - context.tracing.unsafeCall(context.candidateCall.getTrace(), receiverArgumentType); + + context.tracing.unsafeCall(context.candidateCall.getTrace(), receiverArgumentType, implicitInvokeCheck); result = UNSAFE_CALL_ERROR; } else { @@ -653,7 +669,6 @@ public class CallResolver { result = OTHER_ERROR; } } - if (safeAccess && (receiverParameter.getType().isNullable() || !receiverArgumentType.isNullable())) { context.tracing.unnecessarySafeCall(context.candidateCall.getTrace(), receiverArgumentType); } @@ -840,9 +855,6 @@ public class CallResolver { TemporaryBindingTrace temporaryTrace = result.getTrace(); temporaryTrace.commit(); - if (result instanceof VariableAsFunctionResolvedCall) { - ((VariableAsFunctionResolvedCall)result).getVariableCall().getTrace().commit(); - } return OverloadResolutionResultsImpl.success(result); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java index 63486dee599..529daf39f6e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallTransformer.java @@ -101,7 +101,7 @@ public class CallTransformer { candidate, variableCall, temporaryTrace, task); ResolutionCandidate candidateWithoutReceiver = ResolutionCandidate.create( - candidate.getDescriptor(), candidate.getThisObject(), ReceiverDescriptor.NO_RECEIVER); + candidate.getDescriptor(), candidate.getThisObject(), ReceiverDescriptor.NO_RECEIVER, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER); CallResolutionContext contextWithoutReceiver = createContextWithChainedTrace( candidateWithoutReceiver, variableCallWithoutReceiver, temporaryTrace, task); @@ -203,7 +203,7 @@ public class CallTransformer { final JetSimpleNameExpression invokeExpression = (JetSimpleNameExpression) JetPsiFactory.createExpression( task.call.getCallElement().getProject(), "invoke"); - return new DelegatingCall(task.call) { + return new CallForImplicitInvoke(task.call) { @NotNull @Override public ReceiverDescriptor getExplicitReceiver() { @@ -236,4 +236,10 @@ public class CallTransformer { }; } }; + + public static class CallForImplicitInvoke extends DelegatingCall { + public CallForImplicitInvoke(@NotNull Call delegate) { + super(delegate); + } + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ExplicitReceiverKind.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ExplicitReceiverKind.java new file mode 100644 index 00000000000..60acf1db27a --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ExplicitReceiverKind.java @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.lang.resolve.calls; + +/** + * @author svtk + */ +public enum ExplicitReceiverKind { + RECEIVER_ARGUMENT, + THIS_OBJECT, + NO_EXPLICIT_RECEIVER, + + // A very special case. + // In a call 'b.foo(1)' where class 'Foo' has an extension member 'fun B.invoke(Int)' function 'invoke' has two explicit receivers: + // 'b' (as receiver argument) and 'foo' (as this object). + BOTH_RECEIVERS; + + public boolean isReceiver() { + return this == RECEIVER_ARGUMENT || this == BOTH_RECEIVERS; + } + + public boolean isThisObject() { + return this == THIS_OBJECT || this == BOTH_RECEIVERS; + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionCandidate.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionCandidate.java index b134964b378..029e96f71aa 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionCandidate.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionCandidate.java @@ -33,6 +33,7 @@ public class ResolutionCandidate { private final D candidateDescriptor; private ReceiverDescriptor thisObject; // receiver object of a method private ReceiverDescriptor receiverArgument; // receiver of an extension function + private ExplicitReceiverKind explicitReceiverKind; private ResolutionCandidate(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument) { this.candidateDescriptor = descriptor; @@ -44,8 +45,16 @@ public class ResolutionCandidate { return new ResolutionCandidate(descriptor, NO_RECEIVER, NO_RECEIVER); } - public static ResolutionCandidate create(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument) { - return new ResolutionCandidate(descriptor, thisObject, receiverArgument); + public static ResolutionCandidate create(@NotNull D descriptor, @NotNull ExplicitReceiverKind explicitReceiverKind) { + ResolutionCandidate candidate = new ResolutionCandidate(descriptor, NO_RECEIVER, NO_RECEIVER); + candidate.setExplicitReceiverKind(explicitReceiverKind); + return candidate; + } + + public static ResolutionCandidate create(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument, @NotNull ExplicitReceiverKind explicitReceiverKind) { + ResolutionCandidate resolutionCandidate = new ResolutionCandidate(descriptor, thisObject, receiverArgument); + resolutionCandidate.setExplicitReceiverKind(explicitReceiverKind); + return resolutionCandidate; } public void setThisObject(@NotNull ReceiverDescriptor thisObject) { @@ -56,6 +65,10 @@ public class ResolutionCandidate { this.receiverArgument = receiverArgument; } + public void setExplicitReceiverKind(@NotNull ExplicitReceiverKind explicitReceiverKind) { + this.explicitReceiverKind = explicitReceiverKind; + } + @NotNull public D getDescriptor() { return candidateDescriptor; @@ -70,12 +83,19 @@ public class ResolutionCandidate { public ReceiverDescriptor getReceiverArgument() { return receiverArgument; } - + + @NotNull + public ExplicitReceiverKind getExplicitReceiverKind() { + return explicitReceiverKind; + } + @NotNull public static List> convertCollection(@NotNull Collection descriptors) { List> result = Lists.newArrayList(); for (D descriptor : descriptors) { - result.add(create(descriptor)); + ResolutionCandidate candidate = create(descriptor); + candidate.setExplicitReceiverKind(ExplicitReceiverKind.NO_EXPLICIT_RECEIVER); + result.add(candidate); } return result; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionTask.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionTask.java index 6a2c2ca9ac9..3238a9e3d98 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionTask.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolutionTask.java @@ -16,7 +16,6 @@ package org.jetbrains.jet.lang.resolve.calls; -import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; @@ -195,9 +194,9 @@ public class ResolutionTask extends R } @Override - public void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type) { + public void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke) { ASTNode callOperationNode = call.getCallOperationNode(); - if (callOperationNode != null) { + if (callOperationNode != null && !isCallForImplicitInvoke) { trace.report(UNSAFE_CALL.on(callOperationNode.getPsi(), type)); } else { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCall.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCall.java index 1d3e798dfd7..09319f2120a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCall.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCall.java @@ -46,6 +46,10 @@ public interface ResolvedCall { @NotNull ReceiverDescriptor getThisObject(); + /** Determines whether receiver argument or this object is substituted for explicit receiver */ + @NotNull + ExplicitReceiverKind getExplicitReceiverKind(); + /** Values (arguments) for value parameters */ @NotNull Map getValueArguments(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCallImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCallImpl.java index e6399ea151d..f01bafae443 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCallImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/ResolvedCallImpl.java @@ -53,18 +53,14 @@ public class ResolvedCallImpl implements ResolvedC @NotNull public static ResolvedCallImpl create(@NotNull ResolutionCandidate candidate, @NotNull TemporaryBindingTrace trace) { - return create(candidate.getDescriptor(), candidate.getThisObject(), candidate.getReceiverArgument(), trace); - } - - @NotNull - public static ResolvedCallImpl create(@NotNull D descriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument, @NotNull TemporaryBindingTrace trace) { - return new ResolvedCallImpl(descriptor, thisObject, receiverArgument, trace); + return new ResolvedCallImpl(candidate, trace); } private final D candidateDescriptor; private D resultingDescriptor; // Probably substituted private final ReceiverDescriptor thisObject; // receiver object of a method private final ReceiverDescriptor receiverArgument; // receiver of an extension function + private final ExplicitReceiverKind explicitReceiverKind; private final Map typeArguments = Maps.newLinkedHashMap(); private final Map autoCasts = Maps.newHashMap(); @@ -73,13 +69,15 @@ public class ResolvedCallImpl implements ResolvedC private TemporaryBindingTrace trace; private ResolutionStatus status = UNKNOWN_STATUS; - private ResolvedCallImpl(@NotNull D candidateDescriptor, @NotNull ReceiverDescriptor thisObject, @NotNull ReceiverDescriptor receiverArgument, @NotNull TemporaryBindingTrace trace) { - this.candidateDescriptor = candidateDescriptor; - this.thisObject = thisObject; - this.receiverArgument = receiverArgument; + private ResolvedCallImpl(@NotNull ResolutionCandidate candidate, @NotNull TemporaryBindingTrace trace) { + this.candidateDescriptor = candidate.getDescriptor(); + this.thisObject = candidate.getThisObject(); + this.receiverArgument = candidate.getReceiverArgument(); + this.explicitReceiverKind = candidate.getExplicitReceiverKind(); this.trace = trace; } + @Override @NotNull public ResolutionStatus getStatus() { return status; @@ -89,6 +87,7 @@ public class ResolvedCallImpl implements ResolvedC this.status = this.status.combine(status); } + @Override @NotNull public TemporaryBindingTrace getTrace() { return trace; @@ -138,6 +137,12 @@ public class ResolvedCallImpl implements ResolvedC return thisObject; } + @Override + @NotNull + public ExplicitReceiverKind getExplicitReceiverKind() { + return explicitReceiverKind; + } + @Override @NotNull public Map getValueArguments() { @@ -171,6 +176,7 @@ public class ResolvedCallImpl implements ResolvedC this.someArgumentHasNoType = true; } + @Override public boolean isDirty() { return someArgumentHasNoType; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java index eb3f565adab..33f970a8110 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TaskPrioritizer.java @@ -109,7 +109,8 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor AutoCastServiceImpl autoCastService = new AutoCastServiceImpl(context.dataFlowInfo, context.trace.getBindingContext()); List implicitReceivers = Lists.newArrayList(); scope.getImplicitReceiversHierarchy(implicitReceivers); - if (context.call.getThisObject().exists()) { + boolean hasExplicitThisObject = context.call.getThisObject().exists(); + if (hasExplicitThisObject) { implicitReceivers.add(context.call.getThisObject()); } if (receiver.exists()) { @@ -124,7 +125,7 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor Collection> members = Lists.newArrayList(); for (ReceiverDescriptor variant : variantsForExplicitReceiver) { Collection membersForThisVariant = callableDescriptorCollector.getMembersByName(variant.getType(), name); - convertWithReceivers(membersForThisVariant, Collections.singletonList(variant), Collections.singletonList(NO_RECEIVER), members); + convertWithReceivers(membersForThisVariant, Collections.singletonList(variant), Collections.singletonList(NO_RECEIVER), members, hasExplicitThisObject); } result.addLocalExtensions(locals); @@ -134,7 +135,7 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor Collection memberExtensions = callableDescriptorCollector.getNonMembersByName( implicitReceiver.getType().getMemberScope(), name); List variantsForImplicitReceiver = autoCastService.getVariantsForReceiver(implicitReceiver); - result.addNonLocalExtensions(convertWithReceivers(memberExtensions, variantsForImplicitReceiver, variantsForExplicitReceiver)); + result.addNonLocalExtensions(convertWithReceivers(memberExtensions, variantsForImplicitReceiver, variantsForExplicitReceiver, hasExplicitThisObject)); } result.addNonLocalExtensions(nonlocals); @@ -157,20 +158,26 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor } } - private static Collection> convertWithReceivers(Collection descriptors, Iterable thisObjects, Iterable receiverParameters) { + private static Collection> convertWithReceivers(Collection descriptors, Iterable thisObjects, + Iterable receiverParameters, boolean hasExplicitThisObject) { + Collection> result = Lists.newArrayList(); - convertWithReceivers(descriptors, thisObjects, receiverParameters, result); + convertWithReceivers(descriptors, thisObjects, receiverParameters, result, hasExplicitThisObject); return result; } - private static void convertWithReceivers(Collection descriptors, Iterable thisObjects, Iterable receiverParameters, Collection> result) { + private static void convertWithReceivers(Collection descriptors, Iterable thisObjects, Iterable receiverParameters, + Collection> result, boolean hasExplicitThisObject) { + for (ReceiverDescriptor thisObject : thisObjects) { for (ReceiverDescriptor receiverParameter : receiverParameters) { for (D extension : descriptors) { - ResolutionCandidate resolvedCall = ResolutionCandidate.create(extension); - resolvedCall.setThisObject(thisObject); - resolvedCall.setReceiverArgument(receiverParameter); - result.add(resolvedCall); + ResolutionCandidate candidate = ResolutionCandidate.create(extension); + candidate.setThisObject(thisObject); + candidate.setReceiverArgument(receiverParameter); + candidate.setExplicitReceiverKind( + hasExplicitThisObject ? ExplicitReceiverKind.BOTH_RECEIVERS : ExplicitReceiverKind.THIS_OBJECT); + result.add(candidate); } } } @@ -180,10 +187,12 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor Collection> result = Lists.newArrayList(); for (ReceiverDescriptor receiverParameter : receiverParameters) { for (D descriptor : descriptors) { - ResolutionCandidate resolvedCall = ResolutionCandidate.create(descriptor); - resolvedCall.setReceiverArgument(receiverParameter); - if (setImpliedThis(scope, resolvedCall)) { - result.add(resolvedCall); + ResolutionCandidate candidate = ResolutionCandidate.create(descriptor); + candidate.setReceiverArgument(receiverParameter); + candidate.setExplicitReceiverKind( + receiverParameter.exists() ? ExplicitReceiverKind.RECEIVER_ARGUMENT : ExplicitReceiverKind.NO_EXPLICIT_RECEIVER); + if (setImpliedThis(scope, candidate)) { + result.add(candidate); } } } @@ -195,24 +204,25 @@ import static org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor containingDeclaration = containingDeclaration.getContainingDeclaration(); } if (containingDeclaration instanceof ClassDescriptor && ((ClassDescriptor) containingDeclaration).getKind() == ClassKind.OBJECT) { - ResolutionCandidate resolvedCall = ResolutionCandidate.create(descriptor); - resolvedCall.setThisObject(new ClassReceiver((ClassDescriptor) containingDeclaration)); - result.add(resolvedCall); + ResolutionCandidate candidate = ResolutionCandidate.create(descriptor); + candidate.setThisObject(new ClassReceiver((ClassDescriptor) containingDeclaration)); + candidate.setExplicitReceiverKind(ExplicitReceiverKind.NO_EXPLICIT_RECEIVER); + result.add(candidate); } } } return result; } - private static boolean setImpliedThis(@NotNull JetScope scope, ResolutionCandidate resolvedCall) { - ReceiverDescriptor expectedThisObject = resolvedCall.getDescriptor().getExpectedThisObject(); + private static boolean setImpliedThis(@NotNull JetScope scope, ResolutionCandidate candidate) { + ReceiverDescriptor expectedThisObject = candidate.getDescriptor().getExpectedThisObject(); if (!expectedThisObject.exists()) return true; List receivers = Lists.newArrayList(); scope.getImplicitReceiversHierarchy(receivers); for (ReceiverDescriptor receiver : receivers) { if (JetTypeChecker.INSTANCE.isSubtypeOf(receiver.getType(), expectedThisObject.getType())) { // TODO : Autocasts & nullability - resolvedCall.setThisObject(expectedThisObject); + candidate.setThisObject(expectedThisObject); return true; } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TracingStrategy.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TracingStrategy.java index 9207e24747b..47ba86d1376 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TracingStrategy.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/TracingStrategy.java @@ -72,7 +72,7 @@ import java.util.List; public void typeInferenceFailed(@NotNull BindingTrace trace, SolutionStatus status) {} @Override - public void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type) {} + public void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke) {} @Override public void unnecessarySafeCall(@NotNull BindingTrace trace, @NotNull JetType type) {} @@ -108,7 +108,7 @@ import java.util.List; void typeInferenceFailed(@NotNull BindingTrace trace, SolutionStatus status); - void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type); + void unsafeCall(@NotNull BindingTrace trace, @NotNull JetType type, boolean isCallForImplicitInvoke); void unnecessarySafeCall(@NotNull BindingTrace trace, @NotNull JetType type); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/VariableAsFunctionResolvedCall.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/VariableAsFunctionResolvedCall.java index 5c6419e824f..a314992c3dd 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/VariableAsFunctionResolvedCall.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/VariableAsFunctionResolvedCall.java @@ -73,6 +73,12 @@ public class VariableAsFunctionResolvedCall implements ResolvedCallWithTrace getValueArguments() { diff --git a/compiler/testData/diagnostics/tests/extensions/kt1875.jet b/compiler/testData/diagnostics/tests/extensions/kt1875.jet new file mode 100644 index 00000000000..3d2666e8e30 --- /dev/null +++ b/compiler/testData/diagnostics/tests/extensions/kt1875.jet @@ -0,0 +1,21 @@ +//KT-1875 Safe call should be binded with receiver or this object (but not with both by default) + +package kt1875 + +fun f(a : Int?, b : Int.(Int)->Int) = a?.b(1) //unnecessary safe call warning + +trait T { + val f : ((i: Int) -> Unit)? +} + +fun test(t: T) { + t.f(1) //unsafe call error + t.f?.invoke(1) +} + +fun test1(t: T?) { + t.f(1) + t?.f(1) + t.f?.invoke(1) + t?.f?.invoke(1) +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallBuilder.java b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallBuilder.java index 01186e92a35..87979470cff 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallBuilder.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/reference/CallBuilder.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.resolve.BindingTraceContext; import org.jetbrains.jet.lang.resolve.TemporaryBindingTrace; +import org.jetbrains.jet.lang.resolve.calls.ExplicitReceiverKind; import org.jetbrains.jet.lang.resolve.calls.ResolutionCandidate; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.ResolvedCallImpl; @@ -106,7 +107,7 @@ public final class CallBuilder { private CallTranslator finish() { if (resolvedCall == null) { assert descriptor != null; - resolvedCall = ResolvedCallImpl.create(ResolutionCandidate.create(descriptor), + resolvedCall = ResolvedCallImpl.create(ResolutionCandidate.create(descriptor, ExplicitReceiverKind.NO_EXPLICIT_RECEIVER), TemporaryBindingTrace.create(new BindingTraceContext())); //todo } if (descriptor == null) {