/*
* 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 java.util.List;
import polyglot.ast.CanonicalTypeNode;
import polyglot.ast.CanonicalTypeNode_c;
import polyglot.ast.Expr;
import polyglot.ast.Node;
import polyglot.ast.TypeNode;
import polyglot.frontend.Globals;
import polyglot.frontend.SetResolverGoal;
import polyglot.types.ClassDef;
import polyglot.types.CodeDef;
import polyglot.types.ConstructorDef;
import polyglot.types.Context;
import polyglot.types.Def;
import polyglot.types.Flags;
import polyglot.types.LazyRef;
import polyglot.types.Ref;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.Types;
import polyglot.util.CodeWriter;
import polyglot.util.InternalCompilerError;
import polyglot.util.Position;
import polyglot.visit.PrettyPrinter;
import polyglot.visit.TypeBuilder;
import polyglot.visit.TypeCheckPreparer;
import polyglot.visit.TypeChecker;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import x10.constraint.XConstraint;
import x10.errors.Errors;
import x10.extension.X10Del;
import x10.types.ClosureDef;
import x10.types.FunctionType_c;
import x10.types.ConstrainedType;
import x10.types.ParameterType;
import x10.types.X10ClassDef;
import x10.types.X10ClassType;
import polyglot.types.Context;
import x10.types.X10ParsedClassType;
import x10.types.XTypeTranslator;
import polyglot.types.TypeSystem;
import x10.types.constraints.CConstraint;
import x10.visit.X10TypeChecker;
/**
* The X10 version of a CanonicalTypeNode.
* Has an associated DepParameterExpr.
* @author vj
*
*/
public class X10CanonicalTypeNode_c extends CanonicalTypeNode_c implements X10CanonicalTypeNode, AddFlags {
public X10CanonicalTypeNode_c(Position pos, Type type) {
this(pos, Types.<Type>ref(type));
}
public X10CanonicalTypeNode_c(Position pos, Ref<? extends Type> type) {
super(pos, type);
}
Flags flags;
public void addFlags(Flags f) {
flags = f;
}
@Override
public Node typeCheck(ContextVisitor tc) {
Context c = (Context) tc.context();
TypeSystem ts = (TypeSystem) tc.typeSystem();
// Expand, and transfer flags from the type node to the type.
Type t = Types.get(type);
t = ts.expandMacros(t);
Type xt = t;
if (flags != null) {
xt = Types.processFlags(flags, xt);
flags = null;
}
((Ref<Type>) type).update(xt);
if (t instanceof ParameterType) {
ParameterType pt = (ParameterType) t;
Def def = Types.get(pt.def());
boolean inConstructor = false;
Context p = c;
// Pop back to the right context before proceeding
while (p.pop() != null && (p.currentClassDef() != def || p.currentCode() instanceof ClosureDef))
p = p.pop();
if (p.currentCode() instanceof ConstructorDef) {
ConstructorDef td = (ConstructorDef) p.currentCode();
Type container = Types.get(td.container());
if (container instanceof X10ClassType) {
X10ClassType ct = (X10ClassType) container;
if (ct.def() == def) {
inConstructor = true;
}
}
}
if (p.inStaticContext() && def instanceof ClassDef && ! inConstructor) {
Errors.issue(tc.job(),
new Errors.CannotReferToTypeParameterFromStaticContext(pt, def, position()));
}
if (flags != null && ! flags.equals(Flags.NONE)) {
Errors.issue(tc.job(),
new Errors.CannotQualifyTypeParameter(pt, def, flags, position()));
}
}
try {
checkType(tc.context(), t, position());
} catch (SemanticException e) {
Errors.issue(tc.job(), e, this);
}
List<AnnotationNode> as = ((X10Del) this.del()).annotations();
if (as != null && !as.isEmpty()) {
// Eh. Why not?
// if (c.inAnnotation()) {
// throw new SemanticException("Annotations not permitted within annotations.", position());
// }
List<Type> annotationTypes = new ArrayList<Type>();
for (AnnotationNode an : as) {
Type at = an.annotationInterface();
annotationTypes.add(at);
}
Type newType = ts.AnnotatedType(position(), t, annotationTypes);
Ref<Type> tref = (Ref<Type>) type;
tref.update(newType);
}
X10CanonicalTypeNode n = (X10CanonicalTypeNode) super.typeCheck(tc);
Type nt = n.type();
if (nt.isClass()) {
X10ClassType ct = (X10ClassType) nt.toClass();
if (ct.error() != null && !position().isCompilerGenerated() && ct.error().position() != null) {
// The error will have been reported. Say something sensible instead.
String file = position().file();
String dfile = ct.error().position().file();
if (!file.equals(dfile)) {
SemanticException error =
new SemanticException(file+" depends on "+dfile+", which has compilation errors");
Errors.issue(tc.job(), error, this);
}
}
}
return n;
}
// todo: C:\cygwin\home\Yoav\intellij\sourceforge\x10.tests\examples\Benchmarks\SeqArray1.x10
// C:\cygwin\home\Yoav\intellij\sourceforge\x10.tests\examples\Benchmarks\SeqArray2b.x10
// C:\cygwin\home\Yoav\intellij\sourceforge\x10.tests\examples\Constructs\Array\Array1.x10
@Override
public void setResolver(Node parent, final TypeCheckPreparer v) {
if (typeRef() instanceof LazyRef<?>) {
LazyRef<Type> r = (LazyRef<Type>) typeRef();
if (!r.isResolverSet()) {
TypeChecker tc = new X10TypeChecker(v.job(), v.typeSystem(), v.nodeFactory(), v.getMemo());
// similarly to TypeNode_c, freezing is not needed.
tc = (TypeChecker) tc.context(v.context().freeze());
r.setResolver(new X10TypeCheckTypeGoal(parent, this, tc, r));
}
}
}
@Override
public Node conformanceCheck(ContextVisitor tc) {
Type t = type();
XConstraint c = Types.realX(t);
if (! c.consistent()) {
Errors.issue(tc.job(),
new Errors.InvalidType(t, position()));
}
TypeSystem ts = (TypeSystem) t.typeSystem();
if (! ts.consistent(t, tc.context())) {
Errors.issue(tc.job(), new Errors.TypeInconsistent(t,position()));
}
return this;
}
public static void checkType(Context context, Type t, Position pos) throws SemanticException {
if (t == null) throw new SemanticException("Invalid type.", pos);
if (t instanceof ConstrainedType) {
ConstrainedType ct = (ConstrainedType) t;
Type base = Types.get(ct.baseType());
// if (base instanceof ParameterType) {
// throw new SemanticException("Invalid type; cannot constrain a type parameter.", position());
// }
checkType(context, base, pos);
}
if (t instanceof X10ClassType && ((X10ClassType) t).error()==null) {
X10ClassType ct = (X10ClassType) t;
X10ClassDef def = ct.x10Def();
final List<Type> typeArgs = ct.typeArguments();
final int typeArgNum = typeArgs == null ? 0 : typeArgs.size();
final List<ParameterType> typeParam = def.typeParameters();
final int typeParamNum = typeParam.size();
// I want to check that all generic classes have all the required type arguments, i.e., X10TypeMixin.checkMissingParameters(t, position())
// E.g., that you always write: Array[...] and never Array.
// But that is not true for a static method, e.g., Array.make(...)
// so instead we do this check in all other places (e.g., field access, method definitions, new calls, etc)
// But I can check it if there are typeArguments.
if (typeArgNum > 0) Types.checkMissingParameters(t,pos);
for (int j = 0; j < typeArgNum; j++) {
Type actualType = typeArgs.get(j);
Types.checkMissingParameters(actualType,pos);
ParameterType correspondingParam = typeParam.get(j);
if (actualType.isVoid()) {
throw new SemanticException("Cannot instantiate invariant parameter " + correspondingParam + " of " + def + " with type " + actualType + ".", pos);
}
}
// A invariant parameter may not be instantiated on a covariant or contravariant parameter.
// A contravariant parameter may not be instantiated on a covariant parameter.
// A covariant parameter may not be instantiated on a contravariant parameter.
for (int j = 0; j < typeArgNum; j++) {
Type actualType = typeArgs.get(j);
ParameterType correspondingParam = typeParam.get(j);
ParameterType.Variance correspondingVariance = def.variances().get(j);
if (actualType instanceof ParameterType) {
ParameterType pt = (ParameterType) actualType;
if (pt.def() instanceof X10ClassDef) {
X10ClassDef actualDef = (X10ClassDef) pt.def();
for (int i = 0; i < actualDef.typeParameters().size(); i++) {
ParameterType.Variance actualVariance;
if (i < actualDef.variances().size())
actualVariance = actualDef.variances().get(i);
else
actualVariance = ParameterType.Variance.INVARIANT;
if (pt.typeEquals(actualDef.typeParameters().get(i), context)) {
switch (correspondingVariance) {
case INVARIANT:
switch (actualVariance) {
case CONTRAVARIANT:
throw new SemanticException("Cannot instantiate invariant parameter " + correspondingParam + " of " + def + " with contravariant parameter " + pt + " of " + actualDef + ".", pos);
case COVARIANT:
throw new SemanticException("Cannot instantiate invariant parameter " + correspondingParam + " of " + def + " with covariant parameter " + pt + " of " + actualDef + ".", pos);
}
break;
case CONTRAVARIANT:
switch (actualVariance) {
case COVARIANT:
throw new SemanticException("Cannot instantiate contravariant parameter " + correspondingParam + " of " + def + " with covariant parameter " + pt + " of " + actualDef + ".", pos);
}
break;
case COVARIANT:
switch (actualVariance) {
case CONTRAVARIANT:
throw new SemanticException("Cannot instantiate covariant parameter " + correspondingParam + " of " + def + " with contravariant parameter " + pt + " of " + actualDef + ".", pos);
}
break;
}
}
}
}
}
}
}
}
public String toString() {
return (flags == null ? "" : flags.toString() + " ") + super.toString();
}
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
prettyPrint(w, tr, true);
}
public void prettyPrint(CodeWriter w, PrettyPrinter tr, Boolean extras) {
if (type == null) {
w.write("<unknown-type>");
} else {
type.get().print(w);
// vj: printing Parameters should be handled by the type itself.
// Move this code to ClassType_c.print(CodeWriter).
/*final X10ParsedClassType baseType = Types.myBaseType(type.get());
if (extras && baseType!=null
&& !(baseType instanceof FunctionType_c)) {
List<Type> typeArguments = baseType.typeArguments();
if (typeArguments != null && typeArguments.size() > 0) {
w.write("[");
w.allowBreak(2, 2, "", 0); // miser mode
w.begin(0);
for (Iterator<Type> i = typeArguments.iterator(); i.hasNext(); ) {
Type t = i.next();
t.print(w);
if (i.hasNext()) {
w.write(",");
w.allowBreak(0, " ");
}
}
w.write("]");
w.end();
}
}*/
if (extras && type.get() instanceof ConstrainedType) {
((ConstrainedType) type.get()).printConstraint(w);
}
}
}
}