/*
* 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.Collections;
import java.util.LinkedList;
import java.util.List;
import polyglot.ast.Binary;
import polyglot.ast.Expr;
import polyglot.ast.Formal;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Node_c;
import polyglot.ast.TypeCheckFragmentGoal;
import polyglot.frontend.SetResolverGoal;
import polyglot.types.Context;
import polyglot.types.LazyRef;
import polyglot.types.Ref;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.Types;
import polyglot.util.CodeWriter;
import polyglot.util.Position;
import polyglot.util.TypedList;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import polyglot.visit.PrettyPrinter;
import polyglot.visit.TypeBuilder;
import polyglot.visit.TypeCheckPreparer;
import polyglot.visit.TypeChecker;
import x10.errors.Errors;
import polyglot.types.Context;
import polyglot.types.TypeSystem;
import polyglot.types.LazyRef_c;
import x10.types.constraints.CConstraint;
import x10.types.constraints.ConstraintManager;
import x10.types.constraints.TypeConstraint;
import x10.visit.X10TypeChecker;
/** An immutable representation of a dependent type constraint.
* The corresponding syntax is [T](e){x: T; c}
* This node is created for dependent X10 types.
* @author vj Jan 9, 2005
*
*/
public class DepParameterExpr_c extends Node_c implements DepParameterExpr {
protected List<Formal> formals;
/** The boolean condition of a parameteric type.
* Maybe null.
*/
protected List<Expr> condition;
private Ref<CConstraint> valueConstraint;
private Ref<TypeConstraint> typeConstraint;
/**
* @param pos
*/
public DepParameterExpr_c(Position pos, List<Formal> formals, List<Expr> e) {
super(pos);
if (formals == null)
this.formals = Collections.<Formal>emptyList();
else
this.formals = TypedList.copyAndCheck(formals, Formal.class, true);
assert e != null;
this.condition = TypedList.copyAndCheck(e, Expr.class, true);
}
public DepParameterExpr_c(Position pos, List<Expr> e) {
this(pos, null, e);
}
@Override
public Context enterChildScope(Node child, Context c) {
Context xc=c;
if (child instanceof Formal) {
// pop the dep type
c = c.pop();
}
else if (! formals.isEmpty() && child == condition) {
// Add the formals to the scope but outside the dep type scope.
Ref<? extends Type> t = xc.depTypeRef();
if (t != null) {
c = c.pop().pushBlock();
// Push formals so they're in scope in the types of the other formals.
for (Formal f : formals) {
f.addDecls(c);
}
c = c.pushDepType(t);
}
}
return super.enterChildScope(child, c);
}
public Ref<CConstraint> valueConstraint() {
return valueConstraint;
}
public Ref<TypeConstraint> typeConstraint() {
return typeConstraint;
}
public DepParameterExpr typeConstraint(Ref<TypeConstraint> c) {
DepParameterExpr_c n = (DepParameterExpr_c) copy();
n.typeConstraint = c;
return n;
}
public List<Formal> formals() {
return this.formals;
}
public DepParameterExpr formals( List<Formal> formals ) {
DepParameterExpr_c n = (DepParameterExpr_c) copy();
n.formals = TypedList.copyAndCheck(formals, Formal.class, true);
return n;
}
public List<Expr> condition() {
return this.condition;
}
public DepParameterExpr condition( List<Expr> condition) {
DepParameterExpr_c n = (DepParameterExpr_c) copy();
n.condition = condition;
return n;
}
protected DepParameterExpr reconstruct( List<Formal> formals, List<Expr> condition ) {
if (formals == this.formals && condition == this.condition)
return this;
DepParameterExpr_c n = (DepParameterExpr_c) copy();
n.condition = condition;
n.formals = formals;
return n;
}
@Override
public Node visitChildren(NodeVisitor v) {
List<Formal> formals = visitList(this.formals, v);
List<Expr> condition = visitList(this.condition, v);
return reconstruct(formals, condition);
}
public Node buildTypes(TypeBuilder tb) {
DepParameterExpr_c n = (DepParameterExpr_c) copy();
n.valueConstraint = Types.<CConstraint>lazyRef(ConstraintManager.getConstraintSystem().makeCConstraint(), new SetResolverGoal(tb.job()).intern(tb.job().extensionInfo().scheduler()));
n.typeConstraint = Types.<TypeConstraint>lazyRef(new TypeConstraint(), new SetResolverGoal(tb.job()).intern(tb.job().extensionInfo().scheduler()));
return n;
}
// This is a challenge because DepParam is used both in methods and closures
// (for closures we shouldn't create a resolver, but for methods we should but without freezing).
// C:\cygwin\home\Yoav\intellij\sourceforge\x10.tests\examples\Issues\XTENLANG_2330.x10
// C:\cygwin\home\Yoav\intellij\sourceforge\x10.tests\examples\Constructs\Place\At_MustFailCompile.x10
// C:\cygwin\home\Yoav\intellij\sourceforge\x10.tests\examples\Misc\x10\frontend\tests\FrontEndTests_MustFailCompile.x10
public void setResolver(Node parent, final TypeCheckPreparer v) {
TypeChecker tc = new X10TypeChecker(v.job(), v.typeSystem(), v.nodeFactory(), v.getMemo());
tc = (TypeChecker) tc.context(v.context().freeze());
{
LazyRef<CConstraint> xr = (LazyRef<CConstraint>) valueConstraint;
assert xr != null : "setResolver pass run before buildTypes for " + this;
xr.setResolver(new TypeCheckFragmentGoal<CConstraint>(parent, this, tc, xr, false));
}
{
LazyRef<TypeConstraint> xr = (LazyRef<TypeConstraint>) typeConstraint;
assert xr != null : "setResolver pass run before buildTypes for " + this;
xr.setResolver(new TypeCheckFragmentGoal<TypeConstraint>(parent, this, tc, xr, false));
}
}
@Override
public Node disambiguate(ContextVisitor ar) {
DepParameterExpr_c n = this;
if (((Context) ar.context()).inAnnotation() && condition == null) {
return n.condition(Collections.<Expr>emptyList());
}
return n;
}
/** Type check the statement.
*/
@Override
public Node typeCheck(ContextVisitor tc) {
TypeSystem ts = (TypeSystem) tc.typeSystem();
//Report.report(1, "DepParameterExpr: Typechecking " + this + this.getClass() + " " + condition);
if (condition == null) {
return this;
// throw new SemanticError("The condition of a dependent type clause must be non-empty.",
// position());
}
Expr values = null;
Expr types = null;
LinkedList<Expr> cond = new LinkedList<Expr>();
cond.addAll(condition);
while (! cond.isEmpty()) {
Expr e = cond.removeFirst();
Type t = e.type();
if (! t.isBoolean())
Errors.issue(tc.job(),
new Errors.TypeConstraintMustBeBoolean(e, t, position()));
if (e instanceof Binary) {
Binary b = (Binary) e;
if (b.operator() == Binary.COND_AND || b.operator() == Binary.BIT_AND) {
cond.addFirst(b.left());
cond.addFirst(b.right());
continue;
}
}
NodeFactory nf = tc.nodeFactory();
if (Types.isTypeConstraintExpression(e)) {
if (types == null)
types = e;
else
types = nf.Binary(new Position(types.position(), e.position()), types, Binary.COND_AND, e).type(ts.Boolean());
}
else {
if (values == null)
values = e;
else
values = nf.Binary(new Position(values.position(), e.position()), values, Binary.COND_AND, e).type(ts.Boolean());
}
}
try {
CConstraint xvc = ts.xtypeTranslator().constraint(formals, values, (Context) tc.context());
((LazyRef<CConstraint>) valueConstraint).update(xvc);
} catch (SemanticException e) {
Errors.issue(tc.job(), e);
}
try {
TypeConstraint xtc = ts.xtypeTranslator().typeConstraint(formals, types, (Context) tc.context());
((LazyRef<TypeConstraint>) typeConstraint).update(xtc);
} catch (SemanticException e) {
Errors.issue(tc.job(), e);
}
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("{");
String sep = "";
for (Formal f : formals) {
sb.append(sep);
sep = ", ";
sb.append(f.toString());
}
if (! sep.equals(""))
sep = "; ";
if (condition != null) {
sb.append(sep);
sb.append(condition.toString());
}
sb.append("}");
return sb.toString();
}
/** Write the statement to an output file. */
@Override
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
w.write("{");
String sep = "";
for (Formal f : formals) {
w.write(sep);
sep = ", ";
printBlock( f, w, tr);
}
if (! sep.equals(""))
sep = "; ";
for (Expr e: condition) {
w.write(sep);
printBlock( e, w, tr);
sep = ", ";
}
w.write("}");
}
}