/*
* 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.List;
import polyglot.ast.Assign;
import polyglot.ast.Expr;
import polyglot.ast.FieldAssign;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Stmt;
import polyglot.ast.Stmt_c;
import polyglot.ast.Term;
import polyglot.ast.TypeNode;
import polyglot.frontend.Job;
import polyglot.types.ClassType;
import polyglot.types.Context;
import polyglot.types.FieldInstance;
import polyglot.types.LocalInstance;
import polyglot.types.Ref;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.types.UnknownType;
import polyglot.util.Position;
import polyglot.util.TypedList;
import polyglot.visit.CFGBuilder;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import polyglot.visit.TypeBuilder;
import x10.Configuration;
import x10.constraint.XFailure;
import x10.types.constraints.ConstraintManager;
import x10.constraint.XVar;
import x10.constraint.XTerm;
import x10.constraint.XVar;
import x10.errors.Errors;
import x10.errors.Errors.IllegalConstraint;
import x10.types.X10ConstructorDef;
import polyglot.types.Context;
import x10.types.X10ClassType;
import x10.types.X10FieldInstance;
import x10.types.X10ParsedClassType;
import polyglot.types.TypeSystem;
import x10.types.XTypeTranslator;
import x10.types.X10ClassDef;
import x10.types.X10TypeEnv;
import x10.types.X10TypeEnv_c;
import x10.types.checker.ThisChecker;
import x10.types.constraints.CConstraint;
import x10.types.constraints.CConstraint;
import x10.types.constraints.ConstraintMaker;
import x10.types.constraints.ConstraintManager;
import x10.types.matcher.Matcher;
/**
* @author vj
* @author igor
*/
public class AssignPropertyCall_c extends Stmt_c implements AssignPropertyCall {
List<Expr> arguments;
List<X10FieldInstance> properties;
/**
* @param pos
* @param arguments
* @param target
* @param name
*/
public AssignPropertyCall_c(Position pos, List<Expr> arguments) {
super(pos);
this.arguments = TypedList.copyAndCheck(arguments, Expr.class, true);
}
public Term firstChild() {
return listChild(arguments, null);
}
/* (non-Javadoc)
* @see polyglot.ast.Term#acceptCFG(polyglot.visit.CFGBuilder, java.util.List)
*/
public <S> List<S> acceptCFG(CFGBuilder v, List<S> succs) {
v.visitCFGList(arguments, this, EXIT);
return succs;
}
public AssignPropertyCall arguments(List<Expr> args) {
if (args == arguments) return this;
AssignPropertyCall_c n = (AssignPropertyCall_c) copy();
n.arguments = TypedList.copyAndCheck(args, Expr.class, true);
return n;
}
public List<Expr> arguments() {
return arguments;
}
public AssignPropertyCall properties(List<X10FieldInstance> properties) {
if (properties == this.properties) return this;
AssignPropertyCall_c n = (AssignPropertyCall_c) copy();
n.properties = TypedList.copyAndCheck(properties, FieldInstance.class, true);
return n;
}
public List<X10FieldInstance> properties() {
return properties;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("property");
sb.append("(");
boolean first = true;
for (Expr e : arguments) {
if (first) {
first = false;
}
else {
sb.append(", ");
}
sb.append(e);
}
sb.append(");");
return sb.toString();
}
public static X10ConstructorDef getConstructorDef(TypeBuilder tb) {
for (; tb != null && tb.inCode(); tb = tb.pop())
if (tb.def() instanceof X10ConstructorDef)
return (X10ConstructorDef) tb.def();
return null;
}
@Override
public Node buildTypes(TypeBuilder tb) {
X10ConstructorDef cd = getConstructorDef(tb);
if (cd != null) {
cd.derivedReturnType(true);
}
return this;
}
@Override
public Node typeCheck(ContextVisitor tc) {
TypeSystem ts = tc.typeSystem();
Context ctx = tc.context();
NodeFactory nf = (NodeFactory) tc.nodeFactory();
Position pos = position();
Job job = tc.job();
X10ConstructorDef thisConstructor = ctx.getCtorIgnoringAsync();
X10ParsedClassType container = (X10ParsedClassType) ctx.currentClass();
if (thisConstructor==null) {
Errors.issue(job,
new Errors.PropertyStatementMayOnlyOccurInBodyOfConstuctor(position()));
} else {
container = (X10ParsedClassType) thisConstructor.asInstance().container();
}
// Now check that the types of each actual argument are subtypes of the corresponding
// property for the class reachable through the constructor.
List<FieldInstance> definedProperties = container.definedProperties();
int pSize = definedProperties.size();
int aSize = arguments.size();
if (aSize != pSize) {
Errors.issue(job,
new Errors.PropertyInitializerMustHaveSameNumberOfArgumentsAsPropertyForClass(position()));
}
checkAssignments(tc, pos, definedProperties, arguments);
if (thisConstructor != null) {
checkReturnType(tc, pos, thisConstructor, definedProperties, arguments);
}
/* We check that "this" is not allowed in CheckEscapingThis.CheckCtor
ThisChecker thisC = (ThisChecker) new ThisChecker(tc.job()).context(tc.context());
for (int i=0; i < aSize; i++) {
Expr arg = arguments.get(i);
thisC.clearError();
visitChild(arg, thisC);
if (thisC.error()) {
Errors.issue(job, new Errors.ThisNotPermittedInPropertyInitializer(arg, position()));
}
} */
List<X10FieldInstance> properties = new ArrayList<X10FieldInstance>();
for (FieldInstance fi : definedProperties) {
properties.add((X10FieldInstance) fi);
}
return this.properties(properties);
}
protected static void checkAssignments(ContextVisitor tc, Position pos,
List<FieldInstance> props, List<Expr> args)
{
TypeSystem xts = tc.typeSystem();
Context cxt = tc.context();
XVar thisVar = tc.context().thisVar();
Type thisType=null;
// Accumulate in curr constraint the bindings {arg1==this.prop1,..argi==this.propi}.
// If argi does not have a name, make up a name, and add the constraint from typei
// into curr, with argi/self.
CConstraint curr = ConstraintManager.getConstraintSystem().makeCConstraint();
for (int i=0; i < args.size() && i < props.size(); ++i) {
Type yType = args.get(i).type();
yType = Types.addConstraint(yType, curr);
Type xType = props.get(i).type();
if (!xts.isSubtype(yType, xType)) {
Errors.issue(tc.job(),
new Errors.TypeOfPropertyIsNotSubtypeOfPropertyType(args.get(i).type(), props, i, pos));
}
XVar symbol = Types.selfVarBinding(yType);
if (symbol==null) {
symbol = ConstraintManager.getConstraintSystem().makeUQV();
CConstraint c = Types.xclause(yType);
curr.addIn(symbol, c);
}
curr.addBinding(ConstraintManager.getConstraintSystem().makeField(thisVar, props.get(i).def()), symbol);
if (! curr.consistent()) {
Errors.issue(tc.job(),
new SemanticException("Inconsistent environment for property assignment call.", pos));
}
}
if (! curr.valid()) {
curr.addIn(cxt.currentConstraint());
cxt.setCurrentConstraint(curr);
}
}
protected void checkReturnType(ContextVisitor tc, Position pos,
X10ConstructorDef thisConstructor, List<FieldInstance> definedProperties,
List<Expr> args)
{
TypeSystem ts = tc.typeSystem();
final Context ctx = tc.context();
if (ts.hasUnknown(Types.getCached(thisConstructor.returnType()))) {
return;
}
Type returnType = Types.getCached(thisConstructor.returnType());
CConstraint result = Types.xclause(returnType);
if (result != null && result.valid())
result = null;
// FIXME: the code below that infers the return type of a ctor is buggy,
// since it infers "this". see XTENLANG-1770
{
Type supType = thisConstructor.supType();
CConstraint known = Types.realX(supType);
known = (known==null ? ConstraintManager.getConstraintSystem().makeCConstraint() : known.copy());
try {
known.addIn(Types.get(thisConstructor.guard()));
XVar thisVar = thisConstructor.thisVar();
for (int i = 0; i < args.size() && i < definedProperties.size(); i++) {
Expr initializer = args.get(i);
Type initType = initializer.type();
final FieldInstance fii = definedProperties.get(i);
XVar prop = (XVar) ts.xtypeTranslator().translate(known.self(), fii);
// Add in the real clause of the initializer with [self.prop/self]
CConstraint c = Types.realX(initType);
if (! c.consistent()) {
Errors.issue(tc.job(),
new Errors.InconsistentContext(initType, pos));
}
if (c != null)
known.addIn(c.instantiateSelf(prop));
try {
XTerm initVar = ts.xtypeTranslator().translate(known, initializer, ctx, false); // it cannot be top-level, because the constraint will be "prop==initVar"
if (initVar != null)
known.addBinding(prop, initVar);
} catch (IllegalConstraint z) {
Errors.issue(tc.job(), z);
}
}
X10ConstructorCall_c.checkSuperType(tc,supType, position);
if (thisConstructor.inferReturnType()) {
// Set the return type of the enclosing constructor to be this inferred type.
Type inferredResultType = Types.addConstraint(Types.baseType(returnType), known);
inferredResultType = Types.removeLocals( tc.context(), inferredResultType);
if (! Types.consistent(inferredResultType)) {
Errors.issue(tc.job(),
new Errors.InconsistentType(inferredResultType, pos));
}
Ref <? extends Type> r = thisConstructor.returnType();
((Ref<Type>) r).update(inferredResultType);
}
// bind this==self; sup clause may constrain this.
if (thisVar != null) {
known = known.instantiateSelf(thisVar);
// known.addSelfBinding(thisVar);
// known.setThisVar(thisVar);
}
final CConstraint k = known;
if (result != null) {
final CConstraint rr = result.instantiateSelf(thisVar);
if (!k.entails(rr, new ConstraintMaker() {
public CConstraint make() throws XFailure {
return ctx.constraintProjection(k, rr);
}}))
Errors.issue(tc.job(),
new Errors.ConstructorReturnTypeNotEntailed(known, result, pos));
}
// Check that the class invariant is satisfied.
X10ClassType ctype = (X10ClassType) Types.getClassType(Types.get(thisConstructor.container()),ts,ctx);
CConstraint _inv = Types.get(ctype.x10Def().classInvariant()).copy();
X10TypeEnv env = ts.env(tc.context());
boolean isThis = true; // because in the class invariant we use this (and not self)
X10TypeEnv_c env_c = (X10TypeEnv_c) env;
_inv = X10TypeEnv_c.ifNull(env_c.expandProperty(isThis,ctype,_inv),_inv);
final CConstraint inv = _inv;
if (!k.entails(inv, new ConstraintMaker() {
public CConstraint make() throws XFailure {
return ctx.constraintProjection(k, inv);
}}))
Errors.issue(tc.job(),
new Errors.InvariantNotEntailed(known, inv, pos));
// Check that every super interface is entailed.
for (Type intfc : ctype.interfaces()) {
CConstraint cc = Types.realX(intfc);
cc = cc.instantiateSelf(thisVar); // for some reason, the invariant has "self" instead of this, so I fix it here.
if (thisVar != null) {
XVar intfcThisVar = ((X10ClassType) intfc.toClass()).x10Def().thisVar();
cc = cc.substitute(thisVar, intfcThisVar);
}
cc = X10TypeEnv_c.ifNull(env_c.expandProperty(true,ctype,cc),cc);
final CConstraint ccc=cc;
if (!k.entails(cc, new ConstraintMaker() {
public CConstraint make() throws XFailure {
return ctx.constraintProjection(k, ccc);
}}))
Errors.issue(tc.job(),
new Errors.InterfaceInvariantNotEntailed(known, intfc, cc, pos));
}
// Install the known constraint in the context.
CConstraint c = ctx.currentConstraint();
known.addIn(c);
ctx.setCurrentConstraint(known);
}
catch (XFailure e) {
Errors.issue(tc.job(), new Errors.GeneralError(e.getMessage(), position), this);
}
}
}
/** Visit the children of the statement. */
public Node visitChildren(NodeVisitor v) {
List<Expr> args = visitList(this.arguments, v);
return arguments(args);
}
}