From 8cffea6bc29c3cc73ff5c419baa478c6ae8a08ca Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Sun, 5 Jun 2011 11:41:08 +0400 Subject: [PATCH] JET-84 Support generic parameter constraints for class objects JET-89 Report a warning is the only upper bound is final JET-87 Support where-clause for generic parameter constraints --- .../lang/descriptors/ClassDescriptorImpl.java | 2 +- .../descriptors/TypeParameterDescriptor.java | 8 +- .../lang/resolve/ClassDescriptorResolver.java | 164 ++++++++++-------- .../jet/lang/resolve/TopDownAnalyzer.java | 5 +- .../jetbrains/jet/lang/types/ErrorUtils.java | 8 +- idea/testData/checker/Bounds.jet | 8 +- idea/testData/checker/MultipleBounds.jet | 33 +++- 7 files changed, 141 insertions(+), 87 deletions(-) diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/ClassDescriptorImpl.java b/idea/src/org/jetbrains/jet/lang/descriptors/ClassDescriptorImpl.java index 70cb89ddb88..1ee46e04256 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/ClassDescriptorImpl.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/ClassDescriptorImpl.java @@ -67,7 +67,7 @@ public class ClassDescriptorImpl extends DeclarationDescriptorImpl implements Cl @NotNull @Override public FunctionGroup getConstructors(List typeArguments) { - assert typeArguments.size() == getTypeConstructor().getParameters().size(); + assert typeArguments.size() == getTypeConstructor().getParameters().size() : "Argument list length mismatch for " + getName(); if (typeArguments.size() == 0) { return constructors; } diff --git a/idea/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java b/idea/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java index 99baf8e5dc5..4db47927596 100644 --- a/idea/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java +++ b/idea/src/org/jetbrains/jet/lang/descriptors/TypeParameterDescriptor.java @@ -90,8 +90,8 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement @NotNull public JetType getBoundsAsType() { if (boundsAsType == null) { - assert upperBounds != null; - assert upperBounds.size() > 0; + assert upperBounds != null : "Upper bound list is null in " + getName(); + assert upperBounds.size() > 0 : "Upper bound list is empty in " + getName(); boundsAsType = TypeUtils.intersect(JetTypeChecker.INSTANCE, upperBounds); if (boundsAsType == null) { boundsAsType = JetStandardClasses.getNothingType(); @@ -136,7 +136,9 @@ public class TypeParameterDescriptor extends DeclarationDescriptorImpl implement if (classObjectBoundsAsType == null) { classObjectBoundsAsType = TypeUtils.intersect(JetTypeChecker.INSTANCE, classObjectUpperBounds); - assert classObjectBoundsAsType != null; // TODO : Error message + if (classObjectBoundsAsType == null) { + classObjectBoundsAsType = JetStandardClasses.getNothingType(); + } } return classObjectBoundsAsType; } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java index c8169a97a6f..214542e6006 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/ClassDescriptorResolver.java @@ -52,6 +52,7 @@ public class ClassDescriptorResolver { // This call has side-effects on the parameterScope (fills it in) List typeParameters = resolveTypeParameters(classDescriptor, parameterScope, classElement.getTypeParameters()); + resolveGenericBounds(classElement, parameterScope, typeParameters); List delegationSpecifiers = classElement.getDelegationSpecifiers(); // TODO : assuming that the hierarchy is acyclic @@ -133,67 +134,6 @@ public class ClassDescriptorResolver { trace.recordDeclarationResolution(classElement, descriptor); } - public void resolveGenericBounds(@NotNull JetClass jetClass, @NotNull MutableClassDescriptor classDescriptor) { - List typeParameters = jetClass.getTypeParameters(); - List parameters = classDescriptor.getTypeConstructor().getParameters(); - Map parameterByName = Maps.newHashMap(); - for (int i = 0, typeParametersSize = typeParameters.size(); i < typeParametersSize; i++) { - JetTypeParameter jetTypeParameter = typeParameters.get(i); - TypeParameterDescriptor typeParameterDescriptor = parameters.get(i); - parameterByName.put(typeParameterDescriptor.getName(), typeParameterDescriptor); - JetTypeReference extendsBound = jetTypeParameter.getExtendsBound(); - if (extendsBound != null) { - typeParameterDescriptor.addUpperBound(typeResolverNotCheckingBounds.resolveType(classDescriptor.getScopeForSupertypeResolution(), extendsBound)); - } - else { - typeParameterDescriptor.addUpperBound(JetStandardClasses.getDefaultBound()); - } - } - for (JetTypeConstraint constraint : jetClass.getTypeConstaints()) { - JetSimpleNameExpression subjectTypeParameterName = constraint.getSubjectTypeParameterName(); - if (subjectTypeParameterName == null) { - continue; - } - String referencedName = subjectTypeParameterName.getReferencedName(); - if (referencedName == null) { - continue; - } - TypeParameterDescriptor typeParameterDescriptor = parameterByName.get(referencedName); - if (typeParameterDescriptor == null) { - // To tell the user that we look only for locally defined type parameters - ClassifierDescriptor classifier = classDescriptor.getScopeForSupertypeResolution().getClassifier(referencedName); - if (classifier != null) { - trace.getErrorHandler().genericError(subjectTypeParameterName.getNode(), referencedName + " does not refer to a type parameter of class " + classDescriptor.getName()); - trace.recordReferenceResolution(subjectTypeParameterName, classifier); - } - else { - trace.getErrorHandler().unresolvedReference(subjectTypeParameterName); - } - } - else { - JetTypeReference boundTypeReference = constraint.getBoundTypeReference(); - if (boundTypeReference != null) { - JetType bound = typeResolverNotCheckingBounds.resolveType(classDescriptor.getScopeForSupertypeResolution(), boundTypeReference); - if (constraint.isClassObjectContraint()) { - typeParameterDescriptor.addClassObjectBound(bound); - } - else { - typeParameterDescriptor.addUpperBound(bound); - } - } - } - } - - for (TypeParameterDescriptor parameter : parameters) { - if (JetStandardClasses.isNothing(parameter.getBoundsAsType())) { - PsiElement nameIdentifier = typeParameters.get(parameter.getIndex()).getNameIdentifier(); - if (nameIdentifier != null) { - trace.getErrorHandler().genericError(nameIdentifier.getNode(), "Upper bounds of " + parameter.getName() + " have empty intersection"); - } - } - } - } - public void resolveSupertypes(@NotNull JetClassOrObject jetClass, @NotNull MutableClassDescriptor descriptor) { List delegationSpecifiers = jetClass.getDelegationSpecifiers(); // TODO : assuming that the hierarchy is acyclic @@ -221,14 +161,15 @@ public class ClassDescriptorResolver { innerScope.addLabeledDeclaration(functionDescriptor); List typeParameterDescriptors = resolveTypeParameters(functionDescriptor, innerScope, function.getTypeParameters()); + resolveGenericBounds(function, innerScope, typeParameterDescriptors); JetType receiverType = null; JetTypeReference receiverTypeRef = function.getReceiverTypeRef(); if (receiverTypeRef != null) { JetScope scopeForReceiver = function.hasTypeParameterListBeforeFunctionName() - ? innerScope - : scope; + ? innerScope + : scope; receiverType = typeResolver.resolveType(scopeForReceiver, receiverTypeRef); } @@ -312,7 +253,6 @@ public class ClassDescriptorResolver { } public List resolveTypeParameters(DeclarationDescriptor containingDescriptor, WritableScope extensibleScope, List typeParameters) { - // TODO : Where-clause List result = new ArrayList(); for (int i = 0, typeParametersSize = typeParameters.size(); i < typeParametersSize; i++) { JetTypeParameter typeParameter = typeParameters.get(i); @@ -322,11 +262,10 @@ public class ClassDescriptorResolver { } private TypeParameterDescriptor resolveTypeParameter(DeclarationDescriptor containingDescriptor, WritableScope extensibleScope, JetTypeParameter typeParameter, int index) { - // TODO: other bounds from where-clause - JetTypeReference extendsBound = typeParameter.getExtendsBound(); - JetType bound = extendsBound == null - ? JetStandardClasses.getDefaultBound() - : typeResolver.resolveType(extensibleScope, extendsBound); +// JetTypeReference extendsBound = typeParameter.getExtendsBound(); +// JetType bound = extendsBound == null +// ? JetStandardClasses.getDefaultBound() +// : typeResolver.resolveType(extensibleScope, extendsBound); TypeParameterDescriptor typeParameterDescriptor = TypeParameterDescriptor.createForFurtherModification( containingDescriptor, AnnotationResolver.INSTANCE.resolveAnnotations(typeParameter.getModifierList()), @@ -334,12 +273,96 @@ public class ClassDescriptorResolver { JetPsiUtil.safeName(typeParameter.getName()), index ); - typeParameterDescriptor.addUpperBound(bound); +// typeParameterDescriptor.addUpperBound(bound); extensibleScope.addTypeParameterDescriptor(typeParameterDescriptor); trace.recordDeclarationResolution(typeParameter, typeParameterDescriptor); return typeParameterDescriptor; } + public void resolveGenericBounds(@NotNull JetTypeParameterListOwner declaration, JetScope scope, List parameters) { + List typeParameters = declaration.getTypeParameters(); + Map parameterByName = Maps.newHashMap(); + for (int i = 0, typeParametersSize = typeParameters.size(); i < typeParametersSize; i++) { + JetTypeParameter jetTypeParameter = typeParameters.get(i); + TypeParameterDescriptor typeParameterDescriptor = parameters.get(i); + parameterByName.put(typeParameterDescriptor.getName(), typeParameterDescriptor); + JetTypeReference extendsBound = jetTypeParameter.getExtendsBound(); + if (extendsBound != null) { + typeParameterDescriptor.addUpperBound(resolveAndCheckUpperBoundType(extendsBound, scope, false)); + } + } + for (JetTypeConstraint constraint : declaration.getTypeConstaints()) { + JetSimpleNameExpression subjectTypeParameterName = constraint.getSubjectTypeParameterName(); + if (subjectTypeParameterName == null) { + continue; + } + String referencedName = subjectTypeParameterName.getReferencedName(); + if (referencedName == null) { + continue; + } + TypeParameterDescriptor typeParameterDescriptor = parameterByName.get(referencedName); + if (typeParameterDescriptor == null) { + // To tell the user that we look only for locally defined type parameters + ClassifierDescriptor classifier = scope.getClassifier(referencedName); + if (classifier != null) { + trace.getErrorHandler().genericError(subjectTypeParameterName.getNode(), referencedName + " does not refer to a type parameter of " + declaration.getName()); + trace.recordReferenceResolution(subjectTypeParameterName, classifier); + } + else { + trace.getErrorHandler().unresolvedReference(subjectTypeParameterName); + } + } + else { + trace.recordReferenceResolution(subjectTypeParameterName, typeParameterDescriptor); + JetTypeReference boundTypeReference = constraint.getBoundTypeReference(); + if (boundTypeReference != null) { + JetType bound = resolveAndCheckUpperBoundType(boundTypeReference, scope, constraint.isClassObjectContraint()); + if (constraint.isClassObjectContraint()) { + typeParameterDescriptor.addClassObjectBound(bound); + } + else { + typeParameterDescriptor.addUpperBound(bound); + } + } + } + } + + for (TypeParameterDescriptor parameter : parameters) { + if (parameter.getUpperBounds().isEmpty()) { + parameter.addUpperBound(JetStandardClasses.getDefaultBound()); + } + + if (JetStandardClasses.isNothing(parameter.getBoundsAsType())) { + PsiElement nameIdentifier = typeParameters.get(parameter.getIndex()).getNameIdentifier(); + if (nameIdentifier != null) { + trace.getErrorHandler().genericError(nameIdentifier.getNode(), "Upper bounds of " + parameter.getName() + " have empty intersection"); + } + } + + JetType classObjectType = parameter.getClassObjectType(); + if (classObjectType != null && JetStandardClasses.isNothing(classObjectType)) { + PsiElement nameIdentifier = typeParameters.get(parameter.getIndex()).getNameIdentifier(); + if (nameIdentifier != null) { + trace.getErrorHandler().genericError(nameIdentifier.getNode(), "Class object upper bounds of " + parameter.getName() + " have empty intersection"); + } + } + } + } + + private JetType resolveAndCheckUpperBoundType(@NotNull JetTypeReference upperBound, @NotNull JetScope scope, boolean classObjectConstaint) { + JetType jetType = typeResolverNotCheckingBounds.resolveType(scope, upperBound); + if (!TypeUtils.canHaveSubtypes(semanticServices.getTypeChecker(), jetType)) { + String message = jetType + " is a final type, and thus a class object cannot extend it"; + if (classObjectConstaint) { + trace.getErrorHandler().genericError(upperBound.getNode(), message); + } + else { + trace.getErrorHandler().genericWarning(upperBound.getNode(), message); + } + } + return jetType; + } + private Collection resolveDelegationSpecifiers(JetScope extensibleScope, List delegationSpecifiers, @NotNull TypeResolver resolver) { if (delegationSpecifiers.isEmpty()) { return Collections.emptyList(); @@ -438,6 +461,7 @@ public class ClassDescriptorResolver { else { WritableScope writableScope = new WritableScopeImpl(scope, containingDeclaration, trace.getErrorHandler()); typeParameterDescriptors = resolveTypeParameters(containingDeclaration, writableScope, typeParameters); + resolveGenericBounds(property, writableScope, typeParameterDescriptors); scopeWithTypeParameters = writableScope; } diff --git a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java index 31d70c95684..63674006a1e 100644 --- a/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java +++ b/idea/src/org/jetbrains/jet/lang/resolve/TopDownAnalyzer.java @@ -310,7 +310,7 @@ public class TopDownAnalyzer { for (Map.Entry entry : classes.entrySet()) { JetClass jetClass = entry.getKey(); MutableClassDescriptor descriptor = entry.getValue(); - classDescriptorResolver.resolveGenericBounds(jetClass, descriptor); + classDescriptorResolver.resolveGenericBounds(jetClass, descriptor.getScopeForSupertypeResolution(), descriptor.getTypeConstructor().getParameters()); classDescriptorResolver.resolveSupertypes(jetClass, descriptor); } for (Map.Entry entry : objects.entrySet()) { @@ -351,10 +351,7 @@ public class TopDownAnalyzer { } } } - - // TODO : where-bounds } - } private void resolveFunctionAndPropertyHeaders() { diff --git a/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java b/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java index 05e9b66a499..d5a4f37cc9f 100644 --- a/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java +++ b/idea/src/org/jetbrains/jet/lang/types/ErrorUtils.java @@ -92,7 +92,13 @@ public class ErrorUtils { } }; - private static final ClassDescriptorImpl ERROR_CLASS = new ClassDescriptorImpl(ERROR_MODULE, Collections.emptyList(), ""); + private static final ClassDescriptorImpl ERROR_CLASS = new ClassDescriptorImpl(ERROR_MODULE, Collections.emptyList(), "") { + @NotNull + @Override + public FunctionGroup getConstructors(List typeArguments) { + return ERROR_FUNCTION_GROUP; + } + }; private static final ConstructorDescriptor ERROR_CONSTRUCTOR = new ConstructorDescriptorImpl(ERROR_CLASS, Collections.emptyList(), true); static { ERROR_CLASS.initialize( diff --git a/idea/testData/checker/Bounds.jet b/idea/testData/checker/Bounds.jet index 47e9824bcee..baeb1efcdae 100644 --- a/idea/testData/checker/Bounds.jet +++ b/idea/testData/checker/Bounds.jet @@ -1,5 +1,5 @@ namespace boundsWithSubstitutors { - class A + open class A class B>() class C : A @@ -15,8 +15,8 @@ namespace boundsWithSubstitutors { } - class A - class B() + open class A {} + open class B() class CInt>, X : {(B<Char>) : (B<Any>, B)}>() : B<Any>() { // 2 errors val a = new B<Char>() // error @@ -37,4 +37,4 @@ fun test() { fun foo() {} fun bar() {} -fun Int.buzz() : Unit {} +fun Int> Int.buzz() : Unit {} diff --git a/idea/testData/checker/MultipleBounds.jet b/idea/testData/checker/MultipleBounds.jet index ff4c3533eb5..39b7cea0adc 100644 --- a/idea/testData/checker/MultipleBounds.jet +++ b/idea/testData/checker/MultipleBounds.jet @@ -39,9 +39,34 @@ fun test() { class Foo() {} -class Bar +class BarFoo> -class Buzz where T : Bar<Int>, T : nioho +class Buzz where T : Bar<Int>, T : nioho -class X -class Y<T : Foo> where T : Bar \ No newline at end of file +class XFoo> +class Y<T : Foo> where T : Bar + +fun + where + T : B, + B : T, + class object B : T, + class object T : B, + class object T : A + test2(t : T) { + T.foo() + T.bar() + t.foo() + t.bar() +} + +val test1 = test2<A>(new A()) +val test2 = test2<B>(new B()) +val test3 = test2(new C()) + +class Test<T> + where + class object T : Foo, + class object T : A {} + +val x : Int \ No newline at end of file