From 669126d6356367243c116a157aef2412bee02a11 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Tue, 27 Mar 2012 12:19:01 +0400 Subject: [PATCH] Making compiler exit codes type safe. Fixes the problem with wrong command-line argument usage being missed by the IDE --- .../calls/inference/SolutionStatus.java | 5 ++ .../jetbrains/jet/lang/types/TypeUtils.java | 73 +++++++++++++++++-- .../BasicExpressionTypingVisitor.java | 3 +- .../PatternMatchingTypingVisitor.java | 2 +- .../diagnostics/tests/regressions/kt1647.jet | 11 +++ 5 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/regressions/kt1647.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/SolutionStatus.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/SolutionStatus.java index 7fde8e61fb5..1d1b8dbd9f4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/SolutionStatus.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/SolutionStatus.java @@ -25,6 +25,11 @@ public interface SolutionStatus { public boolean isSuccessful() { return true; } + + @Override + public String toString() { + return "SUCCESS"; + } }; boolean isSuccessful(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java index 03cba987a5b..b8609b3b077 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -20,12 +20,18 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; +import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintResolutionListener; +import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemSolution; +import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintSystemWithPriorities; +import org.jetbrains.jet.lang.resolve.calls.inference.ConstraintType; import org.jetbrains.jet.lang.resolve.scopes.ChainedScope; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.checker.JetTypeChecker; @@ -128,11 +134,8 @@ public class TypeUtils { return new JetTypeImpl(type.getAnnotations(), type.getConstructor(), nullable, type.getArguments(), type.getMemberScope()); } - @NotNull - public static JetType safeIntersect(JetTypeChecker typeChecker, Set types) { - JetType intersection = intersect(typeChecker, types); - if (intersection == null) return ErrorUtils.createErrorType("No intersection for " + types); // TODO : message - return intersection; + public static boolean isIntersectionEmpty(@NotNull JetType typeA, @NotNull JetType typeB) { + return intersect(JetTypeChecker.INSTANCE, Sets.newLinkedHashSet(Lists.newArrayList(typeA, typeB))) == null; } @Nullable @@ -164,9 +167,9 @@ public class TypeUtils { for (JetType type : nullabilityStripped) { if (!canHaveSubtypes(typeChecker, type)) { for (JetType other : nullabilityStripped) { - // It makes sense to check for subtyping of other <: type, despite that + // It makes sense to check for subtyping (other <: type), despite that // type is not supposed to be open, for there're enums - if (!type.equals(other) && !typeChecker.isSubtypeOf(type, other) && !typeChecker.isSubtypeOf(other, type)) { + if (!TypeUnifier.mayBeEqual(type, other) && !typeChecker.isSubtypeOf(type, other) && !typeChecker.isSubtypeOf(other, type)) { return null; } } @@ -213,6 +216,59 @@ public class TypeUtils { new ChainedScope(null, scopes)); // TODO : check intersectibility, don't use a chanied scope } + private static class TypeUnifier { + private static class TypeParameterUsage { + private final TypeParameterDescriptor typeParameterDescriptor; + private final Variance howTheTypeParameterIsUsed; + + public TypeParameterUsage(TypeParameterDescriptor typeParameterDescriptor, Variance howTheTypeParameterIsUsed) { + this.typeParameterDescriptor = typeParameterDescriptor; + this.howTheTypeParameterIsUsed = howTheTypeParameterIsUsed; + } + } + + public static boolean mayBeEqual(@NotNull JetType type, @NotNull JetType other) { + return unify(type, other); + } + + private static boolean unify(JetType withParameters, JetType expected) { + ConstraintSystemWithPriorities constraintSystem = new ConstraintSystemWithPriorities(ConstraintResolutionListener.DO_NOTHING); + // T -> how T is used + final Map parameters = Maps.newHashMap(); + Processor processor = new Processor() { + @Override + public boolean process(TypeParameterUsage parameterUsage) { + Variance howTheTypeIsUsedBefore = parameters.get(parameterUsage.typeParameterDescriptor); + if (howTheTypeIsUsedBefore == null) { + howTheTypeIsUsedBefore = Variance.INVARIANT; + } + parameters.put(parameterUsage.typeParameterDescriptor, + parameterUsage.howTheTypeParameterIsUsed.superpose(howTheTypeIsUsedBefore)); + return true; + } + }; + processAllTypeParameters(withParameters, Variance.INVARIANT, processor); + processAllTypeParameters(expected, Variance.INVARIANT, processor); + for (Map.Entry entry : parameters.entrySet()) { + constraintSystem.registerTypeVariable(entry.getKey(), entry.getValue()); + } + constraintSystem.addSubtypingConstraint(ConstraintType.VALUE_ARGUMENT.assertSubtyping(withParameters, expected)); + + ConstraintSystemSolution solution = constraintSystem.solve(); + return solution.getStatus().isSuccessful(); + } + + private static void processAllTypeParameters(JetType type, Variance howThiTypeIsUsed, Processor result) { + ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); + if (descriptor instanceof TypeParameterDescriptor) { + result.process(new TypeParameterUsage((TypeParameterDescriptor)descriptor, howThiTypeIsUsed)); + } + for (TypeProjection projection : type.getArguments()) { + processAllTypeParameters(projection.getType(), projection.getProjectionKind(), result); + } + } + } + private static StringBuilder makeDebugNameForIntersectionType(Iterable resultingTypes) { StringBuilder debugName = new StringBuilder("{"); for (Iterator iterator = resultingTypes.iterator(); iterator.hasNext(); ) { @@ -472,8 +528,9 @@ public class TypeUtils { } public static boolean hasUnsubstitutedTypeParameters(JetType type) { - if(type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) + if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) { return true; + } for(TypeProjection proj : type.getArguments()) { if(hasUnsubstitutedTypeParameters(proj.getType())) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java index 03ba7ba3235..d49ec8fc56d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/BasicExpressionTypingVisitor.java @@ -934,8 +934,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { JetType rightType = facade.getType(right, context.replaceScope(context.scope)); if (rightType != null) { - JetType intersect = TypeUtils.intersect(JetTypeChecker.INSTANCE, new HashSet(Arrays.asList(leftType, rightType))); - if (intersect == null) { + if (TypeUtils.isIntersectionEmpty(leftType, rightType)) { context.trace.report(EQUALITY_NOT_APPLICABLE.on(expression, operationSign, leftType, rightType)); } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java index 56bd680ab88..df89ce15acc 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/PatternMatchingTypingVisitor.java @@ -280,7 +280,7 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { if (type == null) { return; } - if (TypeUtils.intersect(JetTypeChecker.INSTANCE, Sets.newHashSet(type, subjectType)) == null) { + if (TypeUtils.isIntersectionEmpty(type, subjectType)) { context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType)); return; } diff --git a/compiler/testData/diagnostics/tests/regressions/kt1647.jet b/compiler/testData/diagnostics/tests/regressions/kt1647.jet new file mode 100644 index 00000000000..dc0b3e26c38 --- /dev/null +++ b/compiler/testData/diagnostics/tests/regressions/kt1647.jet @@ -0,0 +1,11 @@ +// KT-1647 Pattern matching doesn't work with generics + +open class Abs +class Bar : Abs() + +fun patternMatchingAndGenerics(arg : F) : String { + if(arg is Bar){ + return "Bar"; + } + return "else"; +} \ No newline at end of file