/*
* 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.Iterator;
import polyglot.types.ClassDef;
import polyglot.types.ClassType;
import polyglot.types.ConstructorDef;
import polyglot.types.ConstructorInstance;
import polyglot.types.Context;
import polyglot.types.FieldDef;
import polyglot.types.MethodDef;
import polyglot.types.ProcedureDef;
import polyglot.types.Ref;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeObject;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import java.util.Collection;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.List;
import polyglot.util.CodeWriter;
import polyglot.util.CollectionUtil;
import x10.util.CollectionFactory;
import polyglot.util.Position;
import polyglot.ast.ClassBody;
import polyglot.ast.ClassMember;
import polyglot.ast.Formal;
import polyglot.ast.Node;
import polyglot.ast.MethodDecl;
import polyglot.ast.MethodDecl_c;
import polyglot.ast.Formal_c;
import polyglot.ast.Term;
import polyglot.types.ClassType_c;
import polyglot.util.TypedList;
import polyglot.visit.CFGBuilder;
import polyglot.visit.ContextVisitor;
import polyglot.visit.ExceptionChecker;
import polyglot.visit.NodeVisitor;
import polyglot.visit.PrettyPrinter;
import polyglot.ast.ClassBody_c;
import x10.constraint.XVar;
import x10.errors.Errors;
import x10.types.ClosureDef;
import x10.types.MacroType;
import x10.types.ParameterType;
import x10.types.TypeDef;
import x10.types.TypeParamSubst;
import x10.types.X10ClassDef;
import x10.types.X10ClassType;
import x10.types.X10ConstructorDef;
import polyglot.types.Context;
import x10.types.X10MethodDef;
import x10.types.MethodInstance;
import x10.types.X10ProcedureDef;
import polyglot.types.TypeSystem;
import x10.types.constraints.TypeConstraint;
import x10.types.constraints.XConstrainedTerm;
public class X10ClassBody_c extends ClassBody_c {
protected List<ClassMember> members;
public X10ClassBody_c(Position pos, java.util.List<ClassMember> members) {
super(pos, members);
assert(members != null);
this.members = TypedList.copyAndCheck(members, ClassMember.class, true);
}
public Node conformanceCheck(ContextVisitor tc) {
duplicateTypeDefCheck(tc);
checkMethodCompatibility(tc);
return superConformanceCheck(tc);
}
private Node superConformanceCheck(ContextVisitor tc) {
duplicateFieldCheck(tc);
duplicateConstructorCheck(tc);
duplicateMethodCheck(tc);
duplicateMemberClassCheck(tc);
return this;
}
private void getInheritedVirtualMethods(X10ClassType ct, List<MethodInstance> methods) {
for (MethodInstance mi : ct.methods()) {
if (mi.flags().isStatic()) continue;
methods.add(mi);
}
Type sup = ct.superClass();
if (sup instanceof X10ClassType) {
getInheritedVirtualMethods((X10ClassType) sup, methods);
}
for (Type t : ct.interfaces()) {
if (t instanceof X10ClassType) {
getInheritedVirtualMethods((X10ClassType) t, methods);
}
}
}
private void checkMethodCompatibility(ContextVisitor tc) {
TypeSystem ts = (TypeSystem) tc.typeSystem();
ClassDef cd = tc.context().currentClassDef();
List<MethodInstance> l = new ArrayList<MethodInstance>();
getInheritedVirtualMethods((X10ClassType) cd.asType(), l);
// Remove overridden methods.
for (ListIterator<MethodInstance> i = l.listIterator(); i.hasNext(); ) {
MethodInstance mi = i.next();
MethodInstance mj = ts.findImplementingMethod(cd.asType(), mi, true, tc.context());
if (mj != null && mj.def() != mi.def())
i.remove();
}
// It is a static error if:
// * mi overrides mk
// * mj overrides ml
// * ml != mk
// * mk and ml have compatible signatures
// * mk and ml are parameterized
for (int i = 0; i < l.size(); i++) {
MethodInstance mi = (MethodInstance) l.get(i);
for (int j = i + 1; j < l.size(); j++) {
MethodInstance mj = (MethodInstance) l.get(j);
if (mi.def() == mj.def())
continue;
if (! mi.name().equals(mj.name()))
continue;
for (MethodInstance mk : mi.implemented(tc.context())) {
if (mk.def() == mi.def()) continue;
for (MethodInstance ml : mj.implemented(tc.context())) {
if (ml.def() == mj.def()) continue;
if (ml.def() == mk.def()) continue;
if (hasCompatibleArguments(mk.x10Def(), ml.x10Def(), tc.context()) && isParameterized(mk.x10Def()) && isParameterized(ml.x10Def())) {
Errors.issue(tc.job(),
new Errors.MethodsOverrideWithCompatibleSignatures(mj, mi, mi.position()),
this);
}
}
}
}
}
return;
}
@Override
protected void duplicateConstructorCheck(ContextVisitor tc) {
ClassDef type = tc.context().currentClassDef();
TypeSystem ts = tc.typeSystem();
ArrayList<ConstructorDef> l = new ArrayList<ConstructorDef>(type.constructors());
for (int i = 0; i < l.size(); i++) {
X10ConstructorDef ci = (X10ConstructorDef) l.get(i);
for (int j = i+1; j < l.size(); j++) {
X10ConstructorDef cj = (X10ConstructorDef) l.get(j);
if (hasCompatibleArguments(ci, cj, tc.context())) {
Errors.issue(tc.job(),
new Errors.DuplicateConstructor(cj, ci, cj.position()));
}
}
}
}
@Override
protected void duplicateMethodCheck(ContextVisitor tc) {
ClassDef type = tc.context().currentClassDef();
TypeSystem ts = tc.typeSystem();
ArrayList<MethodDef> l = new ArrayList<MethodDef>(type.methods());
for (int i = 0; i < l.size(); i++) {
X10MethodDef mi = (X10MethodDef) l.get(i);
for (int j = i+1; j < l.size(); j++) {
X10MethodDef mj = (X10MethodDef) l.get(j);
if (mi.name().equals(mj.name()) && hasCompatibleArguments(mi, mj, tc.context())) {
Errors.issue(tc.job(),
new Errors.DuplicateMethod(mj, mi, mj.position()));
}
}
}
}
public static boolean isParameterized(X10ProcedureDef p1) {
TypeSystem ts = (TypeSystem) p1.typeSystem();
for (int i = 0; i < p1.formalTypes().size(); i++) {
Type t1 = Types.get(p1.formalTypes().get(i));
// Erase types and expand formals.
t1 = Types.baseType(t1);
// Parameters conflict with everything
if (t1 instanceof ParameterType)
return true;
}
return false;
}
public static boolean hasCompatibleArguments(List<Type> l1, List<Type> l2, Context context) {
TypeConstraint tc = context.currentTypeConstraint().copy();
TypeSystem ts = context.typeSystem();
for (int i = 0; i < l1.size(); i++) {
Type t1 = l1.get(i);
Type t2 = l2.get(i);
if (!tc.unify(t1,t2,ts) || !tc.consistent(context))
return false;
}
return true;
}
public static boolean hasCompatibleArguments(X10ProcedureDef p1, X10ProcedureDef p2, Context context) {
List<ParameterType> tps1 = p1.typeParameters();
List<ParameterType> tps2 = p2.typeParameters();
if (tps1.size() != tps2.size())
return false;
final int size = p1.formalTypes().size();
if (size != p2.formalTypes().size())
return false;
TypeParamSubst subst = null;
if (!tps1.isEmpty()) {
subst = new TypeParamSubst(p1.typeSystem(), tps1, tps2);
}
ArrayList<Type> l1 = new ArrayList<Type>(size);
ArrayList<Type> l2 = new ArrayList<Type>(size);
for (int i = 0; i < size; i++) {
Type t1 = Types.get(p1.formalTypes().get(i));
Type t2 = Types.get(p2.formalTypes().get(i));
l1.add(t1);
if (subst != null) {
t2 = subst.reinstantiate(t2);
}
l2.add(t2);
}
return hasCompatibleArguments(l1,l2,context);
}
protected void duplicateTypeDefCheck(ContextVisitor tc) {
X10ClassDef type = (X10ClassDef) tc.context().currentClassDef();
TypeSystem ts = tc.typeSystem();
ArrayList<TypeDef> l = new ArrayList<TypeDef>(type.memberTypes());
for (int i = 0; i < l.size(); i++) {
TypeDef mi = l.get(i);
for (int j = i + 1; j < l.size(); j++) {
TypeDef mj = l.get(j);
if (mi.name().equals(mj.name()) &&
mi.typeParameters().size()==mj.typeParameters().size() &&
hasCompatibleArguments(mi, mj, tc.context())) {
Errors.issue(tc.job(),
new Errors.DuplicateTypeDefinition(mj, mi, mj.position()),
this);
}
}
if (mi.formalTypes().isEmpty()) {
for (Ref<? extends Type> tref : type.memberClasses()) {
Type t = Types.get(tref);
t = Types.baseType(t);
if (t instanceof ClassType) {
ClassType ct = (ClassType) t;
if (ct.name().equals(mi.name()) &&
ct.def().typeParameters().size()==mi.typeParameters().size()) {
Errors.issue(tc.job(),
new Errors.TypeDefinitionSameNameAsMemberClass(mi, ct, mi.position()),
this);
}
}
}
}
}
}
public List<ClassMember> members() {
return this.members;
}
public ClassBody members(List<ClassMember> members) {
X10ClassBody_c n = (X10ClassBody_c) copy();
n.members = TypedList.copyAndCheck(members, ClassMember.class, true);
return n;
}
public ClassBody addMember(ClassMember member) {
X10ClassBody_c n = (X10ClassBody_c) copy();
List<ClassMember> l = new ArrayList<ClassMember>(this.members.size() + 1);
l.addAll(this.members);
l.add(member);
n.members = TypedList.copyAndCheck(l, ClassMember.class, true);
return n;
}
protected ClassBody_c reconstruct(List<ClassMember> members) {
if (! CollectionUtil.<ClassMember>allEqual(members, this.members)) {
X10ClassBody_c n = (X10ClassBody_c) copy();
n.members = TypedList.copyAndCheck(members,
ClassMember.class, true);
return n;
}
return this;
}
public Node visitChildren(NodeVisitor v) {
List<ClassMember> members = visitList(this.members, v);
return reconstruct(members);
}
public Node disambiguate(ContextVisitor ar) {
return this;
}
public String toString() {
return "{ ... }";
}
protected void duplicateFieldCheck(ContextVisitor tc) {
ClassDef type = tc.context().currentClassDef();
ArrayList<FieldDef> l = new ArrayList<FieldDef>(type.fields());
for (int i = 0; i < l.size(); i++) {
FieldDef fi = (FieldDef) l.get(i);
for (int j = i+1; j < l.size(); j++) {
FieldDef fj = (FieldDef) l.get(j);
if (fi.name().equals(fj.name())) {
reportDuplicate(fj,tc);
}
}
}
}
protected void duplicateMemberClassCheck(ContextVisitor tc) {
ClassDef type = tc.context().currentClassDef();
ArrayList<Ref<? extends Type>> l = new ArrayList<Ref<? extends Type>>(type.memberClasses());
for (int i = 0; i < l.size(); i++) {
Type mi = l.get(i).get();
for (int j = i+1; j < l.size(); j++) {
Type mj = l.get(j).get();
if (mi.name().equals(mj.name())) {
reportDuplicate(mj,tc);
}
}
}
}
private void reportDuplicate(TypeObject def, ContextVisitor tc) {
new Errors.DuplicateMember(def).issue(tc.job());
}
protected boolean isSameMethod(TypeSystem ts,
MethodInstance mi, MethodInstance mj, Context context) {
return mi.isSameMethod(mj, context);
}
public NodeVisitor exceptionCheckEnter(ExceptionChecker ec) {
return ec.push();
}
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
if (!members.isEmpty()) {
w.newline(4);
w.begin(0);
ClassMember prev = null;
for (Iterator<ClassMember> i = members.iterator(); i.hasNext(); ) {
ClassMember member = i.next();
if ((member instanceof polyglot.ast.CodeDecl) ||
(prev instanceof polyglot.ast.CodeDecl)) {
w.newline(0);
}
prev = member;
printBlock(member, w, tr);
if (i.hasNext()) {
w.newline(0);
}
}
w.end();
w.newline(0);
}
}
/**
* Return the first (sub)term performed when evaluating this
* term.
*/
public Term firstChild() {
// Do _not_ visit class members.
return null;
}
/**
* Visit this term in evaluation order.
*/
public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) {
return succs;
}
}