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 INITIALIZER_LIST = new JetNodeType("INITIALIZER_LIST", JetInitializerList.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 = new JetNodeType("TYPE_CONSTRAINT", JetTypeConstraint.class);
@@ -623,7 +623,9 @@ public class JetParsing extends AbstractJetParsing {
IElementType type;
if (at(THIS_KEYWORD)) {
PsiBuilder.Marker mark = mark();
advance(); // THIS_KEYWORD
mark.done(THIS_CONSTRUCTOR_REFERENCE);
type = THIS_CALL;
}
else if (atSet(TYPE_REF_FIRST)) {
@@ -12,6 +12,7 @@ import java.util.List;
* @author max
*/
public class JetDelegatorToThisCall extends JetDelegationSpecifier implements JetCall {
public JetDelegatorToThisCall(@NotNull ASTNode node) {
super(node);
}
@@ -37,4 +38,8 @@ public class JetDelegatorToThisCall extends JetDelegationSpecifier implements Je
public List<JetExpression> getFunctionLiteralArguments() {
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() {
resolveDelegationSpecifierLists();
//TODO : anonymous initializers
resolvePropertyDeclarationBodies();
resolveFunctionDeclarationBodies();
}
private void resolveFunctionDeclarationBodies() {
for (Map.Entry<JetDeclaration, FunctionDescriptor> entry : functions.entrySet()) {
JetDeclaration declaration = entry.getKey();
FunctionDescriptor descriptor = entry.getValue();
private void resolveDelegationSpecifierLists() {
final JetTypeInferrer typeInferrer = semanticServices.getTypeInferrer(trace, JetFlowInformationProvider.NONE);
for (Map.Entry<JetClass, MutableClassDescriptor> entry : classes.entrySet()) {
final JetClass declaration = entry.getKey();
final MutableClassDescriptor 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, 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.getTypeForConstructorCall(functionInnerScope, typeReference, call);
for (JetDelegationSpecifier delegationSpecifier : declaration.getDelegationSpecifiers()) {
delegationSpecifier.accept(new JetVisitor() {
@Override
public void visitDelegationByExpressionSpecifier(JetDelegatorByExpressionSpecifier specifier) {
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);
}
}
}
}
@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 visitDelegationToSuperCallSpecifier(JetDelegatorToSuperCall call) {
JetTypeReference typeReference = call.getTypeReference();
if (typeReference != null) {
typeInferrer.checkConstructorCall(descriptor.getWritableUnsubstitutedMemberScope(), typeReference, 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) {
if (declaration.getPrimaryConstructorParameterList() != null) {
semanticServices.getErrorHandler().genericError(specifier.getNode(), "Constructor parameters required in initializer");
}
}
@Override
public void visitDelegationToSuperClassSpecifier(JetDelegatorToSuperClass specifier) {
semanticServices.getErrorHandler().genericError(specifier.getNode(), "Constructor parameters required");
}
@Override
public void visitDelegationToThisCall(JetDelegatorToThisCall thisCall) {
throw new IllegalStateException("This-calls should be prohibitied by the parser");
}
@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);
@Override
public void visitJetElement(JetElement elem) {
throw new UnsupportedOperationException(elem.getText() + " : " + elem);
}
});
}
}
}
@@ -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(
@NotNull BindingTrace trace,
@NotNull JetDeclarationWithBody function,
@@ -454,7 +454,7 @@ public class JetTypeInferrer {
}
@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();
if (typeElement instanceof JetUserType) {
JetUserType userType = (JetUserType) typeElement;
@@ -483,50 +483,7 @@ public class JetTypeInferrer {
JetSimpleNameExpression referenceExpression = userType.getReferenceExpression();
if (referenceExpression != null) {
// When one writes 'new Array<in T>(...)' this does not make much sense, and an instance
// of 'Array<T>' must be created anyway.
// Thus, we should either prohibit projections in type arguments in such contexts,
// or treat them as an automatic upcast to the desired type, i.e. for the user not
// to be forced to write
// val a : Array<in T> = new Array<T>(...)
// NOTE: Array may be a bad example here, some classes may have substantial functionality
// not involving their type parameters
//
// The code below upcasts the type automatically
List<TypeProjection> typeArguments = receiverType.getArguments();
List<TypeProjection> projectionsStripped = new ArrayList<TypeProjection>();
for (TypeProjection typeArgument : typeArguments) {
if (typeArgument.getProjectionKind() != Variance.INVARIANT) {
projectionsStripped.add(new TypeProjection(typeArgument.getType()));
}
else
projectionsStripped.add(typeArgument);
}
FunctionGroup constructors = classDescriptor.getConstructors(projectionsStripped);
OverloadDomain constructorsOverloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(constructors);
JetType constructorReturnedType = resolveOverloads(
scope,
wrapForTracing(constructorsOverloadDomain, referenceExpression, call.getValueArgumentList(), false),
Collections.<JetTypeProjection>emptyList(),
call.getValueArguments(),
call.getFunctionLiteralArguments());
if (constructorReturnedType == null && !ErrorUtils.isErrorType(receiverType)) {
trace.recordReferenceResolution(referenceExpression, receiverType.getConstructor().getDeclarationDescriptor());
// TODO : more helpful message
JetArgumentList argumentList = call.getValueArgumentList();
if (argumentList != null) {
semanticServices.getErrorHandler().genericError(argumentList.getNode(), "Cannot find an overload for these arguments");
}
constructorReturnedType = receiverType;
}
// If no upcast needed:
return constructorReturnedType;
// Automatic upcast:
// result = receiverType;
return checkClassConstructorCall(scope, referenceExpression, classDescriptor, receiverType, call);
}
}
else {
@@ -541,6 +498,61 @@ public class JetTypeInferrer {
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
// of 'Array<T>' must be created anyway.
// Thus, we should either prohibit projections in type arguments in such contexts,
// or treat them as an automatic upcast to the desired type, i.e. for the user not
// to be forced to write
// val a : Array<in T> = new Array<T>(...)
// NOTE: Array may be a bad example here, some classes may have substantial functionality
// not involving their type parameters
//
// The code below upcasts the type automatically
List<TypeProjection> typeArguments = receiverType.getArguments();
List<TypeProjection> projectionsStripped = new ArrayList<TypeProjection>();
for (TypeProjection typeArgument : typeArguments) {
if (typeArgument.getProjectionKind() != Variance.INVARIANT) {
projectionsStripped.add(new TypeProjection(typeArgument.getType()));
}
else
projectionsStripped.add(typeArgument);
}
FunctionGroup constructors = classDescriptor.getConstructors(projectionsStripped);
OverloadDomain constructorsOverloadDomain = semanticServices.getOverloadResolver().getOverloadDomain(constructors);
JetType constructorReturnedType = resolveOverloads(
scope,
wrapForTracing(constructorsOverloadDomain, referenceExpression, call.getValueArgumentList(), false),
Collections.<JetTypeProjection>emptyList(),
call.getValueArguments(),
call.getFunctionLiteralArguments());
if (constructorReturnedType == null && !ErrorUtils.isErrorType(receiverType)) {
DeclarationDescriptor declarationDescriptor = receiverType.getConstructor().getDeclarationDescriptor();
assert declarationDescriptor != null;
trace.recordReferenceResolution(referenceExpression, declarationDescriptor);
// TODO : more helpful message
JetArgumentList argumentList = call.getValueArgumentList();
if (argumentList != null) {
semanticServices.getErrorHandler().genericError(argumentList.getNode(), "Cannot find an overload for these arguments");
}
constructorReturnedType = receiverType;
}
// If no upcast needed:
return constructorReturnedType;
// Automatic upcast:
// result = receiverType;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -1133,7 +1145,7 @@ public class JetTypeInferrer {
// TODO : type argument inference
JetTypeReference typeReference = expression.getTypeReference();
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>() {
}
+4 -2
View File
@@ -20,7 +20,8 @@ JetFile: Constructors.jet
PsiWhiteSpace(' ')
INITIALIZER_LIST
THIS_CALL
PsiElement(this)('this')
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this')
VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(')
VALUE_ARGUMENT
@@ -78,7 +79,8 @@ JetFile: Constructors.jet
PsiWhiteSpace(' ')
INITIALIZER_LIST
THIS_CALL
PsiElement(this)('this')
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this')
VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(')
VALUE_ARGUMENT
+8 -4
View File
@@ -101,7 +101,8 @@ JetFile: SimpleClassMembers.jet
PsiWhiteSpace(' ')
INITIALIZER_LIST
THIS_CALL
PsiElement(this)('this')
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this')
VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(')
VALUE_ARGUMENT
@@ -343,7 +344,8 @@ JetFile: SimpleClassMembers.jet
PsiWhiteSpace(' ')
INITIALIZER_LIST
THIS_CALL
PsiElement(this)('this')
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this')
VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(')
VALUE_ARGUMENT
@@ -428,7 +430,8 @@ JetFile: SimpleClassMembers.jet
PsiWhiteSpace(' ')
INITIALIZER_LIST
THIS_CALL
PsiElement(this)('this')
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this')
VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(')
VALUE_ARGUMENT
@@ -477,7 +480,8 @@ JetFile: SimpleClassMembers.jet
PsiWhiteSpace(' ')
INITIALIZER_LIST
THIS_CALL
PsiElement(this)('this')
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this')
VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(')
VALUE_ARGUMENT
+4 -2
View File
@@ -91,7 +91,8 @@ JetFile: SimpleClassMembers_ERR.jet
PsiWhiteSpace(' ')
INITIALIZER_LIST
THIS_CALL
PsiElement(this)('this')
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this')
VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(')
VALUE_ARGUMENT
@@ -141,7 +142,8 @@ JetFile: SimpleClassMembers_ERR.jet
PsiWhiteSpace(' ')
INITIALIZER_LIST
THIS_CALL
PsiElement(this)('this')
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this')
VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(')
VALUE_ARGUMENT
+2 -1
View File
@@ -210,7 +210,8 @@ JetFile: BinaryTree.jet
PsiWhiteSpace(' ')
INITIALIZER_LIST
THIS_CALL
PsiElement(this)('this')
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this')
VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(')
VALUE_ARGUMENT
@@ -975,7 +975,8 @@ JetFile: HashMap.jet
PsiWhiteSpace(' ')
INITIALIZER_LIST
THIS_CALL
PsiElement(this)('this')
THIS_CONSTRUCTOR_REFERENCE
PsiElement(this)('this')
VALUE_ARGUMENT_LIST
PsiElement(LPAR)('(')
VALUE_ARGUMENT
@@ -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);
}
public void testConstructorsAndInitializers() throws Exception {
doTest("/resolve/ConstructorsAndInitializers.jet", true, true);
}
}