/*
* 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
*
* This file was originally derived from the Polyglot extensible compiler framework.
*
* (C) Copyright 2000-2007 Polyglot project group, Cornell University
* (C) Copyright IBM Corporation 2007-2012.
*/
package polyglot.ast;
import java.util.*;
import polyglot.frontend.*;
import polyglot.types.*;
import polyglot.types.VarDef_c.ConstantValue;
import polyglot.util.*;
import polyglot.visit.*;
import x10.errors.Errors;
/**
* A <code>FieldDecl</code> is an immutable representation of the declaration
* of a field of a class.
*/
public abstract class FieldDecl_c extends Term_c implements FieldDecl {
protected FlagsNode flags;
protected TypeNode type;
protected Id name;
protected Expr init;
protected FieldDef fi;
protected InitializerDef ii;
public FieldDecl_c(Position pos, FlagsNode flags, TypeNode type,
Id name, Expr init)
{
super(pos);
assert(flags != null && type != null && name != null); // init may be null
this.flags = flags;
this.type = type;
this.name = name;
this.init = init;
}
public List<Def> defs() {
if (init == null)
return Collections.<Def>singletonList(fi);
else {
return CollectionUtil.<Def>list(fi, ii);
}
}
public MemberDef memberDef() {
return fi;
}
public VarDef varDef() {
return fi;
}
public CodeDef codeDef() {
return ii;
}
/** Get the initializer instance of the initializer. */
public InitializerDef initializerDef() {
return ii;
}
/** Set the initializer instance of the initializer. */
public FieldDecl initializerDef(InitializerDef ii) {
if (ii == this.ii) return this;
FieldDecl_c n = (FieldDecl_c) copy();
n.ii = ii;
return n;
}
/** Get the type of the declaration. */
public Type declType() {
return type.type();
}
/** Get the flags of the declaration. */
public FlagsNode flags() {
return flags;
}
/** Set the flags of the declaration. */
public FieldDecl flags(FlagsNode flags) {
FieldDecl_c n = (FieldDecl_c) copy();
n.flags = flags;
return n;
}
/** Get the type node of the declaration. */
public TypeNode type() {
return type;
}
/** Set the type of the declaration. */
public FieldDecl type(TypeNode type) {
FieldDecl_c n = (FieldDecl_c) copy();
n.type = type;
return n;
}
/** Get the name of the declaration. */
public Id name() {
return name;
}
/** Set the name of the declaration. */
public FieldDecl name(Id name) {
FieldDecl_c n = (FieldDecl_c) copy();
n.name = name;
return n;
}
public Term codeBody() {
return init;
}
/** Get the initializer of the declaration. */
public Expr init() {
return init;
}
/** Set the initializer of the declaration. */
public FieldDecl init(Expr init) {
FieldDecl_c n = (FieldDecl_c) copy();
n.init = init;
return n;
}
/** Set the field instance of the declaration. */
public FieldDecl fieldDef(FieldDef fi) {
if (fi == this.fi) return this;
FieldDecl_c n = (FieldDecl_c) copy();
n.fi = fi;
return n;
}
/** Get the field instance of the declaration. */
public FieldDef fieldDef() {
return fi;
}
/** Reconstruct the declaration. */
protected FieldDecl_c reconstruct(FlagsNode flags, TypeNode type, Id name, Expr init) {
if (this.flags != flags || this.type != type || this.name != name || this.init != init) {
FieldDecl_c n = (FieldDecl_c) copy();
n.flags = flags;
n.type = type;
n.name = name;
n.init = init;
return n;
}
return this;
}
/** Visit the children of the declaration. */
public Node visitChildren(NodeVisitor v) {
FieldDecl_c n = (FieldDecl_c) visitSignature(v);
Expr init = (Expr) n.visitChild(n.init, v);
return init == n.init ? n : n.init(init);
}
public Node buildTypesOverride(TypeBuilder tb) {
TypeSystem ts = tb.typeSystem();
ClassDef ct = tb.currentClass();
assert ct != null;
Flags flags = this.flags.flags();
if (ct.flags().isInterface()) {
flags = flags.Public().Static().Final();
}
FieldDef fi = createFieldDef(ts, ct, flags);
ct.addField(fi);
TypeBuilder tbChk = tb.pushDef(fi);
InitializerDef ii = null;
if (init != null) {
Flags iflags = flags.isStatic() ? Flags.STATIC : Flags.NONE;
ii = createInitializerDef(ts, ct, iflags);
fi.setInitializer(ii);
tbChk = tbChk.pushCode(ii);
}
final TypeBuilder tbx = tb;
final FieldDef mix = fi;
FieldDecl_c n = (FieldDecl_c) this.visitSignature(new NodeVisitor() {
public Node override(Node n) {
return FieldDecl_c.this.visitChild(n, tbx.pushDef(mix));
}
});
fi.setType(n.type().typeRef());
Expr init = (Expr) n.visitChild(n.init, tbChk);
n = (FieldDecl_c) n.init(init);
n = (FieldDecl_c) n.fieldDef(fi);
if (ii != null) {
n = (FieldDecl_c) n.initializerDef(ii);
}
n = (FieldDecl_c) n.flags(n.flags.flags(flags));
return n;
}
protected InitializerDef createInitializerDef(TypeSystem ts, ClassDef ct, Flags iflags) {
InitializerDef ii;
ii = ts.initializerDef(init.position(), Types.<ClassType>ref(ct.asType()), iflags);
return ii;
}
protected abstract FieldDef createFieldDef(TypeSystem ts, ClassDef ct, Flags flags);
public Context enterScope(Context c) {
if (ii != null) {
return c.pushCode(ii);
}
return c;
}
@Override
public abstract void setResolver(final Node parent, TypeCheckPreparer v);
public Node checkConstants(ContextVisitor tc) {
if (init == null || ! init.isConstant() || ! fi.flags().isFinal()) {
fi.setNotConstant();
} else {
x10.types.constants.ConstantValue cv = init.constantValue();
if (cv instanceof x10.types.constants.ClosureValue) {
// Hacky workaround.
// The issue is that the code is structured to cache the ConstantValue
// (which contains a Closure_c AST) in the FieldInstance. This AST
// is not properly visited by various Jobs, and therefore when we pull
// the Closure_c out of the ConstantValue and attempt to inline it
// later in compilation, we end up with a closure body that isn't legal
// because various transformations haven't been performaed on it.
fi.setNotConstant();
} else {
fi.setConstantValue(init.constantValue());
}
}
return this;
}
public Node visitSignature(NodeVisitor v) {
FlagsNode flags = (FlagsNode) this.visitChild(this.flags, v);
TypeNode type = (TypeNode) this.visitChild(this.type, v);
Id name = (Id) this.visitChild(this.name, v);
return reconstruct(flags, type, name, this.init);
}
public Node typeCheckBody(Node parent, TypeChecker tc, TypeChecker childtc) {
FieldDecl_c n = this;
Expr init = (Expr) n.visitChild(n.init, childtc);
n = (FieldDecl_c) n.init(init);
return n.checkConstants(tc);
}
public abstract Node typeCheck(ContextVisitor tc);
public Node conformanceCheck(ContextVisitor tc) {
TypeSystem ts = tc.typeSystem();
// Get the fi flags, not the node flags since the fi flags
// account for being nested within an interface.
Flags flags = fi.flags();
try {
ts.checkFieldFlags(flags);
}
catch (SemanticException e) {
Errors.issue(tc.job(), e, this);
}
Type fcontainer = Types.get(fieldDef().container());
if (fcontainer.isClass()) {
ClassType container = fcontainer.toClass();
if (container.flags().isInterface()) {
if (flags.isProtected() || flags.isPrivate()) {
Errors.issue(tc.job(),
new Errors.InterfaceMembersMustBePublic(position()));
}
}
// check that inner classes do not declare static fields, unless they
// are compile-time constants
if (flags.isStatic() &&
(container.isInnerClass() || container.isLocal() || container.isAnonymous())) {
// it's a static field in an inner class.
if (!flags.isFinal() || init == null || !init.isConstant()) {
Errors.issue(tc.job(),
new Errors.InnerClassCannotDeclareStaticFields(fieldDef(), container, position()));
}
}
}
return this;
}
public NodeVisitor exceptionCheckEnter(ExceptionChecker ec) {
return ec.push(new ExceptionChecker.CodeTypeReporter("A field initializer"));
}
public Term firstChild() {
return type;
}
public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) {
if (init != null) {
v.visitCFG(type, init, ENTRY);
v.visitCFG(init, this, EXIT);
} else {
v.visitCFG(type, this, EXIT);
}
return succs;
}
public String toString() {
return flags.flags().translate() + type + " " + name +
(init != null ? " = " + init : "");
}
public abstract void prettyPrint(CodeWriter w, PrettyPrinter tr);
public void dump(CodeWriter w) {
super.dump(w);
if (fi != null) {
w.allowBreak(4, " ");
w.begin(0);
w.write("(instance " + fi + ")");
w.end();
}
w.allowBreak(4, " ");
w.begin(0);
w.write("(name " + name + ")");
w.end();
}
}