diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java index c7ebed1f1e9..f05397f66a5 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/AnnotationResolver.java @@ -10,7 +10,7 @@ import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetModifierList; import org.jetbrains.jet.lang.resolve.calls.CallMaker; import org.jetbrains.jet.lang.resolve.calls.CallResolver; -import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; @@ -36,7 +36,7 @@ public class AnnotationResolver { this.trace = trace; // this.typeInferrer = new JetTypeInferrer(JetFlowInformationProvider.THROW_EXCEPTION, semanticServices); // this.services = typeInferrer.getServices(this.trace); - this.callResolver = new CallResolver(semanticServices, DataFlowInfo.getEmpty()); + this.callResolver = new CallResolver(semanticServices, DataFlowInfo.EMPTY); } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java index 1eba532f34c..7bc2afd02c8 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java @@ -10,7 +10,7 @@ import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.calls.CallMaker; import org.jetbrains.jet.lang.resolve.calls.CallResolver; -import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; @@ -176,7 +176,7 @@ public class BodyResolver { JetTypeReference typeReference = call.getTypeReference(); if (typeReference != null) { if (descriptor.getUnsubstitutedPrimaryConstructor() != null) { - JetType supertype = new CallResolver(context.getSemanticServices(), DataFlowInfo.getEmpty()).resolveCall( + JetType supertype = new CallResolver(context.getSemanticServices(), DataFlowInfo.EMPTY).resolveCall( context.getTrace(), scopeForConstructor, CallMaker.makeCall(ReceiverDescriptor.NO_RECEIVER, null, call), NO_EXPECTED_TYPE); if (supertype != null) { @@ -322,7 +322,7 @@ public class BodyResolver { private void resolveSecondaryConstructorBody(JetConstructor declaration, final ConstructorDescriptor descriptor, final JetScope declaringScope) { final JetScope functionInnerScope = getInnerScopeForConstructor(descriptor, declaringScope, false); - final CallResolver callResolver = new CallResolver(context.getSemanticServices(), DataFlowInfo.getEmpty()); // TODO: dataFlowInfo + final CallResolver callResolver = new CallResolver(context.getSemanticServices(), DataFlowInfo.EMPTY); // TODO: dataFlowInfo JetClass containingClass = PsiTreeUtil.getParentOfType(declaration, JetClass.class); assert containingClass != null : "This must be guaranteed by the parser"; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastUtils.java deleted file mode 100644 index 63d47a2746f..00000000000 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastUtils.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.jetbrains.jet.lang.resolve.calls; - -import com.google.common.collect.Lists; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.descriptors.*; -import org.jetbrains.jet.lang.psi.JetExpression; -import org.jetbrains.jet.lang.psi.JetPsiUtil; -import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; -import org.jetbrains.jet.lang.psi.JetThisExpression; -import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.jet.lang.resolve.BindingTrace; -import org.jetbrains.jet.lang.resolve.scopes.receivers.*; -import org.jetbrains.jet.lang.types.JetType; -import org.jetbrains.jet.lang.types.JetTypeChecker; - -import java.util.Collections; -import java.util.List; - -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.REFERENCE_TARGET; - -/** - * @author abreslav - */ -public class AutoCastUtils { - - private AutoCastUtils() {} - - public static List getAutoCastVariants(@NotNull final BindingContext bindingContext, @NotNull final DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiverToCast) { - return receiverToCast.accept(new ReceiverDescriptorVisitor, Object>() { - @Override - public List visitNoReceiver(ReceiverDescriptor noReceiver, Object data) { - return Collections.emptyList(); - } - - @Override - public List visitTransientReceiver(TransientReceiver receiver, Object data) { - return Collections.emptyList(); - } - - @Override - public List visitExtensionReceiver(ExtensionReceiver receiver, Object data) { - return castThis(dataFlowInfo, receiver); - } - - @Override - public List visitClassReceiver(ClassReceiver receiver, Object data) { - return castThis(dataFlowInfo, receiver); - } - - @Override - public List visitExpressionReceiver(ExpressionReceiver receiver, Object data) { - JetExpression expression = receiver.getExpression(); - VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(bindingContext, expression); - if (variableDescriptor != null) { - List result = Lists.newArrayList(); - for (JetType possibleType : dataFlowInfo.getPossibleTypesForVariable(variableDescriptor)) { - result.add(new AutoCastReceiver(receiver, possibleType, isAutoCastable(variableDescriptor))); - } - return result; - } - else if (expression instanceof JetThisExpression) { - return castThis(dataFlowInfo, receiver); - } - return Collections.emptyList(); - } - }, null); - } - - private static List castThis(@NotNull DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiver) { - assert receiver.exists(); - List result = Lists.newArrayList(); - for (JetType possibleType : dataFlowInfo.getPossibleTypesForReceiver(receiver)) { - result.add(new AutoCastReceiver(receiver, possibleType, true)); - } - return result; - } - - @Nullable - public static JetType castExpression(@NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace) { - JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; - VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(trace.getBindingContext(), expression); - if (variableDescriptor != null) { - List possibleTypes = Lists.newArrayList(dataFlowInfo.getPossibleTypesForVariable(variableDescriptor)); - Collections.reverse(possibleTypes); - for (JetType possibleType : possibleTypes) { - if (typeChecker.isSubtypeOf(possibleType, expectedType)) { - if (isAutoCastable(variableDescriptor)) { - trace.record(AUTOCAST, expression, possibleType); - } - else { - trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText())); - } - return possibleType; - } - } - } - return null; - } - - @Nullable - public static VariableDescriptor getVariableDescriptorFromSimpleName(@NotNull BindingContext bindingContext, @NotNull JetExpression expression) { - JetExpression receiver = JetPsiUtil.deparenthesize(expression); - VariableDescriptor variableDescriptor = null; - if (receiver instanceof JetSimpleNameExpression) { - JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) receiver; - DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, nameExpression); - if (declarationDescriptor instanceof VariableDescriptor) { - variableDescriptor = (VariableDescriptor) declarationDescriptor; - } - } - return variableDescriptor; - } - - public static boolean isAutoCastable(@NotNull VariableDescriptor variableDescriptor) { - if (variableDescriptor.isVar()) return false; - if (variableDescriptor instanceof PropertyDescriptor) { - PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor; - DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration(); - if (containingDeclaration instanceof ClassDescriptor) { - ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration; - if (classDescriptor.getModality().isOverridable() && propertyDescriptor.getModality().isOverridable()) return false; - } - else { - assert !propertyDescriptor.getModality().isOverridable() : "Property outside a class must not be overridable"; - } - PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); - if (getter == null || !getter.isDefault()) return false; - } - return true; - } - -} 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 4395382f451..2832cfb9b4f 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 @@ -11,6 +11,8 @@ import org.jetbrains.jet.lang.JetSemanticServices; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; +import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; @@ -149,7 +151,7 @@ public class CallResolver { trace.report(NO_CONSTRUCTOR.on(reportAbsenceOn)); return checkArgumentTypesAndFail(trace, scope, call); } - prioritizedTasks.add(new ResolutionTask(TaskPrioritizer.convertWithImpliedThis(scope, Collections.singletonList(NO_RECEIVER), constructors), call, DataFlowInfo.getEmpty())); + prioritizedTasks.add(new ResolutionTask(TaskPrioritizer.convertWithImpliedThis(scope, Collections.singletonList(NO_RECEIVER), constructors), call, DataFlowInfo.EMPTY)); } else { // trace.getErrorHandler().genericError(calleeExpression.getNode(), "Not a class"); @@ -169,7 +171,7 @@ public class CallResolver { trace.report(NO_CONSTRUCTOR.on(reportAbsenceOn)); return checkArgumentTypesAndFail(trace, scope, call); } - prioritizedTasks = Collections.singletonList(new ResolutionTask(ResolvedCall.convertCollection(constructors), call, DataFlowInfo.getEmpty())); + prioritizedTasks = Collections.singletonList(new ResolutionTask(ResolvedCall.convertCollection(constructors), call, DataFlowInfo.EMPTY)); } else { throw new UnsupportedOperationException("Type argument inference not implemented for " + call); @@ -301,7 +303,10 @@ public class CallResolver { JetBinaryExpression binaryExpression = (JetBinaryExpression) callElement; JetSimpleNameExpression operationReference = binaryExpression.getOperationReference(); String operationString = operationReference.getReferencedNameElementType() == JetTokens.IDENTIFIER ? operationReference.getText() : OperatorConventions.getNameForOperationSymbol(operationReference.getReferencedNameElementType()); - trace.report(UNSAFE_INFIX_CALL.on(reference, binaryExpression.getLeft().getText(), operationString, binaryExpression.getRight().getText())); + JetExpression right = binaryExpression.getRight(); + if (right != null) { + trace.report(UNSAFE_INFIX_CALL.on(reference, binaryExpression.getLeft().getText(), operationString, right.getText())); + } } else { trace.report(UNSAFE_CALL.on(reference, type)); @@ -618,7 +623,7 @@ public class CallResolver { // } // } // if (autoCastType != null) { -// if (AutoCastUtils.isAutoCastable(variableDescriptor)) { +// if (AutoCastUtils.isStableVariable(variableDescriptor)) { // temporaryTrace.record(AUTOCAST, argumentExpression, autoCastType); // } // else { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/DataFlowInfo.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/DataFlowInfo.java deleted file mode 100644 index 0b91738ee7f..00000000000 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/DataFlowInfo.java +++ /dev/null @@ -1,224 +0,0 @@ -package org.jetbrains.jet.lang.resolve.calls; - -import com.google.common.collect.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.descriptors.VariableDescriptor; -import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; -import org.jetbrains.jet.lang.types.JetType; -import org.jetbrains.jet.lang.types.TypeUtils; -import org.jetbrains.jet.util.CommonSuppliers; - -import java.util.*; - -/** - * @author abreslav - */ -public class DataFlowInfo { - - public static abstract class CompositionOperator { - - public abstract DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b); - } - public static final CompositionOperator AND = new CompositionOperator() { - @Override - public DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b) { - return a.and(b); - } - }; - - public static final CompositionOperator OR = new CompositionOperator() { - @Override - public DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b) { - return a.or(b); - } - }; - - private static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.of(), Multimaps.newListMultimap(Collections.>emptyMap(), CommonSuppliers.getArrayListSupplier())); - - public static DataFlowInfo getEmpty() { - return EMPTY; - } - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - - private final ImmutableMap nullabilityInfo; - - private final ListMultimap typeInfo; - - private DataFlowInfo(ImmutableMap nullabilityInfo, ListMultimap typeInfo) { - this.nullabilityInfo = nullabilityInfo; - this.typeInfo = typeInfo; - } - - @Nullable - public JetType getOutType(@NotNull VariableDescriptor variableDescriptor) { - JetType outType = variableDescriptor.getOutType(); - if (outType == null) return null; - if (!outType.isNullable()) return outType; - NullabilityFlags nullabilityFlags = nullabilityInfo.get(variableDescriptor); - if (nullabilityFlags != null && !nullabilityFlags.canBeNull()) { - return TypeUtils.makeNotNullable(outType); - } - return outType; - } - - @NotNull - private List getPossibleTypes(Object key, @NotNull JetType originalType) { - List types = typeInfo.get(key); - NullabilityFlags nullabilityFlags = nullabilityInfo.get(key); - if (nullabilityFlags == null || nullabilityFlags.canBeNull()) { - return types; - } - List enrichedTypes = Lists.newArrayListWithCapacity(types.size()); - if (originalType.isNullable()) { - enrichedTypes.add(TypeUtils.makeNotNullable(originalType)); - } - for (JetType type: types) { - if (type.isNullable()) { - enrichedTypes.add(TypeUtils.makeNotNullable(type)); - } - else { - enrichedTypes.add(type); - } - } - return enrichedTypes; - } - - @NotNull - public List getPossibleTypesForVariable(@NotNull VariableDescriptor variableDescriptor) { - return getPossibleTypes(variableDescriptor, variableDescriptor.getOutType()); - } - - public List getPossibleTypesForReceiver(@NotNull ReceiverDescriptor receiver) { - return getPossibleTypes(receiver, receiver.getType()); - } - - public DataFlowInfo equalsToNull(@NotNull VariableDescriptor variableDescriptor, boolean notNull) { - return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, notNull), typeInfo); - } - - private ImmutableMap getEqualsToNullMap(VariableDescriptor variableDescriptor, boolean notNull) { - Map builder = Maps.newHashMap(nullabilityInfo); - NullabilityFlags nullabilityFlags = nullabilityInfo.get(variableDescriptor); - boolean varNotNull = notNull || (nullabilityFlags != null && !nullabilityFlags.canBeNull); - builder.put(variableDescriptor, new NullabilityFlags(!varNotNull, varNotNull)); - return ImmutableMap.copyOf(builder); - } - - private ImmutableMap getEqualsToNullMap(VariableDescriptor[] variableDescriptors, boolean notNull) { - if (variableDescriptors.length == 0) return nullabilityInfo; - Map builder = Maps.newHashMap(nullabilityInfo); - for (VariableDescriptor variableDescriptor : variableDescriptors) { - if (variableDescriptor != null) { - NullabilityFlags nullabilityFlags = nullabilityInfo.get(variableDescriptor); - boolean varNotNull = notNull || (nullabilityFlags != null && !nullabilityFlags.canBeNull); - builder.put(variableDescriptor, new NullabilityFlags(!varNotNull, varNotNull)); - } - } - return ImmutableMap.copyOf(builder); - } - - public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor variableDescriptor, @NotNull JetType type) { - ListMultimap newTypeInfo = copyTypeInfo(); - newTypeInfo.put(variableDescriptor, type); - return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, !type.isNullable()), newTypeInfo); - } - - public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor[] variableDescriptors, @NotNull JetType type) { - if (variableDescriptors.length == 0) return this; - ListMultimap newTypeInfo = copyTypeInfo(); - for (VariableDescriptor variableDescriptor : variableDescriptors) { - if (variableDescriptor != null) { - newTypeInfo.put(variableDescriptor, type); - } - } - return new DataFlowInfo(getEqualsToNullMap(variableDescriptors, !type.isNullable()), newTypeInfo); - } - - public DataFlowInfo and(DataFlowInfo other) { - Map nullabilityMapBuilder = Maps.newHashMap(); - nullabilityMapBuilder.putAll(nullabilityInfo); - for (Map.Entry entry : other.nullabilityInfo.entrySet()) { - Object key = entry.getKey(); - NullabilityFlags otherFlags = entry.getValue(); - NullabilityFlags thisFlags = nullabilityInfo.get(key); - if (thisFlags != null) { - nullabilityMapBuilder.put(key, thisFlags.and(otherFlags)); - } - else { - nullabilityMapBuilder.put(key, otherFlags); - } - } - - ListMultimap newTypeInfo = copyTypeInfo(); - newTypeInfo.putAll(other.typeInfo); - return new DataFlowInfo(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo); - } - - private ListMultimap copyTypeInfo() { - ListMultimap newTypeInfo = Multimaps.newListMultimap(Maps.>newHashMap(), CommonSuppliers.getArrayListSupplier()); - newTypeInfo.putAll(typeInfo); - return newTypeInfo; - } - - public DataFlowInfo or(DataFlowInfo other) { - Map builder = Maps.newHashMap(nullabilityInfo); - builder.keySet().retainAll(other.nullabilityInfo.keySet()); - for (Map.Entry entry : builder.entrySet()) { - Object key = entry.getKey(); - NullabilityFlags thisFlags = entry.getValue(); - NullabilityFlags otherFlags = other.nullabilityInfo.get(key); - assert (otherFlags != null); - builder.put(key, thisFlags.or(otherFlags)); - } - - ListMultimap newTypeInfo = Multimaps.newListMultimap(Maps.>newHashMap(), CommonSuppliers.getArrayListSupplier()); - - Set keys = newTypeInfo.keySet(); - keys.retainAll(other.typeInfo.keySet()); - - for (Object key : keys) { - Collection thisTypes = typeInfo.get(key); - Collection otherTypes = other.typeInfo.get(key); - - Collection newTypes = Sets.newHashSet(thisTypes); - newTypes.retainAll(otherTypes); - - newTypeInfo.putAll(key, newTypes); - } - - return new DataFlowInfo(ImmutableMap.copyOf(builder), newTypeInfo); - } - - public DataFlowInfo nullabilityOnly() { - return new DataFlowInfo(nullabilityInfo, EMPTY.copyTypeInfo()); - } - - private static class NullabilityFlags { - private final boolean canBeNull; - private final boolean canBeNonNull; - - private NullabilityFlags(boolean canBeNull, boolean canBeNonNull) { - this.canBeNull = canBeNull; - this.canBeNonNull = canBeNonNull; - } - - public boolean canBeNull() { - return canBeNull; - } - - public boolean canBeNonNull() { - return canBeNonNull; - } - - public NullabilityFlags and(NullabilityFlags other) { - return new NullabilityFlags(this.canBeNull && other.canBeNull, this.canBeNonNull && other.canBeNonNull); - } - - public NullabilityFlags or(NullabilityFlags other) { - return new NullabilityFlags(this.canBeNull || other.canBeNull, this.canBeNonNull || other.canBeNonNull); - } - - } -} 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 189a2550d0d..1fbce7ba838 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 @@ -4,6 +4,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.CallableDescriptor; import org.jetbrains.jet.lang.psi.Call; import org.jetbrains.jet.lang.resolve.BindingTrace; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import java.util.Collection; 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 581d5251e68..ad1b4c55512 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 @@ -8,6 +8,9 @@ import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; import org.jetbrains.jet.lang.psi.Call; import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastService; +import org.jetbrains.jet.lang.resolve.calls.autocasts.AutoCastServiceImpl; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.JetType; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastService.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastService.java similarity index 90% rename from compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastService.java rename to compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastService.java index 60fbb920eac..4c0de1c1ca5 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastService.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastService.java @@ -1,4 +1,4 @@ -package org.jetbrains.jet.lang.resolve.calls; +package org.jetbrains.jet.lang.resolve.calls.autocasts; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; @@ -13,7 +13,7 @@ public interface AutoCastService { AutoCastService NO_AUTO_CASTS = new AutoCastService() { @Override public DataFlowInfo getDataFlowInfo() { - return DataFlowInfo.getEmpty(); + return DataFlowInfo.EMPTY; } @Override diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastServiceImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastServiceImpl.java similarity index 95% rename from compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastServiceImpl.java rename to compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastServiceImpl.java index d0f8c6f102d..c5cf125e80a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/AutoCastServiceImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastServiceImpl.java @@ -1,4 +1,4 @@ -package org.jetbrains.jet.lang.resolve.calls; +package org.jetbrains.jet.lang.resolve.calls.autocasts; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.resolve.BindingContext; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java new file mode 100644 index 00000000000..e1b62313964 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/AutoCastUtils.java @@ -0,0 +1,109 @@ +package org.jetbrains.jet.lang.resolve.calls.autocasts; + +import com.google.common.collect.Lists; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.calls.AutoCastReceiver; +import org.jetbrains.jet.lang.resolve.scopes.receivers.*; +import org.jetbrains.jet.lang.types.JetType; + +import java.util.Collections; +import java.util.List; + +/** + * @author abreslav + */ +public class AutoCastUtils { + + private AutoCastUtils() {} + + public static List getAutoCastVariants(@NotNull final BindingContext bindingContext, @NotNull final DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiverToCast) { + return receiverToCast.accept(new ReceiverDescriptorVisitor, Object>() { + @Override + public List visitNoReceiver(ReceiverDescriptor noReceiver, Object data) { + return Collections.emptyList(); + } + + @Override + public List visitTransientReceiver(TransientReceiver receiver, Object data) { + return Collections.emptyList(); + } + + @Override + public List visitExtensionReceiver(ExtensionReceiver receiver, Object data) { + return castThis(dataFlowInfo, receiver); + } + + @Override + public List visitClassReceiver(ClassReceiver receiver, Object data) { + return castThis(dataFlowInfo, receiver); + } + + @Override + public List visitExpressionReceiver(ExpressionReceiver receiver, Object data) { +// JetExpression expression = receiver.getExpression(); +// VariableDescriptor variableDescriptor = DataFlowValueFactory.getVariableDescriptorFromSimpleName(bindingContext, expression); +// if (variableDescriptor != null) { +// List result = Lists.newArrayList(); +// for (JetType possibleType : dataFlowInfo.getPossibleTypesForVariable(variableDescriptor)) { +// result.add(new AutoCastReceiver(receiver, possibleType, DataFlowValueFactory.isStableVariable(variableDescriptor))); +// } +// return result; +// } +// else if (expression instanceof JetThisExpression) { +// return castThis(dataFlowInfo, receiver); +// } + DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(receiver.getExpression(),receiver.getType(), bindingContext); + List result = Lists.newArrayList(); + for (JetType possibleType : dataFlowInfo.getPossibleTypes(dataFlowValue)) { + result.add(new AutoCastReceiver(receiver, possibleType, dataFlowValue.isStableIdentifier())); + } + return result; + } + }, null); + } + + private static List castThis(@NotNull DataFlowInfo dataFlowInfo, @NotNull ThisReceiverDescriptor receiver) { + assert receiver.exists(); + List result = Lists.newArrayList(); + for (JetType possibleType : dataFlowInfo.getPossibleTypes(DataFlowValueFactory.INSTANCE.createDataFlowValue(receiver))) { + result.add(new AutoCastReceiver(receiver, possibleType, true)); + } + return result; + } + +// @Nullable +// public static JetType castExpression(@NotNull JetExpression expression, @NotNull JetType expectedType, @NotNull DataFlowInfo dataFlowInfo, @NotNull BindingTrace trace) { +// JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; +// DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, trace.getBindingContext()); +// for (JetType possibleType : dataFlowInfo.getPossibleTypes(dataFlowValue)) { +// if (typeChecker.isSubtypeOf(possibleType, expectedType)) { +// if (dataFlowValue.isStableIdentifier()) { +// trace.record(AUTOCAST, expression, possibleType); +// } +// else { +// trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText())); +// } +// return possibleType; +// } +// } +//// VariableDescriptor variableDescriptor = DataFlowValueFactory.getVariableDescriptorFromSimpleName(trace.getBindingContext(), expression); +//// if (variableDescriptor != null) { +//// List possibleTypes = Lists.newArrayList(dataFlowInfo.getPossibleTypes(variableDescriptor)); +//// Collections.reverse(possibleTypes); +//// for (JetType possibleType : possibleTypes) { +//// if (typeChecker.isSubtypeOf(possibleType, expectedType)) { +//// if (DataFlowValueFactory.isStableVariable(variableDescriptor)) { +//// trace.record(AUTOCAST, expression, possibleType); +//// } +//// else { +//// trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText())); +//// } +//// return possibleType; +//// } +//// } +//// } +// return null; +// } + +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfo.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfo.java new file mode 100644 index 00000000000..f1148ec21d2 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowInfo.java @@ -0,0 +1,390 @@ +package org.jetbrains.jet.lang.resolve.calls.autocasts; + +import com.google.common.collect.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypeUtils; +import org.jetbrains.jet.util.CommonSuppliers; + +import java.util.*; + +import static org.jetbrains.jet.lang.resolve.calls.autocasts.Nullability.NOT_NULL; + +/** + * @author abreslav + */ + +public class DataFlowInfo { + + public static abstract class CompositionOperator { + public abstract DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b); + } + + public static final CompositionOperator AND = new CompositionOperator() { + @Override + public DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b) { + return a.and(b); + } + }; + + public static final CompositionOperator OR = new CompositionOperator() { + @Override + public DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b) { + return a.or(b); + } + }; + + public static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.of(), Multimaps.newListMultimap(Collections.>emptyMap(), CommonSuppliers.getArrayListSupplier())); + + private final ImmutableMap nullabilityInfo; + private final ListMultimap typeInfo; + + private DataFlowInfo(ImmutableMap nullabilityInfo, ListMultimap typeInfo) { + this.nullabilityInfo = nullabilityInfo; + this.typeInfo = typeInfo; + } + + @NotNull + private Nullability getNullability(@NotNull DataFlowValue a) { + if (!a.isStableIdentifier()) return a.getImmanentNullability(); + Nullability nullability = nullabilityInfo.get(a); + if (nullability == null) { + nullability = a.getImmanentNullability(); + } + return nullability; + } + + private boolean putNullability(@NotNull Map map, @NotNull DataFlowValue value, @NotNull Nullability nullability) { + if (!value.isStableIdentifier()) return false; + return map.put(value, nullability) != nullability; + } + + @NotNull + public List getPossibleTypes(DataFlowValue key) { + JetType originalType = key.getType(); + List types = typeInfo.get(key); + Nullability nullability = getNullability(key); + if (nullability.canBeNull()) { + return types; + } + List enrichedTypes = Lists.newArrayListWithCapacity(types.size()); + if (originalType.isNullable()) { + enrichedTypes.add(TypeUtils.makeNotNullable(originalType)); + } + for (JetType type: types) { + if (type.isNullable()) { + enrichedTypes.add(TypeUtils.makeNotNullable(type)); + } + else { + enrichedTypes.add(type); + } + } + return enrichedTypes; + } + + @NotNull + public DataFlowInfo equate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) { + Map builder = Maps.newHashMap(nullabilityInfo); + Nullability nullabilityOfA = getNullability(a); + Nullability nullabilityOfB = getNullability(b); + + boolean changed = false; + changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB)); + changed |= putNullability(builder, b, nullabilityOfA.refine(nullabilityOfA)); + return changed ? new DataFlowInfo(ImmutableMap.copyOf(builder), typeInfo) : this; + } + + @NotNull + public DataFlowInfo disequate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) { + Map builder = Maps.newHashMap(nullabilityInfo); + Nullability nullabilityOfA = getNullability(a); + Nullability nullabilityOfB = getNullability(b); + + boolean changed = false; + changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert())); + changed |= putNullability(builder, b, nullabilityOfA.refine(nullabilityOfA.invert())); + return changed ? new DataFlowInfo(ImmutableMap.copyOf(builder), typeInfo) : this; + } + + @NotNull + public DataFlowInfo establishSubtyping(@NotNull DataFlowValue[] values, @NotNull JetType type) { + ListMultimap newTypeInfo = copyTypeInfo(); + Map newNullabilityInfo = Maps.newHashMap(nullabilityInfo); + boolean changed = false; + for (DataFlowValue value : values) { +// if (!value.isStableIdentifier()) continue; + changed = true; + newTypeInfo.put(value, type); + if (!type.isNullable()) { + putNullability(newNullabilityInfo, value, NOT_NULL); + } + } + if (!changed) return this; + return new DataFlowInfo(ImmutableMap.copyOf(newNullabilityInfo), newTypeInfo); + } + + private ListMultimap copyTypeInfo() { + ListMultimap newTypeInfo = Multimaps.newListMultimap(Maps.>newHashMap(), CommonSuppliers.getArrayListSupplier()); + newTypeInfo.putAll(typeInfo); + return newTypeInfo; + } + + public DataFlowInfo and(DataFlowInfo other) { + Map nullabilityMapBuilder = Maps.newHashMap(); + nullabilityMapBuilder.putAll(nullabilityInfo); + for (Map.Entry entry : other.nullabilityInfo.entrySet()) { + DataFlowValue key = entry.getKey(); + Nullability otherFlags = entry.getValue(); + Nullability thisFlags = nullabilityInfo.get(key); + if (thisFlags != null) { + nullabilityMapBuilder.put(key, thisFlags.and(otherFlags)); + } + else { + nullabilityMapBuilder.put(key, otherFlags); + } + } + + ListMultimap newTypeInfo = copyTypeInfo(); + newTypeInfo.putAll(other.typeInfo); + return new DataFlowInfo(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo); + } + + public DataFlowInfo or(DataFlowInfo other) { + Map builder = Maps.newHashMap(nullabilityInfo); + builder.keySet().retainAll(other.nullabilityInfo.keySet()); + for (Map.Entry entry : builder.entrySet()) { + DataFlowValue key = entry.getKey(); + Nullability thisFlags = entry.getValue(); + Nullability otherFlags = other.nullabilityInfo.get(key); + assert (otherFlags != null); + builder.put(key, thisFlags.or(otherFlags)); + } + + ListMultimap newTypeInfo = Multimaps.newListMultimap(Maps.>newHashMap(), CommonSuppliers.getArrayListSupplier()); + + Set keys = newTypeInfo.keySet(); + keys.retainAll(other.typeInfo.keySet()); + + for (DataFlowValue key : keys) { + Collection thisTypes = typeInfo.get(key); + Collection otherTypes = other.typeInfo.get(key); + + Collection newTypes = Sets.newHashSet(thisTypes); + newTypes.retainAll(otherTypes); + + newTypeInfo.putAll(key, newTypes); + } + + return new DataFlowInfo(ImmutableMap.copyOf(builder), newTypeInfo); + } + +} + +//public class DataFlowInfo { +// +// public static abstract class CompositionOperator { +// +// public abstract DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b); +// } +// public static final CompositionOperator AND = new CompositionOperator() { +// @Override +// public DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b) { +// return a.and(b); +// } +// }; +// +// public static final CompositionOperator OR = new CompositionOperator() { +// @Override +// public DataFlowInfo compose(DataFlowInfo a, DataFlowInfo b) { +// return a.or(b); +// } +// }; +// +// private static DataFlowInfo EMPTY = new DataFlowInfo(ImmutableMap.of(), Multimaps.newListMultimap(Collections.>emptyMap(), CommonSuppliers.getArrayListSupplier())); +// +// public static DataFlowInfo getEmpty() { +// return EMPTY; +// } +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// private final ImmutableMap nullabilityInfo; +// +// private final ListMultimap typeInfo; +// +// private DataFlowInfo(ImmutableMap nullabilityInfo, ListMultimap typeInfo) { +// this.nullabilityInfo = nullabilityInfo; +// this.typeInfo = typeInfo; +// } +// +// @Nullable +// public JetType getOutType(@NotNull VariableDescriptor variableDescriptor) { +// JetType outType = variableDescriptor.getOutType(); +// if (outType == null) return null; +// if (!outType.isNullable()) return outType; +// NullabilityFlags nullabilityFlags = nullabilityInfo.get(variableDescriptor); +// if (nullabilityFlags != null && !nullabilityFlags.canBeNull()) { +// return TypeUtils.makeNotNullable(outType); +// } +// return outType; +// } +// +// @NotNull +// private List getPossibleTypes(Object key, @NotNull JetType originalType) { +// List types = typeInfo.get(key); +// NullabilityFlags nullabilityFlags = nullabilityInfo.get(key); +// if (nullabilityFlags == null || nullabilityFlags.canBeNull()) { +// return types; +// } +// List enrichedTypes = Lists.newArrayListWithCapacity(types.size()); +// if (originalType.isNullable()) { +// enrichedTypes.add(TypeUtils.makeNotNullable(originalType)); +// } +// for (JetType type: types) { +// if (type.isNullable()) { +// enrichedTypes.add(TypeUtils.makeNotNullable(type)); +// } +// else { +// enrichedTypes.add(type); +// } +// } +// return enrichedTypes; +// } +// +// @NotNull +// public List getPossibleTypesForVariable(@NotNull VariableDescriptor variableDescriptor) { +// return getPossibleTypes(variableDescriptor, variableDescriptor.getOutType()); +// } +// +// public List getPossibleTypesForReceiver(@NotNull ReceiverDescriptor receiver) { +// return getPossibleTypes(receiver, receiver.getType()); +// } +// +// public DataFlowInfo establishEqualityToNull(@NotNull VariableDescriptor variableDescriptor, boolean notNull) { +// return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, notNull), typeInfo); +// } +// +// private ImmutableMap getEqualsToNullMap(VariableDescriptor variableDescriptor, boolean notNull) { +// Map builder = Maps.newHashMap(nullabilityInfo); +// NullabilityFlags nullabilityFlags = nullabilityInfo.get(variableDescriptor); +// boolean varNotNull = notNull || (nullabilityFlags != null && !nullabilityFlags.canBeNull); +// builder.put(variableDescriptor, new NullabilityFlags(!varNotNull, varNotNull)); +// return ImmutableMap.copyOf(builder); +// } +// +// private ImmutableMap getEqualsToNullMap(VariableDescriptor[] variableDescriptors, boolean notNull) { +// if (variableDescriptors.length == 0) return nullabilityInfo; +// Map builder = Maps.newHashMap(nullabilityInfo); +// for (VariableDescriptor variableDescriptor : variableDescriptors) { +// if (variableDescriptor != null) { +// NullabilityFlags nullabilityFlags = nullabilityInfo.get(variableDescriptor); +// boolean varNotNull = notNull || (nullabilityFlags != null && !nullabilityFlags.canBeNull); +// builder.put(variableDescriptor, new NullabilityFlags(!varNotNull, varNotNull)); +// } +// } +// return ImmutableMap.copyOf(builder); +// } +// +// public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor variableDescriptor, @NotNull JetType type) { +// ListMultimap newTypeInfo = copyTypeInfo(); +// newTypeInfo.put(variableDescriptor, type); +// return new DataFlowInfo(getEqualsToNullMap(variableDescriptor, !type.isNullable()), newTypeInfo); +// } +// +// public DataFlowInfo isInstanceOf(@NotNull VariableDescriptor[] variableDescriptors, @NotNull JetType type) { +// if (variableDescriptors.length == 0) return this; +// ListMultimap newTypeInfo = copyTypeInfo(); +// for (VariableDescriptor variableDescriptor : variableDescriptors) { +// if (variableDescriptor != null) { +// newTypeInfo.put(variableDescriptor, type); +// } +// } +// return new DataFlowInfo(getEqualsToNullMap(variableDescriptors, !type.isNullable()), newTypeInfo); +// } +// +// public DataFlowInfo and(DataFlowInfo other) { +// Map nullabilityMapBuilder = Maps.newHashMap(); +// nullabilityMapBuilder.putAll(nullabilityInfo); +// for (Map.Entry entry : other.nullabilityInfo.entrySet()) { +// Object key = entry.getKey(); +// NullabilityFlags otherFlags = entry.getValue(); +// NullabilityFlags thisFlags = nullabilityInfo.get(key); +// if (thisFlags != null) { +// nullabilityMapBuilder.put(key, thisFlags.and(otherFlags)); +// } +// else { +// nullabilityMapBuilder.put(key, otherFlags); +// } +// } +// +// ListMultimap newTypeInfo = copyTypeInfo(); +// newTypeInfo.putAll(other.typeInfo); +// return new DataFlowInfo(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo); +// } +// +// private ListMultimap copyTypeInfo() { +// ListMultimap newTypeInfo = Multimaps.newListMultimap(Maps.>newHashMap(), CommonSuppliers.getArrayListSupplier()); +// newTypeInfo.putAll(typeInfo); +// return newTypeInfo; +// } +// +// public DataFlowInfo or(DataFlowInfo other) { +// Map builder = Maps.newHashMap(nullabilityInfo); +// builder.keySet().retainAll(other.nullabilityInfo.keySet()); +// for (Map.Entry entry : builder.entrySet()) { +// Object key = entry.getKey(); +// NullabilityFlags thisFlags = entry.getValue(); +// NullabilityFlags otherFlags = other.nullabilityInfo.get(key); +// assert (otherFlags != null); +// builder.put(key, thisFlags.or(otherFlags)); +// } +// +// ListMultimap newTypeInfo = Multimaps.newListMultimap(Maps.>newHashMap(), CommonSuppliers.getArrayListSupplier()); +// +// Set keys = newTypeInfo.keySet(); +// keys.retainAll(other.typeInfo.keySet()); +// +// for (Object key : keys) { +// Collection thisTypes = typeInfo.get(key); +// Collection otherTypes = other.typeInfo.get(key); +// +// Collection newTypes = Sets.newHashSet(thisTypes); +// newTypes.retainAll(otherTypes); +// +// newTypeInfo.putAll(key, newTypes); +// } +// +// return new DataFlowInfo(ImmutableMap.copyOf(builder), newTypeInfo); +// } +// +// public DataFlowInfo nullabilityOnly() { +// return new DataFlowInfo(nullabilityInfo, EMPTY.copyTypeInfo()); +// } +// +// private static class NullabilityFlags { +// private final boolean canBeNull; +// private final boolean canBeNonNull; +// +// private NullabilityFlags(boolean canBeNull, boolean canBeNonNull) { +// this.canBeNull = canBeNull; +// this.canBeNonNull = canBeNonNull; +// } +// +// public boolean canBeNull() { +// return canBeNull; +// } +// +// public boolean canBeNonNull() { +// return canBeNonNull; +// } +// +// public NullabilityFlags and(NullabilityFlags other) { +// return new NullabilityFlags(this.canBeNull && other.canBeNull, this.canBeNonNull && other.canBeNonNull); +// } +// +// public NullabilityFlags or(NullabilityFlags other) { +// return new NullabilityFlags(this.canBeNull || other.canBeNull, this.canBeNonNull || other.canBeNonNull); +// } +// +// } +//} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValue.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValue.java new file mode 100644 index 00000000000..eaec974829b --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValue.java @@ -0,0 +1,72 @@ +package org.jetbrains.jet.lang.resolve.calls.autocasts; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.types.JetStandardClasses; +import org.jetbrains.jet.lang.types.JetType; + +/** + * @author abreslav + */ +public class DataFlowValue { + + public static final DataFlowValue NULL = new DataFlowValue(new Object(), JetStandardClasses.getNullableNothingType(), false, Nullability.NULL); + public static final DataFlowValue NULLABLE = new DataFlowValue(new Object(), JetStandardClasses.getNullableAnyType(), false, Nullability.UNKNOWN); + + private final boolean stableIdentifier; + private final JetType type; + private final Object id; + private final Nullability immanentNullability; + + // Use DataFlowValueFactory + /*package*/ DataFlowValue(Object id, JetType type, boolean stableIdentifier, Nullability immanentNullability) { + this.stableIdentifier = stableIdentifier; + this.type = type; + this.id = id; + this.immanentNullability = immanentNullability; + } + + @NotNull + public Nullability getImmanentNullability() { + return immanentNullability; + } + + /** + * Stable identifier is a non-literal value that is statically known to be immutable + * @return + */ + public boolean isStableIdentifier() { + return stableIdentifier; + } + + @NotNull + public JetType getType() { + return type; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DataFlowValue that = (DataFlowValue) o; + + if (stableIdentifier != that.stableIdentifier) return false; + if (id != null ? !id.equals(that.id) : that.id != null) return false; + if (type != null ? !type.equals(that.type) : that.type != null) return false; + + return true; + } + + @Override + public String toString() { + return (stableIdentifier ? "stable " : "unstable ") + (id == null ? null : id.toString()) + " " + immanentNullability; + } + + @Override + public int hashCode() { + int result = (stableIdentifier ? 1 : 0); + result = 31 * result + (type != null ? type.hashCode() : 0); + result = 31 * result + (id != null ? id.hashCode() : 0); + return result; + } +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java new file mode 100644 index 00000000000..9051c404887 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/DataFlowValueFactory.java @@ -0,0 +1,156 @@ +package org.jetbrains.jet.lang.resolve.calls.autocasts; + +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.JetNodeTypes; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.JetModuleUtil; +import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiverDescriptor; +import org.jetbrains.jet.lang.types.JetType; + +import static org.jetbrains.jet.lang.resolve.BindingContext.REFERENCE_TARGET; + +/** + * @author abreslav + */ +public class DataFlowValueFactory { + public static final DataFlowValueFactory INSTANCE = new DataFlowValueFactory(); + + private DataFlowValueFactory() {} + + @NotNull + public DataFlowValue createDataFlowValue(@NotNull JetExpression expression, @NotNull JetType type, @NotNull BindingContext bindingContext) { + if (expression instanceof JetConstantExpression) { + JetConstantExpression constantExpression = (JetConstantExpression) expression; + if (constantExpression.getNode().getElementType() == JetNodeTypes.NULL) return DataFlowValue.NULL; + } + Pair result = getIdForStableIdentifier(expression, bindingContext, false); + return new DataFlowValue(result.first == null ? expression : result.first, type, result.second, getImmanentNullability(type)); + } + + @NotNull + public DataFlowValue createDataFlowValue(@NotNull ThisReceiverDescriptor receiver) { + JetType type = receiver.getType(); + return new DataFlowValue(receiver.getDeclarationDescriptor(), type, true, getImmanentNullability(type)); + } + + @NotNull + public DataFlowValue createDataFlowValue(@NotNull VariableDescriptor variableDescriptor) { + JetType type = variableDescriptor.getOutType(); + return new DataFlowValue(variableDescriptor, type, isStableVariable(variableDescriptor), getImmanentNullability(type)); + } + +// private Object getId(@NotNull JetExpression expression, @NotNull BindingContext bindingContext) { +// if (expression instanceof JetThisExpression) { +// JetThisExpression thisExpression = (JetThisExpression) expression; +// DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, thisExpression.getThisReference()); +// if (declarationDescriptor instanceof CallableDescriptor) { +// return ((CallableDescriptor) declarationDescriptor).getReceiverParameter(); +// } +// if (declarationDescriptor instanceof ClassDescriptor) { +// return ((ClassDescriptor) declarationDescriptor).getImplicitReceiver(); +// } +//// throw new AssertionError("No resolution data for expression " + expression.getText()); +// } +// else if (expression instanceof JetReferenceExpression) { +// JetReferenceExpression referenceExpression = (JetReferenceExpression) expression; +// DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, referenceExpression); +// if (declarationDescriptor != null) { +// return declarationDescriptor; +// } +//// throw new AssertionError("No resolution data for expression " + expression.getText() + DiagnosticUtils.atLocation(expression)); +// } +// return expression; +// } + + private Nullability getImmanentNullability(JetType type) { + return type.isNullable() ? Nullability.UNKNOWN : Nullability.NOT_NULL; + } + + @NotNull + private static Pair getIdForStableIdentifier(@NotNull JetExpression expression, @NotNull BindingContext bindingContext, boolean allowNamespaces) { + if (expression instanceof JetParenthesizedExpression) { + JetParenthesizedExpression parenthesizedExpression = (JetParenthesizedExpression) expression; + JetExpression innerExpression = parenthesizedExpression.getExpression(); + if (innerExpression == null) { + return Pair.create(null, false); + } + return getIdForStableIdentifier(innerExpression, bindingContext, allowNamespaces); + } + else if (expression instanceof JetQualifiedExpression) { + JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) expression; + JetExpression selectorExpression = qualifiedExpression.getSelectorExpression(); + if (selectorExpression == null) { + return Pair.create(null, false); + } + Pair receiverId = getIdForStableIdentifier(qualifiedExpression.getReceiverExpression(), bindingContext, true); + Pair selectorId = getIdForStableIdentifier(selectorExpression, bindingContext, allowNamespaces); + return receiverId.second ? selectorId : Pair.create(receiverId.first, false); + } + if (expression instanceof JetSimpleNameExpression) { + JetSimpleNameExpression simpleNameExpression = (JetSimpleNameExpression) expression; + DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, simpleNameExpression); + if (declarationDescriptor instanceof VariableDescriptor) { + return Pair.create((Object) declarationDescriptor, isStableVariable((VariableDescriptor) declarationDescriptor)); + } + if (declarationDescriptor instanceof NamespaceDescriptor) { + return Pair.create((Object) declarationDescriptor, allowNamespaces); + } + if (declarationDescriptor instanceof ClassDescriptor) { + ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; + return Pair.create((Object) classDescriptor, classDescriptor.isClassObjectAValue()); + } + } + else if (expression instanceof JetThisExpression) { + JetThisExpression thisExpression = (JetThisExpression) expression; + DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, thisExpression.getThisReference()); + if (declarationDescriptor instanceof CallableDescriptor) { + return Pair.create((Object) ((CallableDescriptor) declarationDescriptor).getReceiverParameter(), true); + } + if (declarationDescriptor instanceof ClassDescriptor) { + return Pair.create((Object) ((ClassDescriptor) declarationDescriptor).getImplicitReceiver(), true); + } + return Pair.create(null, true); + } + else if (expression instanceof JetRootNamespaceExpression) { + return Pair.create((Object) JetModuleUtil.getRootNamespaceType(expression), allowNamespaces); + } + return Pair.create(null, false); + } + + @Nullable + public static VariableDescriptor getVariableDescriptorFromSimpleName(@NotNull BindingContext bindingContext, @NotNull JetExpression expression) { + JetExpression receiver = JetPsiUtil.deparenthesize(expression); + VariableDescriptor variableDescriptor = null; + if (receiver instanceof JetSimpleNameExpression) { + JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) receiver; + DeclarationDescriptor declarationDescriptor = bindingContext.get(REFERENCE_TARGET, nameExpression); + if (declarationDescriptor instanceof VariableDescriptor) { + variableDescriptor = (VariableDescriptor) declarationDescriptor; + } + } + return variableDescriptor; + } + + public static boolean isStableVariable(@NotNull VariableDescriptor variableDescriptor) { + if (variableDescriptor.isVar()) return false; + if (variableDescriptor instanceof PropertyDescriptor) { + PropertyDescriptor propertyDescriptor = (PropertyDescriptor) variableDescriptor; + DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration(); + if (containingDeclaration instanceof ClassDescriptor) { + ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration; + if (classDescriptor.getModality().isOverridable() && propertyDescriptor.getModality().isOverridable()) return false; + } + else { + assert !propertyDescriptor.getModality().isOverridable() : "Property outside a class must not be overridable"; + } + // TODO: check that it's internal + PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); + if (getter != null && !getter.isDefault()) return false; + } + return true; + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/Nullability.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/Nullability.java new file mode 100644 index 00000000000..52582233db3 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/autocasts/Nullability.java @@ -0,0 +1,83 @@ +package org.jetbrains.jet.lang.resolve.calls.autocasts; + +import org.jetbrains.annotations.NotNull; + +/** +* @author abreslav +*/ +public enum Nullability { + NULL(true, false), + NOT_NULL(false, true), + UNKNOWN(true, true), + IMPOSSIBLE(false, false); + + @NotNull + public static Nullability fromFlags(boolean canBeNull, boolean canBeNonNull) { + if (!canBeNull && !canBeNonNull) return IMPOSSIBLE; + if (!canBeNull && canBeNonNull) return NOT_NULL; + if (canBeNull && !canBeNonNull) return NULL; + return UNKNOWN; + } + + private final boolean canBeNull; + private final boolean canBeNonNull; + + Nullability(boolean canBeNull, boolean canBeNonNull) { + this.canBeNull = canBeNull; + this.canBeNonNull = canBeNonNull; + } + + public boolean canBeNull() { + return canBeNull; + } + + public boolean canBeNonNull() { + return canBeNonNull; + } + + @NotNull + public Nullability refine(@NotNull Nullability other) { + switch (this) { + case UNKNOWN: + return other; + case IMPOSSIBLE: + return other; + case NULL: + switch (other) { + case NOT_NULL: return NOT_NULL; + default: return NULL; + } + case NOT_NULL: + switch (other) { + case NULL: return NOT_NULL; + default: return NOT_NULL; + } + } + throw new IllegalStateException(); + } + + @NotNull + public Nullability invert() { + switch (this) { + case NULL: + return NOT_NULL; + case NOT_NULL: + return UNKNOWN; + case UNKNOWN: + return UNKNOWN; + case IMPOSSIBLE: + return UNKNOWN; + } + throw new IllegalStateException(); + } + + @NotNull + public Nullability and(@NotNull Nullability other) { + return fromFlags(this.canBeNull && other.canBeNull, this.canBeNonNull && other.canBeNonNull); + } + + @NotNull + public Nullability or(@NotNull Nullability other) { + return fromFlags(this.canBeNull || other.canBeNull, this.canBeNonNull || other.canBeNonNull); + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ClassReceiver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ClassReceiver.java index ac1ac9c5b4a..2f5f916a626 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ClassReceiver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ClassReceiver.java @@ -2,12 +2,13 @@ package org.jetbrains.jet.lang.resolve.scopes.receivers; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.types.JetType; /** * @author abreslav */ -public class ClassReceiver implements ReceiverDescriptor { +public class ClassReceiver implements ThisReceiverDescriptor { private final ClassDescriptor classDescriptor; @@ -26,6 +27,12 @@ public class ClassReceiver implements ReceiverDescriptor { return classDescriptor.getDefaultType(); } + @NotNull + @Override + public DeclarationDescriptor getDeclarationDescriptor() { + return classDescriptor; + } + @Override public R accept(@NotNull ReceiverDescriptorVisitor visitor, D data) { return visitor.visitClassReceiver(this, data); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ExtensionReceiver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ExtensionReceiver.java index 834b1420c42..ed7e876e29c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ExtensionReceiver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ExtensionReceiver.java @@ -2,15 +2,25 @@ package org.jetbrains.jet.lang.resolve.scopes.receivers; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.CallableDescriptor; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.types.JetType; /** * @author abreslav */ -public class ExtensionReceiver extends ImplicitReceiverDescriptor { +public class ExtensionReceiver extends AbstractReceiverDescriptor implements ThisReceiverDescriptor { + + private final CallableDescriptor descriptor; public ExtensionReceiver(@NotNull CallableDescriptor callableDescriptor, @NotNull JetType receiverType) { - super(callableDescriptor, receiverType); + super(receiverType); + this.descriptor = callableDescriptor; + } + + @NotNull + @Override + public DeclarationDescriptor getDeclarationDescriptor() { + return descriptor; } @Override diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ImplicitReceiverDescriptor.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ImplicitReceiverDescriptor.java deleted file mode 100644 index 4c566658f83..00000000000 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ImplicitReceiverDescriptor.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.jetbrains.jet.lang.resolve.scopes.receivers; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; -import org.jetbrains.jet.lang.types.JetType; - -/** - * Describes a "this" receiver - * - * @author abreslav - */ -public abstract class ImplicitReceiverDescriptor extends AbstractReceiverDescriptor { - private final DeclarationDescriptor declarationDescriptor; - - protected ImplicitReceiverDescriptor(@NotNull DeclarationDescriptor declarationDescriptor, @NotNull JetType receiverType) { - super(receiverType); - this.declarationDescriptor = declarationDescriptor; - } - - @NotNull - public DeclarationDescriptor getDeclarationDescriptor() { - return declarationDescriptor; - } -} - diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ThisReceiverDescriptor.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ThisReceiverDescriptor.java new file mode 100644 index 00000000000..a6cff4192e5 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/scopes/receivers/ThisReceiverDescriptor.java @@ -0,0 +1,15 @@ +package org.jetbrains.jet.lang.resolve.scopes.receivers; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; + +/** + * Describes a "this" receiver + * + * @author abreslav + */ +public interface ThisReceiverDescriptor extends ReceiverDescriptor { + @NotNull + DeclarationDescriptor getDeclarationDescriptor(); +} + diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index 0e69cc0ab93..3a16b72097b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -10,7 +10,7 @@ import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.calls.CallMaker; -import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults; import org.jetbrains.jet.lang.resolve.constants.*; import org.jetbrains.jet.lang.resolve.scopes.JetScope; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java index 8d915dbe9b0..8fb7c58659b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ControlStructureTypingVisitor.java @@ -7,7 +7,7 @@ import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.VariableDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScope; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingContext.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingContext.java index 68a1be872b1..e4a7da67786 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingContext.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingContext.java @@ -12,7 +12,7 @@ import org.jetbrains.jet.lang.resolve.ClassDescriptorResolver; import org.jetbrains.jet.lang.resolve.TypeResolver; import org.jetbrains.jet.lang.resolve.calls.CallMaker; import org.jetbrains.jet.lang.resolve.calls.CallResolver; -import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults; import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstantResolver; import org.jetbrains.jet.lang.resolve.scopes.JetScope; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingInternals.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingInternals.java index 8dff3d7a264..200174c6bc5 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingInternals.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingInternals.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; -import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.JetType; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java index c4e0141a02f..0ea1a9c113a 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java @@ -14,7 +14,7 @@ import org.jetbrains.jet.lang.diagnostics.Diagnostic; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; -import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; @@ -56,7 +56,7 @@ public class ExpressionTypingServices { ExpressionTypingContext context = ExpressionTypingContext.newContext( semanticServices, new HashMap(), new HashMap>(), new LabelResolver(), - trace, scope, DataFlowInfo.getEmpty(), expectedType, FORBIDDEN, false + trace, scope, DataFlowInfo.EMPTY, expectedType, FORBIDDEN, false ); return expressionTypingFacade.getType(expression, context); } @@ -65,14 +65,14 @@ public class ExpressionTypingServices { ExpressionTypingContext context = ExpressionTypingContext.newContext( semanticServices, new HashMap(), new HashMap>(), new LabelResolver(), - trace, scope, DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, FORBIDDEN, + trace, scope, DataFlowInfo.EMPTY, NO_EXPECTED_TYPE, FORBIDDEN, true); return expressionTypingFacade.getType(expression, context); // return ((ExpressionTypingContext) ExpressionTyperVisitorWithNamespaces).INSTANCE.getType(expression, ExpressionTypingContext.newRootContext(semanticServices, trace, scope, DataFlowInfo.getEmpty(), TypeUtils.NO_EXPECTED_TYPE, TypeUtils.NO_EXPECTED_TYPE)); } public void checkFunctionReturnType(@NotNull JetScope outerScope, @NotNull JetDeclarationWithBody function, @NotNull FunctionDescriptor functionDescriptor) { - checkFunctionReturnType(outerScope, function, functionDescriptor, DataFlowInfo.getEmpty()); + checkFunctionReturnType(outerScope, function, functionDescriptor, DataFlowInfo.EMPTY); } @NotNull @@ -87,7 +87,7 @@ public class ExpressionTypingServices { public void checkFunctionReturnType(JetScope functionInnerScope, JetDeclarationWithBody function, @NotNull final JetType expectedReturnType) { checkFunctionReturnType(function, ExpressionTypingContext.newContext( semanticServices, new HashMap(), new HashMap>(), new LabelResolver(), - trace, functionInnerScope, DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, expectedReturnType, false + trace, functionInnerScope, DataFlowInfo.EMPTY, NO_EXPECTED_TYPE, expectedReturnType, false )); } @@ -145,7 +145,7 @@ public class ExpressionTypingServices { assert bodyExpression != null; JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(outerScope, functionDescriptor, trace); expressionTypingFacade.getType(bodyExpression, ExpressionTypingContext.newContext(semanticServices, new HashMap(), new HashMap>(), new LabelResolver(), - trace, functionInnerScope, DataFlowInfo.getEmpty(), NO_EXPECTED_TYPE, FORBIDDEN, false)); + trace, functionInnerScope, DataFlowInfo.EMPTY, NO_EXPECTED_TYPE, FORBIDDEN, false)); //todo function literals final Collection returnedExpressions = Lists.newArrayList(); if (function.hasBlockBody()) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java index 1b181c887b0..7ce0dce8bc2 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java @@ -1,5 +1,6 @@ package org.jetbrains.jet.lang.types.expressions; +import com.intellij.openapi.util.Ref; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; @@ -12,20 +13,22 @@ import org.jetbrains.jet.lang.descriptors.VariableDescriptor; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.TraceBasedRedeclarationHandler; -import org.jetbrains.jet.lang.resolve.calls.AutoCastUtils; -import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory; import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.JetTypeChecker; import org.jetbrains.jet.lang.types.TypeUtils; import org.jetbrains.jet.lexer.JetTokens; import java.util.List; -import static org.jetbrains.jet.lang.diagnostics.Errors.RESULT_TYPE_MISMATCH; -import static org.jetbrains.jet.lang.diagnostics.Errors.TYPE_MISMATCH; +import static org.jetbrains.jet.lang.diagnostics.Errors.*; +import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST; import static org.jetbrains.jet.lang.resolve.BindingContext.MUST_BE_WRAPPED_IN_A_REF; /** @@ -100,13 +103,13 @@ public class ExpressionTypingUtils { @NotNull public static DataFlowInfo extractDataFlowInfoFromCondition(@Nullable JetExpression condition, final boolean conditionValue, @Nullable final WritableScope scopeToExtend, final ExpressionTypingContext context) { if (condition == null) return context.dataFlowInfo; - final DataFlowInfo[] result = new DataFlowInfo[] {context.dataFlowInfo}; + final Ref result = new Ref(context.dataFlowInfo); condition.accept(new JetVisitorVoid() { @Override public void visitIsExpression(JetIsExpression expression) { if (conditionValue && !expression.isNegated() || !conditionValue && expression.isNegated()) { JetPattern pattern = expression.getPattern(); - result[0] = context.patternsToDataFlowInfo.get(pattern); + result.set(context.patternsToDataFlowInfo.get(pattern)); if (scopeToExtend != null) { List descriptors = context.patternsToBoundVariableLists.get(pattern); if (descriptors != null) { @@ -143,68 +146,94 @@ public class ExpressionTypingUtils { } dataFlowInfo = operator.compose(dataFlowInfo, rightInfo); } - result[0] = dataFlowInfo; + result.set(dataFlowInfo); } - else if (operationToken == JetTokens.EQEQ - || operationToken == JetTokens.EXCLEQ - || operationToken == JetTokens.EQEQEQ - || operationToken == JetTokens.EXCLEQEQEQ) { + else { JetExpression left = expression.getLeft(); JetExpression right = expression.getRight(); if (right == null) return; - if (!(left instanceof JetSimpleNameExpression)) { - JetExpression tmp = left; - left = right; - right = tmp; - - if (!(left instanceof JetSimpleNameExpression)) { - return; - } - } - - VariableDescriptor variableDescriptor = AutoCastUtils.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), left); - if (variableDescriptor == null) return; - - // TODO : validate that DF makes sense for this variable: local, val, internal w/backing field, etc - - // Comparison to a non-null expression + JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left); + if (lhsType == null) return; JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right); - if (rhsType != null && !rhsType.isNullable()) { - extendDataFlowWithNullComparison(operationToken, variableDescriptor, !conditionValue); - return; - } + if (rhsType == null) return; - VariableDescriptor rightVariable = AutoCastUtils.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), right); - if (rightVariable != null) { - JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left); - if (lhsType != null && !lhsType.isNullable()) { - extendDataFlowWithNullComparison(operationToken, rightVariable, !conditionValue); - return; + BindingContext bindingContext = context.trace.getBindingContext(); + DataFlowValue leftValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(left, lhsType, bindingContext); + DataFlowValue rightValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(right, rhsType, bindingContext); + + Boolean equals = null; + if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) { + equals = true; + } + else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) { + equals = false; + } + if (equals != null) { + if (equals == conditionValue) { // this means: equals && conditionValue || !equals && !conditionValue + result.set(context.dataFlowInfo.equate(leftValue, rightValue)); + } + else { + result.set(context.dataFlowInfo.disequate(leftValue, rightValue)); } - } - // Comparison to 'null' - if (!(right instanceof JetConstantExpression)) { - return; - } - JetConstantExpression constantExpression = (JetConstantExpression) right; - if (constantExpression.getNode().getElementType() != JetNodeTypes.NULL) { - return; - } + } - extendDataFlowWithNullComparison(operationToken, variableDescriptor, conditionValue); +// if (!(left instanceof JetSimpleNameExpression)) { +// JetExpression tmp = left; +// left = right; +// right = tmp; +// +// if (!(left instanceof JetSimpleNameExpression)) { +// return; +// } +// } +// +// VariableDescriptor variableDescriptor = DataFlowValueFactory.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), left); +// if (variableDescriptor == null) return; +// +// // TODO : validate that DF makes sense for this variable: local, val, internal w/backing field, etc +// +// // Comparison to a non-null expression +// JetType rhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, right); +// if (rhsType != null && !rhsType.isNullable()) { +// extendDataFlowWithNullComparison(operationToken, variableDescriptor, !conditionValue); +// return; +// } +// +// VariableDescriptor rightVariable = DataFlowValueFactory.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), right); +// if (rightVariable != null) { +// JetType lhsType = context.trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, left); +// if (lhsType != null && !lhsType.isNullable()) { +// extendDataFlowWithNullComparison(operationToken, rightVariable, !conditionValue); +// return; +// } +// } +// +// // Comparison to 'null' +// if (!(right instanceof JetConstantExpression)) { +// return; +// } +// JetConstantExpression constantExpression = (JetConstantExpression) right; +// if (constantExpression.getNode().getElementType() != JetNodeTypes.NULL) { +// return; +// } +// +// extendDataFlowWithNullComparison(operationToken, variableDescriptor, conditionValue); } } - private void extendDataFlowWithNullComparison(IElementType operationToken, @NotNull VariableDescriptor variableDescriptor, boolean equalsToNull) { - if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) { - result[0] = context.dataFlowInfo.equalsToNull(variableDescriptor, !equalsToNull); - } - else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) { - result[0] = context.dataFlowInfo.equalsToNull(variableDescriptor, equalsToNull); - } - } +// private void extendDataFlowWithNullComparison(IElementType operationToken, @NotNull VariableDescriptor variableDescriptor, boolean equalsToNull) { +// boolean equality; +// if (operationToken == JetTokens.EQEQ || operationToken == JetTokens.EQEQEQ) { +//// result[0] = context.dataFlowInfo.establishEqualityToNull(variableDescriptor, !equalsToNull); +// equality = true; +// } +// else if (operationToken == JetTokens.EXCLEQ || operationToken == JetTokens.EXCLEQEQEQ) { +//// result[0] = context.dataFlowInfo.establishEqualityToNull(variableDescriptor, equalsToNull); +// equality = false; +// } +// } @Override public void visitUnaryExpression(JetUnaryExpression expression) { @@ -212,7 +241,7 @@ public class ExpressionTypingUtils { if (operationTokenType == JetTokens.EXCL) { JetExpression baseExpression = expression.getBaseExpression(); if (baseExpression != null) { - result[0] = extractDataFlowInfoFromCondition(baseExpression, !conditionValue, scopeToExtend, context); + result.set(extractDataFlowInfoFromCondition(baseExpression, !conditionValue, scopeToExtend, context)); } } } @@ -225,10 +254,10 @@ public class ExpressionTypingUtils { } } }); - if (result[0] == null) { + if (result.get() == null) { return context.dataFlowInfo; } - return result[0]; + return result.get(); } public static boolean isTypeFlexible(@Nullable JetExpression expression) { @@ -246,17 +275,27 @@ public class ExpressionTypingUtils { context.semanticServices.getTypeChecker().isSubtypeOf(expressionType, context.expectedType)) { return expressionType; } - if (AutoCastUtils.castExpression(expression, context.expectedType, context.dataFlowInfo, context.trace) == null) { - context.trace.report(TYPE_MISMATCH.on(expression, context.expectedType, expressionType)); - return expressionType; + + DataFlowValue dataFlowValue = DataFlowValueFactory.INSTANCE.createDataFlowValue(expression, expressionType, context.trace.getBindingContext()); + for (JetType possibleType : context.dataFlowInfo.getPossibleTypes(dataFlowValue)) { + if (JetTypeChecker.INSTANCE.isSubtypeOf(possibleType, context.expectedType)) { + if (dataFlowValue.isStableIdentifier()) { + context.trace.record(AUTOCAST, expression, possibleType); + } + else { + context.trace.report(AUTOCAST_IMPOSSIBLE.on(expression, possibleType, expression.getText())); + } + return possibleType; + } } - return context.expectedType; + context.trace.report(TYPE_MISMATCH.on(expression, context.expectedType, expressionType)); + return expressionType; } public static void checkWrappingInRef(JetExpression expression, ExpressionTypingContext context) { if (!(expression instanceof JetSimpleNameExpression)) return; JetSimpleNameExpression simpleName = (JetSimpleNameExpression) expression; - VariableDescriptor variable = AutoCastUtils.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), simpleName); + VariableDescriptor variable = DataFlowValueFactory.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), simpleName); if (variable != null) { DeclarationDescriptor containingDeclaration = variable.getContainingDeclaration(); if (context.scope.getContainingDeclaration() != containingDeclaration && containingDeclaration instanceof CallableDescriptor) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorDispatcher.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorDispatcher.java index 0a8b5efd150..735f0e5e6b3 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorDispatcher.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingVisitorDispatcher.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.types.DeferredType; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java index 3cd9cc80cab..5d1783f026b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java @@ -2,12 +2,14 @@ package org.jetbrains.jet.lang.types.expressions; import com.google.common.collect.Sets; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Ref; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.VariableDescriptor; import org.jetbrains.jet.lang.psi.*; -import org.jetbrains.jet.lang.resolve.calls.AutoCastUtils; -import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory; import org.jetbrains.jet.lang.resolve.scopes.WritableScope; import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl; import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver; @@ -19,8 +21,6 @@ import java.util.List; import java.util.Set; import static org.jetbrains.jet.lang.diagnostics.Errors.*; -import static org.jetbrains.jet.lang.diagnostics.Errors.INCOMPATIBLE_TYPES; -import static org.jetbrains.jet.lang.diagnostics.Errors.UNSUPPORTED; import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.ensureBooleanResultWithCustomSubject; import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.newWritableScopeImpl; @@ -35,11 +35,12 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { @Override public JetType visitIsExpression(JetIsExpression expression, ExpressionTypingContext contextWithExpectedType) { ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(TypeUtils.NO_EXPECTED_TYPE); - JetType knownType = facade.safeGetType(expression.getLeftHandSide(), context.replaceScope(context.scope)); + JetExpression leftHandSide = expression.getLeftHandSide(); + JetType knownType = facade.safeGetType(leftHandSide, context.replaceScope(context.scope)); JetPattern pattern = expression.getPattern(); if (pattern != null) { WritableScopeImpl scopeToExtend = newWritableScopeImpl(context).setDebugName("Scope extended in 'is'"); - DataFlowInfo newDataFlowInfo = checkPatternType(pattern, knownType, scopeToExtend, context, AutoCastUtils.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), expression.getLeftHandSide())); + DataFlowInfo newDataFlowInfo = checkPatternType(pattern, knownType, scopeToExtend, context, DataFlowValueFactory.INSTANCE.createDataFlowValue(leftHandSide, knownType, context.trace.getBindingContext())); context.patternsToDataFlowInfo.put(pattern, newDataFlowInfo); context.patternsToBoundVariableLists.put(pattern, scopeToExtend.getDeclaredVariables()); } @@ -53,7 +54,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { final JetExpression subjectExpression = expression.getSubjectExpression(); final JetType subjectType = subjectExpression != null ? context.getServices().safeGetType(context.scope, subjectExpression, TypeUtils.NO_EXPECTED_TYPE) : ErrorUtils.createErrorType("Unknown type"); - final VariableDescriptor variableDescriptor = subjectExpression != null ? AutoCastUtils.getVariableDescriptorFromSimpleName(context.trace.getBindingContext(), subjectExpression) : null; + final DataFlowValue variableDescriptor = subjectExpression != null ? DataFlowValueFactory.INSTANCE.createDataFlowValue(subjectExpression, subjectType, context.trace.getBindingContext()) : null; // TODO : exhaustive patterns @@ -108,7 +109,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { return null; } - private DataFlowInfo checkWhenCondition(@Nullable final JetExpression subjectExpression, final JetType subjectType, JetWhenCondition condition, final WritableScope scopeToExtend, final ExpressionTypingContext context, final VariableDescriptor... subjectVariables) { + private DataFlowInfo checkWhenCondition(@Nullable final JetExpression subjectExpression, final JetType subjectType, JetWhenCondition condition, final WritableScope scopeToExtend, final ExpressionTypingContext context, final DataFlowValue... subjectVariables) { final DataFlowInfo[] newDataFlowInfo = new DataFlowInfo[]{context.dataFlowInfo}; condition.accept(new JetVisitorVoid() { @@ -151,8 +152,8 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { return newDataFlowInfo[0]; } - private DataFlowInfo checkPatternType(@NotNull JetPattern pattern, @NotNull final JetType subjectType, @NotNull final WritableScope scopeToExtend, final ExpressionTypingContext context, @NotNull final VariableDescriptor... subjectVariables) { - final DataFlowInfo[] result = new DataFlowInfo[] {context.dataFlowInfo}; + private DataFlowInfo checkPatternType(@NotNull JetPattern pattern, @NotNull final JetType subjectType, @NotNull final WritableScope scopeToExtend, final ExpressionTypingContext context, @NotNull final DataFlowValue... subjectVariables) { + final Ref result = new Ref(context.dataFlowInfo); pattern.accept(new JetVisitorVoid() { @Override public void visitTypePattern(JetTypePattern typePattern) { @@ -160,7 +161,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { if (typeReference != null) { JetType type = context.getTypeResolver().resolveType(context.scope, typeReference); checkTypeCompatibility(type, subjectType, typePattern); - result[0] = context.dataFlowInfo.isInstanceOf(subjectVariables, type); + result.set(context.dataFlowInfo.establishSubtyping(subjectVariables, type)); } } @@ -187,7 +188,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { JetPattern entryPattern = entry.getPattern(); if (entryPattern != null) { - result[0] = result[0].and(checkPatternType(entryPattern, type, scopeToExtend, context)); + result.set(result.get().and(checkPatternType(entryPattern, type, scopeToExtend, context))); } } } @@ -200,7 +201,9 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { ReceiverDescriptor receiver = new TransientReceiver(subjectType); JetType selectorReturnType = facade.getSelectorReturnType(receiver, null, decomposerExpression, context); - result[0] = checkPatternType(pattern.getArgumentList(), selectorReturnType == null ? ErrorUtils.createErrorType("No type") : selectorReturnType, scopeToExtend, context); + result.set(checkPatternType(pattern.getArgumentList(), selectorReturnType == null + ? ErrorUtils.createErrorType("No type") + : selectorReturnType, scopeToExtend, context)); } } @@ -235,10 +238,10 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { JetWhenCondition condition = pattern.getCondition(); if (condition != null) { int oldLength = subjectVariables.length; - VariableDescriptor[] newSubjectVariables = new VariableDescriptor[oldLength + 1]; + DataFlowValue[] newSubjectVariables = new DataFlowValue[oldLength + 1]; System.arraycopy(subjectVariables, 0, newSubjectVariables, 0, oldLength); - newSubjectVariables[oldLength] = variableDescriptor; - result[0] = checkWhenCondition(null, subjectType, condition, scopeToExtend, context, newSubjectVariables); + newSubjectVariables[oldLength] = DataFlowValueFactory.INSTANCE.createDataFlowValue(variableDescriptor); + result.set(checkWhenCondition(null, subjectType, condition, scopeToExtend, context, newSubjectVariables)); } } @@ -259,7 +262,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { context.trace.report(UNSUPPORTED.on(element, getClass().getCanonicalName())); } }); - return result[0]; + return result.get(); } } diff --git a/idea/testData/checker/infos/Autocasts.jet b/idea/testData/checker/infos/Autocasts.jet index 062b26b75a2..a7e1c07bd34 100644 --- a/idea/testData/checker/infos/Autocasts.jet +++ b/idea/testData/checker/infos/Autocasts.jet @@ -189,11 +189,11 @@ fun tuples(a: Any?) { val s: (Any, String) = (a, a) } if (a is String) { - val s: (Any, String) = (a, a) + val s: (Any, String) = (a, a) } fun illegalTupleReturnType(): (Any, String) = (a, a) if (a is String) { - fun legalTupleReturnType(): (Any, String) = (a, a) + fun legalTupleReturnType(): (Any, String) = (a, a) } val illegalFunctionLiteral: Function0 = { a } val illegalReturnValueInFunctionLiteral: Function0 = { (): Int => a } diff --git a/idea/testData/checkerWithErrorTypes/quick/AutocastsForStableIdentifiers.jet b/idea/testData/checkerWithErrorTypes/quick/AutocastsForStableIdentifiers.jet new file mode 100644 index 00000000000..6bb9d5f51db --- /dev/null +++ b/idea/testData/checkerWithErrorTypes/quick/AutocastsForStableIdentifiers.jet @@ -0,0 +1,91 @@ +namespace example; + +namespace ns { + val y : Any? = 2 +} + +object Obj { + val y : Any? = 2 +} + +class AClass() { + class object { + val y : Any? = 2 + } +} + +val x : Any? = 1 + +fun Any?.vars(a: Any?) : Int { + var b: Int = 0 + if (ns.y is Int) { + b = ns.y + } + if (ns.y is Int) { + b = example.ns.y + } + if (example.ns.y is Int) { + b = ns.y + } + if (example.ns.y is Int) { + b = example.ns.y + } +// if (namespace.bottles.ns.y is Int) { +// b = ns.y +// } + if (Obj.y is Int) { + b = Obj.y + } + if (example.Obj.y is Int) { + b = Obj.y + } + if (AClass.y is Int) { + b = AClass.y + } + if (example.AClass.y is Int) { + b = AClass.y + } + if (x is Int) { + b = x + } + if (example.x is Int) { + b = x + } + if (example.x is Int) { + b = example.x + } + return 1 +} + +fun Any?.foo() : Int { + if (this is Int) + return this + if (this@foo is Int) + return this + if (this@foo is Int) + return this@foo + if (this is Int) + return this@foo + return 1 +} + + +trait T {} + +open class C { + fun foo() { + var t : T? = null + if (this is T) { + t = this + } + if (this is T) { + t = this@C + } + if (this@C is T) { + t = this + } + if (this@C is T) { + t = this@C + } + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java b/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java index 2f07aa92e9f..f921ed4ba05 100644 --- a/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java +++ b/idea/tests/org/jetbrains/jet/resolve/JetResolveTest.java @@ -17,7 +17,7 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor; import org.jetbrains.jet.lang.resolve.calls.CallResolver; -import org.jetbrains.jet.lang.resolve.calls.DataFlowInfo; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.calls.OverloadResolutionResults; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; @@ -111,7 +111,7 @@ public class JetResolveTest extends ExtensibleResolveTestCase { List parameterTypeList = Arrays.asList(parameterType); // JetTypeInferrer.Services typeInferrerServices = JetSemanticServices.createSemanticServices(getProject()).getTypeInferrerServices(new BindingTraceContext()); - CallResolver callResolver = new CallResolver(JetSemanticServices.createSemanticServices(getProject()), DataFlowInfo.getEmpty()); + CallResolver callResolver = new CallResolver(JetSemanticServices.createSemanticServices(getProject()), DataFlowInfo.EMPTY); OverloadResolutionResults functions = callResolver.resolveExactSignature( classDescriptor.getMemberScope(typeArguments), ReceiverDescriptor.NO_RECEIVER, name, parameterTypeList); for (ResolvedCall resolvedCall : functions.getResults()) {