From 7b04755a59661c4b6b994f7fa359d12c2a7821b8 Mon Sep 17 00:00:00 2001 From: Svetlana Isakova Date: Fri, 2 Aug 2013 18:18:48 +0400 Subject: [PATCH] through completion phase result type of call should be updated with respect to being selector in safe call expression --- .../jet/lang/resolve/BindingContextUtils.java | 14 +++++-- .../lang/resolve/calls/CandidateResolver.java | 25 +++++++++--- .../nestedCalls/makeNullableIfSafeCall.kt | 39 +++++++++++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 5 +++ 4 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.kt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java index 47d0d26472b..162a125d66c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContextUtils.java @@ -28,9 +28,9 @@ import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall; -import org.jetbrains.jet.lang.resolve.scopes.JetScope; 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.util.slicedmap.ReadOnlySlice; import org.jetbrains.jet.util.slicedmap.Slices; @@ -274,14 +274,20 @@ public class BindingContextUtils { trace.report(AMBIGUOUS_LABEL.on(targetLabel)); } - public static void updateRecordedType( + @Nullable + public static JetType updateRecordedType( @Nullable JetType type, @NotNull JetExpression expression, - @NotNull BindingTrace trace + @NotNull BindingTrace trace, + boolean shouldBeMadeNullable ) { - if (type == null) return; + if (type == null) return null; + if (shouldBeMadeNullable) { + type = TypeUtils.makeNullable(type); + } trace.record(BindingContext.EXPRESSION_TYPE, expression, type); trace.record(BindingContext.PROCESSED, expression); + return type; } @Nullable diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java index 7800e8e2152..64190a2f118 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CandidateResolver.java @@ -48,6 +48,7 @@ import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.expressions.DataFlowUtils; import org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; +import org.jetbrains.jet.lexer.JetTokens; import javax.inject.Inject; import java.util.ArrayList; @@ -367,8 +368,9 @@ public class CandidateResolver { type = completeNestedCallsInference(contextForArgument); checkValueArgumentTypes(contextForArgument); } - BindingContextUtils.updateRecordedType(type, expression, context.trace); - DataFlowUtils.checkType(type, expression, contextForArgument); + JetType result = BindingContextUtils.updateRecordedType( + type, expression, context.trace, isFairSafeCallExpression(expression, context.trace)); + DataFlowUtils.checkType(result, expression, contextForArgument); } @NotNull @@ -388,6 +390,19 @@ public class CandidateResolver { return expression.accept(selectorExpressionFinder, null); } + private boolean isFairSafeCallExpression(@NotNull JetExpression expression, @NotNull BindingTrace trace) { + // We are interested in type of the last call: + // 'a.b?.foo()' is safe call, but 'a?.b.foo()' is not. + // Since receiver is 'a.b' and selector is 'foo()', + // we can only check if an expression is safe call. + if (!(expression instanceof JetSafeQualifiedExpression)) return false; + + JetSafeQualifiedExpression safeQualifiedExpression = (JetSafeQualifiedExpression) expression; + //If a receiver type is not null, then this safe expression is useless, and we don't need to make the result type nullable. + JetType type = trace.get(BindingContext.EXPRESSION_TYPE, safeQualifiedExpression.getReceiverExpression()); + return type != null && type.isNullable(); + } + @Nullable private JetType updateResultArgumentTypeIfNotDenotable( @NotNull CallCandidateResolutionContext context, @@ -398,7 +413,7 @@ public class CandidateResolver { if (type.getConstructor() instanceof NumberValueTypeConstructor) { NumberValueTypeConstructor constructor = (NumberValueTypeConstructor) type.getConstructor(); type = TypeUtils.getPrimitiveNumberType(constructor, context.expectedType); - BindingContextUtils.updateRecordedType(type, expression, context.trace); + BindingContextUtils.updateRecordedType(type, expression, context.trace, false); } } return type; @@ -537,8 +552,8 @@ public class CandidateResolver { constraintSystem.addSubtypeConstraint(receiverType, receiverParameter.getType(), ConstraintPosition.RECEIVER_POSITION); } - ConstraintSystem - constraintSystemWithRightTypeParameters = constraintSystem.replaceTypeVariables(new Function() { + ConstraintSystem constraintSystemWithRightTypeParameters = constraintSystem.replaceTypeVariables( + new Function() { @Override public TypeParameterDescriptor apply(@Nullable TypeParameterDescriptor typeParameterDescriptor) { assert typeParameterDescriptor != null; diff --git a/compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.kt b/compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.kt new file mode 100644 index 00000000000..fe6ed6b92fc --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.kt @@ -0,0 +1,39 @@ +package a + +trait A { + val b: B + val nb: B? +} + +trait B { + fun foo(): Int +} + +fun test(u: A?, x: A?, y: A?, z: A?, w: A, v: A?) { + u?.b?.foo()!! // was UNNECESSARY_SAFE_CALL everywhere, because result type (of 'foo()') wasn't made nullable + u!!.b?.foo()!! + x?.b!!.foo()!! + x!!.b!!.foo()!! + + y?.nb?.foo()!! + y!!.nb?.foo()!! + z?.nb!!.foo()!! + z!!.nb!!.foo()!! + + w.b?.foo()!! + w.b!!.foo()!! + w.nb?.foo()!! + w.nb!!.foo()!! + + v!!.b.foo()!! +} + +fun B?.bar(): Int = 1 +fun B?.baz(): Int? = 1 + +fun doInt(i: Int) = i + +fun test(a: A?) { + doInt(a?.b.bar()!!) + doInt(a?.b.baz()!!) +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 03d85a16b9e..a31145c215f 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -2866,6 +2866,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage doTest("compiler/testData/diagnostics/tests/inference/nestedCalls/kt3461checkTypes.kt"); } + @TestMetadata("makeNullableIfSafeCall.kt") + public void testMakeNullableIfSafeCall() throws Exception { + doTest("compiler/testData/diagnostics/tests/inference/nestedCalls/makeNullableIfSafeCall.kt"); + } + @TestMetadata("nontrivialCallExpression.kt") public void testNontrivialCallExpression() throws Exception { doTest("compiler/testData/diagnostics/tests/inference/nestedCalls/nontrivialCallExpression.kt");