/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* (C) Copyright IBM Corporation 2006-2010.
*/
package x10.ast;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import polyglot.ast.AmbExpr_c;
import polyglot.ast.Binary;
import polyglot.ast.Block;
import polyglot.ast.Call;
import polyglot.ast.CanonicalTypeNode;
import polyglot.ast.ClassMember;
import polyglot.ast.Expr;
import polyglot.ast.Field;
import polyglot.ast.FieldDecl;
import polyglot.ast.FlagsNode;
import polyglot.ast.Formal;
import polyglot.ast.Id;
import polyglot.ast.Local;
import polyglot.ast.MethodDecl;
import polyglot.ast.MethodDecl_c;
import polyglot.ast.NamedVariable;
import polyglot.ast.New;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Return;
import polyglot.ast.Special;
import polyglot.ast.Stmt;
import polyglot.ast.TypeCheckFragmentGoal;
import polyglot.ast.TypeNode;
import polyglot.frontend.Globals;
import polyglot.frontend.Job;
import polyglot.frontend.SetResolverGoal;
import polyglot.main.Reporter;
import polyglot.types.ClassDef;
import polyglot.types.ClassType;
import polyglot.types.CodeDef;
import polyglot.types.ConstructorDef;
import polyglot.types.ConstructorInstance;
import polyglot.types.Context;
import polyglot.types.Def;
import polyglot.types.ErrorRef_c;
import polyglot.types.FieldInstance;
import polyglot.types.Flags;
import polyglot.types.LazyRef;
import polyglot.types.LocalDef;
import polyglot.types.MemberDef;
import polyglot.types.MemberInstance;
import polyglot.types.MethodDef;
import polyglot.types.Name;
import polyglot.types.Package;
import polyglot.types.QName;
import polyglot.types.Ref;
import polyglot.types.Ref_c;
import polyglot.types.SemanticException;
import polyglot.types.ContainerType;
import polyglot.types.Type;
import polyglot.types.TypeObject;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.util.CodeWriter;
import polyglot.util.CollectionUtil;
import x10.util.AnnotationUtils;
import x10.util.CollectionFactory;
import polyglot.util.ErrorInfo;
import polyglot.util.InternalCompilerError;
import polyglot.util.Position;
import polyglot.util.TypedList;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import polyglot.visit.PrettyPrinter;
import polyglot.visit.Translator;
import polyglot.visit.TypeBuilder;
import polyglot.visit.TypeCheckPreparer;
import polyglot.visit.TypeChecker;
import x10.constraint.XFailure;
import x10.constraint.XVar;
import x10.constraint.XTerm;
import x10.types.constraints.ConstraintManager;
import x10.constraint.XVar;
import x10.errors.Errors;
import x10.errors.Errors.IllegalConstraint;
import x10.extension.X10Del;
import x10.extension.X10Del_c;
import x10.extension.X10Ext;
import x10.types.ConstrainedType;
import x10.types.MacroType;
import x10.types.ParameterType;
import x10.types.X10ClassDef;
import x10.types.X10ClassType;
import x10.types.X10ConstructorDef;
import polyglot.types.Context;
import x10.types.X10MemberDef;
import x10.types.X10MethodDef;
import x10.types.MethodInstance;
import x10.types.X10ParsedClassType_c;
import x10.types.X10ProcedureDef;
import x10.types.X10TypeEnv_c;
import polyglot.types.TypeSystem;
import x10.types.XTypeTranslator;
import x10.types.X10ParsedClassType;
import x10.types.X10MethodDef_c;
import x10.types.checker.Checker;
import x10.types.checker.PlaceChecker;
import x10.types.checker.VarChecker;
import x10.types.checker.Converter;
import x10.types.constraints.CConstraint;
import x10.types.constraints.TypeConstraint;
import x10.types.constraints.XConstrainedTerm;
import x10.visit.X10TypeChecker;
/** A representation of a method declaration.
* Includes an extra field to represent the guard
* in the method definition.
*
* @author vj
*
*/
public class X10MethodDecl_c extends MethodDecl_c implements X10MethodDecl {
// The representation of the guard on the method definition
DepParameterExpr guard;
List<TypeParamNode> typeParameters;
List<TypeNode> throwsTypes;
TypeNode offerType;
TypeNode hasType;
public X10MethodDecl_c(NodeFactory nf, Position pos, FlagsNode flags,
TypeNode returnType, Id name,
List<TypeParamNode> typeParams, List<Formal> formals, DepParameterExpr guard, TypeNode offerType, List<TypeNode> throwsTypes, Block body) {
super(pos, flags, returnType instanceof HasTypeNode_c ? nf.UnknownTypeNode(returnType.position()) : returnType,
name, formals, body);
this.guard = guard;
this.typeParameters = TypedList.copyAndCheck(typeParams, TypeParamNode.class, true);
if (returnType instanceof HasTypeNode_c)
hasType = ((HasTypeNode_c) returnType).typeNode();
this.offerType = offerType;
this.throwsTypes = throwsTypes;
}
public TypeNode offerType() {
return offerType;
}
protected X10MethodDecl_c hasType(TypeNode hasType) {
if (this.hasType != hasType) {
X10MethodDecl_c n = (X10MethodDecl_c) copy();
n.hasType = hasType;
return n;
}
return this;
}
public X10MethodDecl_c offerType(TypeNode offerType) {
if (this.offerType != offerType) {
X10MethodDecl_c n = (X10MethodDecl_c) copy();
n.offerType = offerType;
return n;
}
return this;
}
public List<TypeNode> throwsTypes() {
return throwsTypes;
}
public X10MethodDecl_c throwsTypes(List<TypeNode> throwsTypes) {
if (this.throwsTypes != throwsTypes) {
X10MethodDecl_c n = (X10MethodDecl_c) copy();
n.throwsTypes = throwsTypes;
return n;
}
return this;
}
protected X10MethodDef createMethodDef(TypeSystem ts, X10ClassDef ct, Flags flags) {
X10MethodDef mi = (X10MethodDef) ts.methodDef(position(), name().position(), Types.ref(ct.asType()), flags, returnType.typeRef(), name.id(),
Collections.<Ref<? extends Type>>emptyList(), Collections.<Ref<? extends Type>>emptyList(),
offerType == null ? null : offerType.typeRef());
mi.setThisDef(ct.thisDef());
mi.setPlaceTerm(PlaceChecker.methodPlaceTerm(mi));
return mi;
}
@Override
public Node buildTypesOverride(TypeBuilder tb) {
// Have to inline super.buildTypesOverride(tb) to make sure the body
// is visited after the appropriate information is set up
TypeSystem ts = tb.typeSystem();
X10ClassDef ct = tb.currentClass();
assert ct != null;
Flags flags = this.flags.flags();
if (ct.flags().isInterface()) {
flags = flags.Public().Abstract();
}
X10MethodDecl_c n = this;
X10MethodDef md = createMethodDef(ts, ct, flags);
ct.addMethod(md);
TypeBuilder tbChk = tb.pushCode(md);
final TypeBuilder tbx = tb;
final MethodDef mdx = md;
n = (X10MethodDecl_c) n.visitSignature(new NodeVisitor() {
public Node override(Node n) {
return X10MethodDecl_c.this.visitChild(n, tbx.pushCode(mdx));
}
});
List<Ref<? extends Type>> formalTypes = new ArrayList<Ref<? extends Type>>(n.formals().size());
for (Formal f1 : n.formals()) {
formalTypes.add(f1.type().typeRef());
}
md.setReturnType(n.returnType().typeRef());
md.setFormalTypes(formalTypes);
List<Ref<? extends Type>> throw_types = new ArrayList<Ref<? extends Type>>();
for (TypeNode t : n.throwsTypes()) {
throw_types.add(t.typeRef());
}
md.setThrowTypes(throw_types);
n = (X10MethodDecl_c) X10Del_c.visitAnnotations(n, tb);
List<AnnotationNode> as = ((X10Del) n.del()).annotations();
if (as != null) {
List<Ref<? extends Type>> ats = new ArrayList<Ref<? extends Type>>(as.size());
for (AnnotationNode an : as) {
ats.add(an.annotationType().typeRef());
}
md.setDefAnnotations(ats);
}
// Enable return type inference for this method declaration.
if (n.returnType() instanceof UnknownTypeNode) {
md.inferReturnType(true);
}
if (n.guard() != null) {
md.setGuard(n.guard().valueConstraint());
md.setTypeGuard(n.guard().typeConstraint());
}
List<ParameterType> typeParameters = new ArrayList<ParameterType>(n.typeParameters().size());
for (TypeParamNode tpn : n.typeParameters()) {
typeParameters.add(tpn.type());
}
md.setTypeParameters(typeParameters);
List<LocalDef> formalNames = new ArrayList<LocalDef>(n.formals().size());
for (Formal f : n.formals()) {
formalNames.add(f.localDef());
}
md.setFormalNames(formalNames);
Flags xf = md.flags();
if (xf.isProperty()) {
final LazyRef<XTerm> bodyRef = Types.lazyRef(null);
bodyRef.setResolver(new SetResolverGoal(tb.job()).intern(tb.job().extensionInfo().scheduler()));
md.body(bodyRef);
}
// property implies public, final
if (xf.isProperty()) {
if (xf.isAbstract())
xf = xf.Public();
else
xf = xf.Public().Final();
md.setFlags(xf);
n = (X10MethodDecl_c) n.flags(n.flags().flags(xf));
}
Block body = (Block) n.visitChild(n.body, tbChk);
n = (X10MethodDecl_c) n.body(body);
return n.methodDef(md);
}
@Override
public void addDecls(Context c) {
}
public void setResolver(Node parent, final TypeCheckPreparer v) {
X10MethodDef mi = (X10MethodDef) this.mi;
if (mi.body() instanceof LazyRef<?>) {
LazyRef<XTerm> r = (LazyRef<XTerm>) mi.body();
TypeChecker tc = new X10TypeChecker(v.job(), v.typeSystem(), v.nodeFactory(), v.getMemo());
tc = (TypeChecker) tc.context(v.context().freeze());
r.setResolver(new TypeCheckFragmentGoal<XTerm>(parent, this, tc, r, false));
}
}
/** Visit the children of the method. */
public Node visitSignature(NodeVisitor v) {
FlagsNode flags = (FlagsNode) this.visitChild(this.flags, v);
Id name = (Id) this.visitChild(this.name, v);
List<TypeParamNode> typeParams = visitList(this.typeParameters, v);
List<Formal> formals = this.visitList(this.formals, v);
DepParameterExpr guard = (DepParameterExpr) visitChild(this.guard, v);
TypeNode ht = (TypeNode) visitChild(this.hasType, v);
TypeNode ot = (TypeNode) visitChild(this.offerType, v);
TypeNode returnType = (TypeNode) visitChild(this.returnType, v);
List<TypeNode> throwsTypes = visitList(this.throwsTypes, v);
return reconstruct(flags, name, typeParams, formals, guard, ht, returnType, ot, throwsTypes, this.body);
}
/** Reconstruct the method.
* @param throwsTypes2 */
protected X10MethodDecl_c reconstruct(FlagsNode flags, Id name, List<TypeParamNode> typeParameters, List<Formal> formals, DepParameterExpr guard, TypeNode hasType, TypeNode returnType, TypeNode offerType, List<TypeNode> throwsTypes, Block body) {
X10MethodDecl_c n = (X10MethodDecl_c) super.reconstruct(flags, returnType, name, formals, body);
if (! CollectionUtil.allEqual(typeParameters, n.typeParameters) || guard != n.guard || hasType != n.hasType || offerType != n.offerType || ! CollectionUtil.allEqual(throwsTypes, n.throwsTypes) ) {
if (n == this) {
n = (X10MethodDecl_c) n.copy();
}
n.typeParameters = TypedList.copyAndCheck(typeParameters, TypeParamNode.class, true);
n.guard = guard;
n.hasType = hasType;
n.offerType = offerType;
n.throwsTypes = throwsTypes;
return n;
}
return n;
}
public List<TypeParamNode> typeParameters() {
return typeParameters;
}
public X10MethodDecl_c typeParameters(List<TypeParamNode> typeParams) {
X10MethodDecl_c n = (X10MethodDecl_c) copy();
n.typeParameters=TypedList.copyAndCheck(typeParams, TypeParamNode.class, true);
return n;
}
public DepParameterExpr guard() { return guard; }
public X10MethodDecl_c guard(DepParameterExpr e) {
X10MethodDecl_c n = (X10MethodDecl_c) copy();
n.guard = e;
return n;
}
@Override
public X10MethodDecl_c flags(FlagsNode flags) {
return (X10MethodDecl_c) super.flags(flags);
}
@Override
public X10MethodDecl_c returnType(TypeNode returnType) {
return (X10MethodDecl_c) super.returnType(returnType);
}
@Override
public X10MethodDecl_c name(Id name) {
return (X10MethodDecl_c) super.name(name);
}
@Override
public X10MethodDecl_c formals(List<Formal> formals) {
return (X10MethodDecl_c) super.formals(formals);
}
@Override
public X10MethodDecl_c methodDef(MethodDef mi) {
return (X10MethodDecl_c) super.methodDef(mi);
}
@Override
public X10MethodDef methodDef() {
return (X10MethodDef) super.methodDef();
}
@Override
public Context enterScope(Context c) {
c = super.enterScope(c);
if (!c.inStaticContext() && methodDef().thisDef() != null)
c.addVariable(methodDef().thisDef().asInstance());
return c;
}
@Override
public Context enterChildScope(Node child, Context c) {
// We should have entered the method scope already.
assert c.currentCode() == this.methodDef();
Context oldC = c;
if (child != body()) {
// Push formals so they're in scope in the types of the other formals.
c = c.pushBlock();
for (TypeParamNode f : typeParameters) {
f.addDecls(c);
}
for (int i=0; i < formals.size(); i++) {
Formal f = formals.get(i);
f.addDecls(c);
if (f == child)
break; // do not add downstream formals
}
}
// Ensure that the place constraint is set appropriately when
// entering the appropriate children
if (child == body || child == returnType || child == hasType || child == offerType || child == guard
|| (formals != null && formals.contains(child))|| (throwsTypes != null && throwsTypes.contains(child))) {
X10MethodDef md = methodDef();
XConstrainedTerm placeTerm = md == null ? null : md.placeTerm();
if (placeTerm == null) {
placeTerm = PlaceChecker.methodPlaceTerm(md);
}
if (c == oldC)
c = c.pushBlock();
c.setPlace(placeTerm);
}
if (child == body && offerType != null && offerType.typeRef().known()) {
if (oldC == c)
c = c.pushBlock();
c.setCollectingFinishScope(offerType.type());
}
// Add the method guard into the environment.
if (guard != null) {
if (child == body || child == offerType || child == hasType || child == returnType
|| (formals != null && formals.contains(child))|| (throwsTypes != null && throwsTypes.contains(child))) {
Ref<CConstraint> vc = guard.valueConstraint();
Ref<TypeConstraint> tc = guard.typeConstraint();
if (vc != null || tc != null) {
if (oldC==c) {
c = c.pushBlock();
}
c.setName(" MethodGuard for |" + mi.name() + "| ");
if (vc != null)
c.addConstraint(vc);
if (tc != null) {
c.setTypeConstraintWithContextTerms(tc);
}
}
}
}
addInClassInvariantIfNeeded(c, false);
return super.enterChildScope(child, c);
}
public void addInClassInvariantIfNeeded(Context c, boolean force) {
if (!mi.flags().isStatic()) {
// this call occurs in the body of an instance method for T.
// Pick up the real clause for T -- that information is known
// statically about "this"
Ref<? extends ContainerType> container = mi.container();
if (container.known()) {
X10ClassType type = (X10ClassType) Types.get(container);
Ref<CConstraint> rc = type.x10Def().realClauseWithThis();
c.addConstraint(rc);
Ref<TypeConstraint> tc = type.x10Def().typeBounds();
if (tc != null) {
c.setTypeConstraintWithContextTerms(tc);
}
}
}
}
public void translate(CodeWriter w, Translator tr) {
Context c = tr.context();
Flags flags = flags().flags();
if (c.currentClass().flags().isInterface()) {
flags = flags.clearPublic();
flags = flags.clearAbstract();
}
FlagsNode oldFlags = this.flags;
try {
this.flags = this.flags.flags(flags.retainJava()); // ensure that X10Flags are not printed out .. javac will not know what to do with them.
super.translate(w, tr);
}
finally {
this.flags = oldFlags;
}
}
@Override
public Node setResolverOverride(Node parent, TypeCheckPreparer v) {
if (returnType() instanceof UnknownTypeNode && body() != null) {
UnknownTypeNode tn = (UnknownTypeNode) returnType();
NodeVisitor childv = v.enter(parent, this);
childv = childv.enter(this, returnType());
if (childv instanceof TypeCheckPreparer) {
TypeCheckPreparer tcp = (TypeCheckPreparer) childv;
final LazyRef<Type> r = (LazyRef<Type>) tn.typeRef();
TypeChecker tc = new X10TypeChecker(v.job(), v.typeSystem(), v.nodeFactory(), v.getMemo(), true);
tc = (TypeChecker) tc.context(tcp.context().freeze());
// todo: if the return type is void, let's skip this whole resolver stuff.
r.setResolver(new TypeCheckReturnTypeGoal(this, new Node[] { guard(), offerType() }, body(), tc, r));
}
}
return super.setResolverOverride(parent, v);
}
@Override
protected void checkFlags(ContextVisitor tc, Flags xf) {
// Set the native flag if incomplete or extern so super.checkFlags doesn't complain.
super.checkFlags(tc, xf);
if (xf.isProperty() && ! xf.isAbstract() && ! xf.isFinal()) {
Errors.issue(tc.job(),
new Errors.NonAbstractPropertyMethodMustBeFinal(position()));
}
//if (xf.isProperty() && xf.isStatic()) {
// Errors.issue(tc.job(),
// new Errors.PropertyMethodCannotBeStatic(position()));
//}
}
private Type getType(ContextVisitor tc, String name) throws SemanticException {
return tc.typeSystem().systemResolver().findOne(QName.make(name));
}
private boolean nodeHasOneAnnotation(ContextVisitor tc, Node n, String ann_name) {
X10Ext ext = (X10Ext) n.ext();
try {
List<X10ClassType> anns = ext.annotationMatching(getType(tc, ann_name));
if (anns.size() == 0) return false;
if (anns.size() > 1) {
Errors.issue(tc.job(), new SemanticException("Cannot have more than one @Opaque annotation", n.position()));
}
return true;
} catch (SemanticException e) {
assert false : e;
return false; // in case asserts are off
}
}
@Override
public Node typeCheck(ContextVisitor tc) {
X10MethodDecl_c n = this;
NodeFactory nf = tc.nodeFactory();
TypeSystem ts = (TypeSystem) tc.typeSystem();
if (((TypeSystem) tc.typeSystem()).isStructType(mi.container().get())) {
Flags xf = mi.flags().Final();
mi.setFlags(xf);
n = (X10MethodDecl_c) n.flags(n.flags().flags(xf));
}
Flags xf = mi.flags();
//if (xf.isProperty() && body == null) {
// Errors.issue(tc.job(),
// new SemanticException("A property method must have a body.", position()));
//}
if (xf.isProperty()) {
boolean ok = false;
if (xf.isAbstract() || xf.isNative()) {
ok = true;
}
if (nodeHasOneAnnotation(tc,n,"x10.compiler.Opaque")) {
ok = true;
} else if (n.body != null && n.body.statements().size() == 1) {
Stmt s = n.body.statements().get(0);
if (s instanceof Return) {
Return r = (Return) s;
if (r.expr() != null) {
final X10MethodDef_c mdef = (X10MethodDef_c) mi;
// detecting cycles in property methods
// recurse into the expr to see what other methods we call
if (mdef.isCircularPropertyMethod(r.expr())) {
Errors.issue(tc.job(), new SemanticException("Circular property method definition. Expanding the property method may result in an infinite loop.\n\tProperty:"+mi, position));
} else {
// while expanding this expression we might recursively type check the same method
XTerm v = null;
try {
Context cxt = tc.context();
// Translate the body with the SpecialAsQualifiedVar
// bit turned on.
cxt = cxt.pushSpecialAsQualifiedVar();
v = ts.xtypeTranslator().translate((CConstraint) null, r.expr(), cxt, true);
ok = true;
X10MethodDef mi = (X10MethodDef) this.mi;
if (mi.body() instanceof LazyRef<?>) {
LazyRef<XTerm> bodyRef = (LazyRef<XTerm>) mi.body();
bodyRef.update(v);
}
} catch (IllegalConstraint z) {
Errors.issue(tc.job(),z);
ok = true;
}
}
}
}
}
if (! ok)
Errors.issue(tc.job(),
new Errors.MethodBodyMustBeConstraintExpressiong(position()));
}
n = (X10MethodDecl_c) n.superTypeCheck(tc);
try {
dupFormalCheck(typeParameters, formals);
} catch (SemanticException e) {
Errors.issue(tc.job(), e, n);
}
try {
Types.checkMissingParameters(n.returnType());
} catch (SemanticException e) {
Errors.issue(tc.job(), e, n.returnType());
}
if (!position.isCompilerGenerated()) { // for struct X[+T], we generate equals(T), but it is type-safe because it is readonly and struct are final.
// formals are always in contravariant positions, while return type in covariant position (ctors are ignored)
for (Formal f : n.formals) {
final TypeNode fType = f.type();
Types.checkVariance(fType, ParameterType.Variance.CONTRAVARIANT,tc.job());
}
Types.checkVariance(n.returnType, ParameterType.Variance.COVARIANT,tc.job());
}
// check subtype restriction on implicit and explicit operator as:
// the return type must be a subtype of the container type
final Name nameId = n.name.id();
if (nameId==Converter.implicit_operator_as || nameId==Converter.operator_as) {
final X10MethodDef methodDef = n.methodDef();
final ContainerType container = methodDef.container().get();
final Type returnT = Types.baseType(methodDef.returnType().get());
final List<Ref<? extends Type>> formals = methodDef.formalTypes();
assert formals.size()==1 : "Currently it is a parsing error if the number of formals for an implicit or explicit 'as' operator is different than 1! formals="+formals;
final Type argumentT = Types.baseType(formals.get(0).get());
// I compare ClassDef due to this example:
//class B[U] {
// public static operator[T](x:T):B[T] = null;
//}
// We only search in the target type (not the source type), so
// public static operator (x:Bar) as Any = null; // ERR
assert container instanceof X10ParsedClassType : container;
boolean isReturnWrong = !(returnT instanceof X10ParsedClassType) || ((X10ParsedClassType)returnT ).def()!=((X10ParsedClassType)container).def();
boolean isFormalWrong = SEARCH_CASTS_ONLY_IN_TARGET || !(argumentT instanceof X10ParsedClassType) || ((X10ParsedClassType)argumentT).def()!=((X10ParsedClassType)container).def();
if (isReturnWrong && isFormalWrong) {
Errors.issue(tc.job(),
new Errors.MustHaveSameClassAsContainer(n.position()));
}
}
return n;
}
private static boolean SEARCH_CASTS_ONLY_IN_TARGET = true; // see XTENLANG_2667
public static void dupFormalCheck(List<TypeParamNode> typeParams, List<Formal> formals) throws SemanticException {
Set<Name> pnames = CollectionFactory.newHashSet();
for (TypeParamNode p : typeParams) {
Name name = p.name().id();
if (pnames.contains(name))
throw new Errors.TypeParameterMultiplyDefined(name, p.position());
pnames.add(name);
}
// Check for duplicate formals. This isn't caught in Formal_c
// because we add all the formals into the scope before visiting a
// formal, so the lookup of a duplicate formal returns itself rather
// than the previous formal.
Set<Name> names = CollectionFactory.newHashSet();
LinkedList<Formal> q = new LinkedList<Formal>();
q.addAll(formals);
while (! q.isEmpty()) {
Formal f = q.removeFirst();
Name name = f.name().id();
if (! name.equals(Name.make(""))) {
if (names.contains(name))
throw new Errors.LocalVariableMultiplyDefined(name, f.position());
names.add(name);
}
if (f instanceof X10Formal) {
X10Formal ff = (X10Formal) f;
q.addAll(ff.vars());
}
}
}
protected X10MethodDecl_c superTypeCheck(ContextVisitor tc) {
return (X10MethodDecl_c) super.typeCheck(tc);
}
@Override
public Node conformanceCheck(ContextVisitor tc) {
//checkVariance(tc);
MethodDef mi = this.methodDef();
TypeSystem xts = (TypeSystem) tc.typeSystem();
if (mi.flags().isProperty()) {
MethodInstance xmi = (MethodInstance) mi.asInstance();
if (xmi.guard() != null && ! xmi.guard().valid())
Errors.issue(tc.job(),
new Errors.PropertyMethodCannotHaveGuard(guard, position()));
}
checkVisibility(tc, this);
// Need to ensure that method overriding is checked in the right context
// The classInvariant needs to be added.
// Note that the guard should not be added, since we need to check that the
// guard is entailed by any method that is overridden by this method.
Context childtc = tc.context().pushBlock();
addInClassInvariantIfNeeded(childtc, true);
ContextVisitor childVisitor = tc.context(childtc);
return super.conformanceCheck(childVisitor);
}
final static boolean CHECK_VISIBILITY = false;
protected static void checkVisibility(ContextVisitor tc, final ClassMember mem) {
// This doesn't work since we've already translated away expressions into constraints.
if (! CHECK_VISIBILITY)
return;
final SemanticException[] ex = new SemanticException[1];
// Check if all fields, methods, etc accessed from the signature of mem are in scope wherever mem can be accessed.
// Assumes the fields, methods, etc are accessible from mem, at least.
mem.visitChildren(new NodeVisitor() {
boolean on = false;
@Override
public Node override(Node parent, Node n) {
if (! on) {
if (n instanceof TypeNode) {
try {
on = true;
return this.visitEdgeNoOverride(parent, n);
}
finally {
on = false;
}
}
else {
return this.visitEdgeNoOverride(parent, n);
}
}
if (n instanceof Stmt) {
return n;
}
if (parent instanceof FieldDecl && n == ((FieldDecl) parent).init()) {
return n;
}
if (n instanceof Field) {
FieldInstance fi = (((Field) n).fieldInstance());
if (! hasSameScope(fi, mem.memberDef())) {
ex[0] = new SemanticException("Field " + fi.name() + " cannot be used in this signature; not accessible from all contexts in which the member is accessible.", n.position());
}
}
if (n instanceof Call) {
MethodInstance mi = (((Call) n).methodInstance());
if (! hasSameScope(mi, mem.memberDef())) {
ex[0] = new SemanticException("Method " + mi.signature() + " cannot be used in this signature; not accessible from all contexts in which the member is accessible.", n.position());
}
}
if (n instanceof ClosureCall) {
MethodInstance mi = (((ClosureCall) n).closureInstance());
if (! hasSameScope(mi, mem.memberDef())) {
ex[0] = new SemanticException("Method " + mi.signature() + " cannot be used in this signature; not accessible from all contexts in which the member is accessible.", n.position());
}
}
if (n instanceof TypeNode) {
TypeNode tn = (TypeNode) n;
Type t = tn.type();
t = Types.baseType(t);
if (t instanceof X10ClassType) {
X10ClassType ct = (X10ClassType) t;
if (! hasSameScope(ct, mem.memberDef())) {
ex[0] = new SemanticException("Class " + ct.fullName() + " cannot be used in this signature; not accessible from all contexts in which the member is accessible.", n.position());
}
}
}
if (n instanceof New) {
ConstructorInstance ci = (((New) n).constructorInstance());
if (! hasSameScope(ci, mem.memberDef())) {
ex[0] = new SemanticException("Constructor " + ci.signature() + " cannot be used in this signature; not accessible from all contexts in which the member is accessible.", n.position());
}
}
return null;
}
Flags getSignatureFlags(MemberDef def) {
Flags sigFlags = def.flags();
if (def instanceof ClassDef) {
ClassDef cd = (ClassDef) def;
if (cd.isTopLevel()) {
return sigFlags;
}
if (cd.isMember()) {
ClassDef outer = Types.get(cd.outer());
Flags outerFlags = getSignatureFlags(outer);
return combineFlagsWithContainerFlags(sigFlags, outerFlags);
}
return Flags.PRIVATE;
}
else {
Type t = Types.get(def.container());
t = Types.baseType(t);
if (t instanceof ClassType) {
ClassType ct = (ClassType) t;
Flags outerFlags = getSignatureFlags(ct.def());
return combineFlagsWithContainerFlags(sigFlags, outerFlags);
}
}
return sigFlags;
}
private Flags combineFlagsWithContainerFlags(Flags sigFlags, Flags outerFlags) {
if (outerFlags.isPrivate() || sigFlags.isPrivate())
return Flags.PRIVATE;
if (outerFlags.isPackage() || sigFlags.isPackage())
return Flags.NONE;
if (outerFlags.isProtected())
return Flags.NONE;
if (sigFlags.isProtected())
return Flags.PROTECTED;
return Flags.PUBLIC;
}
private <T extends Def> boolean hasSameScope(MemberInstance<T> mi, MemberDef signature) {
Flags sigFlags = getSignatureFlags(signature);
if (sigFlags.isPrivate()) {
return true;
}
if (sigFlags.isPackage()) {
if (mi.flags().isPublic())
return true;
if (mi.flags().isProtected() || mi.flags().isPackage()) {
return hasSamePackage(mi.def(), signature);
}
return false;
}
if (sigFlags.isProtected()) {
if (mi.flags().isPublic())
return true;
if (mi.flags().isProtected()) {
return hasSameClass(mi.def(), signature);
}
return false;
}
if (sigFlags.isPublic()) {
if (mi.flags().isPublic())
return true;
return false;
}
return false;
}
private ClassDef getClass(Def def) {
if (def instanceof ClassDef) {
return (ClassDef) def;
}
if (def instanceof MemberDef) {
MemberDef md = (MemberDef) def;
Type container = Types.get(md.container());
if (container != null) {
container = Types.baseType(container);
if (container instanceof ClassType) {
return ((ClassType) container).def();
}
}
}
return null;
}
private boolean hasSameClass(Def def, MemberDef accessor) {
ClassDef c1 = getClass(def);
ClassDef c2 = getClass(accessor);
if (c1 == null || c2 == null)
return false;
return c1.equals(c2);
}
private boolean hasSamePackage(Def def, MemberDef accessor) {
ClassDef c1 = getClass(def);
ClassDef c2 = getClass(accessor);
if (c1 == null || c2 == null)
return false;
Package p1 = Types.get(c1.package_());
Package p2 = Types.get(c2.package_());
if (p1 == null && p2 == null)
return true;
if (p1 == null || p2 == null)
return false;
return p1.equals(p2);
}
});
if (ex[0] != null)
Errors.issue(tc.job(), ex[0], mem);
}
/*protected void checkVariance(ContextVisitor tc) {
if (methodDef().flags().isStatic())
return;
X10ClassDef cd = (X10ClassDef) tc.context().currentClassDef();
final Map<Name,ParameterType.Variance> vars = CollectionFactory.newHashMap();
for (int i = 0; i < cd.typeParameters().size(); i++) {
ParameterType pt = cd.typeParameters().get(i);
ParameterType.Variance v = cd.variances().get(i);
vars.put(pt.name(), v);
}
try {
Checker.checkVariancesOfType(returnType.position(), returnType.type(), ParameterType.Variance.COVARIANT, "as a method return type", vars, tc);
} catch (SemanticException e) {
Errors.issue(tc.job(), e, this);
}
for (Formal f : formals) {
try {
Checker.checkVariancesOfType(f.type().position(), f.declType(), ParameterType.Variance.CONTRAVARIANT, "as a method parameter type", vars, tc);
} catch (SemanticException e) {
Errors.issue(tc.job(), e, this);
}
}
}*/
public Node typeCheckOverride(Node parent, ContextVisitor tc) {
X10MethodDecl nn = this;
X10MethodDecl old = nn;
TypeSystem xts = (TypeSystem) tc.typeSystem();
// Step 0. Process annotations.
TypeChecker childtc = (TypeChecker) tc.enter(parent, nn);
nn = (X10MethodDecl) X10Del_c.visitAnnotations(nn, childtc);
// Do not infer types of native methods
if (nn.returnType() instanceof UnknownTypeNode && ! X10FieldDecl_c.shouldInferType(nn, xts))
Errors.issue(tc.job(), new Errors.CannotInferNativeMethodReturnType(position()));
// Step I.a. Check the formals.
// First, record the final status of each of the type params and formals.
List<TypeParamNode> processedTypeParams = nn.visitList(nn.typeParameters(), childtc);
nn = (X10MethodDecl) nn.typeParameters(processedTypeParams);
List<Formal> processedFormals = nn.visitList(nn.formals(), childtc);
nn = (X10MethodDecl) nn.formals(processedFormals);
// [NN]: Don't do this here, do it on lookup of the formal. We don't want spurious self constraints in the signature.
// for (Formal n : processedFormals) {
// Ref<Type> ref = (Ref<Type>) n.type().typeRef();
// Type newType = ref.get();
//
// if (n.localDef().flags().isFinal()) {
// XConstraint c = X10TypeMixin.xclause(newType);
// if (c == null)
// c = new XConstraint_c();
// else
// c = c.copy();
// try {
// c.addSelfBinding(xts.xtypeTranslator().trans(n.localDef().asInstance()));
// }
// catch (XFailure e) {
// throw new SemanticException(e.getMessage(), position());
// }
// newType = X10TypeMixin.xclause(newType, c);
// }
//
// ref.update(newType);
// }
// Step I.b. Check the guard.
if (nn.guard() != null) {
ContextVisitor guardtc = childtc.context(childtc.context().pushDepType(Types.<Type>ref(xts.unknownType(nn.guard().position()))));
DepParameterExpr processedWhere = (DepParameterExpr) nn.visitChild(nn.guard(), guardtc);
nn = (X10MethodDecl) nn.guard(processedWhere);
VarChecker ac = new VarChecker(childtc.job());
ac = (VarChecker) ac.context(childtc.context());
processedWhere.visit(ac);
if (ac.error != null) {
Errors.issue(ac.job(), ac.error, this);
}
// Now build the new formal arg list.
// TODO: Add a marker to the TypeChecker which records
// whether in fact it changed the type of any formal.
List<Formal> formals = processedFormals;
//List newFormals = new ArrayList(formals.size());
X10ProcedureDef pi = (X10ProcedureDef) nn.memberDef();
CConstraint c = pi.guard().get();
try {
if (c != null) {
c = c.copy();
for (Formal n : formals) {
Ref<Type> ref = (Ref<Type>) n.type().typeRef();
Type newType = ref.get();
// Fold the formal's constraint into the guard.
XVar var = xts.xtypeTranslator().translate(n.localDef().asInstance());
CConstraint dep = Types.xclause(newType);
if (dep != null) {
dep = dep.copy();
dep = dep.substitute(var, c.self());
/*
XPromise p = dep.intern(var);
dep = dep.substitute(p.term(), c.self());
*/
c.addIn(dep);
}
ref.update(newType);
}
}
// reporter.report(1, "X10MethodDecl_c: typeoverride mi= " + nn.methodInstance());
// Fold this's constraint (the class invariant) into the guard.
{
Type t = tc.context().currentClass();
CConstraint dep = Types.xclause(t);
if (c != null && dep != null) {
XVar thisVar = methodDef().thisVar();
if (thisVar != null)
dep = dep.substitute(thisVar, c.self());
// dep = dep.copy();
// XPromise p = dep.intern(xts.xtypeTranslator().transThis(t));
// dep = dep.substitute(p.term(), c.self());
c.addIn(dep);
}
}
}
catch (XFailure e) {
tc.errorQueue().enqueue(ErrorInfo.SEMANTIC_ERROR, e.getMessage(), position());
c = null;
}
// Check if the guard is consistent.
if (c != null && ! c.consistent()) {
Errors.issue(tc.job(),
new Errors.DependentClauseIsInconsistent("method", guard),
this);
// FIXME: [IP] mark constraint clause as invalid
}
}
List<TypeNode> processedThrowsTypes = nn.visitList(nn.throwsTypes(), childtc);
nn = (X10MethodDecl) nn.throwsTypes(processedThrowsTypes);
// Step II. Check the return type.
// Now visit the returntype to ensure that its depclause, if any is processed.
// Visit the formals so that they get added to the scope .. the return type
// may reference them.
//TypeChecker tc1 = (TypeChecker) tc.copy();
// childtc will have a "wrong" mi pushed in, but that doesnt matter.
// we simply need to push in a non-null mi here.
TypeChecker childtc1 = (TypeChecker) tc.enter(parent, nn);
if (childtc1.context() == tc.context())
childtc1 = (TypeChecker) childtc1.context((Context) tc.context().shallowCopy());
// Add the type params and formals to the context.
nn.visitList(nn.typeParameters(),childtc1);
nn.visitList(nn.formals(),childtc1);
Context childCxt = childtc1.context();
addInClassInvariantIfNeeded(childCxt, true);
childCxt.setVarWhoseTypeIsBeingElaborated(null);
{
final TypeNode r = (TypeNode) nn.visitChild(nn.returnType(), childtc1);
nn = (X10MethodDecl) nn.returnType(r);
Type type = PlaceChecker.ReplaceHereByPlaceTerm(r.type(), ( Context ) childtc1.context());
((Ref<Type>) nn.methodDef().returnType()).update(r.type());
if (hasType != null) {
final TypeNode h = (TypeNode) nn.visitChild(((X10MethodDecl_c) nn).hasType, childtc1);
Type hasType = PlaceChecker.ReplaceHereByPlaceTerm(h.type(), ( Context ) childtc1.context());
nn = (X10MethodDecl) ((X10MethodDecl_c) nn).hasType(h);
boolean checkSubType = true;
try {
Types.checkMissingParameters(h);
} catch (SemanticException e) {
Errors.issue(tc.job(), e, h);
checkSubType = false;
}
if (checkSubType && ! xts.isSubtype(type, hasType, tc.context())) {
Errors.issue(tc.job(),
new Errors.TypeIsNotASubtypeOfTypeBound(type, hasType, position()));
}
}
// Check the offer type
if (offerType != null) {
final TypeNode o = (TypeNode) nn.visitChild(((X10MethodDecl_c) nn).offerType, childtc1);
nn = (X10MethodDecl) ((X10MethodDecl_c) nn).offerType(o);
((X10MethodDef) nn.methodDef()).setOfferType(Types.ref(o.type()));
}
}
// reporter.report(1, "X10MethodDecl_c: typeoverride mi= " + nn.methodInstance());
// Step III. Check the body.
// We must do it with the correct mi -- the return type will be
// checked by return e; statements in the body, and the offerType by offer e; statements in the body.
TypeChecker childtc2 = (TypeChecker) tc.enter(parent, nn); // this will push in the right mi.
// Add the type params and formals to the context.
nn.visitList(nn.typeParameters(),childtc2);
nn.visitList(nn.formals(),childtc2);
addInClassInvariantIfNeeded(childtc2.context(), true);
//reporter.report(1, "X10MethodDecl_c: after visiting formals " + childtc2.context());
// Now process the body.
nn = (X10MethodDecl) nn.body((Block) nn.visitChild(nn.body(), childtc2));
nn = (X10MethodDecl) childtc2.leave(parent, old, nn, childtc2);
if (nn.returnType() instanceof UnknownTypeNode && X10FieldDecl_c.shouldInferType(nn, xts)) {
NodeFactory nf = tc.nodeFactory();
if (nn.body() == null) {
Errors.issue(tc.job(), new Errors.CannotInferMethodReturnType(nn.position()));
Position rtpos = nn.returnType().position();
nn = (X10MethodDecl_c) nn.returnType(nf.CanonicalTypeNode(rtpos, xts.unknownType(rtpos)));
}
// Body had no return statement. Set to void.
Type t;
if (!xts.isUnknown(nn.returnType().typeRef().getCached())) {
t = nn.returnType().typeRef().getCached();
}
else {
t = xts.Void();
}
((Ref<Type>) nn.returnType().typeRef()).update(t);
nn = (X10MethodDecl) nn.returnType(nf.CanonicalTypeNode(nn.returnType().position(), t));
}
return nn;
}
private static final Collection<String> TOPICS =
CollectionUtil.list(Reporter.types, Reporter.context);
/** Write the method to an output file. */
public void prettyPrintHeader(CodeWriter w, PrettyPrinter tr) {
w.begin(0);
for (Iterator<AnnotationNode> i = (((X10Ext) this.ext()).annotations()).iterator(); i.hasNext(); ) {
AnnotationNode an = i.next();
an.prettyPrint(w, tr);
w.allowBreak(0, " ");
}
print(flags, w, tr);
w.write("def " + name);
if (!typeParameters.isEmpty()) {
w.write("[");
w.begin(0);
for (Iterator<TypeParamNode> pi = typeParameters.iterator(); pi.hasNext(); ) {
TypeParamNode pn = pi.next();
print(pn, w, tr);
if (pi.hasNext()) {
w.write(",");
w.allowBreak(0, " ");
}
}
w.end();
w.write("]");
}
w.write("(");
w.allowBreak(2, 2, "", 0);
w.begin(0);
for (Iterator<Formal> i = formals.iterator(); i.hasNext(); ) {
Formal f = i.next();
print(f, w, tr);
if (i.hasNext()) {
w.write(",");
w.allowBreak(0, " ");
}
}
w.end();
w.write("):");
w.allowBreak(2, 2, "", 1);
print(returnType, w, tr);
w.end();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(flags.flags().translate());
sb.append(returnType).append(" ");
sb.append(name);
if (typeParameters != null && !typeParameters.isEmpty())
sb.append(typeParameters);
sb.append("(...)");
return sb.toString();
}
}