Erased casts supported
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
package org.jetbrains.jet.lang.types;
|
package org.jetbrains.jet.lang.types;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.jet.lang.PlatformToKotlinClassMap;
|
import org.jetbrains.jet.lang.PlatformToKotlinClassMap;
|
||||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||||
@@ -8,10 +10,12 @@ import org.jetbrains.jet.lang.descriptors.ClassKind;
|
|||||||
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
|
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
|
||||||
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
|
||||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
||||||
|
import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class CastDiagnosticsUtil {
|
public class CastDiagnosticsUtil {
|
||||||
|
|
||||||
@@ -44,11 +48,8 @@ public class CastDiagnosticsUtil {
|
|||||||
List<JetType> aTypes = mapToPlatformIndependentTypes(a, platformToKotlinClassMap);
|
List<JetType> aTypes = mapToPlatformIndependentTypes(a, platformToKotlinClassMap);
|
||||||
List<JetType> bTypes = mapToPlatformIndependentTypes(b, platformToKotlinClassMap);
|
List<JetType> bTypes = mapToPlatformIndependentTypes(b, platformToKotlinClassMap);
|
||||||
|
|
||||||
for (int i = 0; i < aTypes.size(); i++) {
|
for (JetType aType : aTypes) {
|
||||||
JetType aType = aTypes.get(i);
|
for (JetType bType : bTypes) {
|
||||||
for (int j = 0; j < bTypes.size(); j++) {
|
|
||||||
JetType bType = bTypes.get(j);
|
|
||||||
|
|
||||||
if (JetTypeChecker.INSTANCE.isSubtypeOf(aType, bType)) return true;
|
if (JetTypeChecker.INSTANCE.isSubtypeOf(aType, bType)) return true;
|
||||||
if (JetTypeChecker.INSTANCE.isSubtypeOf(bType, aType)) return true;
|
if (JetTypeChecker.INSTANCE.isSubtypeOf(bType, aType)) return true;
|
||||||
}
|
}
|
||||||
@@ -91,5 +92,82 @@ public class CastDiagnosticsUtil {
|
|||||||
return descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() == ClassKind.TRAIT;
|
return descriptor instanceof ClassDescriptor && ((ClassDescriptor) descriptor).getKind() == ClassKind.TRAIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if cast from supertype to subtype is erased.
|
||||||
|
* It is an error in "is" statement and warning in "as".
|
||||||
|
*/
|
||||||
|
public static boolean isCastErased(@NotNull JetType supertype, @NotNull JetType subtype, @NotNull JetTypeChecker typeChecker) {
|
||||||
|
// cast between T and T? is always OK
|
||||||
|
if (supertype.isNullable() || subtype.isNullable()) {
|
||||||
|
return isCastErased(TypeUtils.makeNotNullable(supertype), TypeUtils.makeNotNullable(subtype), typeChecker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it is a upcast, it's never erased
|
||||||
|
if (typeChecker.isSubtypeOf(supertype, subtype)) return false;
|
||||||
|
|
||||||
|
// downcasting to a type parameter is always erased
|
||||||
|
if (isTypeParameter(subtype)) return true;
|
||||||
|
|
||||||
|
// Check that we are actually casting to a generic type
|
||||||
|
// NOTE: this does not account for 'as Array<List<T>>'
|
||||||
|
if (allParametersReified(subtype)) return false;
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
|
||||||
|
// Now, let's find a supertype of List<T> that is a Collection of something,
|
||||||
|
// in this case it will be Collection<T>
|
||||||
|
JetType supertypeWithVariables = TypeCheckingProcedure.findCorrespondingSupertype(subtypeWithVariables, supertype);
|
||||||
|
if (supertypeWithVariables == null) return true;
|
||||||
|
|
||||||
|
// Now, let's try to unify Collection<T> and Collection<Foo>
|
||||||
|
// solution is a map from T to Foo
|
||||||
|
final List<TypeParameterDescriptor> variables = subtypeWithVariables.getConstructor().getParameters();
|
||||||
|
TypeUnifier.UnificationResult solution = TypeUnifier.unify(
|
||||||
|
new TypeProjection(supertype), new TypeProjection(supertypeWithVariables),
|
||||||
|
new Predicate<TypeConstructor>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(TypeConstructor typeConstructor) {
|
||||||
|
ClassifierDescriptor descriptor = typeConstructor.getDeclarationDescriptor();
|
||||||
|
return descriptor instanceof TypeParameterDescriptor && variables.contains(descriptor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// If some of the parameters are not determined by unification, it means that these parameters are lost,
|
||||||
|
// let's put stars instead, so that we can only cast to something like List<*>, e.g. (a: Any) as List<*>
|
||||||
|
Map<TypeConstructor, TypeProjection> substitution = Maps.newHashMap(solution.getSubstitution());
|
||||||
|
for (TypeParameterDescriptor variable : variables) {
|
||||||
|
TypeProjection value = substitution.get(variable.getTypeConstructor());
|
||||||
|
if (value == null) {
|
||||||
|
substitution.put(
|
||||||
|
variable.getTypeConstructor(),
|
||||||
|
SubstitutionUtils.makeStarProjection(variable)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean allParametersReified(JetType subtype) {
|
||||||
|
for (TypeParameterDescriptor parameterDescriptor : subtype.getConstructor().getParameters()) {
|
||||||
|
if (!parameterDescriptor.isReified()) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private CastDiagnosticsUtil() {}
|
private CastDiagnosticsUtil() {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -353,13 +353,18 @@ public class TypeUtils {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static JetType makeUnsubstitutedType(ClassDescriptor classDescriptor, JetScope unsubstitutedMemberScope) {
|
public static JetType makeUnsubstitutedType(ClassDescriptor classDescriptor, JetScope unsubstitutedMemberScope) {
|
||||||
if (ErrorUtils.isError(classDescriptor)) {
|
return makeUnsubstitutedType(classDescriptor.getTypeConstructor(), unsubstitutedMemberScope);
|
||||||
return ErrorUtils.createErrorType("Unsubstituted type for " + classDescriptor);
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static JetType makeUnsubstitutedType(TypeConstructor typeConstructor, JetScope unsubstitutedMemberScope) {
|
||||||
|
if (ErrorUtils.isError(typeConstructor)) {
|
||||||
|
return ErrorUtils.createErrorType("Unsubstituted type for " + typeConstructor);
|
||||||
}
|
}
|
||||||
List<TypeProjection> arguments = getDefaultTypeProjections(classDescriptor.getTypeConstructor().getParameters());
|
List<TypeProjection> arguments = getDefaultTypeProjections(typeConstructor.getParameters());
|
||||||
return new JetTypeImpl(
|
return new JetTypeImpl(
|
||||||
Collections.<AnnotationDescriptor>emptyList(),
|
Collections.<AnnotationDescriptor>emptyList(),
|
||||||
classDescriptor.getTypeConstructor(),
|
typeConstructor,
|
||||||
false,
|
false,
|
||||||
arguments,
|
arguments,
|
||||||
unsubstitutedMemberScope
|
unsubstitutedMemberScope
|
||||||
|
|||||||
+1
-146
@@ -17,7 +17,6 @@
|
|||||||
package org.jetbrains.jet.lang.types.expressions;
|
package org.jetbrains.jet.lang.types.expressions;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Multimap;
|
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement;
|
||||||
import com.intellij.psi.tree.IElementType;
|
import com.intellij.psi.tree.IElementType;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -57,7 +56,6 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
|
|||||||
import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver;
|
import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver;
|
||||||
import org.jetbrains.jet.lang.types.*;
|
import org.jetbrains.jet.lang.types.*;
|
||||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
||||||
import org.jetbrains.jet.lang.types.checker.TypeCheckingProcedure;
|
|
||||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||||
import org.jetbrains.jet.lexer.JetTokens;
|
import org.jetbrains.jet.lexer.JetTokens;
|
||||||
import org.jetbrains.jet.utils.ThrowingList;
|
import org.jetbrains.jet.utils.ThrowingList;
|
||||||
@@ -235,156 +233,13 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
context.trace.report(USELESS_CAST.on(expression.getOperationReference()));
|
context.trace.report(USELESS_CAST.on(expression.getOperationReference()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (isCastErased(actualType, targetType, typeChecker)) {
|
if (CastDiagnosticsUtil.isCastErased(actualType, targetType, typeChecker)) {
|
||||||
context.trace.report(Errors.UNCHECKED_CAST.on(expression, actualType, targetType));
|
context.trace.report(Errors.UNCHECKED_CAST.on(expression, actualType, targetType));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if assignment from supertype to subtype is erased.
|
|
||||||
* It is an error in "is" statement and warning in "as".
|
|
||||||
*/
|
|
||||||
public static boolean isCastErased(@NotNull JetType supertype, @NotNull JetType subtype, @NotNull JetTypeChecker typeChecker) {
|
|
||||||
if (!(subtype.getConstructor().getDeclarationDescriptor() instanceof ClassDescriptor)) {
|
|
||||||
// TODO: what if it is TypeParameterDescriptor?
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// do not crash on error types
|
|
||||||
if (ErrorUtils.isErrorType(supertype) || ErrorUtils.isErrorType(subtype)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return hasDowncastsInTypeArguments(supertype, subtype, typeChecker) || hasErasedTypeArguments(supertype, subtype);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Check if type arguments are downcasted, which cannot be checked at run-time.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
1. a: MutableList<out Any> is MutableList<String> - true, because 'String' is more specific
|
|
||||||
2. a: Collection<String> is List<Any> - false, because 'Any' is less specific, and it is guaranteed by static checker
|
|
||||||
3. a: MutableCollection<String> is MutableList<Any> - false, because these types have empty intersection (type parameter is invariant)
|
|
||||||
*/
|
|
||||||
private static boolean hasDowncastsInTypeArguments(
|
|
||||||
@NotNull JetType supertype,
|
|
||||||
@NotNull JetType subtype,
|
|
||||||
@NotNull JetTypeChecker typeChecker
|
|
||||||
) {
|
|
||||||
List<TypeParameterDescriptor> superParameters = supertype.getConstructor().getParameters();
|
|
||||||
|
|
||||||
// This map holds arguments for type parameters of all superclasses of a type (see method comment for sample)
|
|
||||||
Multimap<TypeConstructor, TypeProjection> subtypeSubstitutionMap = SubstitutionUtils.buildDeepSubstitutionMultimap(subtype);
|
|
||||||
|
|
||||||
for (int i = 0; i < superParameters.size(); i++) {
|
|
||||||
TypeProjection superArgument = supertype.getArguments().get(i);
|
|
||||||
TypeParameterDescriptor parameter = superParameters.get(i);
|
|
||||||
|
|
||||||
if (parameter.isReified()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Collection<TypeProjection> substituted = subtypeSubstitutionMap.get(parameter.getTypeConstructor());
|
|
||||||
for (TypeProjection substitutedArgument : substituted) {
|
|
||||||
// For sample #2 (a: Collection<String> is List<Any>):
|
|
||||||
// parameter = E declared in Collection
|
|
||||||
// superArgument = String
|
|
||||||
// substitutedArgument = Any, because Collection<Any> is the supertype of List<Any>
|
|
||||||
|
|
||||||
// 1. Any..Nothing
|
|
||||||
// 2. String..Nothing
|
|
||||||
// 3. String..String
|
|
||||||
JetType superOut = TypeCheckingProcedure.getOutType(parameter, superArgument);
|
|
||||||
JetType superIn = TypeCheckingProcedure.getInType(parameter, superArgument);
|
|
||||||
|
|
||||||
// 1. String..String
|
|
||||||
// 2. Any..Nothing
|
|
||||||
// 3. Any..Any
|
|
||||||
JetType subOut = TypeCheckingProcedure.getOutType(parameter, substitutedArgument);
|
|
||||||
JetType subIn = TypeCheckingProcedure.getInType(parameter, substitutedArgument);
|
|
||||||
|
|
||||||
// super type range must be a subset of sub type range
|
|
||||||
if (typeChecker.isSubtypeOf(superOut, subOut) && typeChecker.isSubtypeOf(subIn, superIn)) {
|
|
||||||
// continue
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Check if type arguments are erased, that is they are not mapped to type parameters of supertype's class
|
|
||||||
|
|
||||||
Examples (MyMap is defined like this: trait MyMap<T>: Map<String, T>):
|
|
||||||
1. a: Any is List<String> - true
|
|
||||||
2. a: Collection<CharSequence> is List<String> - false
|
|
||||||
3. a: Map<String, String> is MyMap<String> - false
|
|
||||||
*/
|
|
||||||
private static boolean hasErasedTypeArguments(
|
|
||||||
@NotNull JetType supertype,
|
|
||||||
@NotNull JetType subtype
|
|
||||||
) {
|
|
||||||
// Erase all type arguments, replacing them with unsubstituted versions:
|
|
||||||
// 1. List<E>
|
|
||||||
// 2. List<E>
|
|
||||||
// 3. MyMap<T>
|
|
||||||
JetType subtypeCleared = TypeUtils.makeUnsubstitutedType(
|
|
||||||
(ClassDescriptor) subtype.getConstructor().getDeclarationDescriptor(), null);
|
|
||||||
|
|
||||||
// This map holds arguments for type parameters of all superclasses of a type (see method comment for sample)
|
|
||||||
// For all "E" declared in Collection, Iterable, etc., value will be type "E", where the latter E is declared in List
|
|
||||||
Multimap<TypeConstructor, TypeProjection> clearTypeSubstitutionMap =
|
|
||||||
SubstitutionUtils.buildDeepSubstitutionMultimap(subtypeCleared);
|
|
||||||
|
|
||||||
|
|
||||||
// This set will contain all arguments for type parameters of superclass which are mapped from type parameters of subtype's class
|
|
||||||
// 1. empty
|
|
||||||
// 2. [E declared in List]
|
|
||||||
// 3. [T declared in MyMap]
|
|
||||||
Set<JetType> clearSubstituted = new HashSet<JetType>();
|
|
||||||
|
|
||||||
List<TypeParameterDescriptor> superParameters = supertype.getConstructor().getParameters();
|
|
||||||
for (TypeParameterDescriptor superParameter : superParameters) {
|
|
||||||
Collection<TypeProjection> substituted = clearTypeSubstitutionMap.get(superParameter.getTypeConstructor());
|
|
||||||
for (TypeProjection substitutedProjection : substituted) {
|
|
||||||
clearSubstituted.add(substitutedProjection.getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each type parameter of subtype's class, we check that it is mapped to type parameters of supertype,
|
|
||||||
// that is its type is present in clearSubstituted set
|
|
||||||
List<TypeParameterDescriptor> subParameters = subtype.getConstructor().getParameters();
|
|
||||||
for (int i = 0; i < subParameters.size(); i++) {
|
|
||||||
TypeParameterDescriptor parameter = subParameters.get(i);
|
|
||||||
TypeProjection argument = subtype.getArguments().get(i);
|
|
||||||
|
|
||||||
if (parameter.isReified()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// "is List<*>", no check for type argument, actually
|
|
||||||
if (argument.equals(SubstitutionUtils.makeStarProjection(parameter))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if parameter is mapped to nothing then it is erased
|
|
||||||
// 1. return from here
|
|
||||||
// 2. contains = true, don't return
|
|
||||||
// 3. contains = true, don't return
|
|
||||||
if (!clearSubstituted.contains(parameter.getDefaultType())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JetTypeInfo visitThisExpression(JetThisExpression expression, ExpressionTypingContext context) {
|
public JetTypeInfo visitThisExpression(JetThisExpression expression, ExpressionTypingContext context) {
|
||||||
JetType result = null;
|
JetType result = null;
|
||||||
|
|||||||
+1
-1
@@ -285,7 +285,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
|||||||
context.trace.report(Errors.USELESS_NULLABLE_CHECK.on(nullableType));
|
context.trace.report(Errors.USELESS_NULLABLE_CHECK.on(nullableType));
|
||||||
}
|
}
|
||||||
checkTypeCompatibility(context, type, subjectType, typeReferenceAfterIs);
|
checkTypeCompatibility(context, type, subjectType, typeReferenceAfterIs);
|
||||||
if (BasicExpressionTypingVisitor.isCastErased(subjectType, type, JetTypeChecker.INSTANCE)) {
|
if (CastDiagnosticsUtil.isCastErased(subjectType, type, JetTypeChecker.INSTANCE)) {
|
||||||
context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, type));
|
context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, type));
|
||||||
}
|
}
|
||||||
return new DataFlowInfos(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, type), context.dataFlowInfo);
|
return new DataFlowInfos(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, type), context.dataFlowInfo);
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
trait Map<K, out V>
|
||||||
|
trait MutableMap<K, V>: Map<K, V> {
|
||||||
|
fun set(k: K, v: V)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun p(p: Map<String, Int>) {
|
||||||
|
if (p is MutableMap<String, Int>) {
|
||||||
|
p[""] = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fun f(a: Array<out Number>) = a is Array<Int>
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
open class BaseTwo<A, B>
|
||||||
|
open class DerivedWithOne<D>: BaseTwo<D, String>()
|
||||||
|
|
||||||
|
// a is BaseTwo<T, U> => if (a is DerivedWithOne<?>) a is DerivedWithOne<T>
|
||||||
|
fun <T, U> testing(a: BaseTwo<T, U>) = a is DerivedWithOne<T>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
open class Base<A>
|
||||||
|
class Some: Base<Int>()
|
||||||
|
|
||||||
|
// a is Some => a is Base<Int>
|
||||||
|
fun f(a: Some) = a is Base<Int>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
open class A
|
||||||
|
open class B: A()
|
||||||
|
|
||||||
|
open class Base<out T>
|
||||||
|
open class SubBase<T> : Base<T>()
|
||||||
|
|
||||||
|
// l is Base<+B> => if (l is SubBase<?>) l is SubBase<+B> => l is SubBase<+A>
|
||||||
|
fun ff(l: Base<B>) = l is SubBase<out A>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
open class A
|
||||||
|
open class B: A()
|
||||||
|
|
||||||
|
open class Base<in T>
|
||||||
|
class SubBase: Base<A>()
|
||||||
|
|
||||||
|
// f is SubBase => f is Base<A> => (Base<Contravariant T>, B <: A) f is Base<B>
|
||||||
|
fun test(f: SubBase) = f is Base<B>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
open class A
|
||||||
|
open class B: A()
|
||||||
|
|
||||||
|
open class Base<out T>
|
||||||
|
class SubBase: Base<B>()
|
||||||
|
|
||||||
|
// f is SubBase => (SubBase <: Base<B>) f is Base<B> => (B <: A, Base<Covariant T> => SubBase <: Base<A>) f is Base<A>
|
||||||
|
fun test(f: SubBase) = f is Base<A>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
trait A
|
||||||
|
trait B: A
|
||||||
|
trait D
|
||||||
|
|
||||||
|
trait BaseSuper<T>
|
||||||
|
trait BaseImpl: BaseSuper<D>
|
||||||
|
trait DerivedSuper<out S>: <!INCONSISTENT_TYPE_PARAMETER_VALUES!>BaseSuper<S>, BaseImpl<!>
|
||||||
|
|
||||||
|
fun test(t: BaseSuper<B>) = t is DerivedSuper<A>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
open class BaseMulti<out A, B>
|
||||||
|
class SomeMultiDerived<out D>: BaseMulti<D, Any>()
|
||||||
|
|
||||||
|
// t is BaseMulti<+String, String> => if (t is SomeMultiDerived<?>) => t is SomeMultiDerived<+String> =>
|
||||||
|
// => (String <: Any, SomeMultiDerived<Covariant D>) t is SomeMultiDerived<+Any>
|
||||||
|
fun someDerived(t: BaseMulti<String, String>) = t is SomeMultiDerived<Any>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
open class Base<A>
|
||||||
|
class Some: Base<Int>()
|
||||||
|
|
||||||
|
// No erased types in check
|
||||||
|
fun <A> f(a: Base<A>) = a is Some
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
open class A
|
||||||
|
open class B: A()
|
||||||
|
|
||||||
|
open class Base<out T>
|
||||||
|
open class SubBase<T> : Base<T>()
|
||||||
|
|
||||||
|
|
||||||
|
fun ff(l: Base<B>) = l is <!CANNOT_CHECK_FOR_ERASED!>SubBase<A><!>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
trait A
|
||||||
|
trait B: A
|
||||||
|
|
||||||
|
trait Base<T>
|
||||||
|
|
||||||
|
fun <T> test(a: Base<B>) where T: Base<A> = a is <!CANNOT_CHECK_FOR_ERASED!>T<!>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
open class A
|
||||||
|
open class B: A()
|
||||||
|
open class D
|
||||||
|
|
||||||
|
open class Base<out T, out U>
|
||||||
|
open class Derived<out S>: Base<S, S>()
|
||||||
|
|
||||||
|
fun test(a: Base<D, B>) = a is <!CANNOT_CHECK_FOR_ERASED!>Derived<A><!>
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
open class A
|
||||||
|
open class B: A()
|
||||||
|
open class D
|
||||||
|
|
||||||
|
open class Base<out T, out U>
|
||||||
|
open class Derived<out S>: Base<S, S>()
|
||||||
|
|
||||||
|
fun test(a: Base<B, D>) = a is <!CANNOT_CHECK_FOR_ERASED!>Derived<A><!>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
trait A
|
||||||
|
trait B
|
||||||
|
fun testing(a: A) = a as B
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fun <T: Any> testing(a: T?) = a is T
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fun <T> testing(a: T) = a is T
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
fun testing(a: Any) = a is <!UNRESOLVED_REFERENCE!>UnresolvedType<!><Int>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
open class RecA<T>: <!CYCLIC_INHERITANCE_HIERARCHY!>RecB<T><!>()
|
||||||
|
open class RecB<T>: <!CYCLIC_INHERITANCE_HIERARCHY!>RecA<T><!>()
|
||||||
|
open class SelfR<T>: <!CYCLIC_INHERITANCE_HIERARCHY!>SelfR<T><!>()
|
||||||
|
|
||||||
|
fun test(f: SelfR<String>) = f is <!CANNOT_CHECK_FOR_ERASED!>RecA<String><!>
|
||||||
|
fun test(f: RecB<String>) = f is RecA<String>
|
||||||
@@ -6,7 +6,7 @@ public fun foo(a: Any) {
|
|||||||
a is Map<out Any?, Any?>
|
a is Map<out Any?, Any?>
|
||||||
a is Map<*, *>
|
a is Map<*, *>
|
||||||
a is Map<<!SYNTAX!><!>>
|
a is Map<<!SYNTAX!><!>>
|
||||||
a is <!CANNOT_CHECK_FOR_ERASED!>List<<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Map<!>><!>
|
a is List<<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Map<!>>
|
||||||
a is <!NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION!>List<!>
|
a is <!NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION!>List<!>
|
||||||
a is Int
|
a is Int
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ public fun foo(a: Any, <!UNUSED_PARAMETER!>b<!>: <!WRONG_NUMBER_OF_TYPE_ARGUMENT
|
|||||||
is Map<out Any?, Any?> -> {}
|
is Map<out Any?, Any?> -> {}
|
||||||
is Map<*, *> -> {}
|
is Map<*, *> -> {}
|
||||||
is Map<<!SYNTAX!><!>> -> {}
|
is Map<<!SYNTAX!><!>> -> {}
|
||||||
is <!CANNOT_CHECK_FOR_ERASED!>List<<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Map<!>><!> -> {}
|
is List<<!WRONG_NUMBER_OF_TYPE_ARGUMENTS!>Map<!>> -> {}
|
||||||
is <!NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION!>List<!> -> {}
|
is <!NO_TYPE_ARGUMENTS_ON_RHS_OF_IS_EXPRESSION!>List<!> -> {}
|
||||||
is Int -> {}
|
is Int -> {}
|
||||||
else -> {}
|
else -> {}
|
||||||
|
|||||||
@@ -980,6 +980,56 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
|||||||
doTest("compiler/testData/diagnostics/tests/cast/AsErasedWarning.kt");
|
doTest("compiler/testData/diagnostics/tests/cast/AsErasedWarning.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("DowncastMap.kt")
|
||||||
|
public void testDowncastMap() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/DowncastMap.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsArray.kt")
|
||||||
|
public void testIsArray() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsArray.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedAllowForDerivedWithOneSubstitutedAndOneSameGeneric.kt")
|
||||||
|
public void testIsErasedAllowForDerivedWithOneSubstitutedAndOneSameGeneric() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedAllowForDerivedWithOneSubstitutedAndOneSameGeneric.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedAllowForExactSupertypeCheck.kt")
|
||||||
|
public void testIsErasedAllowForExactSupertypeCheck() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedAllowForExactSupertypeCheck.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedAllowForOverridenVarianceWithProjection.kt")
|
||||||
|
public void testIsErasedAllowForOverridenVarianceWithProjection() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedAllowForOverridenVarianceWithProjection.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedAllowForSupertypeCheckWithContrvariance.kt")
|
||||||
|
public void testIsErasedAllowForSupertypeCheckWithContrvariance() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedAllowForSupertypeCheckWithContrvariance.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedAllowForSupertypeCheckWithCovariance.kt")
|
||||||
|
public void testIsErasedAllowForSupertypeCheckWithCovariance() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedAllowForSupertypeCheckWithCovariance.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedAllowForTypeWithIrrelevantMixin.kt")
|
||||||
|
public void testIsErasedAllowForTypeWithIrrelevantMixin() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedAllowForTypeWithIrrelevantMixin.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedAllowForTypeWithTwoSameTypeSubstitutions.kt")
|
||||||
|
public void testIsErasedAllowForTypeWithTwoSameTypeSubstitutions() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedAllowForTypeWithTwoSameTypeSubstitutions.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedAllowForTypeWithoutTypeArguments.kt")
|
||||||
|
public void testIsErasedAllowForTypeWithoutTypeArguments() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedAllowForTypeWithoutTypeArguments.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("IsErasedAllowFromOut.kt")
|
@TestMetadata("IsErasedAllowFromOut.kt")
|
||||||
public void testIsErasedAllowFromOut() throws Exception {
|
public void testIsErasedAllowFromOut() throws Exception {
|
||||||
doTest("compiler/testData/diagnostics/tests/cast/IsErasedAllowFromOut.kt");
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedAllowFromOut.kt");
|
||||||
@@ -1015,6 +1065,16 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
|||||||
doTest("compiler/testData/diagnostics/tests/cast/IsErasedDisallowDifferentArgInvariantPosition.kt");
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedDisallowDifferentArgInvariantPosition.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedDisallowForOverridenVariance.kt")
|
||||||
|
public void testIsErasedDisallowForOverridenVariance() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedDisallowForOverridenVariance.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedDisallowForTypeWithConstraints.kt")
|
||||||
|
public void testIsErasedDisallowForTypeWithConstraints() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedDisallowForTypeWithConstraints.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("IsErasedDisallowFromAny.kt")
|
@TestMetadata("IsErasedDisallowFromAny.kt")
|
||||||
public void testIsErasedDisallowFromAny() throws Exception {
|
public void testIsErasedDisallowFromAny() throws Exception {
|
||||||
doTest("compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromAny.kt");
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromAny.kt");
|
||||||
@@ -1035,11 +1095,46 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
|||||||
doTest("compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOutAtClass.kt");
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedDisallowFromOutAtClass.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedDissallowForSubtypeMappedToTwoParamsWithFirstInvalid.kt")
|
||||||
|
public void testIsErasedDissallowForSubtypeMappedToTwoParamsWithFirstInvalid() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithFirstInvalid.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedDissallowForSubtypeMappedToTwoParamsWithSecondInvalid.kt")
|
||||||
|
public void testIsErasedDissallowForSubtypeMappedToTwoParamsWithSecondInvalid() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedDissallowForSubtypeMappedToTwoParamsWithSecondInvalid.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedNonGeneric.kt")
|
||||||
|
public void testIsErasedNonGeneric() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedNonGeneric.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedNullableTasT.kt")
|
||||||
|
public void testIsErasedNullableTasT() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedNullableTasT.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("IsErasedStar.kt")
|
@TestMetadata("IsErasedStar.kt")
|
||||||
public void testIsErasedStar() throws Exception {
|
public void testIsErasedStar() throws Exception {
|
||||||
doTest("compiler/testData/diagnostics/tests/cast/IsErasedStar.kt");
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedStar.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedTasT.kt")
|
||||||
|
public void testIsErasedTasT() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedTasT.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsErasedToErrorType.kt")
|
||||||
|
public void testIsErasedToErrorType() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsErasedToErrorType.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IsRecursionSustainable.kt")
|
||||||
|
public void testIsRecursionSustainable() throws Exception {
|
||||||
|
doTest("compiler/testData/diagnostics/tests/cast/IsRecursionSustainable.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("IsReified.kt")
|
@TestMetadata("IsReified.kt")
|
||||||
public void testIsReified() throws Exception {
|
public void testIsReified() throws Exception {
|
||||||
doTest("compiler/testData/diagnostics/tests/cast/IsReified.kt");
|
doTest("compiler/testData/diagnostics/tests/cast/IsReified.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user