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
This commit is contained in:
Andrey Breslav
2011-06-05 11:41:08 +04:00
parent 9450e1bdf9
commit 8cffea6bc2
7 changed files with 141 additions and 87 deletions
@@ -67,7 +67,7 @@ public class ClassDescriptorImpl extends DeclarationDescriptorImpl implements Cl
@NotNull
@Override
public FunctionGroup getConstructors(List<TypeProjection> 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;
}
@@ -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;
}
@@ -52,6 +52,7 @@ public class ClassDescriptorResolver {
// This call has side-effects on the parameterScope (fills it in)
List<TypeParameterDescriptor> typeParameters
= resolveTypeParameters(classDescriptor, parameterScope, classElement.getTypeParameters());
resolveGenericBounds(classElement, parameterScope, typeParameters);
List<JetDelegationSpecifier> 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<JetTypeParameter> typeParameters = jetClass.getTypeParameters();
List<TypeParameterDescriptor> parameters = classDescriptor.getTypeConstructor().getParameters();
Map<String, TypeParameterDescriptor> 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<JetDelegationSpecifier> delegationSpecifiers = jetClass.getDelegationSpecifiers();
// TODO : assuming that the hierarchy is acyclic
@@ -221,14 +161,15 @@ public class ClassDescriptorResolver {
innerScope.addLabeledDeclaration(functionDescriptor);
List<TypeParameterDescriptor> 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<TypeParameterDescriptor> resolveTypeParameters(DeclarationDescriptor containingDescriptor, WritableScope extensibleScope, List<JetTypeParameter> typeParameters) {
// TODO : Where-clause
List<TypeParameterDescriptor> result = new ArrayList<TypeParameterDescriptor>();
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<TypeParameterDescriptor> parameters) {
List<JetTypeParameter> typeParameters = declaration.getTypeParameters();
Map<String, TypeParameterDescriptor> 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<JetType> resolveDelegationSpecifiers(JetScope extensibleScope, List<JetDelegationSpecifier> 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;
}
@@ -310,7 +310,7 @@ public class TopDownAnalyzer {
for (Map.Entry<JetClass, MutableClassDescriptor> 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<JetObjectDeclaration, MutableClassDescriptor> entry : objects.entrySet()) {
@@ -351,10 +351,7 @@ public class TopDownAnalyzer {
}
}
}
// TODO : where-bounds
}
}
private void resolveFunctionAndPropertyHeaders() {
@@ -92,7 +92,13 @@ public class ErrorUtils {
}
};
private static final ClassDescriptorImpl ERROR_CLASS = new ClassDescriptorImpl(ERROR_MODULE, Collections.<Annotation>emptyList(), "<ERROR CLASS>");
private static final ClassDescriptorImpl ERROR_CLASS = new ClassDescriptorImpl(ERROR_MODULE, Collections.<Annotation>emptyList(), "<ERROR CLASS>") {
@NotNull
@Override
public FunctionGroup getConstructors(List<TypeProjection> typeArguments) {
return ERROR_FUNCTION_GROUP;
}
};
private static final ConstructorDescriptor ERROR_CONSTRUCTOR = new ConstructorDescriptorImpl(ERROR_CLASS, Collections.<Annotation>emptyList(), true);
static {
ERROR_CLASS.initialize(
+4 -4
View File
@@ -1,5 +1,5 @@
namespace boundsWithSubstitutors {
class A<T>
open class A<T>
class B<X : A<X>>()
class C : A<C>
@@ -15,8 +15,8 @@ namespace boundsWithSubstitutors {
}
class A
class B<T : A>()
open class A {}
open class B<T : A>()
class C<T : B<<error>Int</error>>, X : {(B<<error>Char</error>>) : (B<<error>Any</error>>, B<A>)}>() : B<<error>Any</error>>() { // 2 errors
val a = new B<<error>Char</error>>() // error
@@ -37,4 +37,4 @@ fun test() {
fun foo<T : Any>() {}
fun bar<T : Int?>() {}
fun <T : Int> Int.buzz() : Unit {}
fun <T : <warning>Int</warning>> Int.buzz() : Unit {}
+29 -4
View File
@@ -39,9 +39,34 @@ fun test() {
class Foo() {}
class Bar<T : Foo>
class Bar<T : <warning>Foo</warning>>
class Buzz<T> where T : Bar<<error>Int</error>>, T : <error>nioho</error>
class Buzz<T> where T : <warning>Bar<<error>Int</error>></warning>, T : <error>nioho</error>
class X<T : Foo>
class Y<<error>T</error> : Foo> where T : Bar<Foo>
class X<T : <warning>Foo</warning>>
class Y<<error>T</error> : <warning>Foo</warning>> where T : <warning>Bar<Foo></warning>
fun <T : A>
where
T : B,
<error>B</error> : T,
class object <error>B</error> : T,
class object T : B,
class object T : A
test2(t : T) {
T.foo()
T.bar()
t.foo()
t.bar()
}
val test1 = test2<<error>A</error>>(new A())
val test2 = test2<<error>B</error>>(new B())
val test3 = test2<C>(new C())
class Test<<error>T</error>>
where
class object T : <error>Foo</error>,
class object T : A {}
val <T, B : T> x : Int