Allow bare types on the right-hand side of as/as?/is/!is
This commit is contained in:
@@ -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 BindingTrace trace;
|
||||
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.trace = trace;
|
||||
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;
|
||||
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
@@ -35,6 +34,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
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.*;
|
||||
|
||||
public class TypeResolver {
|
||||
@@ -60,32 +60,41 @@ public class TypeResolver {
|
||||
|
||||
@NotNull
|
||||
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
|
||||
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);
|
||||
if (cachedType != null) return cachedType;
|
||||
if (cachedType != null) return type(cachedType);
|
||||
|
||||
List<AnnotationDescriptor> annotations = annotationResolver.getResolvedAnnotations(typeReference.getAnnotations(), c.trace);
|
||||
|
||||
JetTypeElement typeElement = typeReference.getTypeElement();
|
||||
JetType type = resolveTypeElement(c, annotations, typeElement);
|
||||
c.trace.record(BindingContext.TYPE, typeReference, type);
|
||||
PossiblyBareType type = resolveTypeElement(c, annotations, typeElement);
|
||||
if (!type.isBare()) {
|
||||
c.trace.record(BindingContext.TYPE, typeReference, type.getActualType());
|
||||
}
|
||||
c.trace.record(BindingContext.TYPE_RESOLUTION_SCOPE, typeReference, c.scope);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JetType resolveTypeElement(
|
||||
private PossiblyBareType resolveTypeElement(
|
||||
final TypeResolutionContext c,
|
||||
final List<AnnotationDescriptor> annotations,
|
||||
JetTypeElement typeElement
|
||||
) {
|
||||
|
||||
final JetType[] result = new JetType[1];
|
||||
final PossiblyBareType[] result = new PossiblyBareType[1];
|
||||
if (typeElement != null) {
|
||||
typeElement.accept(new JetVisitorVoid() {
|
||||
@Override
|
||||
@@ -109,16 +118,16 @@ public class TypeResolver {
|
||||
|
||||
JetScope scopeForTypeParameter = getScopeForTypeParameter(c, typeParameterDescriptor);
|
||||
if (scopeForTypeParameter instanceof ErrorUtils.ErrorScope) {
|
||||
result[0] = ErrorUtils.createErrorType("?");
|
||||
result[0] = type(ErrorUtils.createErrorType("?"));
|
||||
}
|
||||
else {
|
||||
result[0] = new JetTypeImpl(
|
||||
result[0] = type(new JetTypeImpl(
|
||||
annotations,
|
||||
typeParameterDescriptor.getTypeConstructor(),
|
||||
TypeUtils.hasNullableLowerBound(typeParameterDescriptor),
|
||||
Collections.<TypeProjection>emptyList(),
|
||||
scopeForTypeParameter
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
resolveTypeProjections(c, ErrorUtils.createErrorType("No type").getConstructor(), type.getTypeArguments());
|
||||
@@ -138,32 +147,33 @@ public class TypeResolver {
|
||||
int expectedArgumentCount = parameters.size();
|
||||
int actualArgumentCount = arguments.size();
|
||||
if (ErrorUtils.isError(typeConstructor)) {
|
||||
result[0] = ErrorUtils.createErrorType("[Error type: " + typeConstructor + "]");
|
||||
result[0] = type(ErrorUtils.createErrorType("[Error type: " + typeConstructor + "]"));
|
||||
}
|
||||
else {
|
||||
if (actualArgumentCount != expectedArgumentCount) {
|
||||
if (actualArgumentCount == 0) {
|
||||
if (rhsOfIsExpression(type) || rhsOfIsPattern(type)) {
|
||||
c.trace.report(NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION.on(type, expectedArgumentCount, allStarProjectionsString(typeConstructor)));
|
||||
}
|
||||
else {
|
||||
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(type, expectedArgumentCount));
|
||||
// See docs for PossiblyBareType
|
||||
if (c.allowBareTypes) {
|
||||
result[0] = PossiblyBareType.bare(typeConstructor, false);
|
||||
return;
|
||||
}
|
||||
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(type, expectedArgumentCount));
|
||||
}
|
||||
else {
|
||||
c.trace.report(WRONG_NUMBER_OF_TYPE_ARGUMENTS.on(type.getTypeArgumentList(), expectedArgumentCount));
|
||||
}
|
||||
}
|
||||
else {
|
||||
result[0] = new JetTypeImpl(
|
||||
JetTypeImpl resultingType = new JetTypeImpl(
|
||||
annotations,
|
||||
typeConstructor,
|
||||
false,
|
||||
arguments,
|
||||
classDescriptor.getMemberScope(arguments)
|
||||
);
|
||||
result[0] = type(resultingType);
|
||||
if (c.checkBounds) {
|
||||
TypeSubstitutor substitutor = TypeSubstitutor.create(result[0]);
|
||||
TypeSubstitutor substitutor = TypeSubstitutor.create(resultingType);
|
||||
for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
|
||||
TypeParameterDescriptor parameter = parameters.get(i);
|
||||
JetType argument = arguments.get(i).getType();
|
||||
@@ -181,35 +191,35 @@ public class TypeResolver {
|
||||
|
||||
@Override
|
||||
public void visitNullableType(JetNullableType nullableType) {
|
||||
JetType baseType = resolveTypeElement(c, annotations, nullableType.getInnerType());
|
||||
PossiblyBareType baseType = resolveTypeElement(c, annotations, nullableType.getInnerType());
|
||||
if (baseType.isNullable()) {
|
||||
c.trace.report(REDUNDANT_NULLABLE.on(nullableType));
|
||||
}
|
||||
else if (TypeUtils.hasNullableSuperType(baseType)) {
|
||||
c.trace.report(BASE_WITH_NULLABLE_UPPER_BOUND.on(nullableType, baseType));
|
||||
else if (!baseType.isBare() && TypeUtils.hasNullableSuperType(baseType.getActualType())) {
|
||||
c.trace.report(BASE_WITH_NULLABLE_UPPER_BOUND.on(nullableType, baseType.getActualType()));
|
||||
}
|
||||
result[0] = TypeUtils.makeNullable(baseType);
|
||||
result[0] = baseType.makeNullable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFunctionType(JetFunctionType type) {
|
||||
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>();
|
||||
for (JetParameter parameter : type.getParameters()) {
|
||||
parameterTypes.add(resolveType(c, parameter.getTypeReference()));
|
||||
parameterTypes.add(resolveType(c.noBareTypes(), parameter.getTypeReference()));
|
||||
}
|
||||
|
||||
JetTypeReference returnTypeRef = type.getReturnTypeRef();
|
||||
JetType returnType;
|
||||
if (returnTypeRef != null) {
|
||||
returnType = resolveType(c, returnTypeRef);
|
||||
returnType = resolveType(c.noBareTypes(), returnTypeRef);
|
||||
}
|
||||
else {
|
||||
returnType = KotlinBuiltIns.getInstance().getUnitType();
|
||||
}
|
||||
result[0] = KotlinBuiltIns.getInstance().getFunctionType(annotations, receiverType, parameterTypes, returnType);
|
||||
result[0] = type(KotlinBuiltIns.getInstance().getFunctionType(annotations, receiverType, parameterTypes, returnType));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -219,32 +229,11 @@ public class TypeResolver {
|
||||
});
|
||||
}
|
||||
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];
|
||||
}
|
||||
|
||||
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) {
|
||||
if (c.checkBounds) {
|
||||
return typeParameterDescriptor.getUpperBoundsAsType().getMemberScope();
|
||||
@@ -283,7 +272,7 @@ public class TypeResolver {
|
||||
}
|
||||
else {
|
||||
// TODO : handle the Foo<in *> case
|
||||
type = resolveType(c, argumentElement.getTypeReference());
|
||||
type = resolveType(c.noBareTypes(), argumentElement.getTypeReference());
|
||||
Variance kind = resolveProjectionKind(projectionKind);
|
||||
if (constructor.getParameters().size() > i) {
|
||||
TypeParameterDescriptor parameterDescriptor = constructor.getParameters().get(i);
|
||||
@@ -333,15 +322,4 @@ public class TypeResolver {
|
||||
}
|
||||
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>>'
|
||||
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>
|
||||
// First, let's make List<T>, where T is a type variable
|
||||
JetType subtypeWithVariables = TypeUtils.makeUnsubstitutedType(
|
||||
subtype.getConstructor(),
|
||||
ErrorUtils.createErrorScope("Scope for intermediate type. This type shouldn't be used outside isCastErased()", true));
|
||||
ClassifierDescriptor descriptor = subtypeConstructor.getDeclarationDescriptor();
|
||||
assert descriptor != null : "Can't create default type for " + subtypeConstructor;
|
||||
JetType subtypeWithVariables = descriptor.getDefaultType();
|
||||
|
||||
// Now, let's find a supertype of List<T> that is a Collection of something,
|
||||
// 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
|
||||
// Let's make a type by substituting them: List<T> -> List<Foo>
|
||||
JetType staticallyKnownSubtype = 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);
|
||||
return TypeSubstitutor.create(substitution).substitute(subtypeWithVariables, Variance.INVARIANT);
|
||||
}
|
||||
|
||||
private static boolean allParametersReified(JetType subtype) {
|
||||
|
||||
+26
-6
@@ -19,6 +19,7 @@ package org.jetbrains.jet.lang.types.expressions;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
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.resolveSpecialConstructionAsCall;
|
||||
import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.*;
|
||||
import static org.jetbrains.jet.lexer.JetTokens.*;
|
||||
|
||||
@SuppressWarnings("SuspiciousMethodCalls")
|
||||
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;
|
||||
|
||||
protected BasicExpressionTypingVisitor(@NotNull ExpressionTypingInternals facade, @NotNull PlatformToKotlinClassMap platformToKotlinClassMap) {
|
||||
@@ -167,10 +171,17 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
return JetTypeInfo.create(null, leftTypeInfo.getDataFlowInfo());
|
||||
}
|
||||
|
||||
JetType targetType = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, right, context.trace, true);
|
||||
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) {
|
||||
// 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));
|
||||
checkBinaryWithTypeRHS(expression, context, targetType, typeInfo.getType());
|
||||
@@ -180,15 +191,24 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
||||
JetTypeInfo typeInfo = facade.getTypeInfo(left, contextWithNoExpectedType);
|
||||
|
||||
DataFlowInfo dataFlowInfo = context.dataFlowInfo;
|
||||
if (typeInfo.getType() != null) {
|
||||
checkBinaryWithTypeRHS(expression, contextWithNoExpectedType, targetType, typeInfo.getType());
|
||||
JetType subjectType = typeInfo.getType();
|
||||
JetType targetType;
|
||||
if (subjectType != null) {
|
||||
targetType = possiblyBareTarget.reconstruct(subjectType);
|
||||
|
||||
checkBinaryWithTypeRHS(expression, contextWithNoExpectedType, targetType, subjectType);
|
||||
dataFlowInfo = typeInfo.getDataFlowInfo();
|
||||
if (operationType == JetTokens.AS_KEYWORD) {
|
||||
DataFlowValue value = DataFlowValueFactory.INSTANCE.createDataFlowValue(left, typeInfo.getType(), context.trace.getBindingContext());
|
||||
if (operationType == AS_KEYWORD) {
|
||||
DataFlowValue value =
|
||||
DataFlowValueFactory.INSTANCE.createDataFlowValue(left, subjectType, context.trace.getBindingContext());
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -23,6 +23,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.diagnostics.Errors;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
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.DataFlowValue;
|
||||
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValueFactory;
|
||||
@@ -279,8 +281,12 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
if (typeReferenceAfterIs == null) {
|
||||
return noChange(context);
|
||||
}
|
||||
JetType type = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, typeReferenceAfterIs, context.trace,
|
||||
true);
|
||||
TypeResolutionContext typeResolutionContext = new TypeResolutionContext(context.scope, context.trace, true, /*allowBareTypes=*/ 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()) {
|
||||
JetTypeElement element = typeReferenceAfterIs.getTypeElement();
|
||||
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")
|
||||
@InnerTestClasses({Cast.NeverSucceeds.class})
|
||||
@InnerTestClasses({Cast.Bare.class, Cast.NeverSucceeds.class})
|
||||
public static class Cast extends AbstractDiagnosticsTestWithEagerResolve {
|
||||
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);
|
||||
@@ -1185,6 +1185,84 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
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")
|
||||
public static class NeverSucceeds extends AbstractDiagnosticsTestWithEagerResolve {
|
||||
public void testAllFilesPresentInNeverSucceeds() throws Exception {
|
||||
@@ -1216,6 +1294,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
public static Test innerSuite() {
|
||||
TestSuite suite = new TestSuite("Cast");
|
||||
suite.addTestSuite(Cast.class);
|
||||
suite.addTestSuite(Bare.class);
|
||||
suite.addTestSuite(NeverSucceeds.class);
|
||||
return suite;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user