KT-284 Implement static checks for traits (in progress)

This commit is contained in:
Andrey Breslav
2011-09-07 11:34:21 +04:00
parent 5b0eb5f4a7
commit cec836900b
7 changed files with 175 additions and 68 deletions
@@ -145,7 +145,7 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme
@Override
public boolean isAbstract() {
return isAbstract;
return isAbstract || trait;
}
public void setAbstract(boolean isAbstract) {
@@ -166,15 +166,15 @@ public class ClassDescriptorResolver {
trace.getErrorHandler().genericError(((JetEnumEntry) jetClass).getNameIdentifier().getNode(), "generic arguments of the base type must be specified");
}
}
Collection<? extends JetType> superclasses = delegationSpecifiers.isEmpty()
Collection<? extends JetType> supertypes = delegationSpecifiers.isEmpty()
? Collections.singleton(defaultSupertype)
: resolveDelegationSpecifiers(
descriptor.getScopeForSupertypeResolution(),
delegationSpecifiers,
typeResolverNotCheckingBounds);
for (JetType superclass : superclasses) {
descriptor.addSupertype(superclass);
for (JetType supertype : supertypes) {
descriptor.addSupertype(supertype);
}
}
@@ -2,6 +2,7 @@ package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
@@ -119,7 +120,7 @@ public class TopDownAnalyzer {
createTypeConstructors(); // create type constructors for classes and generic parameters
resolveTypesInClassHeaders(); // Generic bounds and types in supertype lists (no expressions or constructor resolution)
checkGenericBoundsInClassHeaders(); // For the types resolved so far
checkTypesInClassHeaders(); // Generic bounds and supertype lists
resolveConstructorHeaders();
@@ -346,7 +347,7 @@ public class TopDownAnalyzer {
}
}
private void checkGenericBoundsInClassHeaders() {
private void checkTypesInClassHeaders() {
for (Map.Entry<JetClass, MutableClassDescriptor> entry : classes.entrySet()) {
JetClass jetClass = entry.getKey();
@@ -354,7 +355,9 @@ public class TopDownAnalyzer {
JetTypeReference typeReference = delegationSpecifier.getTypeReference();
if (typeReference != null) {
JetType type = trace.getBindingContext().get(BindingContext.TYPE, typeReference);
classDescriptorResolver.checkBounds(typeReference, type);
if (type != null) {
classDescriptorResolver.checkBounds(typeReference, type);
}
}
}
@@ -479,6 +482,10 @@ public class TopDownAnalyzer {
private void processPrimaryConstructor(MutableClassDescriptor classDescriptor, JetClass klass) {
if (!klass.hasPrimaryConstructor()) return;
if (classDescriptor.isTrait()) {
trace.getErrorHandler().genericError(klass.getPrimaryConstructorParameterList().getNode(), "A trait may not have a constructor");
}
// TODO : not all the parameters are real properties
JetScope memberScope = classDescriptor.getScopeForSupertypeResolution();
ConstructorDescriptor constructorDescriptor = classDescriptorResolver.resolvePrimaryConstructorDescriptor(memberScope, classDescriptor, klass);
@@ -497,6 +504,9 @@ public class TopDownAnalyzer {
}
private void processSecondaryConstructor(MutableClassDescriptor classDescriptor, JetConstructor constructor) {
if (classDescriptor.isTrait()) {
trace.getErrorHandler().genericError(constructor.getNameNode(), "A trait may not have a constructor");
}
ConstructorDescriptor constructorDescriptor = classDescriptorResolver.resolveSecondaryConstructorDescriptor(
classDescriptor.getScopeForMemberResolution(),
classDescriptor,
@@ -591,65 +601,99 @@ public class TopDownAnalyzer {
: getInnerScopeForConstructor(primaryConstructor, descriptor.getScopeForMemberResolution(), true);
final JetTypeInferrer.Services typeInferrer = semanticServices.getTypeInferrerServices(traceForConstructors, JetFlowInformationProvider.NONE); // TODO : flow
for (JetDelegationSpecifier delegationSpecifier : jetClass.getDelegationSpecifiers()) {
delegationSpecifier.accept(new JetVisitorVoid() {
@Override
public void visitDelegationByExpressionSpecifier(JetDelegatorByExpressionSpecifier specifier) {
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 && !semanticServices.getTypeChecker().isSubtypeOf(type, supertype)) { // TODO : Convertible?
trace.getErrorHandler().typeMismatch(delegateExpression, supertype, type);
}
}
JetVisitorVoid visitor = new JetVisitorVoid() {
private boolean superclassAppeared = false;
@Override
public void visitDelegationByExpressionSpecifier(JetDelegatorByExpressionSpecifier specifier) {
if (descriptor.isTrait()) {
trace.getErrorHandler().genericError(specifier.getNode(), "Traits can not use delegation");
}
@Override
public void visitDelegationToSuperCallSpecifier(JetDelegatorToSuperCall call) {
JetTypeReference typeReference = call.getTypeReference();
if (typeReference != null) {
if (descriptor.getUnsubstitutedPrimaryConstructor() != null) {
typeInferrer.getCallResolver().resolveCall(trace, scopeForConstructor, null, call, NO_EXPECTED_TYPE);
}
else {
JetValueArgumentList valueArgumentList = call.getValueArgumentList();
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");
}
}
}
@Override
public void visitDelegationToSuperClassSpecifier(JetDelegatorToSuperClass specifier) {
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 (supertype != null) {
DeclarationDescriptor declarationDescriptor = supertype.getConstructor().getDeclarationDescriptor();
if (declarationDescriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
if (classDescriptor.hasConstructors() && !ErrorUtils.isError(classDescriptor.getTypeConstructor())) {
if (type != null && supertype != null && !semanticServices.getTypeChecker().isSubtypeOf(type, supertype)) {
trace.getErrorHandler().typeMismatch(delegateExpression, supertype, type);
}
}
}
@Override
public void visitDelegationToSuperCallSpecifier(JetDelegatorToSuperCall call) {
if (descriptor.isTrait()) {
JetValueArgumentList valueArgumentList = call.getValueArgumentList();
ASTNode node = valueArgumentList == null ? call.getNode() : valueArgumentList.getNode();
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.isTrait()) {
trace.getErrorHandler().genericError(call.getValueArgumentList().getNode(), "A trait may not have a constructor");
}
}
}
}
else {
JetValueArgumentList valueArgumentList = call.getValueArgumentList();
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");
}
}
}
@Override
public void visitDelegationToSuperClassSpecifier(JetDelegatorToSuperClass specifier) {
JetType supertype = trace.getBindingContext().get(BindingContext.TYPE, specifier.getTypeReference());
if (supertype != null) {
DeclarationDescriptor declarationDescriptor = supertype.getConstructor().getDeclarationDescriptor();
if (declarationDescriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) declarationDescriptor;
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 {
trace.getErrorHandler().genericError(specifier.getNode(), "Only classes may serve as supertypes");
if (!classDescriptor.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");
}
@Override
public void visitDelegationToThisCall(JetDelegatorToThisCall thisCall) {
throw new IllegalStateException("This-calls should be prohibited by the parser");
}
}
@Override
public void visitJetElement(JetElement element) {
throw new UnsupportedOperationException(element.getText() + " : " + element);
}
});
@Override
public void visitDelegationToThisCall(JetDelegatorToThisCall thisCall) {
throw new IllegalStateException("This-calls should be prohibited by the parser");
}
@Override
public void visitJetElement(JetElement element) {
throw new UnsupportedOperationException(element.getText() + " : " + element);
}
};
for (JetDelegationSpecifier delegationSpecifier : jetClass.getDelegationSpecifiers()) {
delegationSpecifier.accept(visitor);
}
}
@@ -815,7 +859,7 @@ public class TopDownAnalyzer {
}
resolvePropertyAccessors(property, propertyDescriptor, declaringScope);
checkPropertyCorrectness(property, propertyDescriptor, classDescriptor);
checkProperty(property, propertyDescriptor, classDescriptor);
processed.add(property);
}
}
@@ -834,7 +878,7 @@ public class TopDownAnalyzer {
}
resolvePropertyAccessors(property, propertyDescriptor, declaringScope);
checkPropertyCorrectness(property, propertyDescriptor, null);
checkProperty(property, propertyDescriptor, null);
}
}
@@ -874,17 +918,19 @@ public class TopDownAnalyzer {
// }
}
protected void checkPropertyCorrectness(JetProperty property, PropertyDescriptor propertyDescriptor, @Nullable ClassDescriptor classDescriptor) {
protected void checkProperty(JetProperty property, PropertyDescriptor propertyDescriptor, @Nullable ClassDescriptor classDescriptor) {
JetExpression initializer = property.getInitializer();
JetPropertyAccessor getter = property.getGetter();
JetPropertyAccessor setter = property.getSetter();
PsiElement nameIdentifier = property.getNameIdentifier();
ASTNode nameNode = nameIdentifier == null ? property.getNode() : nameIdentifier.getNode();
if (propertyDescriptor.getModifiers().isAbstract()) {
if (classDescriptor == null) {
trace.getErrorHandler().genericError(property.getModifierList().getModifierNode(JetTokens.ABSTRACT_KEYWORD),
"Global property can not be abstract");
return;
}
if (! classDescriptor.isAbstract()) {
if (!classDescriptor.isAbstract()) {
trace.getErrorHandler().genericError(property.getModifierList().getModifierNode(JetTokens.ABSTRACT_KEYWORD),
"Abstract property " + property.getName() + " in non-abstract class " + classDescriptor.getName());
return;
@@ -901,14 +947,18 @@ public class TopDownAnalyzer {
return;
}
boolean backingFieldRequired = trace.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor);
if (initializer != null && ! backingFieldRequired) {
trace.getErrorHandler().genericError(initializer.getNode(), "Initializer is not allowed here because this property has no backing field");
if (backingFieldRequired) {
if (initializer == null && !trace.getBindingContext().get(BindingContext.IS_INITIALIZED, propertyDescriptor)) {
if (classDescriptor == null || (getter != null && getter.getBodyExpression() != null) || (setter != null && setter.getBodyExpression() != null)) {
trace.getErrorHandler().genericError(nameNode, "Property must be initialized");
} else if (!classDescriptor.isTrait()) {
trace.getErrorHandler().genericError(nameNode, "Property must be initialized or be abstract");
}
}
}
if (initializer == null && backingFieldRequired && ! trace.getBindingContext().get(BindingContext.IS_INITIALIZED, propertyDescriptor)) {
if (classDescriptor == null || (getter != null && getter.getBodyExpression() != null) || (setter != null && setter.getBodyExpression() != null)) {
trace.getErrorHandler().genericError(property.getNameIdentifier().getNode(), "Property must be initialized");
} else {
trace.getErrorHandler().genericError(property.getNameIdentifier().getNode(), "Property must be initialized or be abstract");
else {
if (initializer != null) {
trace.getErrorHandler().genericError(initializer.getNode(), "Initializer is not allowed here because this property has no backing field");
}
}
}
@@ -23,7 +23,7 @@ public class TopDownAnalyzerForStandardLibrary extends TopDownAnalyzer {
}
@Override
protected void checkPropertyCorrectness(JetProperty property, PropertyDescriptor propertyDescriptor, ClassDescriptor classDescriptor) {}
protected void checkProperty(JetProperty property, PropertyDescriptor propertyDescriptor, ClassDescriptor classDescriptor) {}
@Override
@@ -37,14 +37,27 @@ public class BasicWritableSlice<K, V> implements WritableSlice<K,V> {
return value;
}
@Override
public RewritePolicy getRewritePolicy() {
return rewritePolicy;
}
@Override
public String toString() {
return debugName;
}
@Override
public ReadOnlySlice<K, V> makeRawValueVersion() {
return new DelegatingSlice<K, V>(this) {
@Override
public V computeValue(SlicedMap map, K key, V value, boolean valueNotFound) {
if (valueNotFound) assert value == null;
return value;
}
};
}
}
@@ -0,0 +1,40 @@
package org.jetbrains.jet.util.slicedmap;
import org.jetbrains.annotations.NotNull;
/**
* @author abreslav
*/
public class DelegatingSlice<K, V> implements WritableSlice<K, V> {
private final WritableSlice<K, V> delegate;
public DelegatingSlice(@NotNull WritableSlice<K, V> delegate) {
this.delegate = delegate;
}
public boolean check(K key, V value) {
return delegate.check(key, value);
}
public void afterPut(MutableSlicedMap map, K key, V value) {
delegate.afterPut(map, key, value);
}
@Override
public RewritePolicy getRewritePolicy() {
return delegate.getRewritePolicy();
}
public SlicedMapKey<K, V> makeKey(K key) {
return delegate.makeKey(key);
}
public V computeValue(SlicedMap map, K key, V value, boolean valueNotFound) {
return delegate.computeValue(map, key, value, valueNotFound);
}
@Override
public ReadOnlySlice<K, V> makeRawValueVersion() {
return delegate.makeRawValueVersion();
}
}
@@ -8,4 +8,8 @@ public interface ReadOnlySlice<K, V> {
V computeValue(SlicedMap map, K key, V value, boolean valueNotFound);
/**
* @return a slice that only retrieves the value from the storage and skips any computeValue() calls
*/
ReadOnlySlice<K, V> makeRawValueVersion();
}