/*
* 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.Collections;
import java.util.List;
import java.util.Map;
import polyglot.ast.AmbExpr;
import polyglot.ast.AmbTypeNode_c;
import polyglot.ast.Binary;
import polyglot.ast.Binary.Operator;
import polyglot.ast.CanonicalTypeNode;
import polyglot.ast.Disamb;
import polyglot.ast.Expr;
import polyglot.ast.Field;
import polyglot.ast.Id;
import polyglot.ast.Local;
import polyglot.ast.NamedVariable;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.PackageNode;
import polyglot.ast.Prefix;
import polyglot.ast.TypeNode;
import polyglot.ast.Unary;
import polyglot.frontend.Globals;
import polyglot.frontend.Goal;
import polyglot.frontend.Job;
import polyglot.types.Context;
import polyglot.types.Flags;
import polyglot.types.LazyRef;
import polyglot.types.LocalDef;
import polyglot.types.Name;
import polyglot.types.QName;
import polyglot.types.Ref;
import polyglot.types.Resolver;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.util.CodeWriter;
import polyglot.util.CollectionUtil; import x10.util.CollectionFactory;
import polyglot.util.CodedErrorInfo;
import polyglot.util.InternalCompilerError;
import polyglot.util.Position;
import polyglot.util.TypedList;
import polyglot.visit.ContextVisitor;
import polyglot.visit.ExceptionChecker;
import polyglot.visit.NodeVisitor;
import polyglot.visit.PrettyPrinter;
import polyglot.visit.TypeCheckPreparer;
import polyglot.visit.TypeChecker;
import x10.constraint.XTerm;
import x10.types.constraints.ConstraintManager;
import x10.constraint.XVar;
import x10.errors.Errors;
import x10.errors.Errors.IllegalConstraint;
import x10.errors.Warnings;
import x10.extension.X10Del;
import x10.extension.X10Del_c;
import x10.types.MacroType;
import x10.types.ParameterType;
import x10.types.TypeDef_c;
import x10.types.X10ClassType;
import polyglot.types.Context;
import x10.types.X10Def;
import x10.types.X10ParsedClassType;
import polyglot.types.TypeSystem;
import polyglot.types.TypeSystem_c;
import polyglot.types.NoClassException;
import x10.types.X10Use;
import x10.types.TypeDefMatcher;
import x10.visit.X10TypeChecker;
import x10.visit.ChangePositionVisitor;
import x10.types.checker.VarChecker;
public class AmbMacroTypeNode_c extends X10AmbTypeNode_c implements AmbMacroTypeNode, AddFlags {
protected List<TypeNode> typeArgs;
protected List<Expr> args;
public AmbMacroTypeNode_c(Position pos, Prefix prefix, Id name, List<TypeNode> typeArgs, List<Expr> args) {
super(pos, prefix, name);
this.typeArgs = TypedList.copyAndCheck(typeArgs, TypeNode.class, true);
this.args = TypedList.copyAndCheck(args, Expr.class, true);
}
public AmbMacroTypeNode prefix(Prefix prefix) {
if (prefix == this.prefix) return this;
return (AmbMacroTypeNode) super.prefix(prefix);
}
public AmbMacroTypeNode name(Id name) {
if (name == this.name) return this;
return (AmbMacroTypeNode) super.name(name);
}
public List<TypeNode> typeArgs() {
return this.typeArgs;
}
public AmbMacroTypeNode typeArgs(List<TypeNode> typeArgs) {
AmbMacroTypeNode_c n = (AmbMacroTypeNode_c) copy();
n.typeArgs = TypedList.copyAndCheck(typeArgs, TypeNode.class, true);
return n;
}
public List<Expr> args() {
return this.args;
}
public AmbMacroTypeNode args(List<Expr> args) {
AmbMacroTypeNode_c n = (AmbMacroTypeNode_c) copy();
n.args = TypedList.copyAndCheck(args, Expr.class, true);
return n;
}
public AmbMacroTypeNode reconstruct(Prefix prefix, Id name, List<TypeNode> typeArgs, List<Expr> args) {
if (prefix == this.prefix && name == this.name && typeArgs == this.typeArgs && args == this.args) return this;
AmbMacroTypeNode_c n = (AmbMacroTypeNode_c) copy();
n.prefix = prefix;
n.name = name;
n.typeArgs = typeArgs;
n.args = args;
return n;
}
public Node visitChildren(NodeVisitor v) {
Prefix prefix = (Prefix) visitChild(this.prefix, v);
Id name = (Id) visitChild(this.name, v);
List<TypeNode> typeArgs = visitList(this.typeArgs, v);
List<Expr> args = visitList(this.args, v);
return reconstruct(prefix, name, typeArgs, args);
}
public String toString() {
return super.toString() + (typeArgs.isEmpty() ? "" : typeArgs) + (args.isEmpty() ? "" : "(" + CollectionUtil.listToString(args) + ")");
}
protected TypeNode disambiguateAnnotation(ContextVisitor tc) throws SemanticException {
Position pos = position();
TypeSystem ts = (TypeSystem) tc.typeSystem();
NodeFactory nf = (NodeFactory) tc.nodeFactory();
Context c = (Context) tc.context();
if (! c.inAnnotation())
return null;
SemanticException ex;
// Look for a simply-named type.
try {
Disamb disamb = tc.nodeFactory().disamb();
Node n = disamb.disambiguate(this, tc, pos, prefix, name);
if (n instanceof TypeNode) {
TypeNode tn = (TypeNode) n;
Ref<Type> tref = (Ref<Type>) tn.typeRef();
Type t = tref.get();
if (t instanceof X10ClassType) {
X10ClassType ct = (X10ClassType) t;
if (ct.flags().isInterface()) {
List<Type> typeArgs2 = new ArrayList<Type>();
for (TypeNode a : typeArgs) {
typeArgs2.add(a.type());
}
if (ct.x10Def().typeParameters().size() == typeArgs2.size()) {
if (typeArgs2.size() > 0) {
ct = ct.typeArguments(typeArgs2);
}
}
else {
throw new SemanticException("Incorrect number of type arguments for annotation type " + ct + ".", position());
}
if (ct.x10Def().properties().size() == args().size()) {
if (args().size() > 0) {
ct = ct.propertyInitializers(args());
}
}
else {
throw new SemanticException("Incorrect number of property initializers for annotation type " + ct + ".", position());
}
tref.update(ct);
return tn;
}
}
throw new SemanticException("Annotation type must be an interface.", position());
}
String typeName = (prefix == null ? name.toString() : prefix.toString() + "." + name.toString());
ex = new SemanticException("Could not find type \"" + typeName + "\".", pos);
Map<String, Object> map = CollectionFactory.newHashMap();
map.put(CodedErrorInfo.ERROR_CODE_KEY, CodedErrorInfo.ERROR_CODE_TYPE_NOT_FOUND);
map.put("TYPE", typeName);
ex.setAttributes(map);
}
catch (SemanticException e) {
ex = e;
}
throw ex;
}
protected TypeNode disambiguateBase(ContextVisitor tc) throws SemanticException {
SemanticException ex = null;
Position pos = position();
// First look for a typedef.
try {
MacroType mt = null;
TypeSystem ts = (TypeSystem) tc.typeSystem();
NodeFactory nf = (NodeFactory) tc.nodeFactory();
Context c = (Context) tc.context();
List<Type> typeArgs = new ArrayList<Type>(this.typeArgs.size());
for (TypeNode tn2 : this.typeArgs) {
typeArgs.add(tn2.type());
}
List<Type> argTypes = new ArrayList<Type>(this.args.size());
for (Expr e : this.args) {
argTypes.add(e.type());
}
// FIXME: move this code into X10Disamb_c
if (prefix == null) {
// Search the context.
TypeDefMatcher matcher = new TypeDefMatcher(null, name.id(), typeArgs, argTypes, c);
List<Type> tl = c.find(matcher);
for (Type n : tl) {
if (n instanceof MacroType) {
mt = (MacroType) n;
break;
}
}
}
else if (prefix instanceof PackageNode) {
PackageNode pn = (PackageNode) prefix;
Resolver pc = ts.packageContextResolver(pn.package_().get());
TypeDefMatcher matcher = new TypeDefMatcher(null, name.id(), typeArgs, argTypes, c);
List<Type> tl = pc.find(matcher);
if (tl != null) {
for (Type n : tl) {
if (n instanceof MacroType) {
mt = (MacroType) n;
break;
}
}
}
}
else if (prefix instanceof TypeNode) {
TypeNode tn = (TypeNode) prefix;
Type container = tn.type();
mt = ts.findTypeDef(container, name.id(), typeArgs, argTypes, c);
}
if (mt != null) {
LazyRef<Type> sym = (LazyRef<Type>) type;
sym.update(mt);
// Reset the resolver goal to one that can run when the ref is deserialized.
Goal resolver = tc.job().extensionInfo().scheduler().LookupGlobalType(sym);
resolver.update(Goal.Status.SUCCESS);
sym.setResolver(resolver);
return nf.CanonicalTypeNode(pos, sym);
}
} catch (SemanticException e) {
// These can happen normally:
// polyglot.types.SemanticException: No type defintion found in x10.util.Map for x10.util.Map.Entry[K, V].
// NoClassException
// But in other cases we want to report the error, e.g.,
//class TestMemberTypeResolution {
// static type Foo(i:Int{self!=0}) = Int;
// var x:Foo(0); // ERR: todo: improve error: Semantic Error: Could not find type "Foo".
//}
// throw the error: (but we ignore it)
// x10.errors.Errors$InvalidParameter: Invalid Parameter.
// Expected type: x10.lang.Int{self!=0}
// Found type: x10.lang.Int{self==0}
Throwable e2 = e;
}
// Otherwise, if there are no arguments, look for a simply-named type.
if (this.args.isEmpty()) {
try {
Disamb disamb = tc.nodeFactory().disamb();
Node n = disamb.disambiguate(this, tc, pos, prefix, name);
if (n instanceof TypeNode) {
TypeNode tn = (TypeNode) n;
return tn;
}
}
catch (SemanticException e) {
ex = e;
}
}
if (ex == null) {
String typeName = (prefix == null ? name.toString() : prefix.toString() + "." + name.toString());
ex = new SemanticException("Could not find type \"" + typeName + argsString() + "\".", pos);
Map<String, Object> map = CollectionFactory.newHashMap();
map.put(CodedErrorInfo.ERROR_CODE_KEY, CodedErrorInfo.ERROR_CODE_TYPE_NOT_FOUND);
map.put("TYPE", typeName);
ex.setAttributes(map);
}
throw ex;
}
private String argsString() {
if (this.args.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder("(");
boolean first = true;
for (Expr e : this.args) {
if (!first) {
sb.append(",");
} else {
first = true;
}
sb.append(e.type().fullName());
}
sb.append(")");
return sb.toString();
}
public Context enterChildScope(Node child, Context c) {
Context oldC = c;
if (child != this.prefix) {
TypeSystem ts = c.typeSystem();
c = c.pushDepType(Types.<Type>ref(ts.unknownType(this.position)));
}
if (c == oldC && c.inAnnotation()) {
c = c.shallowCopy();
}
c.clearAnnotation();
Context cc = super.enterChildScope(child, c);
return cc;
}
public Node typeCheckOverride(Node parent, ContextVisitor tc) {
TypeSystem ts = tc.typeSystem();
NodeFactory nf = tc.nodeFactory();
AmbMacroTypeNode_c n = this;
LazyRef<Type> sym = (LazyRef<Type>) n.type;
assert sym != null;
final TypeChecker childtc = (TypeChecker) tc.enter(parent, n);
Prefix prefix = (Prefix) n.visitChild(n.prefix, childtc);
Id name = (Id) n.visitChild(n.name, childtc);
n = (AmbMacroTypeNode_c) n.prefix(prefix);
n = (AmbMacroTypeNode_c) n.name(name);
List<TypeNode> typeArgs = n.visitList(n.typeArgs, childtc);
List<Expr> args = n.visitList(n.args, childtc);
n = (AmbMacroTypeNode_c) n.typeArgs(typeArgs);
n = (AmbMacroTypeNode_c) n.args(args);
TypeNode tn;
boolean foundError = false;
try {
tn = n.disambiguateAnnotation(childtc);
if (tn != null)
return postprocess((X10CanonicalTypeNode) tn, n, childtc);
}
catch (SemanticException e) {
if (!foundError) {
// Mark the type resolved to prevent us from trying to resolve this again and again.
X10ClassType ut = ts.createFakeClass(QName.make(fullName(prefix), name().id()), e);
ut.def().position(n.position());
sym.update(ut);
foundError = true;
}
}
// Do not permit arguments to macro calls to be Boolean and
// &&, || or !. These cannot be handled by the constraint system.
class CheckMacroCallArgsVisitor extends NodeVisitor {
IllegalConstraint error;
@Override
public Node override(Node n) {
if (n instanceof Binary) {
Binary b = (Binary) n;
Binary.Operator bop = b.operator();
if (b.type().isBoolean() && bop.equals(Binary.COND_AND)
|| bop.equals(Binary.COND_OR)) {
error = new IllegalConstraint(b);
}
}
if (n instanceof Unary) {
Unary u = (Unary) n;
Unary.Operator uop = u.operator();
if (u.type().isBoolean() && uop.equals(Unary.NOT)) {
error = new IllegalConstraint(u);
}
}
return null;
}
}
for (Expr arg : args) {
CheckMacroCallArgsVisitor v = new CheckMacroCallArgsVisitor();
arg.visit(v);
if (v.error != null) {
Errors.issue(tc.job(), v.error, arg);
}
}
for (Expr arg : args) {
VarChecker ac = (VarChecker) new VarChecker(childtc.job()).context(childtc.context());
try {
arg.visit(ac);
} catch (InternalCompilerError e) {
Errors.issue(childtc.job(),
new Errors.GeneralError(e.getMessage(), e.position()), arg);
}
if (ac.error != null) {
Errors.issue(childtc.job(), ac.error, arg);
}
}
try {
tn = n.disambiguateBase(tc);
}
catch (SemanticException e) {
if (!foundError) {
// Mark the type resolved to prevent us from trying to resolve this again and again.
X10ClassType ut = ts.createFakeClass(QName.make(fullName(prefix), name().id()), e);
ut.def().position(n.position());
sym.update(ut);
foundError = true;
}
tn = n;
}
Type t = tn.type();
if (ts.isUnknown(t)) {
// Mark the type resolved to prevent us from trying to resolve this again and again.
sym.update(ts.unknownType(n.position()));
return postprocess(nf.CanonicalTypeNode(n.position(), sym), n, childtc);
}
if (t instanceof MacroType) {
// FIXME: [IP] We are losing the arguments here!
n = (AmbMacroTypeNode_c) n.typeArgs(Collections.<TypeNode>emptyList());
n = (AmbMacroTypeNode_c) n.args(Collections.<Expr>emptyList());
}
if (t instanceof X10Use<?>) Warnings.checkErrorAndGuard(tc,((X10Use<?>) t), n);
if (! typeArgs.isEmpty()) {
if (t instanceof X10ParsedClassType) {
X10ParsedClassType ct = (X10ParsedClassType) t;
int numParams = ct.x10Def().typeParameters().size();
if (numParams != typeArgs.size()) {
if (ct.error() == null) {
SemanticException e = new Errors.NumberTypeArgumentsNotSameAsNumberTypeParameters(typeArgs.size(), ct.fullName(), numParams, n.position());
Errors.issue(tc.job(), e, this);
ct = (X10ParsedClassType) ts.createFakeClass(ct.fullName(), e);
int i = 0;
for (TypeNode ta : typeArgs) {
ct.def().addTypeParameter(new ParameterType(ts, Position.COMPILER_GENERATED, Position.COMPILER_GENERATED, Name.make("T"+(i++)), Types.ref(ct.def())), ParameterType.Variance.INVARIANT);
}
}
}
List<Type> typeArgsTypes = new ArrayList<Type>(numParams);
for (TypeNode tni : typeArgs) {
typeArgsTypes.add(tni.type());
}
t = ct.typeArguments(typeArgsTypes);
n = (AmbMacroTypeNode_c) n.typeArgs(Collections.<TypeNode>emptyList());
typeArgs = Collections.<TypeNode>emptyList();
}
}
if (n.flags != null) {
t = Types.processFlags(n.flags, t);
n.flags = null;
}
// Update the symbol with the base type so that if we try to get the type while checking the constraint, we don't get a cyclic
// dependency error, but instead get a less precise type.
sym.update(t);
/*if (! n.typeArgs().isEmpty() || ! n.args().isEmpty())
throw new SemanticException("Could not find or instantiate type \"" + n + "\".", position());
*/
X10CanonicalTypeNode result = nf.CanonicalTypeNode(n.position(), sym);
return postprocess(result, n, childtc);
}
public static Node postprocess(X10CanonicalTypeNode result, AmbMacroTypeNode_c n, ContextVisitor childtc) {
n = (AmbMacroTypeNode_c) X10Del_c.visitAnnotations(n, childtc);
result = (X10CanonicalTypeNode) ((X10Del) result.del()).annotations(((X10Del) n.del()).annotations());
result = (X10CanonicalTypeNode) ((X10Del) result.del()).setComment(((X10Del) n.del()).comment());
result = (X10CanonicalTypeNode) result.typeCheck(childtc);
return result;
}
public Node exceptionCheck(ExceptionChecker ec) {
throw new InternalCompilerError(position(),
"Cannot exception check ambiguous node " + this + ".");
}
@Override
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
if (prefix != null) {
tr.print(this, prefix, w);
w.write(".");
}
tr.print(this, name, w);
if (typeArgs != null && typeArgs.isEmpty()) {
w.write("[");
String sep = "";
for (TypeNode a : typeArgs) {
w.write(sep);
sep = ", ";
tr.print(this, a, w);
}
w.write("]");
}
if (args != null && args.isEmpty()) {
w.write("(");
String sep = "";
for (Expr a : args) {
w.write(sep);
sep = ", ";
tr.print(this, a, w);
}
w.write(")");
}
}
}