Allow bare types on the right-hand side of as/as?/is/!is

This commit is contained in:
Andrey Breslav
2013-09-06 17:33:28 +04:00
parent e9e23c0069
commit 5c86a5bd7c
21 changed files with 447 additions and 82 deletions
@@ -0,0 +1,99 @@
package org.jetbrains.jet.lang.resolve;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.types.*;
/**
* Bare types are somewhat like raw types, but in Kotlin they are only allowed on the right-hand side of is/as.
* For example:
*
* fun foo(a: Any) {
* if (a is List) {
* // a is known to be List<*> here
* }
* }
*
* Another example:
*
* fun foo(a: Collection<String>) {
* if (a is List) {
* // a is known to be List<String> here
* }
* }
*
* One can call reconstruct(supertype) to get an actual type from a bare type
*/
public class PossiblyBareType {
@NotNull
public static PossiblyBareType bare(@NotNull TypeConstructor bareTypeConstructor, boolean nullable) {
return new PossiblyBareType(null, bareTypeConstructor, nullable);
}
@NotNull
public static PossiblyBareType type(@NotNull JetType actualType) {
return new PossiblyBareType(actualType, null, false);
}
private final JetType actualType;
private final TypeConstructor bareTypeConstructor;
private final boolean nullable;
private PossiblyBareType(@Nullable JetType actualType, @Nullable TypeConstructor bareTypeConstructor, boolean nullable) {
this.actualType = actualType;
this.bareTypeConstructor = bareTypeConstructor;
this.nullable = nullable;
}
public boolean isBare() {
return actualType == null;
}
@NotNull
public JetType getActualType() {
//noinspection ConstantConditions
return actualType;
}
@NotNull
public TypeConstructor getBareTypeConstructor() {
//noinspection ConstantConditions
return bareTypeConstructor;
}
private boolean isBareTypeNullable() {
return nullable;
}
public boolean isNullable() {
if (isBare()) return isBareTypeNullable();
return getActualType().isNullable();
}
public PossiblyBareType makeNullable() {
if (isBare()) {
return isBareTypeNullable() ? this : bare(getBareTypeConstructor(), true);
}
return type(TypeUtils.makeNullable(getActualType()));
}
@NotNull
public JetType reconstruct(@NotNull JetType subjectType) {
if (!isBare()) return getActualType();
JetType type = CastDiagnosticsUtil.findStaticallyKnownSubtype(
TypeUtils.makeNotNullable(subjectType),
getBareTypeConstructor()
);
// No need to make an absent type nullable
if (type == null) return type;
return TypeUtils.makeNullableAsSpecified(type, isBareTypeNullable());
}
@Override
public String toString() {
return isBare() ? "bare " + bareTypeConstructor + (isBareTypeNullable() ? "?" : "") : getActualType().toString();
}
}
@@ -7,10 +7,16 @@ public class TypeResolutionContext {
public final JetScope scope; public final JetScope scope;
public final BindingTrace trace; public final BindingTrace trace;
public final boolean checkBounds; public final boolean checkBounds;
public final boolean allowBareTypes;
public TypeResolutionContext(@NotNull JetScope scope, @NotNull BindingTrace trace, boolean checkBounds) { public TypeResolutionContext(@NotNull JetScope scope, @NotNull BindingTrace trace, boolean checkBounds, boolean allowBareTypes) {
this.scope = scope; this.scope = scope;
this.trace = trace; this.trace = trace;
this.checkBounds = checkBounds; this.checkBounds = checkBounds;
this.allowBareTypes = allowBareTypes;
}
public TypeResolutionContext noBareTypes() {
return new TypeResolutionContext(scope, trace, checkBounds, false);
} }
} }
@@ -16,7 +16,6 @@
package org.jetbrains.jet.lang.resolve; package org.jetbrains.jet.lang.resolve;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.*;
@@ -35,6 +34,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import static org.jetbrains.jet.lang.diagnostics.Errors.*; import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.resolve.PossiblyBareType.type;
import static org.jetbrains.jet.lang.types.Variance.*; import static org.jetbrains.jet.lang.types.Variance.*;
public class TypeResolver { public class TypeResolver {
@@ -60,32 +60,41 @@ public class TypeResolver {
@NotNull @NotNull
public JetType resolveType(@NotNull JetScope scope, @NotNull JetTypeReference typeReference, BindingTrace trace, boolean checkBounds) { public JetType resolveType(@NotNull JetScope scope, @NotNull JetTypeReference typeReference, BindingTrace trace, boolean checkBounds) {
return resolveType(new TypeResolutionContext(scope, trace, checkBounds), typeReference); // bare types are not allowed
return resolveType(new TypeResolutionContext(scope, trace, checkBounds, false), typeReference);
} }
@NotNull @NotNull
public JetType resolveType(@NotNull TypeResolutionContext c, @NotNull JetTypeReference typeReference) { public JetType resolveType(@NotNull TypeResolutionContext c, @NotNull JetTypeReference typeReference) {
assert !c.allowBareTypes : "Use resolvePossiblyBareType() when bare types are allowed";
return resolvePossiblyBareType(c, typeReference).getActualType();
}
@NotNull
public PossiblyBareType resolvePossiblyBareType(@NotNull TypeResolutionContext c, @NotNull JetTypeReference typeReference) {
JetType cachedType = c.trace.getBindingContext().get(BindingContext.TYPE, typeReference); JetType cachedType = c.trace.getBindingContext().get(BindingContext.TYPE, typeReference);
if (cachedType != null) return cachedType; if (cachedType != null) return type(cachedType);
List<AnnotationDescriptor> annotations = annotationResolver.getResolvedAnnotations(typeReference.getAnnotations(), c.trace); List<AnnotationDescriptor> annotations = annotationResolver.getResolvedAnnotations(typeReference.getAnnotations(), c.trace);
JetTypeElement typeElement = typeReference.getTypeElement(); JetTypeElement typeElement = typeReference.getTypeElement();
JetType type = resolveTypeElement(c, annotations, typeElement); PossiblyBareType type = resolveTypeElement(c, annotations, typeElement);
c.trace.record(BindingContext.TYPE, typeReference, type); if (!type.isBare()) {
c.trace.record(BindingContext.TYPE, typeReference, type.getActualType());
}
c.trace.record(BindingContext.TYPE_RESOLUTION_SCOPE, typeReference, c.scope); c.trace.record(BindingContext.TYPE_RESOLUTION_SCOPE, typeReference, c.scope);
return type; return type;
} }
@NotNull @NotNull
private JetType resolveTypeElement( private PossiblyBareType resolveTypeElement(
final TypeResolutionContext c, final TypeResolutionContext c,
final List<AnnotationDescriptor> annotations, final List<AnnotationDescriptor> annotations,
JetTypeElement typeElement JetTypeElement typeElement
) { ) {
final JetType[] result = new JetType[1]; final PossiblyBareType[] result = new PossiblyBareType[1];
if (typeElement != null) { if (typeElement != null) {
typeElement.accept(new JetVisitorVoid() { typeElement.accept(new JetVisitorVoid() {
@Override @Override
@@ -109,16 +118,16 @@ public class TypeResolver {
JetScope scopeForTypeParameter = getScopeForTypeParameter(c, typeParameterDescriptor); JetScope scopeForTypeParameter = getScopeForTypeParameter(c, typeParameterDescriptor);
if (scopeForTypeParameter instanceof ErrorUtils.ErrorScope) { if (scopeForTypeParameter instanceof ErrorUtils.ErrorScope) {
result[0] = ErrorUtils.createErrorType("?"); result[0] = type(ErrorUtils.createErrorType("?"));
} }
else { else {
result[0] = new JetTypeImpl( result[0] = type(new JetTypeImpl(
annotations, annotations,
typeParameterDescriptor.getTypeConstructor(), typeParameterDescriptor.getTypeConstructor(),
TypeUtils.hasNullableLowerBound(typeParameterDescriptor), TypeUtils.hasNullableLowerBound(typeParameterDescriptor),
Collections.<TypeProjection>emptyList(), Collections.<TypeProjection>emptyList(),
scopeForTypeParameter scopeForTypeParameter
); ));
} }
resolveTypeProjections(c, ErrorUtils.createErrorType("No type").getConstructor(), type.getTypeArguments()); resolveTypeProjections(c, ErrorUtils.createErrorType("No type").getConstructor(), type.getTypeArguments());
@@ -138,32 +147,33 @@ public class TypeResolver {
int expectedArgumentCount = parameters.size(); int expectedArgumentCount = parameters.size();
int actualArgumentCount = arguments.size(); int actualArgumentCount = arguments.size();
if (ErrorUtils.isError(typeConstructor)) { if (ErrorUtils.isError(typeConstructor)) {
result[0] = ErrorUtils.createErrorType("[Error type: " + typeConstructor + "]"); result[0] = type(ErrorUtils.createErrorType("[Error type: " + typeConstructor + "]"));
} }
else { else {
if (actualArgumentCount != expectedArgumentCount) { if (actualArgumentCount != expectedArgumentCount) {
if (actualArgumentCount == 0) { if (actualArgumentCount == 0) {
if (rhsOfIsExpression(type) || rhsOfIsPattern(type)) { // See docs for PossiblyBareType
c.trace.report(NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION.on(type, expectedArgumentCount, allStarProjectionsString(typeConstructor))); if (c.allowBareTypes) {
} result[0] = PossiblyBareType.bare(typeConstructor, false);
else { return;
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(type, expectedArgumentCount));
} }
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(type, expectedArgumentCount));
} }
else { else {
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(type.getTypeArgumentList(), expectedArgumentCount)); c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(type.getTypeArgumentList(), expectedArgumentCount));
} }
} }
else { else {
result[0] = new JetTypeImpl( JetTypeImpl resultingType = new JetTypeImpl(
annotations, annotations,
typeConstructor, typeConstructor,
false, false,
arguments, arguments,
classDescriptor.getMemberScope(arguments) classDescriptor.getMemberScope(arguments)
); );
result[0] = type(resultingType);
if (c.checkBounds) { if (c.checkBounds) {
TypeSubstitutor substitutor = TypeSubstitutor.create(result[0]); TypeSubstitutor substitutor = TypeSubstitutor.create(resultingType);
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) { for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
TypeParameterDescriptor parameter = parameters.get(i); TypeParameterDescriptor parameter = parameters.get(i);
JetType argument = arguments.get(i).getType(); JetType argument = arguments.get(i).getType();
@@ -181,35 +191,35 @@ public class TypeResolver {
@Override @Override
public void visitNullableType(JetNullableType nullableType) { public void visitNullableType(JetNullableType nullableType) {
JetType baseType = resolveTypeElement(c, annotations, nullableType.getInnerType()); PossiblyBareType baseType = resolveTypeElement(c, annotations, nullableType.getInnerType());
if (baseType.isNullable()) { if (baseType.isNullable()) {
c.trace.report(REDUNDANT_NULLABLE.on(nullableType)); c.trace.report(REDUNDANT_NULLABLE.on(nullableType));
} }
else if (TypeUtils.hasNullableSuperType(baseType)) { else if (!baseType.isBare() && TypeUtils.hasNullableSuperType(baseType.getActualType())) {
c.trace.report(BASE_WITH_NULLABLE_UPPER_BOUND.on(nullableType, baseType)); c.trace.report(BASE_WITH_NULLABLE_UPPER_BOUND.on(nullableType, baseType.getActualType()));
} }
result[0] = TypeUtils.makeNullable(baseType); result[0] = baseType.makeNullable();
} }
@Override @Override
public void visitFunctionType(JetFunctionType type) { public void visitFunctionType(JetFunctionType type) {
JetTypeReference receiverTypeRef = type.getReceiverTypeRef(); JetTypeReference receiverTypeRef = type.getReceiverTypeRef();
JetType receiverType = receiverTypeRef == null ? null : resolveType(c, receiverTypeRef); JetType receiverType = receiverTypeRef == null ? null : resolveType(c.noBareTypes(), receiverTypeRef);
List<JetType> parameterTypes = new ArrayList<JetType>(); List<JetType> parameterTypes = new ArrayList<JetType>();
for (JetParameter parameter : type.getParameters()) { for (JetParameter parameter : type.getParameters()) {
parameterTypes.add(resolveType(c, parameter.getTypeReference())); parameterTypes.add(resolveType(c.noBareTypes(), parameter.getTypeReference()));
} }
JetTypeReference returnTypeRef = type.getReturnTypeRef(); JetTypeReference returnTypeRef = type.getReturnTypeRef();
JetType returnType; JetType returnType;
if (returnTypeRef != null) { if (returnTypeRef != null) {
returnType = resolveType(c, returnTypeRef); returnType = resolveType(c.noBareTypes(), returnTypeRef);
} }
else { else {
returnType = KotlinBuiltIns.getInstance().getUnitType(); returnType = KotlinBuiltIns.getInstance().getUnitType();
} }
result[0] = KotlinBuiltIns.getInstance().getFunctionType(annotations, receiverType, parameterTypes, returnType); result[0] = type(KotlinBuiltIns.getInstance().getFunctionType(annotations, receiverType, parameterTypes, returnType));
} }
@Override @Override
@@ -219,32 +229,11 @@ public class TypeResolver {
}); });
} }
if (result[0] == null) { if (result[0] == null) {
return ErrorUtils.createErrorType(typeElement == null ? "No type element" : typeElement.getText()); return type(ErrorUtils.createErrorType(typeElement == null ? "No type element" : typeElement.getText()));
} }
return result[0]; return result[0];
} }
private static boolean rhsOfIsExpression(@NotNull JetUserType type) {
// Look for the FIRST expression containing this type
JetExpression outerExpression = PsiTreeUtil.getParentOfType(type, JetExpression.class);
if (outerExpression instanceof JetIsExpression) {
JetIsExpression isExpression = (JetIsExpression) outerExpression;
// If this expression is JetIsExpression, and the type is the outermost on the RHS
if (type.getParent() == isExpression.getTypeRef()) {
return true;
}
}
return false;
}
private static boolean rhsOfIsPattern(@NotNull JetUserType type) {
// Look for the is-pattern containing this type
JetWhenConditionIsPattern outerPattern = PsiTreeUtil.getParentOfType(type, JetWhenConditionIsPattern.class, false, JetExpression.class);
if (outerPattern == null) return false;
// We are interested only in the outermost type on the RHS
return type.getParent() == outerPattern.getTypeRef();
}
private JetScope getScopeForTypeParameter(TypeResolutionContext c, final TypeParameterDescriptor typeParameterDescriptor) { private JetScope getScopeForTypeParameter(TypeResolutionContext c, final TypeParameterDescriptor typeParameterDescriptor) {
if (c.checkBounds) { if (c.checkBounds) {
return typeParameterDescriptor.getUpperBoundsAsType().getMemberScope(); return typeParameterDescriptor.getUpperBoundsAsType().getMemberScope();
@@ -283,7 +272,7 @@ public class TypeResolver {
} }
else { else {
// TODO : handle the Foo<in *> case // TODO : handle the Foo<in *> case
type = resolveType(c, argumentElement.getTypeReference()); type = resolveType(c.noBareTypes(), argumentElement.getTypeReference());
Variance kind = resolveProjectionKind(projectionKind); Variance kind = resolveProjectionKind(projectionKind);
if (constructor.getParameters().size() > i) { if (constructor.getParameters().size() > i) {
TypeParameterDescriptor parameterDescriptor = constructor.getParameters().get(i); TypeParameterDescriptor parameterDescriptor = constructor.getParameters().get(i);
@@ -333,15 +322,4 @@ public class TypeResolver {
} }
return null; return null;
} }
@NotNull
private static String allStarProjectionsString(@NotNull TypeConstructor constructor) {
int size = constructor.getParameters().size();
assert size != 0 : "No projections possible for a nilary type constructor" + constructor;
ClassifierDescriptor declarationDescriptor = constructor.getDeclarationDescriptor();
assert declarationDescriptor != null : "No declaration descriptor for type constructor " + constructor;
String name = declarationDescriptor.getName().asString();
return TypeUtils.getTypeNameAndStarProjectionsString(name, size);
}
} }
@@ -112,11 +112,41 @@ public class CastDiagnosticsUtil {
// NOTE: this does not account for 'as Array<List<T>>' // NOTE: this does not account for 'as Array<List<T>>'
if (allParametersReified(subtype)) return false; if (allParametersReified(subtype)) return false;
JetType staticallyKnownSubtype = findStaticallyKnownSubtype(supertype, subtype.getConstructor());
// If the substitution failed, it means that the result is an impossible type, e.g. something like Out<in Foo>
// In this case, we can't guarantee anything, so the cast is considered to be erased
if (staticallyKnownSubtype == null) return true;
// If the type we calculated is a subtype of the cast target, it's OK to use the cast target instead.
// If not, it's wrong to use it
return !typeChecker.isSubtypeOf(staticallyKnownSubtype, subtype);
}
/**
* Remember that we are trying to cast something of type {@code supertype} to {@code subtype}.
*
* Since at runtime we can only check the class (type constructor), the rest of the subtype should be known statically, from supertype.
* This method reconstructs all static information that can be obtained from supertype.
*
* Example 1:
* supertype = Collection<String>
* subtype = List<...>
* result = List<String>
*
* Example 2:
* supertype = Any
* subtype = List<...>
* result = List<*>
*/
public static JetType findStaticallyKnownSubtype(@NotNull JetType supertype, @NotNull TypeConstructor subtypeConstructor) {
assert !supertype.isNullable() : "This method only makes sense for non-nullable types";
// Assume we are casting an expression of type Collection<Foo> to List<Bar> // Assume we are casting an expression of type Collection<Foo> to List<Bar>
// First, let's make List<T>, where T is a type variable // First, let's make List<T>, where T is a type variable
JetType subtypeWithVariables = TypeUtils.makeUnsubstitutedType( ClassifierDescriptor descriptor = subtypeConstructor.getDeclarationDescriptor();
subtype.getConstructor(), assert descriptor != null : "Can't create default type for " + subtypeConstructor;
ErrorUtils.createErrorScope("Scope for intermediate type. This type shouldn't be used outside isCastErased()", true)); JetType subtypeWithVariables = descriptor.getDefaultType();
// Now, let's find a supertype of List<T> that is a Collection of something, // Now, let's find a supertype of List<T> that is a Collection of something,
// in this case it will be Collection<T> // in this case it will be Collection<T>
@@ -158,15 +188,7 @@ public class CastDiagnosticsUtil {
// At this point we have values for all type parameters of List // At this point we have values for all type parameters of List
// Let's make a type by substituting them: List<T> -> List<Foo> // Let's make a type by substituting them: List<T> -> List<Foo>
JetType staticallyKnownSubtype = TypeSubstitutor.create(substitution).substitute(subtypeWithVariables, Variance.INVARIANT); return TypeSubstitutor.create(substitution).substitute(subtypeWithVariables, Variance.INVARIANT);
// If the substitution failed, it means that the result is an impossible type, e.g. something like Out<in Foo>
// In this case, we can't guarantee anything, so the cast is considered to be erased
if (staticallyKnownSubtype == null) return true;
// If the type we calculated is a subtype of the cast target, it's OK to use the cast target instead.
// If not, it's wrong to use it
return !typeChecker.isSubtypeOf(staticallyKnownSubtype, subtype);
} }
private static boolean allParametersReified(JetType subtype) { private static boolean allParametersReified(JetType subtype) {
@@ -19,6 +19,7 @@ package org.jetbrains.jet.lang.types.expressions;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes; import org.jetbrains.jet.JetNodeTypes;
@@ -76,10 +77,13 @@ import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
import static org.jetbrains.jet.lang.types.expressions.ControlStructureTypingUtils.createCallForSpecialConstruction; import static org.jetbrains.jet.lang.types.expressions.ControlStructureTypingUtils.createCallForSpecialConstruction;
import static org.jetbrains.jet.lang.types.expressions.ControlStructureTypingUtils.resolveSpecialConstructionAsCall; import static org.jetbrains.jet.lang.types.expressions.ControlStructureTypingUtils.resolveSpecialConstructionAsCall;
import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.*; import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.*;
import static org.jetbrains.jet.lexer.JetTokens.*;
@SuppressWarnings("SuspiciousMethodCalls") @SuppressWarnings("SuspiciousMethodCalls")
public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
private static final TokenSet BARE_TYPES_ALLOWED = TokenSet.create(AS_KEYWORD, AS_SAFE, IS_KEYWORD, NOT_IS);
private final PlatformToKotlinClassMap platformToKotlinClassMap; private final PlatformToKotlinClassMap platformToKotlinClassMap;
protected BasicExpressionTypingVisitor(@NotNull ExpressionTypingInternals facade, @NotNull PlatformToKotlinClassMap platformToKotlinClassMap) { protected BasicExpressionTypingVisitor(@NotNull ExpressionTypingInternals facade, @NotNull PlatformToKotlinClassMap platformToKotlinClassMap) {
@@ -167,10 +171,17 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
return JetTypeInfo.create(null, leftTypeInfo.getDataFlowInfo()); return JetTypeInfo.create(null, leftTypeInfo.getDataFlowInfo());
} }
JetType targetType = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, right, context.trace, true);
IElementType operationType = expression.getOperationReference().getReferencedNameElementType(); IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
boolean allowBareTypes = BARE_TYPES_ALLOWED.contains(operationType);
TypeResolutionContext typeResolutionContext = new TypeResolutionContext(context.scope, context.trace, true, allowBareTypes);
PossiblyBareType possiblyBareTarget = context.expressionTypingServices.getTypeResolver().resolvePossiblyBareType(typeResolutionContext, right);
if (isTypeFlexible(left) || operationType == JetTokens.COLON) { if (isTypeFlexible(left) || operationType == JetTokens.COLON) {
// We do not allow bare types on static assertions, because static assertions provide an expected type for their argument,
// thus causing a circularity in type dependencies
assert !possiblyBareTarget.isBare() : "Bare types should not be allowed for static assertions, because argument inference makes no sense there";
JetType targetType = possiblyBareTarget.getActualType();
JetTypeInfo typeInfo = facade.getTypeInfo(left, contextWithNoExpectedType.replaceExpectedType(targetType)); JetTypeInfo typeInfo = facade.getTypeInfo(left, contextWithNoExpectedType.replaceExpectedType(targetType));
checkBinaryWithTypeRHS(expression, context, targetType, typeInfo.getType()); checkBinaryWithTypeRHS(expression, context, targetType, typeInfo.getType());
@@ -180,15 +191,24 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
JetTypeInfo typeInfo = facade.getTypeInfo(left, contextWithNoExpectedType); JetTypeInfo typeInfo = facade.getTypeInfo(left, contextWithNoExpectedType);
DataFlowInfo dataFlowInfo = context.dataFlowInfo; DataFlowInfo dataFlowInfo = context.dataFlowInfo;
if (typeInfo.getType() != null) { JetType subjectType = typeInfo.getType();
checkBinaryWithTypeRHS(expression, contextWithNoExpectedType, targetType, typeInfo.getType()); JetType targetType;
if (subjectType != null) {
targetType = possiblyBareTarget.reconstruct(subjectType);
checkBinaryWithTypeRHS(expression, contextWithNoExpectedType, targetType, subjectType);
dataFlowInfo = typeInfo.getDataFlowInfo(); dataFlowInfo = typeInfo.getDataFlowInfo();
if (operationType == JetTokens.AS_KEYWORD) { if (operationType == AS_KEYWORD) {
DataFlowValue value = DataFlowValueFactory.INSTANCE.createDataFlowValue(left, typeInfo.getType(), context.trace.getBindingContext()); DataFlowValue value =
DataFlowValueFactory.INSTANCE.createDataFlowValue(left, subjectType, context.trace.getBindingContext());
dataFlowInfo = dataFlowInfo.establishSubtyping(value, targetType); dataFlowInfo = dataFlowInfo.establishSubtyping(value, targetType);
} }
} }
JetType result = operationType == JetTokens.AS_SAFE ? TypeUtils.makeNullable(targetType) : targetType; else {
// Recovery: let's reconstruct as if we were casting from Any, to get some type there
targetType = possiblyBareTarget.reconstruct(KotlinBuiltIns.getInstance().getAnyType());
}
JetType result = operationType == AS_SAFE ? TypeUtils.makeNullable(targetType) : targetType;
return DataFlowUtils.checkType(result, expression, context, dataFlowInfo); return DataFlowUtils.checkType(result, expression, context, dataFlowInfo);
} }
@@ -23,6 +23,8 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.PossiblyBareType;
import org.jetbrains.jet.lang.resolve.TypeResolutionContext;
import org.jetbrains.jet.lang.resolve.calls.autocasts.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.DataFlowValue;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
@@ -279,8 +281,12 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
if (typeReferenceAfterIs == null) { if (typeReferenceAfterIs == null) {
return noChange(context); return noChange(context);
} }
JetType type = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, typeReferenceAfterIs, context.trace, TypeResolutionContext typeResolutionContext = new TypeResolutionContext(context.scope, context.trace, true, /*allowBareTypes=*/ true);
true); PossiblyBareType possiblyBareTarget = context.expressionTypingServices.getTypeResolver().resolvePossiblyBareType(typeResolutionContext, typeReferenceAfterIs);
JetType type = possiblyBareTarget.reconstruct(subjectType);
if (possiblyBareTarget.isBare()) {
context.trace.record(BindingContext.TYPE, typeReferenceAfterIs, type);
}
if (!subjectType.isNullable() && type.isNullable()) { if (!subjectType.isNullable() && type.isNullable()) {
JetTypeElement element = typeReferenceAfterIs.getTypeElement(); JetTypeElement element = typeReferenceAfterIs.getTypeElement();
assert element instanceof JetNullableType : "element must be instance of " + JetNullableType.class.getName(); assert element instanceof JetNullableType : "element must be instance of " + JetNullableType.class.getName();
@@ -0,0 +1,10 @@
trait Tr
trait G<T>
fun test(tr: Tr): Any {
return tr as G<<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>G<!>>
}
fun test1(tr: Tr): Any {
return tr as <!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>G<!>.(<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>G<!>) -> <!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>G<!>
}
@@ -0,0 +1,8 @@
trait Tr<T>
trait G<T> : Tr<T>
fun test(tr: Tr<String>) {
val v = tr as G?
// If v is not nullable, there will be a warning on this line:
v!!: G<String>
}
@@ -0,0 +1,16 @@
trait Either<out A, out B>
trait Left<out A>: Either<A, Nothing>
trait Right<out B>: Either<Nothing, B>
class C1(val v1: Int)
class C2(val v2: Int)
fun _as_left(e: Either<C1, C2>): Any {
val v = e as Left
return v: Left<C1>
}
fun _as_right(e: Either<C1, C2>): Any {
val v = e as Right
return v: Right<C2>
}
@@ -0,0 +1,24 @@
trait Either<out A, out B>
trait Left<out A>: Either<A, Nothing> {
val value: A
}
trait Right<out B>: Either<Nothing, B> {
val value: B
}
class C1(val v1: Int)
class C2(val v2: Int)
fun _is_l(e: Either<C1, C2>): Any {
if (e is Left) {
return e.value.v1
}
return e
}
fun _is_r(e: Either<C1, C2>): Any {
if (e is Right) {
return e.value.v2
}
return e
}
@@ -0,0 +1,24 @@
trait Either<out A, out B>
trait Left<out A>: Either<A, Nothing> {
val value: A
}
trait Right<out B>: Either<Nothing, B> {
val value: B
}
class C1(val v1: Int)
class C2(val v2: Int)
fun _is_l(e: Either<C1, C2>): Any {
if (e !is Left) {
return e
}
return e.value.v1
}
fun _is_r(e: Either<C1, C2>): Any {
if (e !is Right) {
return e
}
return e.value.v2
}
@@ -0,0 +1,16 @@
trait Either<out A, out B>
trait Left<out A>: Either<A, Nothing>
trait Right<out B>: Either<Nothing, B>
class C1(val v1: Int)
class C2(val v2: Int)
fun _as_left(e: Either<C1, C2>): Any? {
val v = e as? Left
return v: Left<C1>?
}
fun _as_right(e: Either<C1, C2>): Any? {
val v = e as? Right
return v: Right<C2>?
}
@@ -0,0 +1,18 @@
trait Either<out A, out B>
trait Left<out A>: Either<A, Nothing> {
val value: A
}
trait Right<out B>: Either<Nothing, B> {
val value: B
}
class C1(val v1: Int)
class C2(val v2: Int)
fun _when(e: Either<C1, C2>): Any {
return when (e) {
is Left -> e.value.v1
is Right -> e.value.v2
else -> e
}
}
@@ -0,0 +1,7 @@
trait B<T>
trait G<T>: B<T>
fun f(p: B<<!UNRESOLVED_REFERENCE!>Foo<!>>): Any {
val v = <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>p<!> as G
return <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>v<!>: G<*>
}
@@ -0,0 +1,7 @@
trait Tr<T>
trait G<T> : Tr<T>
fun test(tr: Tr<String>?) {
val v = tr as G
v: G<String>
}
@@ -0,0 +1,4 @@
trait B<T>
class G<T>: B<T>
fun f(b: B<String>?) = b is G?<!REDUNDANT_NULLABLE!>?<!>
@@ -0,0 +1,6 @@
class P
fun foo(p: P): Any {
val v = p <!USELESS_CAST!>as<!> <!UNRESOLVED_REFERENCE!>G<!>
return <!DEBUG_INFO_ELEMENT_WITH_ERROR_TYPE!>v<!>
}
@@ -0,0 +1,7 @@
trait Tr
trait G<T>
fun test(tr: Tr) {
val v = tr as G
v: G<*>
}
@@ -0,0 +1,4 @@
trait Tr
trait G<T>
fun test(tr: Tr) = tr: <!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>G<!>
@@ -0,0 +1,4 @@
trait Tr
trait G<T>
fun test(tr: Tr) = tr is G
@@ -964,7 +964,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
} }
@TestMetadata("compiler/testData/diagnostics/tests/cast") @TestMetadata("compiler/testData/diagnostics/tests/cast")
@InnerTestClasses({Cast.NeverSucceeds.class}) @InnerTestClasses({Cast.Bare.class, Cast.NeverSucceeds.class})
public static class Cast extends AbstractDiagnosticsTestWithEagerResolve { public static class Cast extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInCast() throws Exception { public void testAllFilesPresentInCast() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/cast"), Pattern.compile("^(.+)\\.kt$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/cast"), Pattern.compile("^(.+)\\.kt$"), true);
@@ -1185,6 +1185,84 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
doTest("compiler/testData/diagnostics/tests/cast/WhenWithExpression.kt"); doTest("compiler/testData/diagnostics/tests/cast/WhenWithExpression.kt");
} }
@TestMetadata("compiler/testData/diagnostics/tests/cast/bare")
public static class Bare extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInBare() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/cast/bare"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("AsNestedBare.kt")
public void testAsNestedBare() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/AsNestedBare.kt");
}
@TestMetadata("AsNullable.kt")
public void testAsNullable() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/AsNullable.kt");
}
@TestMetadata("EitherAs.kt")
public void testEitherAs() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/EitherAs.kt");
}
@TestMetadata("EitherIs.kt")
public void testEitherIs() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/EitherIs.kt");
}
@TestMetadata("EitherNotIs.kt")
public void testEitherNotIs() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/EitherNotIs.kt");
}
@TestMetadata("EitherSafeAs.kt")
public void testEitherSafeAs() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/EitherSafeAs.kt");
}
@TestMetadata("EitherWhen.kt")
public void testEitherWhen() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/EitherWhen.kt");
}
@TestMetadata("ErrorsInSubstitution.kt")
public void testErrorsInSubstitution() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/ErrorsInSubstitution.kt");
}
@TestMetadata("NullableAs.kt")
public void testNullableAs() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/NullableAs.kt");
}
@TestMetadata("RedundantNullable.kt")
public void testRedundantNullable() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/RedundantNullable.kt");
}
@TestMetadata("ToErrorType.kt")
public void testToErrorType() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/ToErrorType.kt");
}
@TestMetadata("UnrelatedAs.kt")
public void testUnrelatedAs() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/UnrelatedAs.kt");
}
@TestMetadata("UnrelatedColon.kt")
public void testUnrelatedColon() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/UnrelatedColon.kt");
}
@TestMetadata("UnrelatedIs.kt")
public void testUnrelatedIs() throws Exception {
doTest("compiler/testData/diagnostics/tests/cast/bare/UnrelatedIs.kt");
}
}
@TestMetadata("compiler/testData/diagnostics/tests/cast/neverSucceeds") @TestMetadata("compiler/testData/diagnostics/tests/cast/neverSucceeds")
public static class NeverSucceeds extends AbstractDiagnosticsTestWithEagerResolve { public static class NeverSucceeds extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInNeverSucceeds() throws Exception { public void testAllFilesPresentInNeverSucceeds() throws Exception {
@@ -1216,6 +1294,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
public static Test innerSuite() { public static Test innerSuite() {
TestSuite suite = new TestSuite("Cast"); TestSuite suite = new TestSuite("Cast");
suite.addTestSuite(Cast.class); suite.addTestSuite(Cast.class);
suite.addTestSuite(Bare.class);
suite.addTestSuite(NeverSucceeds.class); suite.addTestSuite(NeverSucceeds.class);
return suite; return suite;
} }