record smartcast to the most specific type

This commit is contained in:
Svetlana Isakova
2014-01-31 12:55:30 +04:00
parent 9832676701
commit 9063b3803e
2 changed files with 29 additions and 17 deletions
@@ -17,7 +17,9 @@
package org.jetbrains.jet.lang.resolve.calls.autocasts; package org.jetbrains.jet.lang.resolve.calls.autocasts;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.BindingTrace;
@@ -28,9 +30,11 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils; import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
import static org.jetbrains.jet.lang.diagnostics.Errors.AUTOCAST_IMPOSSIBLE; import static org.jetbrains.jet.lang.diagnostics.Errors.AUTOCAST_IMPOSSIBLE;
import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST; import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST;
@@ -96,20 +100,28 @@ public class AutoCastUtils {
@NotNull ResolutionContext context @NotNull ResolutionContext context
) { ) {
List<JetType> autoCastTypes = getAutoCastVariants(receiverArgument, context); List<JetType> autoCastTypes = getAutoCastVariants(receiverArgument, context);
return isSubTypeByAutoCast(receiverParameterType, autoCastTypes); return getAutoCastSubType(receiverParameterType, autoCastTypes) != null;
} }
private static boolean isSubTypeByAutoCast( @Nullable
private static JetType getAutoCastSubType(
@NotNull JetType receiverParameterType, @NotNull JetType receiverParameterType,
@NotNull List<JetType> autoCastTypes @NotNull List<JetType> autoCastTypes
) { ) {
Set<JetType> subTypes = Sets.newHashSet();
for (JetType autoCastType : autoCastTypes) { for (JetType autoCastType : autoCastTypes) {
JetType effectiveAutoCastType = TypeUtils.makeNotNullable(autoCastType); JetType effectiveAutoCastType = TypeUtils.makeNotNullable(autoCastType);
if (ArgumentTypeResolver.isSubtypeOfForArgumentType(effectiveAutoCastType, receiverParameterType)) { if (ArgumentTypeResolver.isSubtypeOfForArgumentType(effectiveAutoCastType, receiverParameterType)) {
return true; subTypes.add(autoCastType);
} }
} }
return false; if (subTypes.isEmpty()) return null;
JetType intersection = TypeUtils.intersect(JetTypeChecker.INSTANCE, subTypes);
if (intersection == null || !intersection.getConstructor().isDenotable()) {
return receiverParameterType;
}
return intersection;
} }
public static boolean recordAutoCastIfNecessary( public static boolean recordAutoCastIfNecessary(
@@ -125,14 +137,14 @@ public class AutoCastUtils {
List<JetType> autoCastTypesExcludingReceiver = getAutoCastVariantsExcludingReceiver( List<JetType> autoCastTypesExcludingReceiver = getAutoCastVariantsExcludingReceiver(
context.trace.getBindingContext(), context.dataFlowInfo, receiver); context.trace.getBindingContext(), context.dataFlowInfo, receiver);
boolean autoCast = isSubTypeByAutoCast(receiverType, autoCastTypesExcludingReceiver); JetType autoCastSubType = getAutoCastSubType(receiverType, autoCastTypesExcludingReceiver);
if (autoCast) { if (autoCastSubType == null) return false;
JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context.trace.getBindingContext());
recordCastOrError(expression, receiverType, context.trace, dataFlowValue.isStableIdentifier(), true); JetExpression expression = ((ExpressionReceiver) receiver).getExpression();
} DataFlowValue dataFlowValue = DataFlowValueFactory.createDataFlowValue(receiver, context.trace.getBindingContext());
return autoCast;
recordCastOrError(expression, autoCastSubType, context.trace, dataFlowValue.isStableIdentifier(), true);
return true;
} }
public static void recordAutoCastToNotNullableType(@NotNull ReceiverValue receiver, @NotNull BindingTrace trace) { public static void recordAutoCastToNotNullableType(@NotNull ReceiverValue receiver, @NotNull BindingTrace trace) {
+6 -6
View File
@@ -11,7 +11,7 @@ fun f9(a : A?) {
a<info>?.</info><error descr="[UNRESOLVED_REFERENCE] Unresolved reference: bar">bar</error>() a<info>?.</info><error descr="[UNRESOLVED_REFERENCE] Unresolved reference: bar">bar</error>()
if (a is B) { if (a is B) {
<info descr="Automatically cast to B">a</info>.bar() <info descr="Automatically cast to B">a</info>.bar()
<info descr="Automatically cast to A">a</info>.foo() <info descr="Automatically cast to B">a</info>.foo()
} }
a<info>?.</info>foo() a<info>?.</info>foo()
a<info>?.</info><error descr="[UNRESOLVED_REFERENCE] Unresolved reference: bar">bar</error>() a<info>?.</info><error descr="[UNRESOLVED_REFERENCE] Unresolved reference: bar">bar</error>()
@@ -26,7 +26,7 @@ fun f9(a : A?) {
return; return;
} }
<info descr="Automatically cast to B">a</info>.bar() <info descr="Automatically cast to B">a</info>.bar()
<info descr="Automatically cast to A">a</info>.foo() <info descr="Automatically cast to B">a</info>.foo()
} }
fun f10(a : A?) { fun f10(a : A?) {
@@ -80,7 +80,7 @@ fun f12(a : A?) {
fun f13(a : A?) { fun f13(a : A?) {
if (a is B) { if (a is B) {
<info descr="Automatically cast to A">a</info>.foo() <info descr="Automatically cast to B">a</info>.foo()
<info descr="Automatically cast to B">a</info>.bar() <info descr="Automatically cast to B">a</info>.bar()
} }
else { else {
@@ -93,12 +93,12 @@ fun f13(a : A?) {
a<info>?.</info>foo() a<info>?.</info>foo()
} }
else { else {
<info descr="Automatically cast to A">a</info>.foo() <info descr="Automatically cast to B">a</info>.foo()
} }
a<info>?.</info>foo() a<info>?.</info>foo()
if (a is B && <info descr="Automatically cast to A">a</info>.foo() == Unit.VALUE) { if (a is B && <info descr="Automatically cast to B">a</info>.foo() == Unit.VALUE) {
<info descr="Automatically cast to A">a</info>.foo() <info descr="Automatically cast to B">a</info>.foo()
<info descr="Automatically cast to B">a</info>.bar() <info descr="Automatically cast to B">a</info>.bar()
} }
else { else {