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:
@@ -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(
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user