diff --git a/idea/src/jet/Library.jet b/idea/src/jet/Library.jet index 0d535035baa..a1af5251d7e 100644 --- a/idea/src/jet/Library.jet +++ b/idea/src/jet/Library.jet @@ -40,12 +40,12 @@ fun Any?.equals(other : Any?) : Boolean// = this === other // Returns "null" for null fun Any?.toString() : String// = this === other -class Iterator { +trait class Iterator { fun next() : T abstract fun hasNext() : Boolean } -class Iterable { +trait class Iterable { fun iterator() : Iterator } @@ -56,11 +56,11 @@ class Array(val size : Int) { fun iterator() : Iterator } -virtual class Comparable { +trait class Comparable { fun compareTo(other : T) : Int } -virtual class Hashable { +trait class Hashable { fun hashCode() : Int fun equals(other : Any?) : Boolean } @@ -92,7 +92,7 @@ class String() : Comparable { fun trim(): String } -class Range> { +trait class Range> { fun contains(item : T) : Boolean } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index f746751f90c..7dab8fd1340 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -606,21 +606,26 @@ public class TopDownAnalyzer { : getInnerScopeForConstructor(primaryConstructor, descriptor.getScopeForMemberResolution(), true); final JetTypeInferrer.Services typeInferrer = semanticServices.getTypeInferrerServices(traceForConstructors, JetFlowInformationProvider.NONE); // TODO : flow + final Map supertypes = Maps.newLinkedHashMap(); JetVisitorVoid visitor = new JetVisitorVoid() { - private boolean superclassAppeared = false; - + private void recordSupertype(JetTypeReference typeReference, JetType supertype) { + if (supertype == null) return; + supertypes.put(typeReference, supertype); + } + @Override public void visitDelegationByExpressionSpecifier(JetDelegatorByExpressionSpecifier specifier) { if (descriptor.getClassModifiers().isTrait()) { trace.getErrorHandler().genericError(specifier.getNode(), "Traits can not use delegation"); } + JetType supertype = trace.getBindingContext().get(BindingContext.TYPE, specifier.getTypeReference()); + recordSupertype(specifier.getTypeReference(), supertype); JetExpression delegateExpression = specifier.getDelegateExpression(); if (delegateExpression != null) { JetScope scope = scopeForConstructor == null ? descriptor.getScopeForMemberResolution() : scopeForConstructor; JetType type = typeInferrer.getType(scope, delegateExpression, NO_EXPECTED_TYPE); - JetType supertype = trace.getBindingContext().get(BindingContext.TYPE, specifier.getTypeReference()); if (type != null && supertype != null && !semanticServices.getTypeChecker().isSubtypeOf(type, supertype)) { trace.getErrorHandler().typeMismatch(delegateExpression, supertype, type); } @@ -629,27 +634,32 @@ public class TopDownAnalyzer { @Override public void visitDelegationToSuperCallSpecifier(JetDelegatorToSuperCall call) { - if (descriptor.getClassModifiers().isTrait()) { - JetValueArgumentList valueArgumentList = call.getValueArgumentList(); - ASTNode node = valueArgumentList == null ? call.getNode() : valueArgumentList.getNode(); + JetValueArgumentList valueArgumentList = call.getValueArgumentList(); + ASTNode node = valueArgumentList == null ? call.getNode() : valueArgumentList.getNode(); + if (descriptor.isTrait()) { trace.getErrorHandler().genericError(node, "Traits can not initialize supertypes"); } JetTypeReference typeReference = call.getTypeReference(); if (typeReference != null) { if (descriptor.getUnsubstitutedPrimaryConstructor() != null) { - JetType type = typeInferrer.getCallResolver().resolveCall(trace, scopeForConstructor, null, call, NO_EXPECTED_TYPE); - if (type != null) { - DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor(); - if (declarationDescriptor instanceof ClassDescriptor) { - ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; - if (classDescriptor.getClassModifiers().isTrait()) { - trace.getErrorHandler().genericError(call.getValueArgumentList().getNode(), "A trait may not have a constructor"); + JetType supertype = typeInferrer.getCallResolver().resolveCall(trace, scopeForConstructor, null, call, NO_EXPECTED_TYPE); + if (supertype != null) { + recordSupertype(typeReference, supertype); + ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(supertype); + if (classDescriptor != null) { + if (classDescriptor.isTrait()) { + trace.getErrorHandler().genericError(node, "A trait may not have a constructor"); } } } + else { + recordSupertype(typeReference, trace.getBindingContext().get(BindingContext.TYPE, typeReference)); + } } - else { - JetValueArgumentList valueArgumentList = call.getValueArgumentList(); + else if (!descriptor.isTrait()) { + JetType supertype = trace.getBindingContext().get(BindingContext.TYPE, typeReference); + recordSupertype(typeReference, supertype); + assert valueArgumentList != null; trace.getErrorHandler().genericError(valueArgumentList.getNode(), "Class " + JetPsiUtil.safeName(jetClass.getName()) + " must have a constructor in order to be able to initialize supertypes"); @@ -659,31 +669,18 @@ public class TopDownAnalyzer { @Override public void visitDelegationToSuperClassSpecifier(JetDelegatorToSuperClass specifier) { - JetType supertype = trace.getBindingContext().get(BindingContext.TYPE, specifier.getTypeReference()); + JetTypeReference typeReference = specifier.getTypeReference(); + JetType supertype = trace.getBindingContext().get(BindingContext.TYPE, typeReference); + recordSupertype(typeReference, supertype); if (supertype != null) { - DeclarationDescriptor declarationDescriptor = supertype.getConstructor().getDeclarationDescriptor(); - if (declarationDescriptor instanceof ClassDescriptor) { - ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor; - if (!descriptor.getClassModifiers().isTrait()) { - if (classDescriptor.hasConstructors() && !ErrorUtils.isError(classDescriptor.getTypeConstructor()) && !classDescriptor.getClassModifiers().isTrait()) { + ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(supertype); + if (classDescriptor != null) { + if (!descriptor.isTrait()) { + if (classDescriptor.hasConstructors() && !ErrorUtils.isError(classDescriptor.getTypeConstructor()) && !classDescriptor.isTrait()) { trace.getErrorHandler().genericError(specifier.getNode(), "This type has a constructor, and thus must be initialized here"); } } - else { - if (!classDescriptor.getClassModifiers().isTrait()) { - if (superclassAppeared) { - trace.getErrorHandler().genericError(specifier.getNode(), "A trait may extend only one class"); - } - else { - superclassAppeared = true; - } - } - } } - else { - trace.getErrorHandler().genericError(specifier.getNode(), "Only classes may serve as supertypes"); - } - } } @@ -697,9 +694,52 @@ public class TopDownAnalyzer { throw new UnsupportedOperationException(element.getText() + " : " + element); } }; + for (JetDelegationSpecifier delegationSpecifier : jetClass.getDelegationSpecifiers()) { delegationSpecifier.accept(visitor); } + + + Set parentEnum = Collections.emptySet(); + if (jetClass instanceof JetEnumEntry) { + parentEnum = Collections.singleton(((ClassDescriptor) descriptor.getContainingDeclaration().getContainingDeclaration()).getTypeConstructor()); + } + + checkSupertypeList(descriptor, supertypes, parentEnum); + } + + // allowedFinalSupertypes typically contains a enum type of which supertypeOwner is an entry + private void checkSupertypeList(@NotNull MutableClassDescriptor supertypeOwner, @NotNull Map supertypes, Set allowedFinalSupertypes) { + Set typeConstructors = Sets.newHashSet(); + boolean classAppeared = false; + for (Map.Entry entry : supertypes.entrySet()) { + JetTypeReference typeReference = entry.getKey(); + JetType supertype = entry.getValue(); + + ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(supertype); + if (classDescriptor != null) { + if (!classDescriptor.isTrait()) { + if (classAppeared) { + trace.getErrorHandler().genericError(typeReference.getNode(), "Only one class may appear in a supertype list"); + } + else { + classAppeared = true; + } + } + } + else { + trace.getErrorHandler().genericError(typeReference.getNode(), "Only classes and traits may serve as supertypes"); + } + + TypeConstructor constructor = supertype.getConstructor(); + if (!typeConstructors.add(constructor)) { + trace.getErrorHandler().genericError(typeReference.getNode(), "A supertype appears twice"); + } + + if (constructor.isSealed() && !allowedFinalSupertypes.contains(constructor)) { + trace.getErrorHandler().genericError(typeReference.getNode(), "This type is final, so it cannot be inherited from"); + } + } } private void resolveClassAnnotations() { diff --git a/idea/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java index 28ed2b1f94d..98bca6a932a 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/calls/CallResolver.java @@ -253,7 +253,6 @@ public class CallResolver { } if (traceForFirstNonemptyCandidateSet != null) { traceForFirstNonemptyCandidateSet.commit(); - assert resultForFirstNonemptyCandidateSet != null; if (resultForFirstNonemptyCandidateSet.singleDescriptor()) { return resultForFirstNonemptyCandidateSet.getDescriptor(); } diff --git a/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java b/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java index 75ed47acf37..34f32ea9203 100644 --- a/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java +++ b/idea/src/org/jetbrains/jet/lang/types/TypeUtils.java @@ -321,6 +321,15 @@ public class TypeUtils { return declarationDescriptor1.getOriginal().equals(declarationDescriptor2.getOriginal()); } + @Nullable + public static ClassDescriptor getClassDescriptor(@NotNull JetType type) { + DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor(); + if (declarationDescriptor instanceof ClassDescriptor) { + return (ClassDescriptor) declarationDescriptor; + } + return null; + } + public static boolean hasUnsubstitutedTypeParameters(JetType type) { if(type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) return true; diff --git a/idea/testData/checker/Builders.jet b/idea/testData/checker/Builders.jet index bc1d5d0bc5d..1d26cbbc8b2 100644 --- a/idea/testData/checker/Builders.jet +++ b/idea/testData/checker/Builders.jet @@ -49,7 +49,7 @@ namespace html { class Title() : TagWithText("title") - class BodyTag(name : String) : TagWithText(name) { + abstract class BodyTag(name : String) : TagWithText(name) { } class Body() : BodyTag("body") { diff --git a/idea/testData/checker/Constructors.jet b/idea/testData/checker/Constructors.jet index 1e964eac399..70d1ce5a88a 100644 --- a/idea/testData/checker/Constructors.jet +++ b/idea/testData/checker/Constructors.jet @@ -1,9 +1,9 @@ -class NoC +open class NoC class NoC1 : NoC class WithC0() : NoC() -class WithC1() : NoC +open class WithC1() : NoC class NoC2 : WithC1 class NoC3 : WithC1() class WithC2() : WithC1 diff --git a/idea/testData/checker/MultipleBounds.jet b/idea/testData/checker/MultipleBounds.jet index e7489eabe0a..3caff264ca5 100644 --- a/idea/testData/checker/MultipleBounds.jet +++ b/idea/testData/checker/MultipleBounds.jet @@ -4,14 +4,14 @@ open class A() { fun foo() : Int = 1 } -open class B() { +trait class B { fun bar() : Double = 1.0; } -class C() : A(), B() +class C() : A(), B class D() { - class object : A(), B () {} + class object : A(), B {} } class Test1() @@ -61,7 +61,7 @@ fun test2(t : T) } val t1 = test2<A>(A()) -val t2 = test2<B>(B()) +val t2 = test2<B>(C()) val t3 = test2(C()) class Test<T> diff --git a/idea/testData/checker/Objects.jet b/idea/testData/checker/Objects.jet index 7bf4d985c94..512fa6a0485 100644 --- a/idea/testData/checker/Objects.jet +++ b/idea/testData/checker/Objects.jet @@ -1,5 +1,5 @@ namespace toplevelObjectDeclarations { - class Foo(y : Int) { + open class Foo(y : Int) { virtual fun foo() : Int = 1 } @@ -63,7 +63,7 @@ namespace localObjects { val x : Int = 0 } - class Foo { + open class Foo { fun foo() : Int = 1 } diff --git a/idea/testData/checker/PrimaryConstructors.jet b/idea/testData/checker/PrimaryConstructors.jet index d0a4707c60b..2ea2ef34ab9 100644 --- a/idea/testData/checker/PrimaryConstructors.jet +++ b/idea/testData/checker/PrimaryConstructors.jet @@ -2,7 +2,7 @@ class X { val x : Int } -class Y() { +open class Y() { val x : Int = 2 } diff --git a/idea/testData/checker/SupertypeListChecks.jet b/idea/testData/checker/SupertypeListChecks.jet new file mode 100644 index 00000000000..a2dcbe1e40f --- /dev/null +++ b/idea/testData/checker/SupertypeListChecks.jet @@ -0,0 +1,51 @@ +// KT-286 Check supertype lists + +/* +In a supertype list: + Same type should not be mentioned twice + Same type should not be indirectly mentioned with incoherent type arguments + Every trait's required dependencies should be satisfied + No final types should appear + Only one class is allowed +*/ + +class C1() + +open class OC1() + +open class C2 {} + +open class C3 {} + +trait class T1 {} + +trait class T2 {} + +trait class Test() { + this(x : Int) {} +} + +trait class Test1 : C2() {} + +trait class Test2 : C2 {} + +trait class Test3 : C2, C3 {} + +trait class Test4 : T1 {} + +trait class Test5 : T1, T1 {} + +trait class Test6 : C1 {} + +class CTest1() : OC1() {} + +class CTest2 : C2 {} + +class CTest3 : C2, C3 {} + +class CTest4 : T1 {} + +class CTest5 : T1, T1 {} + +class CTest6 : C1 {} + diff --git a/idea/testData/checker/TraitSupertypeList.jet b/idea/testData/checker/TraitSupertypeList.jet index 1648e44d332..3aa451d6557 100644 --- a/idea/testData/checker/TraitSupertypeList.jet +++ b/idea/testData/checker/TraitSupertypeList.jet @@ -1,11 +1,11 @@ -class bar() +open class bar() -trait open class Foo() : bar(), bar, bar { +trait open class Foo() : bar(), bar, bar { this(x : Int) {} } trait open class Foo2 : bar, Foo { } -open class Foo1() : bar(), bar, Foo, Foo() {} +open class Foo1() : bar(), bar, Foo, Foo() {} open class Foo12 : bar(), bar {} \ No newline at end of file diff --git a/idea/testData/checker/infos/Autocasts.jet b/idea/testData/checker/infos/Autocasts.jet index 4a201ea3397..7cf18aadd48 100644 --- a/idea/testData/checker/infos/Autocasts.jet +++ b/idea/testData/checker/infos/Autocasts.jet @@ -1,4 +1,4 @@ -class A() { +open class A() { fun foo() {} } diff --git a/idea/testData/checker/regression/Jet11.jet b/idea/testData/checker/regression/Jet11.jet index d78aa91a35d..e2e2a110a52 100644 --- a/idea/testData/checker/regression/Jet11.jet +++ b/idea/testData/checker/regression/Jet11.jet @@ -1,4 +1,4 @@ // JET-11 Redeclaration & Forward reference for classes cause an exception -class NoC +open class NoC class NoC1 : NoC -class NoC +open class NoC diff --git a/idea/testData/codegen/bridge.jet b/idea/testData/codegen/bridge.jet index 22f30c592e1..421e675e7e7 100644 --- a/idea/testData/codegen/bridge.jet +++ b/idea/testData/codegen/bridge.jet @@ -1,6 +1,6 @@ import java.lang.Integer -class C { +open class C { fun f(): Any = "C f" } diff --git a/idea/testData/codegen/classes/diamondInheritance.jet b/idea/testData/codegen/classes/diamondInheritance.jet index aefec63bfda..5f4d1c3f29a 100644 --- a/idea/testData/codegen/classes/diamondInheritance.jet +++ b/idea/testData/codegen/classes/diamondInheritance.jet @@ -1,11 +1,13 @@ -class Base() { +// Changed when traits were introduced. May not make sense any more + +open class Base() { public var v : Int = 0 } -class Left() : Base() {} -class Right() : Base() {} +open class Left() : Base() {} +trait class Right : Base {} -class D() : Left(), Right() +class D() : Left(), Right fun vl(l : Left) : Int = l.v fun vr(r : Right) : Int = r.v diff --git a/idea/testData/codegen/classes/funDelegation.jet b/idea/testData/codegen/classes/funDelegation.jet index f56f9d0d29b..25f2acc796d 100644 --- a/idea/testData/codegen/classes/funDelegation.jet +++ b/idea/testData/codegen/classes/funDelegation.jet @@ -1,8 +1,8 @@ -class Base() { +open class Base() { fun n(n : Int) : Int = n + 1 } -class Abstract {} +trait class Abstract {} class Derived1() : Base(), Abstract {} class Derived2() : Abstract, Base() {} diff --git a/idea/testData/codegen/classes/inheritance.jet b/idea/testData/codegen/classes/inheritance.jet index 54445e1f860..164971fe30f 100644 --- a/idea/testData/codegen/classes/inheritance.jet +++ b/idea/testData/codegen/classes/inheritance.jet @@ -1,9 +1,17 @@ -class X(val x : Int) {} -class Y(val y : Int) {} +// Changed when traits were introduced. May not make sense any more -class Point(x : Int, y : Int) : X(x) , Y(y) {} +open class X(val x : Int) {} +trait class Y { + abstract val y : Int +} -class Abstract {} +class YImpl(val y : Int) : Y {} + +class Point(x : Int, yy : Int) : X(x) , Y { + override val y : Int = yy +} + +trait class Abstract {} class P1(x : Int, yy : Y) : Abstract, X(x), Y by yy {} class P2(x : Int, yy : Y) : X(x), Abstract, Y by yy {} @@ -12,12 +20,12 @@ class P4(x : Int, yy : Y) : Y by yy, Abstract, X(x) {} fun box() : String { if (X(239).x != 239) return "FAIL #1" - if (Y(239).y != 239) return "FAIL #2" + if (YImpl(239).y != 239) return "FAIL #2" val p = Point(240, -1) if (p.x + p.y != 239) return "FAIL #3" - val y = Y(-1) + val y = YImpl(-1) val p1 = P1(240, y) if (p1.x + p1.y != 239) return "FAIL #4" val p2 = P2(240, y) diff --git a/idea/testData/codegen/classes/inheritedInnerClass.jet b/idea/testData/codegen/classes/inheritedInnerClass.jet index 54a7b4bb78c..4717bea55a8 100644 --- a/idea/testData/codegen/classes/inheritedInnerClass.jet +++ b/idea/testData/codegen/classes/inheritedInnerClass.jet @@ -1,5 +1,5 @@ class Outer() { - class InnerBase() { + open class InnerBase() { } class InnerDerived(): InnerBase() { diff --git a/idea/testData/codegen/classes/inheritedMethod.jet b/idea/testData/codegen/classes/inheritedMethod.jet index 1005cd31be7..597910dc7b4 100644 --- a/idea/testData/codegen/classes/inheritedMethod.jet +++ b/idea/testData/codegen/classes/inheritedMethod.jet @@ -1,4 +1,4 @@ -class Foo { +open class Foo { fun xyzzy(): String = "xyzzy" } diff --git a/idea/testData/codegen/classes/propertyDelegation.jet b/idea/testData/codegen/classes/propertyDelegation.jet index af33786e627..3f9a5646b91 100644 --- a/idea/testData/codegen/classes/propertyDelegation.jet +++ b/idea/testData/codegen/classes/propertyDelegation.jet @@ -1,4 +1,4 @@ -class Base() { +open class Base() { public val plain = 239 public val read : Int get() = 239 @@ -10,7 +10,7 @@ class Base() { } } -class Abstract {} +trait class Abstract {} class Derived1() : Base(), Abstract {} class Derived2() : Abstract, Base() {} diff --git a/idea/testData/codegen/classes/propertyInInitializer.jet b/idea/testData/codegen/classes/propertyInInitializer.jet index 824da2313aa..ed507f6278f 100644 --- a/idea/testData/codegen/classes/propertyInInitializer.jet +++ b/idea/testData/codegen/classes/propertyInInitializer.jet @@ -1,7 +1,7 @@ class Outer() { public val s = "xyzzy" - class InnerBase(public val name: String) { + open class InnerBase(public val name: String) { } class InnerDerived(): InnerBase(s) { diff --git a/idea/testData/codegen/classes/rightHandOverride.jet b/idea/testData/codegen/classes/rightHandOverride.jet index 66c47d7c1cc..eda09d25be6 100644 --- a/idea/testData/codegen/classes/rightHandOverride.jet +++ b/idea/testData/codegen/classes/rightHandOverride.jet @@ -1,9 +1,11 @@ -class Left() {} -class Right() { +// Changed when traits were introduced. May not make sense any more + +trait class Left {} +open class Right() { virtual fun f() = 42 } -class D() : Left(), Right() { +class D() : Left, Right() { override fun f() = 239 }