Working on constructors: references on 'this()' calls, etc. Many checks pending

This commit is contained in:
Andrey Breslav
2011-04-22 21:45:44 +04:00
parent 913a01c107
commit 968e22f999
14 changed files with 266 additions and 120 deletions
@@ -53,6 +53,7 @@ public interface JetNodeTypes {
JetNodeType PROPERTY_ACCESSOR = new JetNodeType("PROPERTY_ACCESSOR", JetPropertyAccessor.class); JetNodeType PROPERTY_ACCESSOR = new JetNodeType("PROPERTY_ACCESSOR", JetPropertyAccessor.class);
JetNodeType INITIALIZER_LIST = new JetNodeType("INITIALIZER_LIST", JetInitializerList.class); JetNodeType INITIALIZER_LIST = new JetNodeType("INITIALIZER_LIST", JetInitializerList.class);
JetNodeType THIS_CALL = new JetNodeType("THIS_CALL", JetDelegatorToThisCall.class); JetNodeType THIS_CALL = new JetNodeType("THIS_CALL", JetDelegatorToThisCall.class);
JetNodeType THIS_CONSTRUCTOR_REFERENCE = new JetNodeType("THIS_CONSTRUCTOR_REFERENCE", JetThisReferenceExpression.class);
JetNodeType TYPE_CONSTRAINT_LIST = new JetNodeType("TYPE_CONSTRAINT_LIST", JetTypeConstraintList.class); JetNodeType TYPE_CONSTRAINT_LIST = new JetNodeType("TYPE_CONSTRAINT_LIST", JetTypeConstraintList.class);
JetNodeType TYPE_CONSTRAINT = new JetNodeType("TYPE_CONSTRAINT", JetTypeConstraint.class); JetNodeType TYPE_CONSTRAINT = new JetNodeType("TYPE_CONSTRAINT", JetTypeConstraint.class);
@@ -623,7 +623,9 @@ public class JetParsing extends AbstractJetParsing {
IElementType type; IElementType type;
if (at(THIS_KEYWORD)) { if (at(THIS_KEYWORD)) {
PsiBuilder.Marker mark = mark();
advance(); // THIS_KEYWORD advance(); // THIS_KEYWORD
mark.done(THIS_CONSTRUCTOR_REFERENCE);
type = THIS_CALL; type = THIS_CALL;
} }
else if (atSet(TYPE_REF_FIRST)) { else if (atSet(TYPE_REF_FIRST)) {
@@ -12,6 +12,7 @@ import java.util.List;
* @author max * @author max
*/ */
public class JetDelegatorToThisCall extends JetDelegationSpecifier implements JetCall { public class JetDelegatorToThisCall extends JetDelegationSpecifier implements JetCall {
public JetDelegatorToThisCall(@NotNull ASTNode node) { public JetDelegatorToThisCall(@NotNull ASTNode node) {
super(node); super(node);
} }
@@ -37,4 +38,8 @@ public class JetDelegatorToThisCall extends JetDelegationSpecifier implements Je
public List<JetExpression> getFunctionLiteralArguments() { public List<JetExpression> getFunctionLiteralArguments() {
return Collections.emptyList(); return Collections.emptyList();
} }
public JetReferenceExpression getThisReference() {
return findChildByClass(JetThisReferenceExpression.class);
}
} }
@@ -0,0 +1,32 @@
package org.jetbrains.jet.lang.psi;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import org.jetbrains.annotations.NotNull;
/**
* @author abreslav
*/
public class JetThisReferenceExpression extends JetReferenceExpression {
public JetThisReferenceExpression(@NotNull ASTNode node) {
super(node);
}
@Override
public PsiReference getReference() {
return new JetPsiReference() {
@Override
public PsiElement getElement() {
return JetThisReferenceExpression.this;
}
@Override
public TextRange getRangeInElement() {
return new TextRange(0, getElement().getTextLength());
}
};
}
}
@@ -270,81 +270,61 @@ public class TopDownAnalyzer {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void resolveBehaviorDeclarationBodies() { private void resolveBehaviorDeclarationBodies() {
resolveDelegationSpecifierLists();
//TODO : anonymous initializers
resolvePropertyDeclarationBodies(); resolvePropertyDeclarationBodies();
resolveFunctionDeclarationBodies(); resolveFunctionDeclarationBodies();
} }
private void resolveFunctionDeclarationBodies() { private void resolveDelegationSpecifierLists() {
for (Map.Entry<JetDeclaration, FunctionDescriptor> entry : functions.entrySet()) { final JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.NONE);
JetDeclaration declaration = entry.getKey(); for (Map.Entry<JetClass, MutableClassDescriptor> entry : classes.entrySet()) {
FunctionDescriptor descriptor = entry.getValue(); final JetClass declaration = entry.getKey();
final MutableClassDescriptor descriptor = entry.getValue();
WritableScope declaringScope = declaringScopes.get(declaration); for (JetDelegationSpecifier delegationSpecifier : declaration.getDelegationSpecifiers()) {
assert declaringScope != null; delegationSpecifier.accept(new JetVisitor() {
@Override
if (declaration instanceof JetFunction) { public void visitDelegationByExpressionSpecifier(JetDelegatorByExpressionSpecifier specifier) {
resolveFunctionBody(trace, (JetFunction) declaration, (FunctionDescriptorImpl) descriptor, declaringScope); JetExpression delegateExpression = specifier.getDelegateExpression();
if (delegateExpression != null) {
JetType type = typeInferrer.getType(descriptor.getWritableUnsubstitutedMemberScope(), delegateExpression, false);
JetType supertype = trace.resolveTypeReference(specifier.getTypeReference());
if (type != null && !semanticServices.getTypeChecker().isSubtypeOf(type, supertype)) { // TODO : Convertible?
semanticServices.getErrorHandler().typeMismatch(delegateExpression, supertype, type);
} }
else if (declaration instanceof JetConstructor) {
resolveConstructorBody((JetConstructor) declaration, descriptor, declaringScope);
}
else {
throw new UnsupportedOperationException(); // TODO
}
assert descriptor.getUnsubstitutedReturnType() != null;
} }
} }
private void resolveConstructorBody(JetConstructor declaration, FunctionDescriptor descriptor, final WritableScope declaringScope) {
WritableScope constructorScope = semanticServices.createWritableScope(declaringScope, declaringScope.getContainingDeclaration());
for (PropertyDescriptor propertyDescriptor : declaringScopesToProperties.get(descriptor.getContainingDeclaration())) {
constructorScope.addPropertyDescriptorByFieldName("$" + propertyDescriptor.getName(), propertyDescriptor);
}
final JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(constructorScope, descriptor, semanticServices);
final JetTypeInferrer typeInferrerForInitializers = semanticServices.getTypeInferrer(traceForConstructors, JetFlowInformationProvider.NONE);
for (JetDelegationSpecifier initializer : declaration.getInitializers()) {
// TODO : check that the type being referenced is actually a supertype
initializer.accept(new JetVisitor() {
@Override @Override
public void visitDelegationToSuperCallSpecifier(JetDelegatorToSuperCall call) { public void visitDelegationToSuperCallSpecifier(JetDelegatorToSuperCall call) {
JetTypeReference typeReference = call.getTypeReference(); JetTypeReference typeReference = call.getTypeReference();
if (typeReference != null) { if (typeReference != null) {
typeInferrerForInitializers.getTypeForConstructorCall(functionInnerScope, typeReference, call); typeInferrer.checkConstructorCall(descriptor.getWritableUnsubstitutedMemberScope(), typeReference, call);
} }
} }
@Override
public void visitDelegationToThisCall(JetDelegatorToThisCall call) {
JetTypeReference typeReference = call.getTypeReference(); // TODO : use explicit type here
if (typeReference != null) {
typeInferrerForInitializers.getTypeForConstructorCall(functionInnerScope, typeReference, call);
}
}
@Override
public void visitDelegationByExpressionSpecifier(JetDelegatorByExpressionSpecifier specifier) {
semanticServices.getErrorHandler().genericError(specifier.getNode(), "'by'-clause is only supported for primary constructors");
}
@Override @Override
public void visitDelegationToSuperClassSpecifier(JetDelegatorToSuperClass specifier) { public void visitDelegationToSuperClassSpecifier(JetDelegatorToSuperClass specifier) {
semanticServices.getErrorHandler().genericError(specifier.getNode(), "Constructor parameters required"); if (declaration.getPrimaryConstructorParameterList() != null) {
semanticServices.getErrorHandler().genericError(specifier.getNode(), "Constructor parameters required in initializer");
}
} }
@Override @Override
public void visitDelegationSpecifier(JetDelegationSpecifier specifier) { public void visitDelegationToThisCall(JetDelegatorToThisCall thisCall) {
throw new IllegalStateException(); throw new IllegalStateException("This-calls should be prohibitied by the parser");
}
@Override
public void visitJetElement(JetElement elem) {
throw new UnsupportedOperationException(elem.getText() + " : " + elem);
} }
}); });
} }
JetExpression bodyExpression = declaration.getBodyExpression();
if (bodyExpression != null) {
computeFlowData(declaration, bodyExpression);
JetFlowInformationProvider flowInformationProvider = computeFlowData(declaration, bodyExpression);
JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(traceForConstructors, flowInformationProvider);
typeInferrer.getType(functionInnerScope, bodyExpression, true);
} }
} }
@@ -414,6 +394,85 @@ public class TopDownAnalyzer {
} }
} }
private void resolveFunctionDeclarationBodies() {
for (Map.Entry<JetDeclaration, FunctionDescriptor> entry : functions.entrySet()) {
JetDeclaration declaration = entry.getKey();
FunctionDescriptor descriptor = entry.getValue();
WritableScope declaringScope = declaringScopes.get(declaration);
assert declaringScope != null;
if (declaration instanceof JetFunction) {
resolveFunctionBody(trace, (JetFunction) declaration, (FunctionDescriptorImpl) descriptor, declaringScope);
}
else if (declaration instanceof JetConstructor) {
resolveConstructorBody((JetConstructor) declaration, descriptor, declaringScope);
}
else {
throw new UnsupportedOperationException(); // TODO
}
assert descriptor.getUnsubstitutedReturnType() != null;
}
}
private void resolveConstructorBody(JetConstructor declaration, final FunctionDescriptor descriptor, final WritableScope declaringScope) {
WritableScope constructorScope = semanticServices.createWritableScope(declaringScope, declaringScope.getContainingDeclaration());
for (PropertyDescriptor propertyDescriptor : declaringScopesToProperties.get(descriptor.getContainingDeclaration())) {
constructorScope.addPropertyDescriptorByFieldName("$" + propertyDescriptor.getName(), propertyDescriptor);
}
final JetScope functionInnerScope = FunctionDescriptorUtil.getFunctionInnerScope(constructorScope, descriptor, semanticServices);
final JetTypeInferrer typeInferrerForInitializers = semanticServices.getTypeInferrer(traceForConstructors, JetFlowInformationProvider.NONE);
for (JetDelegationSpecifier initializer : declaration.getInitializers()) {
// TODO : check that the type being referenced is actually a supertype
initializer.accept(new JetVisitor() {
@Override
public void visitDelegationToSuperCallSpecifier(JetDelegatorToSuperCall call) {
JetTypeReference typeReference = call.getTypeReference();
if (typeReference != null) {
typeInferrerForInitializers.checkConstructorCall(functionInnerScope, typeReference, call);
}
}
@Override
public void visitDelegationToThisCall(JetDelegatorToThisCall call) {
// TODO : check that there's no recursion in this() calls
// TODO : check: if a this() call is present, no other initializers are allowed
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor.getContainingDeclaration();
typeInferrerForInitializers.checkClassConstructorCall(
functionInnerScope,
call.getThisReference(),
classDescriptor,
classDescriptor.getDefaultType(),
call);
}
@Override
public void visitDelegationByExpressionSpecifier(JetDelegatorByExpressionSpecifier specifier) {
semanticServices.getErrorHandler().genericError(specifier.getNode(), "'by'-clause is only supported for primary constructors");
}
@Override
public void visitDelegationToSuperClassSpecifier(JetDelegatorToSuperClass specifier) {
semanticServices.getErrorHandler().genericError(specifier.getNode(), "Constructor parameters required");
}
@Override
public void visitDelegationSpecifier(JetDelegationSpecifier specifier) {
throw new IllegalStateException();
}
});
}
JetExpression bodyExpression = declaration.getBodyExpression();
if (bodyExpression != null) {
computeFlowData(declaration, bodyExpression);
JetFlowInformationProvider flowInformationProvider = computeFlowData(declaration, bodyExpression);
JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(traceForConstructors, flowInformationProvider);
typeInferrer.getType(functionInnerScope, bodyExpression, true);
}
}
private void resolveFunctionBody( private void resolveFunctionBody(
@NotNull BindingTrace trace, @NotNull BindingTrace trace,
@NotNull JetDeclarationWithBody function, @NotNull JetDeclarationWithBody function,
@@ -454,7 +454,7 @@ public class JetTypeInferrer {
} }
@Nullable @Nullable
public JetType getTypeForConstructorCall(JetScope scope, @NotNull JetTypeReference typeReference, @NotNull JetCall call) { public JetType checkConstructorCall(JetScope scope, @NotNull JetTypeReference typeReference, @NotNull JetCall call) {
JetTypeElement typeElement = typeReference.getTypeElement(); JetTypeElement typeElement = typeReference.getTypeElement();
if (typeElement instanceof JetUserType) { if (typeElement instanceof JetUserType) {
JetUserType userType = (JetUserType) typeElement; JetUserType userType = (JetUserType) typeElement;
@@ -483,6 +483,28 @@ public class JetTypeInferrer {
JetSimpleNameExpression referenceExpression = userType.getReferenceExpression(); JetSimpleNameExpression referenceExpression = userType.getReferenceExpression();
if (referenceExpression != null) { if (referenceExpression != null) {
return checkClassConstructorCall(scope, referenceExpression, classDescriptor, receiverType, call);
}
}
else {
semanticServices.getErrorHandler().genericError(((JetElement) call).getNode(), "Calling a constructor is only supported for ordinary classes"); // TODO : review the message
}
}
else {
if (typeElement != null) {
semanticServices.getErrorHandler().genericError(typeElement.getNode(), "Calling a constructor is only supported for ordinary classes"); // TODO : Better message
}
}
return null;
}
@Nullable
public JetType checkClassConstructorCall(
@NotNull JetScope scope,
@NotNull JetReferenceExpression referenceExpression,
@NotNull ClassDescriptor classDescriptor,
@NotNull JetType receiverType,
@NotNull JetCall call) {
// When one writes 'new Array<in T>(...)' this does not make much sense, and an instance // When one writes 'new Array<in T>(...)' this does not make much sense, and an instance
// of 'Array<T>' must be created anyway. // of 'Array<T>' must be created anyway.
// Thus, we should either prohibit projections in type arguments in such contexts, // Thus, we should either prohibit projections in type arguments in such contexts,
@@ -514,7 +536,9 @@ public class JetTypeInferrer {
call.getValueArguments(), call.getValueArguments(),
call.getFunctionLiteralArguments()); call.getFunctionLiteralArguments());
if (constructorReturnedType == null && !ErrorUtils.isErrorType(receiverType)) { if (constructorReturnedType == null && !ErrorUtils.isErrorType(receiverType)) {
trace.recordReferenceResolution(referenceExpression, receiverType.getConstructor().getDeclarationDescriptor()); DeclarationDescriptor declarationDescriptor = receiverType.getConstructor().getDeclarationDescriptor();
assert declarationDescriptor != null;
trace.recordReferenceResolution(referenceExpression, declarationDescriptor);
// TODO : more helpful message // TODO : more helpful message
JetArgumentList argumentList = call.getValueArgumentList(); JetArgumentList argumentList = call.getValueArgumentList();
if (argumentList != null) { if (argumentList != null) {
@@ -528,18 +552,6 @@ public class JetTypeInferrer {
// Automatic upcast: // Automatic upcast:
// result = receiverType; // result = receiverType;
} }
}
else {
semanticServices.getErrorHandler().genericError(((JetElement) call).getNode(), "Calling a constructor is only supported for ordinary classes"); // TODO : review the message
}
}
else {
if (typeElement != null) {
semanticServices.getErrorHandler().genericError(typeElement.getNode(), "Calling a constructor is only supported for ordinary classes"); // TODO : Better message
}
}
return null;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1133,7 +1145,7 @@ public class JetTypeInferrer {
// TODO : type argument inference // TODO : type argument inference
JetTypeReference typeReference = expression.getTypeReference(); JetTypeReference typeReference = expression.getTypeReference();
if (typeReference != null) { if (typeReference != null) {
result = getTypeForConstructorCall(scope, typeReference, expression); result = checkConstructorCall(scope, typeReference, expression);
} }
} }
+9
View File
@@ -0,0 +1,9 @@
class Z {
this() : this(1, true) {}
this(x : Int, y : Boolean) : this<error>(1)</error> {}
}
class Foo() : <error>Z</error>, <error>this</error>() {
}
+2
View File
@@ -20,6 +20,7 @@ JetFile: Constructors.jet
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
INITIALIZER_LIST INITIALIZER_LIST
THIS_CALL THIS_CALL
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this') PsiElement(this)('this')
VALUE_ARGUMENT_LIST VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(') PsiElement(LPAR)('(')
@@ -78,6 +79,7 @@ JetFile: Constructors.jet
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
INITIALIZER_LIST INITIALIZER_LIST
THIS_CALL THIS_CALL
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this') PsiElement(this)('this')
VALUE_ARGUMENT_LIST VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(') PsiElement(LPAR)('(')
+4
View File
@@ -101,6 +101,7 @@ JetFile: SimpleClassMembers.jet
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
INITIALIZER_LIST INITIALIZER_LIST
THIS_CALL THIS_CALL
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this') PsiElement(this)('this')
VALUE_ARGUMENT_LIST VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(') PsiElement(LPAR)('(')
@@ -343,6 +344,7 @@ JetFile: SimpleClassMembers.jet
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
INITIALIZER_LIST INITIALIZER_LIST
THIS_CALL THIS_CALL
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this') PsiElement(this)('this')
VALUE_ARGUMENT_LIST VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(') PsiElement(LPAR)('(')
@@ -428,6 +430,7 @@ JetFile: SimpleClassMembers.jet
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
INITIALIZER_LIST INITIALIZER_LIST
THIS_CALL THIS_CALL
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this') PsiElement(this)('this')
VALUE_ARGUMENT_LIST VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(') PsiElement(LPAR)('(')
@@ -477,6 +480,7 @@ JetFile: SimpleClassMembers.jet
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
INITIALIZER_LIST INITIALIZER_LIST
THIS_CALL THIS_CALL
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this') PsiElement(this)('this')
VALUE_ARGUMENT_LIST VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(') PsiElement(LPAR)('(')
@@ -91,6 +91,7 @@ JetFile: SimpleClassMembers_ERR.jet
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
INITIALIZER_LIST INITIALIZER_LIST
THIS_CALL THIS_CALL
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this') PsiElement(this)('this')
VALUE_ARGUMENT_LIST VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(') PsiElement(LPAR)('(')
@@ -141,6 +142,7 @@ JetFile: SimpleClassMembers_ERR.jet
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
INITIALIZER_LIST INITIALIZER_LIST
THIS_CALL THIS_CALL
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this') PsiElement(this)('this')
VALUE_ARGUMENT_LIST VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(') PsiElement(LPAR)('(')
@@ -210,6 +210,7 @@ JetFile: BinaryTree.jet
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
INITIALIZER_LIST INITIALIZER_LIST
THIS_CALL THIS_CALL
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this') PsiElement(this)('this')
VALUE_ARGUMENT_LIST VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(') PsiElement(LPAR)('(')
@@ -975,6 +975,7 @@ JetFile: HashMap.jet
PsiWhiteSpace(' ') PsiWhiteSpace(' ')
INITIALIZER_LIST INITIALIZER_LIST
THIS_CALL THIS_CALL
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this') PsiElement(this)('this')
VALUE_ARGUMENT_LIST VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(') PsiElement(LPAR)('(')
@@ -0,0 +1,12 @@
class Z {
~c1~this() : `c2`this(1, true) {}
~c2~this(x : Int, y : Boolean) : `c1`this() {}
}
~Z1.c()~class Z1() : Z {
this(x : Int, y : Boolean) : `Z1.c()`this() {}
}
class Foo
@@ -143,4 +143,8 @@ public class JetResolveTest extends ExtensibleResolveTestCase {
doTest("/resolve/Classifiers.jet", true, true); doTest("/resolve/Classifiers.jet", true, true);
} }
public void testConstructorsAndInitializers() throws Exception {
doTest("/resolve/ConstructorsAndInitializers.jet", true, true);
}
} }