From 1e63c6257cb5ac57bfe58e45afff47aaa881a204 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 24 Nov 2011 21:14:50 +0300 Subject: [PATCH 1/5] Throw exception if compilation fails on server --- .../jetbrains/jet/codegen/GenerationState.java | 2 ++ .../jet/lang/resolve/java/AnalyzerFacade.java | 16 +++------------- .../jet/lang/diagnostics/DiagnosticUtils.java | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/GenerationState.java b/compiler/backend/src/org/jetbrains/jet/codegen/GenerationState.java index 56b432cf2c9..21c99a991f5 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/GenerationState.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/GenerationState.java @@ -10,6 +10,7 @@ import org.jetbrains.jet.codegen.intrinsics.IntrinsicMethods; import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor; +import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.AnalyzingUtils; import org.jetbrains.jet.lang.resolve.BindingContext; @@ -113,6 +114,7 @@ public class GenerationState { } catch (Throwable e) { errorHandler.reportException(e, namespace.getContainingFile().getVirtualFile().getUrl()); + DiagnosticUtils.throwIfRunningOnServer(e); } } } diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AnalyzerFacade.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AnalyzerFacade.java index 6ad38396927..ad4e1f20d74 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AnalyzerFacade.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/AnalyzerFacade.java @@ -11,6 +11,7 @@ import com.intellij.psi.util.PsiModificationTracker; import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; +import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.psi.JetDeclaration; import org.jetbrains.jet.lang.psi.JetFile; @@ -68,18 +69,8 @@ public class AnalyzerFacade { throw e; } catch (Throwable e) { - // This is needed for the Web Demo server to log the exceptions coming from the analyzer instead of showing them in the editor. - if (System.getProperty("kotlin.running.in.server.mode", "false").equals("true")) { - if (e instanceof RuntimeException) { - RuntimeException runtimeException = (RuntimeException) e; - throw runtimeException; - } - if (e instanceof Error) { - Error error = (Error) e; - throw error; - } - throw new RuntimeException(e); - } + DiagnosticUtils.throwIfRunningOnServer(e); + e.printStackTrace(); BindingTraceContext bindingTraceContext = new BindingTraceContext(); bindingTraceContext.report(Errors.EXCEPTION_WHILE_ANALYZING.on(file, e)); @@ -93,5 +84,4 @@ public class AnalyzerFacade { } return bindingContextCachedValue.getValue(); } - } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticUtils.java index 0ff433add71..0e85d64ce6d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticUtils.java @@ -81,4 +81,19 @@ public class DiagnosticUtils { } return position; } + + public static void throwIfRunningOnServer(Throwable e) { + // This is needed for the Web Demo server to log the exceptions coming from the analyzer instead of showing them in the editor. + if (System.getProperty("kotlin.running.in.server.mode", "false").equals("true")) { + if (e instanceof RuntimeException) { + RuntimeException runtimeException = (RuntimeException) e; + throw runtimeException; + } + if (e instanceof Error) { + Error error = (Error) e; + throw error; + } + throw new RuntimeException(e); + } + } } From aba6b3d6b935a2ce153c4633ac92e16106c4d6c5 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 24 Nov 2011 22:56:14 +0300 Subject: [PATCH 2/5] KT-549 type inference failed KT-580 Type inference failed KT-600 Problem with 'sure' extension function type inference KT-571 Type inference failed --- .../jet/lang/resolve/calls/CallResolver.java | 14 +++-- .../jetbrains/jet/lang/types/ErrorUtils.java | 13 +++- .../jetbrains/jet/lang/types/TypeUtils.java | 61 +++++++++++++------ .../types/inference/ConstraintSystemImpl.java | 49 ++++++++++++--- .../checkerWithErrorTypes/full/kt600.jet | 8 +++ .../full/regression/kt549.jet | 16 +++++ .../full/regression/kt580.jet | 20 ++++++ .../quick/regressions/kt571.jet | 3 + .../testData/codegen/regressions/kt247.jet | 3 +- .../jet/types/JetTypeCheckerTest.java | 16 +++++ 10 files changed, 167 insertions(+), 36 deletions(-) create mode 100644 compiler/testData/checkerWithErrorTypes/full/kt600.jet create mode 100644 compiler/testData/checkerWithErrorTypes/full/regression/kt549.jet create mode 100644 compiler/testData/checkerWithErrorTypes/full/regression/kt580.jet create mode 100644 compiler/testData/checkerWithErrorTypes/quick/regressions/kt571.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index f9bedad327d..68effdfba5e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -39,6 +39,8 @@ import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE; * @author abreslav */ public class CallResolver { + private static final JetType DONT_CARE = ErrorUtils.createErrorTypeWithCustomDebugName("DONT_CARE"); + private final JetSemanticServices semanticServices; private final OverloadingConflictResolver overloadingConflictResolver; private final DataFlowInfo dataFlowInfo; @@ -412,7 +414,7 @@ public class CallResolver { constraintSystem.registerTypeVariable(typeParameterDescriptor, Variance.INVARIANT); // TODO } - TypeSubstitutor substituteUnknown = ConstraintSystemImpl.makeConstantSubstitutor(candidate.getTypeParameters(), ErrorUtils.createErrorType("Unknown")); + TypeSubstitutor substituteDontCare = ConstraintSystemImpl.makeConstantSubstitutor(candidate.getTypeParameters(), DONT_CARE); for (Map.Entry entry : candidateCall.getValueArguments().entrySet()) { ResolvedValueArgument valueArgument = entry.getValue(); @@ -428,7 +430,7 @@ public class CallResolver { // We'll type check the arguments later, with the inferred types expected TemporaryBindingTrace traceForUnknown = TemporaryBindingTrace.create(temporaryTrace); ExpressionTypingServices temporaryServices = new ExpressionTypingServices(semanticServices, traceForUnknown); - JetType type = temporaryServices.getType(scope, expression, substituteUnknown.substitute(valueParameterDescriptor.getOutType(), Variance.INVARIANT)); + JetType type = temporaryServices.getType(scope, expression, substituteDontCare.substitute(valueParameterDescriptor.getOutType(), Variance.INVARIANT)); if (type != null) { constraintSystem.addSubtypingConstraint(type, effectiveExpectedType); } @@ -439,10 +441,10 @@ public class CallResolver { } // Error is already reported if something is missing - ReceiverDescriptor receiverParameter = candidateCall.getReceiverArgument(); - ReceiverDescriptor candidateReceiver = candidate.getReceiverParameter(); - if (receiverParameter.exists() && candidateReceiver.exists()) { - constraintSystem.addSubtypingConstraint(receiverParameter.getType(), candidateReceiver.getType()); + ReceiverDescriptor receiverArgument = candidateCall.getReceiverArgument(); + ReceiverDescriptor receiverParameter = candidate.getReceiverParameter(); + if (receiverArgument.exists() && receiverParameter.exists()) { + constraintSystem.addSubtypingConstraint(receiverArgument.getType(), receiverParameter.getType()); } if (expectedType != NO_EXPECTED_TYPE) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java index 5cfbbc1301f..1c77b65f443 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java @@ -171,11 +171,20 @@ public class ErrorUtils { } private static JetType createErrorType(String debugMessage, JetScope memberScope) { - return new ErrorTypeImpl(new TypeConstructorImpl(ERROR_CLASS, Collections.emptyList(), false, "[ERROR : " + debugMessage + "]", Collections.emptyList(), Collections.singleton(JetStandardClasses.getAnyType())), memberScope); + return createErrorTypeWithCustomDebugName(memberScope, "[ERROR : " + debugMessage + "]"); + } + + @NotNull + public static JetType createErrorTypeWithCustomDebugName(String debugName) { + return createErrorTypeWithCustomDebugName(ERROR_SCOPE, debugName); + } + + private static JetType createErrorTypeWithCustomDebugName(JetScope memberScope, String debugName) { + return new ErrorTypeImpl(new TypeConstructorImpl(ERROR_CLASS, Collections.emptyList(), false, debugName, Collections.emptyList(), Collections.singleton(JetStandardClasses.getAnyType())), memberScope); } public static JetType createWrongVarianceErrorType(TypeProjection value) { - return createErrorType(value + " is not allowed here]", value.getType().getMemberScope()); + return createErrorType(value + " is not allowed here", value.getType().getMemberScope()); } public static ClassifierDescriptor getErrorClass() { 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 6304adf84db..82e4416c056 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -110,54 +110,65 @@ public class TypeUtils { } @Nullable - public static JetType intersect(JetTypeChecker typeChecker, Set types) { + public static JetType intersect(@NotNull JetTypeChecker typeChecker, @NotNull Set types) { assert !types.isEmpty(); if (types.size() == 1) { return types.iterator().next(); } - StringBuilder debugName = new StringBuilder(); - boolean nullable = false; - Set resultingTypes = Sets.newHashSet(); + // Intersection of T1..Tn is an intersection of their non-null versions, + // made nullable is they all were nullable + boolean allNullable = true; + boolean nothingTypePresent = false; + List nullabilityStripped = Lists.newArrayList(); + for (JetType type : types) { + nothingTypePresent |= JetStandardClasses.isNothingOrNullableNothing(type); + allNullable &= type.isNullable(); + nullabilityStripped.add(makeNotNullable(type)); + } + + if (nothingTypePresent) { + return allNullable ? JetStandardClasses.getNullableNothingType() : JetStandardClasses.getNothingType(); + } + // Now we remove types that have subtypes in the list + List resultingTypes = Lists.newArrayList(); outer: - for (Iterator iterator = types.iterator(); iterator.hasNext();) { - JetType type = iterator.next(); - + for (JetType type : nullabilityStripped) { if (!canHaveSubtypes(typeChecker, type)) { - for (JetType other : types) { + for (JetType other : nullabilityStripped) { // It makes sense to check for subtyping of 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)) { return null; } } - return type; + return makeNullableAsSpecified(type, allNullable); } else { - for (JetType other : types) { + for (JetType other : nullabilityStripped) { if (!type.equals(other) && typeChecker.isSubtypeOf(other, type)) { continue outer; } + } } - nullable |= type.isNullable(); - resultingTypes.add(type); - debugName.append(type.toString()); - if (iterator.hasNext()) { - debugName.append(" & "); - } } + + if (resultingTypes.size() == 1) { + return makeNullableAsSpecified(resultingTypes.get(0), allNullable); + } + List noAnnotations = Collections.emptyList(); TypeConstructor constructor = new TypeConstructorImpl( null, noAnnotations, false, - debugName.toString(), + makeDebugNameForIntersectionType(resultingTypes).toString(), Collections.emptyList(), resultingTypes); @@ -171,11 +182,25 @@ public class TypeUtils { return new JetTypeImpl( noAnnotations, constructor, - nullable, + allNullable, Collections.emptyList(), new ChainedScope(null, scopes)); // TODO : check intersectibility, don't use a chanied scope } + private static StringBuilder makeDebugNameForIntersectionType(Iterable resultingTypes) { + StringBuilder debugName = new StringBuilder("{"); + for (Iterator iterator = resultingTypes.iterator(); iterator.hasNext(); ) { + JetType type = iterator.next(); + + debugName.append(type.toString()); + if (iterator.hasNext()) { + debugName.append(" & "); + } + } + debugName.append("}"); + return debugName; + } + public static boolean canHaveSubtypes(JetTypeChecker typeChecker, JetType type) { if (type.isNullable()) { return true; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemImpl.java index e693c3dddf1..c5aff27be5c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/inference/ConstraintSystemImpl.java @@ -9,6 +9,7 @@ import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import org.jetbrains.jet.lang.types.*; import java.util.Collection; +import java.util.Collections; import java.util.Map; import java.util.Set; @@ -46,8 +47,19 @@ public class ConstraintSystemImpl implements ConstraintSystem { } public static abstract class TypeValue { - private final Set upperBounds = Sets.newHashSet(); - private final Set lowerBounds = Sets.newHashSet(); + private final Set mutableUpperBounds = Sets.newHashSet(); + private final Set upperBounds = Collections.unmodifiableSet(mutableUpperBounds); + + private final Set mutableLowerBounds = Sets.newHashSet(); + private final Set lowerBounds = Collections.unmodifiableSet(mutableLowerBounds); + + public void addUpperBound(@NotNull TypeValue bound) { + mutableUpperBounds.add(bound); + } + + public void addLowerBound(@NotNull TypeValue bound) { + mutableLowerBounds.add(bound); + } @NotNull public Set getUpperBounds() { @@ -86,8 +98,8 @@ public class ConstraintSystemImpl implements ConstraintSystem { throw new LoopInTypeVariableConstraintsException(); } if (value == null) { - JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; beingComputed = true; + JetTypeChecker typeChecker = JetTypeChecker.INSTANCE; try { if (positionVariance == Variance.IN_VARIANCE) { // maximal solution @@ -320,8 +332,8 @@ public class ConstraintSystemImpl implements ConstraintSystem { private void addSubtypingConstraintOnTypeValues(TypeValue typeValueForLower, TypeValue typeValueForUpper) { println(typeValueForLower + " :< " + typeValueForUpper); if (typeValueForLower != typeValueForUpper) { - typeValueForLower.getUpperBounds().add(typeValueForUpper); - typeValueForUpper.getLowerBounds().add(typeValueForLower); + typeValueForLower.addUpperBound(typeValueForUpper); + typeValueForUpper.addLowerBound(typeValueForLower); } } @@ -358,13 +370,20 @@ public class ConstraintSystemImpl implements ConstraintSystem { // effective bounds for each node Set visited = Sets.newHashSet(); - for (KnownType knownType : knownTypes.values()) { - transitiveClosure(knownType, visited); - } for (UnknownType unknownType : unknownTypes.values()) { transitiveClosure(unknownType, visited); } + for (UnknownType unknownType : unknownTypes.values()) { + println("Constraints for " + unknownType.getTypeParameterDescriptor()); + printTypeValue(unknownType); + } + + for (KnownType knownType : knownTypes.values()) { + println("Constraints for " + knownType.getType()); + printTypeValue(knownType); + } + // Find inconsistencies Solution solution = new Solution(); @@ -382,6 +401,15 @@ public class ConstraintSystemImpl implements ConstraintSystem { return solution; } + private void printTypeValue(TypeValue typeValue) { + for (TypeValue bound : typeValue.getUpperBounds()) { + println(" :< " + bound); + } + for (TypeValue bound : typeValue.getLowerBounds()) { + println(" :> " + bound); + } + } + private void check(TypeValue typeValue, Solution solution) { try { KnownType resultingValue = typeValue.getValue(); @@ -416,7 +444,7 @@ public class ConstraintSystemImpl implements ConstraintSystem { } } solution.registerError("[TODO] Loop in constraints"); - e.printStackTrace(); +// e.printStackTrace(); } } @@ -426,6 +454,9 @@ public class ConstraintSystemImpl implements ConstraintSystem { } for (TypeValue upperBound : Sets.newHashSet(current.getUpperBounds())) { + if (upperBound instanceof KnownType) { + continue; + } transitiveClosure(upperBound, visited); Set upperBounds = upperBound.getUpperBounds(); for (TypeValue transitiveBound : upperBounds) { diff --git a/compiler/testData/checkerWithErrorTypes/full/kt600.jet b/compiler/testData/checkerWithErrorTypes/full/kt600.jet new file mode 100644 index 00000000000..b5734ce9471 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/kt600.jet @@ -0,0 +1,8 @@ +//KT-600 Problem with 'sure' extension function type inference + +fun T?.sure() : T { if (this != null) return this else throw NullPointerException() } + +fun test() { + val i : Int? = 10 + val i2 : Int = i.sure() // inferred type is Int? but Int was excepted +} \ No newline at end of file diff --git a/compiler/testData/checkerWithErrorTypes/full/regression/kt549.jet b/compiler/testData/checkerWithErrorTypes/full/regression/kt549.jet new file mode 100644 index 00000000000..9e4725f142e --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/regression/kt549.jet @@ -0,0 +1,16 @@ +//KT-549 type inference failed +namespace demo + + fun filter(list : Array, filter : fun (T) : Boolean) : java.util.List { + val answer = java.util.ArrayList(); + for (l in list) { + if (filter(l)) answer.add(l) + } + return answer; + } + +fun main(args : Array) { + for (a in filter(args, {it.length > 1})) { + System.out?.println("Hello, ${a}!") + } +} diff --git a/compiler/testData/checkerWithErrorTypes/full/regression/kt580.jet b/compiler/testData/checkerWithErrorTypes/full/regression/kt580.jet new file mode 100644 index 00000000000..d05397ef6e0 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/regression/kt580.jet @@ -0,0 +1,20 @@ +//KT-580 Type inference failed +namespace whats.the.difference + +import java.util.* + +fun iarray(vararg a : String) = a // BUG + +fun main(vals : IntArray) { + val vals = iarray("789", "678", "567") + val diffs = ArrayList + for (i in vals.indices) { + for (j in i..vals.lastIndex()) // Type inference failed + diffs.add(vals[i].length - vals[j].length) + for (j in i..vals.lastIndex) // Type inference failed + diffs.add(vals[i].length - vals[j].length) + } +} + +fun Array.lastIndex() = size - 1 +val Array.lastIndex : Int get() = size - 1 \ No newline at end of file diff --git a/compiler/testData/checkerWithErrorTypes/quick/regressions/kt571.jet b/compiler/testData/checkerWithErrorTypes/quick/regressions/kt571.jet new file mode 100644 index 00000000000..2333eb78644 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/quick/regressions/kt571.jet @@ -0,0 +1,3 @@ +//KT-571 Type inference failed +fun let(t : T, body : fun(T) : R) = body(t) +private fun double(d : Int) : Int = let(d * 2) {it / 10 + it * 2 % 10} diff --git a/compiler/testData/codegen/regressions/kt247.jet b/compiler/testData/codegen/regressions/kt247.jet index 49637518d25..af13811dee9 100644 --- a/compiler/testData/codegen/regressions/kt247.jet +++ b/compiler/testData/codegen/regressions/kt247.jet @@ -19,7 +19,7 @@ fun t3() { fun t4() { val e: E? = E() - System.out?.println(e?.foo() == e) //verify error + System.out?.println(e?.bar() == e) //verify error System.out?.println(e?.foo()) //verify error } @@ -35,4 +35,5 @@ class C(val x: Int) class D(val s: String) class E() { fun foo() = 1 + fun bar() = this } diff --git a/compiler/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java b/compiler/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java index bc2d5e09fa3..81687edd8c8 100644 --- a/compiler/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java +++ b/compiler/tests/org/jetbrains/jet/types/JetTypeCheckerTest.java @@ -162,6 +162,22 @@ public class JetTypeCheckerTest extends JetLiteFixture { assertIntersection("Int?", "Int?", "Int?"); assertIntersection("Int", "Int?", "Int"); assertIntersection("Int", "Int", "Int?"); + + assertIntersection("Int", "Any", "Int"); + assertIntersection("Int", "Int", "Any"); + + assertIntersection("Int", "Any", "Int?"); + assertIntersection("Int", "Int?", "Any"); + assertIntersection("Int", "Any?", "Int"); + assertIntersection("Int", "Int", "Any?"); + + assertIntersection("Nothing", "Nothing", "Nothing"); + assertIntersection("Nothing?", "Nothing?", "Nothing?"); + assertIntersection("Nothing", "Nothing", "Nothing?"); + assertIntersection("Nothing", "Nothing?", "Nothing"); + + assertIntersection("Nothing?", "String?", "Nothing?"); + assertIntersection("Nothing?", "Nothing?", "String?"); } public void testBasicSubtyping() throws Exception { From 9ef4790976206d26687a39e34cdc9d99ad93ac98 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Thu, 24 Nov 2011 23:13:03 +0400 Subject: [PATCH 3/5] allow null from CallableDescriptor.getReturnType --- .../jetbrains/jet/lang/descriptors/CallableDescriptor.java | 4 +++- .../jet/lang/descriptors/FunctionDescriptorImpl.java | 1 - .../jetbrains/jet/lang/descriptors/PropertyDescriptor.java | 1 - .../jet/lang/descriptors/PropertyGetterDescriptor.java | 1 - .../jet/lang/descriptors/VariableDescriptorImpl.java | 1 - .../src/org/jetbrains/jet/resolve/DescriptorRenderer.java | 7 +++++-- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/CallableDescriptor.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/CallableDescriptor.java index 4e78d2c81f5..752d0da55d3 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/CallableDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/CallableDescriptor.java @@ -21,7 +21,9 @@ public interface CallableDescriptor extends DeclarationDescriptor { @NotNull List getTypeParameters(); - @NotNull + /** + * Method may return null for not yet fully initialized object or if error occurred. + */ JetType getReturnType(); @NotNull diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorImpl.java index ef3ab5aefa0..490b9f889b1 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorImpl.java @@ -120,7 +120,6 @@ public class FunctionDescriptorImpl extends DeclarationDescriptorImpl implements } @Override - @NotNull public JetType getReturnType() { return unsubstitutedReturnType; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/PropertyDescriptor.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/PropertyDescriptor.java index 69555990589..361eb70fbb4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/PropertyDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/PropertyDescriptor.java @@ -113,7 +113,6 @@ public class PropertyDescriptor extends VariableDescriptorImpl implements Callab return expectedThisObject; } - @NotNull @Override public JetType getReturnType() { return getOutType(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/PropertyGetterDescriptor.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/PropertyGetterDescriptor.java index 4181da25779..095254c7f3d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/PropertyGetterDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/PropertyGetterDescriptor.java @@ -34,7 +34,6 @@ public class PropertyGetterDescriptor extends PropertyAccessorDescriptor { return Collections.emptyList(); } - @NotNull @Override public JetType getReturnType() { return returnType; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/VariableDescriptorImpl.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/VariableDescriptorImpl.java index 6daefbce285..9ff1027e6c6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/VariableDescriptorImpl.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/VariableDescriptorImpl.java @@ -84,7 +84,6 @@ public abstract class VariableDescriptorImpl extends DeclarationDescriptorImpl i return ReceiverDescriptor.NO_RECEIVER; } - @NotNull @Override public JetType getReturnType() { return getOutType(); diff --git a/compiler/frontend/src/org/jetbrains/jet/resolve/DescriptorRenderer.java b/compiler/frontend/src/org/jetbrains/jet/resolve/DescriptorRenderer.java index a378e31d39b..4eeb1635dc3 100644 --- a/compiler/frontend/src/org/jetbrains/jet/resolve/DescriptorRenderer.java +++ b/compiler/frontend/src/org/jetbrains/jet/resolve/DescriptorRenderer.java @@ -73,7 +73,11 @@ public class DescriptorRenderer implements Renderer { } public String renderType(JetType type) { - return escape(type.toString()); + if (type != null) { + return escape(""); + } else { + return escape(type.toString()); + } } protected String escape(String s) { @@ -222,7 +226,6 @@ public class DescriptorRenderer implements Renderer { renderName(descriptor, builder); renderValueParameters(descriptor, builder); - // TODO: getReturnType may be uninitialized and throw IllegalStateException // stepan.koltsov@ 2011-11-21 builder.append(" : ").append(escape(renderType(descriptor.getReturnType()))); return super.visitFunctionDescriptor(descriptor, builder); } From 7020d6e15c87db4cbf68509f42d2d76e0080a5f4 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Thu, 24 Nov 2011 23:52:10 +0400 Subject: [PATCH 4/5] more check for casts KT-445 (Don't allow deep instanceof for erased parameters) and more see new tests in compiler/testData/checkerWithErrorTypes/full/cast --- .../jet/lang/diagnostics/Errors.java | 6 +- .../BasicExpressionTypingVisitor.java | 55 +++++++++++++++++++ .../PatternMatchingTypingVisitor.java | 14 ++++- .../full/cast/AsErasedError.jet | 4 ++ .../full/cast/AsErasedFine.jet | 4 ++ .../full/cast/AsErasedStar.jet | 4 ++ .../full/cast/AsErasedWarning.jet | 3 + .../full/cast/IsErasedAllow.jet | 5 ++ .../cast/IsErasedAllowParameterSubtype.jet | 9 +++ .../full/cast/IsErasedDisallowFromAny.jet | 3 + .../full/cast/IsErasedStar.jet | 3 + .../full/cast/IsReified.jet | 3 + .../full/cast/IsTraits.jet | 4 ++ .../full/cast/WhenErasedDisallowFromAny.jet | 6 ++ .../testData/codegen/patternMatching/is.jet | 4 +- 15 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 compiler/testData/checkerWithErrorTypes/full/cast/AsErasedError.jet create mode 100644 compiler/testData/checkerWithErrorTypes/full/cast/AsErasedFine.jet create mode 100644 compiler/testData/checkerWithErrorTypes/full/cast/AsErasedStar.jet create mode 100644 compiler/testData/checkerWithErrorTypes/full/cast/AsErasedWarning.jet create mode 100644 compiler/testData/checkerWithErrorTypes/full/cast/IsErasedAllow.jet create mode 100644 compiler/testData/checkerWithErrorTypes/full/cast/IsErasedAllowParameterSubtype.jet create mode 100644 compiler/testData/checkerWithErrorTypes/full/cast/IsErasedDisallowFromAny.jet create mode 100644 compiler/testData/checkerWithErrorTypes/full/cast/IsErasedStar.jet create mode 100644 compiler/testData/checkerWithErrorTypes/full/cast/IsReified.jet create mode 100644 compiler/testData/checkerWithErrorTypes/full/cast/IsTraits.jet create mode 100644 compiler/testData/checkerWithErrorTypes/full/cast/WhenErasedDisallowFromAny.jet diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index 98f7f7d5d69..02bd8737c36 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -16,6 +16,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Iterator; +import java.util.List; import static org.jetbrains.jet.lang.diagnostics.Severity.ERROR; import static org.jetbrains.jet.lang.diagnostics.Severity.WARNING; @@ -288,7 +289,10 @@ public interface Errors { ParameterizedDiagnosticFactory2 TYPE_MISMATCH_IN_TUPLE_PATTERN = ParameterizedDiagnosticFactory2.create(ERROR, "Type mismatch: subject is of type {0} but the pattern is of type Tuple{1}"); // TODO: message ParameterizedDiagnosticFactory2 TYPE_MISMATCH_IN_BINDING_PATTERN = ParameterizedDiagnosticFactory2.create(ERROR, "{0} must be a supertype of {1}. Use 'is' to match against {0}"); ParameterizedDiagnosticFactory2 INCOMPATIBLE_TYPES = ParameterizedDiagnosticFactory2.create(ERROR, "Incompatible types: {0} and {1}"); - + + ParameterizedDiagnosticFactory1 CANNOT_CHECK_FOR_ERASED = ParameterizedDiagnosticFactory1.create(ERROR, "Cannot check for instance of erased type: {0}"); + ParameterizedDiagnosticFactory2 UNCHECKED_CAST = ParameterizedDiagnosticFactory2.create(WARNING, "Unchecked cast: {0} to {1}"); + ParameterizedDiagnosticFactory3> INCONSISTENT_TYPE_PARAMETER_VALUES = new ParameterizedDiagnosticFactory3>(ERROR, "Type parameter {0} of {1} has inconsistent values: {2}") { @Override protected String makeMessageForA(@NotNull TypeParameterDescriptor typeParameterDescriptor) { 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 0446105d479..720155d0206 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 @@ -1,6 +1,7 @@ package org.jetbrains.jet.lang.types.expressions; import com.google.common.collect.Lists; +import com.google.common.collect.Multimap; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; @@ -8,6 +9,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.JetNodeTypes; import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.calls.CallMaker; @@ -245,10 +247,63 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { else { if (typeChecker.isSubtypeOf(actualType, targetType)) { context.trace.report(USELESS_CAST.on(expression, expression.getOperationSign())); + } else { + if (isCastErased(actualType, targetType)) { + context.trace.report(Errors.UNCHECKED_CAST.on(expression, actualType, targetType)); + } } } } + /** + * Check if assignment from ActualType to TargetType is erased. + * It is an error in "is" statement and warning in "as". + */ + public static boolean isCastErased(JetType actualType, JetType targetType) { + + if (!(targetType.getConstructor().getDeclarationDescriptor() instanceof ClassDescriptor)) { + // TODO: what if it is TypeParameterDescriptor? + return false; + } + + JetType targetTypeClerared = TypeUtils.makeUnsubstitutedType( + (ClassDescriptor) targetType.getConstructor().getDeclarationDescriptor(), null); + + Multimap clearTypeSubstitutionMap = + TypeUtils.buildDeepSubstitutionMultimap(targetTypeClerared); + + Set clearSubstituted = new HashSet(); + + for (int i = 0; i < actualType.getConstructor().getParameters().size(); ++i) { + TypeParameterDescriptor subjectTypeParameterDescriptor = actualType.getConstructor().getParameters().get(i); + + Collection subst = clearTypeSubstitutionMap.get(subjectTypeParameterDescriptor.getTypeConstructor()); + for (TypeProjection proj : subst) { + clearSubstituted.add(proj.getType()); + } + } + + for (int i = 0; i < targetType.getConstructor().getParameters().size(); ++i) { + TypeParameterDescriptor typeParameter = targetType.getConstructor().getParameters().get(i); + TypeProjection typeProjection = targetType.getArguments().get(i); + + if (typeParameter.isReified()) { + continue; + } + + // "is List<*>" + if (typeProjection.equals(TypeUtils.makeStarProjection(typeParameter))) { + continue; + } + + // if parameter is mapped to nothing then it is erased + if (!clearSubstituted.contains(typeParameter.getDefaultType())) { + return true; + } + } + return false; + } + @Override public JetType visitTupleExpression(JetTupleExpression expression, ExpressionTypingContext context) { List entries = expression.getEntries(); 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 943343f4d70..3753726ce0c 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 @@ -1,11 +1,14 @@ package org.jetbrains.jet.lang.types.expressions; +import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.Ref; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import org.jetbrains.jet.lang.descriptors.VariableDescriptor; +import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowValue; @@ -17,8 +20,7 @@ import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverDescriptor; import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver; import org.jetbrains.jet.lang.types.*; -import java.util.List; -import java.util.Set; +import java.util.*; import static org.jetbrains.jet.lang.diagnostics.Errors.*; import static org.jetbrains.jet.lang.types.expressions.ExpressionTypingUtils.ensureBooleanResultWithCustomSubject; @@ -245,6 +247,9 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { } } + /* + * (a: SubjectType) is Type + */ private void checkTypeCompatibility(@Nullable JetType type, @NotNull JetType subjectType, @NotNull JetElement reportErrorOn) { // TODO : Take auto casts into account? if (type == null) { @@ -253,6 +258,11 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor { if (TypeUtils.intersect(context.semanticServices.getTypeChecker(), Sets.newHashSet(type, subjectType)) == null) { // context.trace.getErrorHandler().genericError(reportErrorOn.getNode(), "Incompatible types: " + type + " and " + subjectType); context.trace.report(INCOMPATIBLE_TYPES.on(reportErrorOn, type, subjectType)); + return; + } + + if (BasicExpressionTypingVisitor.isCastErased(subjectType, type)) { + context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(reportErrorOn, type)); } } diff --git a/compiler/testData/checkerWithErrorTypes/full/cast/AsErasedError.jet b/compiler/testData/checkerWithErrorTypes/full/cast/AsErasedError.jet new file mode 100644 index 00000000000..bff067d20ee --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/cast/AsErasedError.jet @@ -0,0 +1,4 @@ +import java.util.List; +import java.util.Collection; + +fun ff(c: Collection) = c as List diff --git a/compiler/testData/checkerWithErrorTypes/full/cast/AsErasedFine.jet b/compiler/testData/checkerWithErrorTypes/full/cast/AsErasedFine.jet new file mode 100644 index 00000000000..341f370d108 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/cast/AsErasedFine.jet @@ -0,0 +1,4 @@ +import java.util.List; +import java.util.Collection; + +fun ff(c: Collection) = c as List diff --git a/compiler/testData/checkerWithErrorTypes/full/cast/AsErasedStar.jet b/compiler/testData/checkerWithErrorTypes/full/cast/AsErasedStar.jet new file mode 100644 index 00000000000..b9c2faf0d9a --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/cast/AsErasedStar.jet @@ -0,0 +1,4 @@ +import java.util.List; + +fun ff(l: Any) = l as List<*> + diff --git a/compiler/testData/checkerWithErrorTypes/full/cast/AsErasedWarning.jet b/compiler/testData/checkerWithErrorTypes/full/cast/AsErasedWarning.jet new file mode 100644 index 00000000000..a55f6b7d58b --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/cast/AsErasedWarning.jet @@ -0,0 +1,3 @@ +import java.util.List; + +fun ff(a: Any) = a as List diff --git a/compiler/testData/checkerWithErrorTypes/full/cast/IsErasedAllow.jet b/compiler/testData/checkerWithErrorTypes/full/cast/IsErasedAllow.jet new file mode 100644 index 00000000000..7f32b79eba2 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/cast/IsErasedAllow.jet @@ -0,0 +1,5 @@ +import java.util.Collection; +import java.util.List; + +fun ff(l: Collection) = l is List + diff --git a/compiler/testData/checkerWithErrorTypes/full/cast/IsErasedAllowParameterSubtype.jet b/compiler/testData/checkerWithErrorTypes/full/cast/IsErasedAllowParameterSubtype.jet new file mode 100644 index 00000000000..d1694229dc6 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/cast/IsErasedAllowParameterSubtype.jet @@ -0,0 +1,9 @@ +import java.util.Collection; +import java.util.List; + +open class A + +class B : A + +fun ff(l: Collection) = l is List + diff --git a/compiler/testData/checkerWithErrorTypes/full/cast/IsErasedDisallowFromAny.jet b/compiler/testData/checkerWithErrorTypes/full/cast/IsErasedDisallowFromAny.jet new file mode 100644 index 00000000000..eb3b402ccc7 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/cast/IsErasedDisallowFromAny.jet @@ -0,0 +1,3 @@ +import java.util.List; + +fun ff(l: Any) = l is List diff --git a/compiler/testData/checkerWithErrorTypes/full/cast/IsErasedStar.jet b/compiler/testData/checkerWithErrorTypes/full/cast/IsErasedStar.jet new file mode 100644 index 00000000000..404d8f8f3b0 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/cast/IsErasedStar.jet @@ -0,0 +1,3 @@ +import java.util.List; + +fun ff(l: Any) = l is List<*> diff --git a/compiler/testData/checkerWithErrorTypes/full/cast/IsReified.jet b/compiler/testData/checkerWithErrorTypes/full/cast/IsReified.jet new file mode 100644 index 00000000000..247196d6e84 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/cast/IsReified.jet @@ -0,0 +1,3 @@ +class MyList + +fun ff(a: Any) = a is MyList diff --git a/compiler/testData/checkerWithErrorTypes/full/cast/IsTraits.jet b/compiler/testData/checkerWithErrorTypes/full/cast/IsTraits.jet new file mode 100644 index 00000000000..99656c2ab24 --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/cast/IsTraits.jet @@ -0,0 +1,4 @@ +trait Aaa +trait Bbb + +fun f(a: Aaa) = a is Bbb diff --git a/compiler/testData/checkerWithErrorTypes/full/cast/WhenErasedDisallowFromAny.jet b/compiler/testData/checkerWithErrorTypes/full/cast/WhenErasedDisallowFromAny.jet new file mode 100644 index 00000000000..f38a4b14fbe --- /dev/null +++ b/compiler/testData/checkerWithErrorTypes/full/cast/WhenErasedDisallowFromAny.jet @@ -0,0 +1,6 @@ +import java.util.List; + +fun ff(l: Any) = when(l) { + is List => 1 + else 2 +} diff --git a/compiler/testData/codegen/patternMatching/is.jet b/compiler/testData/codegen/patternMatching/is.jet index a5b5f33195f..9fac50c586b 100644 --- a/compiler/testData/codegen/patternMatching/is.jet +++ b/compiler/testData/codegen/patternMatching/is.jet @@ -1,6 +1,6 @@ fun typeName(a: Any?) : String { return when(a) { - is java.util.ArrayList => "array list" + is java.util.ArrayList<*> => "array list" else => "no idea" } } @@ -8,4 +8,4 @@ fun typeName(a: Any?) : String { fun box() : String { if(typeName(java.util.ArrayList ()) != "array list") return "array list failed" return "OK" -} \ No newline at end of file +} From 0b689f32a74b505b2c1626118433ceedcd086bf7 Mon Sep 17 00:00:00 2001 From: Stepan Koltsov Date: Fri, 25 Nov 2011 00:26:31 +0400 Subject: [PATCH 5/5] print environment in bootstrap.xml --- bootstrap.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/bootstrap.xml b/bootstrap.xml index 1697aacc6b7..52b29aca855 100644 --- a/bootstrap.xml +++ b/bootstrap.xml @@ -1,4 +1,20 @@ + + + + @{prop}=${@{prop}} + + + + + + + + + + + +