Autocasts for 'this' and stable qualified expressions

This commit is contained in:
Andrey Breslav
2011-10-18 22:30:28 +04:00
parent abbb31080f
commit 1b4bebaaf0
29 changed files with 1094 additions and 494 deletions
@@ -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
@@ -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";
@@ -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<ReceiverDescriptor> getAutoCastVariants(@NotNull final BindingContext bindingContext, @NotNull final DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiverToCast) {
return receiverToCast.accept(new ReceiverDescriptorVisitor<List<ReceiverDescriptor>, Object>() {
@Override
public List<ReceiverDescriptor> visitNoReceiver(ReceiverDescriptor noReceiver, Object data) {
return Collections.emptyList();
}
@Override
public List<ReceiverDescriptor> visitTransientReceiver(TransientReceiver receiver, Object data) {
return Collections.emptyList();
}
@Override
public List<ReceiverDescriptor> visitExtensionReceiver(ExtensionReceiver receiver, Object data) {
return castThis(dataFlowInfo, receiver);
}
@Override
public List<ReceiverDescriptor> visitClassReceiver(ClassReceiver receiver, Object data) {
return castThis(dataFlowInfo, receiver);
}
@Override
public List<ReceiverDescriptor> visitExpressionReceiver(ExpressionReceiver receiver, Object data) {
JetExpression expression = receiver.getExpression();
VariableDescriptor variableDescriptor = getVariableDescriptorFromSimpleName(bindingContext, expression);
if (variableDescriptor != null) {
List<ReceiverDescriptor> 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<ReceiverDescriptor> castThis(@NotNull DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiver) {
assert receiver.exists();
List<ReceiverDescriptor> 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<JetType> 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;
}
}
@@ -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<FunctionDescriptor>(TaskPrioritizer.convertWithImpliedThis(scope, Collections.<ReceiverDescriptor>singletonList(NO_RECEIVER), constructors), call, DataFlowInfo.getEmpty()));
prioritizedTasks.add(new ResolutionTask<FunctionDescriptor>(TaskPrioritizer.convertWithImpliedThis(scope, Collections.<ReceiverDescriptor>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<FunctionDescriptor>(ResolvedCall.convertCollection(constructors), call, DataFlowInfo.getEmpty()));
prioritizedTasks = Collections.singletonList(new ResolutionTask<FunctionDescriptor>(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 {
@@ -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.<Object, NullabilityFlags>of(), Multimaps.newListMultimap(Collections.<Object, Collection<JetType>>emptyMap(), CommonSuppliers.<JetType>getArrayListSupplier()));
public static DataFlowInfo getEmpty() {
return EMPTY;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private final ImmutableMap<Object, NullabilityFlags> nullabilityInfo;
private final ListMultimap<Object, JetType> typeInfo;
private DataFlowInfo(ImmutableMap<Object, NullabilityFlags> nullabilityInfo, ListMultimap<Object, JetType> 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<JetType> getPossibleTypes(Object key, @NotNull JetType originalType) {
List<JetType> types = typeInfo.get(key);
NullabilityFlags nullabilityFlags = nullabilityInfo.get(key);
if (nullabilityFlags == null || nullabilityFlags.canBeNull()) {
return types;
}
List<JetType> 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<JetType> getPossibleTypesForVariable(@NotNull VariableDescriptor variableDescriptor) {
return getPossibleTypes(variableDescriptor, variableDescriptor.getOutType());
}
public List<JetType> 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<Object, NullabilityFlags> getEqualsToNullMap(VariableDescriptor variableDescriptor, boolean notNull) {
Map<Object, NullabilityFlags> 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<Object, NullabilityFlags> getEqualsToNullMap(VariableDescriptor[] variableDescriptors, boolean notNull) {
if (variableDescriptors.length == 0) return nullabilityInfo;
Map<Object, NullabilityFlags> 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<Object, JetType> 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<Object, JetType> 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<Object, NullabilityFlags> nullabilityMapBuilder = Maps.newHashMap();
nullabilityMapBuilder.putAll(nullabilityInfo);
for (Map.Entry<Object, NullabilityFlags> 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<Object, JetType> newTypeInfo = copyTypeInfo();
newTypeInfo.putAll(other.typeInfo);
return new DataFlowInfo(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo);
}
private ListMultimap<Object, JetType> copyTypeInfo() {
ListMultimap<Object, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<Object, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
newTypeInfo.putAll(typeInfo);
return newTypeInfo;
}
public DataFlowInfo or(DataFlowInfo other) {
Map<Object, NullabilityFlags> builder = Maps.newHashMap(nullabilityInfo);
builder.keySet().retainAll(other.nullabilityInfo.keySet());
for (Map.Entry<Object, NullabilityFlags> 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<Object, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<Object, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
Set<Object> keys = newTypeInfo.keySet();
keys.retainAll(other.typeInfo.keySet());
for (Object key : keys) {
Collection<JetType> thisTypes = typeInfo.get(key);
Collection<JetType> otherTypes = other.typeInfo.get(key);
Collection<JetType> 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);
}
}
}
@@ -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;
@@ -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;
@@ -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
@@ -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;
@@ -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<ReceiverDescriptor> getAutoCastVariants(@NotNull final BindingContext bindingContext, @NotNull final DataFlowInfo dataFlowInfo, @NotNull ReceiverDescriptor receiverToCast) {
return receiverToCast.accept(new ReceiverDescriptorVisitor<List<ReceiverDescriptor>, Object>() {
@Override
public List<ReceiverDescriptor> visitNoReceiver(ReceiverDescriptor noReceiver, Object data) {
return Collections.emptyList();
}
@Override
public List<ReceiverDescriptor> visitTransientReceiver(TransientReceiver receiver, Object data) {
return Collections.emptyList();
}
@Override
public List<ReceiverDescriptor> visitExtensionReceiver(ExtensionReceiver receiver, Object data) {
return castThis(dataFlowInfo, receiver);
}
@Override
public List<ReceiverDescriptor> visitClassReceiver(ClassReceiver receiver, Object data) {
return castThis(dataFlowInfo, receiver);
}
@Override
public List<ReceiverDescriptor> visitExpressionReceiver(ExpressionReceiver receiver, Object data) {
// JetExpression expression = receiver.getExpression();
// VariableDescriptor variableDescriptor = DataFlowValueFactory.getVariableDescriptorFromSimpleName(bindingContext, expression);
// if (variableDescriptor != null) {
// List<ReceiverDescriptor> 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<ReceiverDescriptor> result = Lists.newArrayList();
for (JetType possibleType : dataFlowInfo.getPossibleTypes(dataFlowValue)) {
result.add(new AutoCastReceiver(receiver, possibleType, dataFlowValue.isStableIdentifier()));
}
return result;
}
}, null);
}
private static List<ReceiverDescriptor> castThis(@NotNull DataFlowInfo dataFlowInfo, @NotNull ThisReceiverDescriptor receiver) {
assert receiver.exists();
List<ReceiverDescriptor> 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<JetType> 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;
// }
}
@@ -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.<DataFlowValue, Nullability>of(), Multimaps.newListMultimap(Collections.<DataFlowValue, Collection<JetType>>emptyMap(), CommonSuppliers.<JetType>getArrayListSupplier()));
private final ImmutableMap<DataFlowValue, Nullability> nullabilityInfo;
private final ListMultimap<DataFlowValue, JetType> typeInfo;
private DataFlowInfo(ImmutableMap<DataFlowValue, Nullability> nullabilityInfo, ListMultimap<DataFlowValue, JetType> 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<DataFlowValue, Nullability> map, @NotNull DataFlowValue value, @NotNull Nullability nullability) {
if (!value.isStableIdentifier()) return false;
return map.put(value, nullability) != nullability;
}
@NotNull
public List<JetType> getPossibleTypes(DataFlowValue key) {
JetType originalType = key.getType();
List<JetType> types = typeInfo.get(key);
Nullability nullability = getNullability(key);
if (nullability.canBeNull()) {
return types;
}
List<JetType> 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<DataFlowValue, Nullability> 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<DataFlowValue, Nullability> 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<DataFlowValue, JetType> newTypeInfo = copyTypeInfo();
Map<DataFlowValue, Nullability> 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<DataFlowValue, JetType> copyTypeInfo() {
ListMultimap<DataFlowValue, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<DataFlowValue, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
newTypeInfo.putAll(typeInfo);
return newTypeInfo;
}
public DataFlowInfo and(DataFlowInfo other) {
Map<DataFlowValue, Nullability> nullabilityMapBuilder = Maps.newHashMap();
nullabilityMapBuilder.putAll(nullabilityInfo);
for (Map.Entry<DataFlowValue, Nullability> 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<DataFlowValue, JetType> newTypeInfo = copyTypeInfo();
newTypeInfo.putAll(other.typeInfo);
return new DataFlowInfo(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo);
}
public DataFlowInfo or(DataFlowInfo other) {
Map<DataFlowValue, Nullability> builder = Maps.newHashMap(nullabilityInfo);
builder.keySet().retainAll(other.nullabilityInfo.keySet());
for (Map.Entry<DataFlowValue, Nullability> 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<DataFlowValue, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<DataFlowValue, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
Set<DataFlowValue> keys = newTypeInfo.keySet();
keys.retainAll(other.typeInfo.keySet());
for (DataFlowValue key : keys) {
Collection<JetType> thisTypes = typeInfo.get(key);
Collection<JetType> otherTypes = other.typeInfo.get(key);
Collection<JetType> 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.<Object, NullabilityFlags>of(), Multimaps.newListMultimap(Collections.<Object, Collection<JetType>>emptyMap(), CommonSuppliers.<JetType>getArrayListSupplier()));
//
// public static DataFlowInfo getEmpty() {
// return EMPTY;
// }
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// private final ImmutableMap<Object, NullabilityFlags> nullabilityInfo;
//
// private final ListMultimap<Object, JetType> typeInfo;
//
// private DataFlowInfo(ImmutableMap<Object, NullabilityFlags> nullabilityInfo, ListMultimap<Object, JetType> 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<JetType> getPossibleTypes(Object key, @NotNull JetType originalType) {
// List<JetType> types = typeInfo.get(key);
// NullabilityFlags nullabilityFlags = nullabilityInfo.get(key);
// if (nullabilityFlags == null || nullabilityFlags.canBeNull()) {
// return types;
// }
// List<JetType> 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<JetType> getPossibleTypesForVariable(@NotNull VariableDescriptor variableDescriptor) {
// return getPossibleTypes(variableDescriptor, variableDescriptor.getOutType());
// }
//
// public List<JetType> 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<Object, NullabilityFlags> getEqualsToNullMap(VariableDescriptor variableDescriptor, boolean notNull) {
// Map<Object, NullabilityFlags> 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<Object, NullabilityFlags> getEqualsToNullMap(VariableDescriptor[] variableDescriptors, boolean notNull) {
// if (variableDescriptors.length == 0) return nullabilityInfo;
// Map<Object, NullabilityFlags> 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<Object, JetType> 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<Object, JetType> 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<Object, NullabilityFlags> nullabilityMapBuilder = Maps.newHashMap();
// nullabilityMapBuilder.putAll(nullabilityInfo);
// for (Map.Entry<Object, NullabilityFlags> 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<Object, JetType> newTypeInfo = copyTypeInfo();
// newTypeInfo.putAll(other.typeInfo);
// return new DataFlowInfo(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo);
// }
//
// private ListMultimap<Object, JetType> copyTypeInfo() {
// ListMultimap<Object, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<Object, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
// newTypeInfo.putAll(typeInfo);
// return newTypeInfo;
// }
//
// public DataFlowInfo or(DataFlowInfo other) {
// Map<Object, NullabilityFlags> builder = Maps.newHashMap(nullabilityInfo);
// builder.keySet().retainAll(other.nullabilityInfo.keySet());
// for (Map.Entry<Object, NullabilityFlags> 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<Object, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<Object, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
//
// Set<Object> keys = newTypeInfo.keySet();
// keys.retainAll(other.typeInfo.keySet());
//
// for (Object key : keys) {
// Collection<JetType> thisTypes = typeInfo.get(key);
// Collection<JetType> otherTypes = other.typeInfo.get(key);
//
// Collection<JetType> 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);
// }
//
// }
//}
@@ -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;
}
}
@@ -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<Object, Boolean> 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<Object, Boolean> 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<Object, Boolean> receiverId = getIdForStableIdentifier(qualifiedExpression.getReceiverExpression(), bindingContext, true);
Pair<Object, Boolean> 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;
}
}
@@ -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);
}
}
@@ -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, D> R accept(@NotNull ReceiverDescriptorVisitor<R, D> visitor, D data) {
return visitor.visitClassReceiver(this, data);
@@ -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
@@ -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;
}
}
@@ -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();
}
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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<JetPattern, DataFlowInfo>(), new HashMap<JetPattern, List<VariableDescriptor>>(), 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<JetPattern, DataFlowInfo>(), new HashMap<JetPattern, List<VariableDescriptor>>(), 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<JetPattern, DataFlowInfo>(), new HashMap<JetPattern, List<VariableDescriptor>>(), 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<JetPattern, DataFlowInfo>(), new HashMap<JetPattern, List<VariableDescriptor>>(), 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<JetExpression> returnedExpressions = Lists.newArrayList();
if (function.hasBlockBody()) {
@@ -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<DataFlowInfo> result = new Ref<DataFlowInfo>(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<VariableDescriptor> 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) {
@@ -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;
@@ -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<DataFlowInfo> result = new Ref<DataFlowInfo>(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();
}
}
+2 -2
View File
@@ -189,11 +189,11 @@ fun tuples(a: Any?) {
val s: (Any, String) = (<info descr="Automatically cast to Any">a</info>, <error descr="[TYPE_MISMATCH] Type mismatch: inferred type is Any? but String was expected">a</error>)
}
if (a is String) {
val s: (Any, String) = (<info descr="Automatically cast to String">a</info>, <info descr="Automatically cast to String">a</info>)
val s: (Any, String) = (<info descr="Automatically cast to Any">a</info>, <info descr="Automatically cast to String">a</info>)
}
fun illegalTupleReturnType(): (Any, String) = (<error descr="[TYPE_MISMATCH] Type mismatch: inferred type is Any? but Any was expected">a</error>, <error descr="[TYPE_MISMATCH] Type mismatch: inferred type is Any? but String was expected">a</error>)
if (a is String) {
fun legalTupleReturnType(): (Any, String) = (<info descr="Automatically cast to String">a</info>, <info descr="Automatically cast to String">a</info>)
fun legalTupleReturnType(): (Any, String) = (<info descr="Automatically cast to Any">a</info>, <info descr="Automatically cast to String">a</info>)
}
val illegalFunctionLiteral: Function0<Int> = <error descr="[TYPE_MISMATCH] Type mismatch: inferred type is Function0<Any?> but Function0<Int> was expected">{ <error descr="[TYPE_MISMATCH] Type mismatch: inferred type is Any? but Int was expected">a</error> }</error>
val illegalReturnValueInFunctionLiteral: Function0<Int> = { (): Int => <error descr="[TYPE_MISMATCH] Type mismatch: inferred type is Any? but Int was expected">a</error> }
@@ -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
}
}
}
@@ -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<JetType> 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<FunctionDescriptor> functions = callResolver.resolveExactSignature(
classDescriptor.getMemberScope(typeArguments), ReceiverDescriptor.NO_RECEIVER, name, parameterTypeList);
for (ResolvedCall<FunctionDescriptor> resolvedCall : functions.getResults()) {