/*
* 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.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import polyglot.ast.Call;
import polyglot.ast.Expr;
import polyglot.ast.Field;
import polyglot.ast.Field_c;
import polyglot.ast.Id;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Receiver;
import polyglot.ast.Special;
import polyglot.ast.TypeNode;
import polyglot.types.ClassType;
import polyglot.types.Context;
import polyglot.types.FieldDef;
import polyglot.types.FieldInstance;
import polyglot.types.Flags;
import polyglot.types.Name;
import polyglot.types.QName;
import polyglot.types.SemanticException;
import polyglot.types.ContainerType;
import polyglot.types.Type;
import polyglot.types.Types;
import polyglot.util.ErrorInfo;
import polyglot.util.Position;
import polyglot.visit.ContextVisitor;
import x10.constraint.XVar;
import x10.constraint.XTerm;
import x10.types.constraints.ConstraintManager;
import x10.constraint.XVar;
import x10.types.ParameterType;
import x10.types.ParametrizedType_c;
import x10.types.X10ClassType;
import polyglot.types.Context;
import x10.types.X10FieldInstance;
import x10.types.MethodInstance;
import polyglot.types.TypeSystem;
import x10.types.checker.Checker;
import x10.types.checker.PlaceChecker;
import x10.types.constraints.CConstraint;
import x10.types.matcher.Subst;
import x10.types.matcher.X10FieldMatcher;
import x10.visit.X10TypeChecker;
import x10.errors.Errors;
import x10.errors.Errors.IllegalConstraint;
/**
* An immutable representation of an X10 Field access.
*
* @author vj May 23, 2005
*/
public class X10Field_c extends Field_c {
/**
* @param pos
* @param target
* @param name
*/
public X10Field_c(Position pos, Receiver target, Id name) {
super(pos, target, name);
}
@Override
public X10FieldInstance fieldInstance() {
return (X10FieldInstance) super.fieldInstance();
}
public X10Field_c reconstruct(Receiver target, Id name) {
return (X10Field_c) super.reconstruct(target, name);
}
public static X10FieldInstance findAppropriateField(ContextVisitor tc,
Type targetType, Name name, boolean isStatic, boolean receiverInContext, Position pos) {
TypeSystem ts = (TypeSystem) tc.typeSystem();
Context c = (Context) tc.context();
X10FieldInstance fi = null;
try {
//// vj: Hack to work around the design decision to represent "here" as this.home for
//// instance methods. This decision creates a problem for non-final variables that are
//// located in the current place. "this" is going to get quantified out by the FieldMatcher.
//// therefore we temporarily replace this.home with a new UQV, currentPlace, and then on
//// return from the matcher, substitute it back in.
//XTerm placeTerm = c.currentPlaceTerm()==null ? null: c.currentPlaceTerm().term();
//XVar currentPlace = XTerms.makeUQV("place");
//Type tType2 = placeTerm==null ? targetType : Subst.subst(targetType, currentPlace, (XVar) placeTerm);
//fi = (X10FieldInstance) ts.findField(targetType, tType2, name, c, receiverInContext);
// IP: we may still need the above hack because "here" is this.home in field initializers
fi = (X10FieldInstance) ts.findField(targetType, targetType, name, c, receiverInContext);
if (isStatic && !fi.flags().isStatic())
throw new Errors.CannotAccessNonStaticFromStaticContext(fi, pos);
assert (fi != null);
//// substitute currentPlace back in.
//fi = placeTerm == null ? fi : Subst.subst(fi, placeTerm, currentPlace);
} catch (SemanticException e) {
fi = findAppropriateField(tc, targetType, name, isStatic, e);
}
return fi;
}
private static X10FieldInstance findAppropriateField(ContextVisitor tc, Type targetType,
Name name, boolean isStatic, SemanticException e)
{
X10FieldInstance fi;
TypeSystem xts = tc.typeSystem();
Context context = tc.context();
boolean haveUnknown = xts.hasUnknown(targetType);
Set<FieldInstance> fis = xts.findFields(targetType, targetType, name, context);
// If exception was not thrown, there is at least one match. Fake it.
// See if all matches have the same type, and save that to avoid losing information.
Type rt = null;
for (FieldInstance xfi : fis) {
if (rt == null) {
rt = xfi.type();
} else if (!xts.typeEquals(rt, xfi.type(), context)) {
if (xts.typeBaseEquals(rt, xfi.type(), context)) {
rt = Types.baseType(rt);
} else {
rt = null;
break;
}
}
}
// See if all matches have the same container, and save that to avoid losing information.
Type ct = null;
for (FieldInstance xfi : fis) {
if (ct == null) {
ct = xfi.container();
} else if (!xts.typeEquals(ct, xfi.container(), context)) {
if (xts.typeBaseEquals(ct, xfi.container(), context)) {
ct = Types.baseType(ct);
} else {
ct = null;
break;
}
}
}
if (ct != null) targetType = ct;
Flags flags = Flags.PUBLIC;
if (isStatic) flags = flags.Static();
if (haveUnknown)
e = new SemanticException(); // null message
if (!targetType.isClass()) {
QName tName = targetType.fullName();
if (tName == null) {
tName = QName.make(null, targetType.toString());
}
targetType = xts.createFakeClass(tName, new SemanticException("Target type is not a class: "+targetType));
}
fi = xts.createFakeField(targetType.toClass(), flags, name, e);
if (rt == null) rt = fi.type();
rt = PlaceChecker.AddIsHereClause(rt, context);
fi = fi.type(rt);
return fi;
}
// XTENLANG-945
public static boolean isInterfaceProperty(FieldInstance fi) {
if (fi.flags().isProperty()) {
// check if the container is interface
Flags flags = getFlags(Types.baseType(fi.container()));
if (flags != null) {
return flags.isInterface();
}
}
return false;
}
private static Flags getFlags(Type type) {
if (type instanceof ClassType) {
return ((ClassType) type).flags();
} else if (type instanceof ParametrizedType_c) {
// TODO add flags() to ParametrizedType and use it
return ((ParametrizedType_c) type).flags();
}
return null;
}
@Override
public Node typeCheck(ContextVisitor tc) {
final TypeSystem ts = (TypeSystem) tc.typeSystem();
final NodeFactory nf = (NodeFactory) tc.nodeFactory();
final Context c = (Context) tc.context();
Type tType = target != null ? target.type() : c.currentClass();
SemanticException error = null;
Position pos = position();
if (target instanceof TypeNode) {
Type t = Types.baseType(tType);
if (t instanceof ParameterType) {
SemanticException e = new Errors.CannotAccessStaticFieldOfTypeParameter(t, pos);
if (error == null) { error = e; }
Errors.issue(tc.job(), e);
}
}
if (c.inSuperTypeDeclaration()) {
Type tBase = Types.baseType(tType);
if (tBase instanceof X10ClassType) {
X10ClassType tCt = (X10ClassType) tBase;
if (tCt.def() == c.supertypeDeclarationType()) {
// The only fields in scope here are the ones explicitly declared here.
for (FieldDef fd : tCt.x10Def().properties()) {
if (fd.name().equals(name.id())) {
X10FieldInstance fi = (X10FieldInstance) fd.asInstance();
try {
fi = X10FieldMatcher.instantiateAccess((X10FieldInstance)fi,name.id(),tType,false,c);
}
catch (SemanticException e) {
}
if (fi != null) {
// Found!
X10Field_c result = this;
Type t = c.inDepType()? Checker.rightType(fi.rightType(), fi.x10Def(), target, c)
: fi.rightType(); // fieldRightType(fi.rightType(), fi.x10Def(), target, c);
result = (X10Field_c) result.fieldInstance(fi).type(t);
return result;
}
}
}
// the outer instance is also a property-like entity (see XTENLANG-47)
boolean isStatic = c.inStaticContext();
if (!isStatic) {
ClassType outer = tCt.outer();
while (outer!=null && !outer.flags().isStatic()) {
X10FieldInstance fi = findAppropriateField(tc, outer, name.id(),
false, Types.contextKnowsType(target), position());
if (fi.error()==null) {
Position genP = pos.markCompilerGenerated();
Special special = (Special) nf.This(genP, nf.CanonicalTypeNode(genP, outer)).type(outer);
Field result = (Field) this.target(special);
result = (Field) result.typeCheck(tc);
return result;
}
outer = outer.outer();
}
}
SemanticException e = new Errors.CannotAccessField(name, tCt, pos);
if (error == null) { error = e; }
Errors.issue(tc.job(), e);
}
}
}
X10FieldInstance fi = findAppropriateField(tc, tType, name.id(),
target instanceof TypeNode, Types.contextKnowsType(target), position());
if (fi.error() == null && !c.inDepType() && isInterfaceProperty(fi)) { // XTENLANG-945
fi = fi.error(new Errors.UnableToFindImplementingPropertyMethod(fi.name(), position()));
}
if (fi.error() != null) {
if (target instanceof Expr) {
// Now try 0-ary property methods.
try {
MethodInstance mi = ts.findMethod(target.type(),
ts.MethodMatcher(target.type(), name.id(),
Collections.<Type>emptyList(), c));
if (mi.flags().isProperty()) {
Call call = nf.Call(pos, target, this.name);
call = call.methodInstance(mi);
Type nt = mi.rightType();
if (c.inDepType()) {
nt = Checker.rightType(nt, mi.x10Def(), target, c);
} else {
try {
nt = Checker.expandCall(nt, call, c);
} catch (IllegalConstraint z) {
// ignore, we will go with mi.rightType.
}
}
call = (Call) call.type(nt);
return call;
}
}
catch (SemanticException ex) {
}
}
fi.error().setPosition(position); // because in X10FieldMatcher.instantiateAccess the position is of the instance which is incorrect.
Errors.issue(tc.job(), fi.error(), this);
}
if (fi.error() == null && error != null) {
fi = fi.error(error);
}
if (isFieldOfThis(this)) {
c.recordCapturedVariable(fi);
}
Type type = c.inDepType()? Checker.rightType(fi.rightType(), fi.x10Def(), target, c) :
Checker.fieldRightType(fi.rightType(), fi.x10Def(), target, c);
Type retType = type;
// Substitute in the actual target for this. This is done by findField, now.
// Type thisType = tType;
// CConstraint rc = X10TypeMixin.realX(retType);
// if (rc != null) {
// XVar var= X10TypeMixin.selfVar(thisType);
// if (var == null)
// var = ts.xtypeTranslator().genEQV(rc, thisType);
// CConstraint newRC = rc.substitute(var, ts.xtypeTranslator().transThis(thisType));
// retType = X10TypeMixin.xclause(retType, newRC);
// fi = fi.type(retType);
// }
X10Field_c result = (X10Field_c)fieldInstance(fi).type(retType);
if (fi.error() == null) {
result.checkConsistency(c);
try {
checkFieldAccessesInDepClausesAreFinal(pos, target, fi, tc);
} catch (SemanticException e) {
Errors.issue(tc.job(), e);
}
try {
checkClockedFieldAccessesAreInClockedMethods(pos, fi, tc);
} catch (SemanticException e) {
Errors.issue(tc.job(), e);
}
}
// Not needed in the orthogonal locality proposal.
// result = PlaceChecker.makeFieldAccessLocalIfNecessary(result, tc);
//System.err.println("X10Field_c: typeCheck " + result+ " has type " + result.type());
return result;
}
public static boolean isFieldOfThis(Field f) {
Receiver target = f.target();
if (target == null && !f.flags().isStatic()) return true;
return target instanceof X10Special && ((X10Special) target).qualifier() == null;
}
/**
* Check that if this field is a clocked field, it is being accessed from within a clocked method.
* @param fi
* @param tc
* @throws SemanticException
*/
protected static void checkClockedFieldAccessesAreInClockedMethods(Position pos,
X10FieldInstance fi, ContextVisitor tc) throws SemanticException {
// Check that field accesses in dep clauses refer to final fields.
Context xtc = tc.context();
if (fi.flags().isClocked() && !xtc.isClocked()) {
throw new Errors.IllegalClockedAccess(fi, pos);
}
}
private static final boolean ENABLE_PLACE_TYPES = true;
protected static void checkFieldAccessesInDepClausesAreFinal(Position pos, Receiver target,
X10FieldInstance fi, ContextVisitor tc) throws SemanticException {
// Check that field accesses in dep clauses refer to final fields.
Context xtc = tc.context();
if (xtc.inDepType()) {
if (! fi.flags().contains(Flags.FINAL))
throw new Errors.DependentClauseErrorFieldMustBeFinal(fi, pos);
if ((target instanceof X10Special) &&
((X10Special)target).kind()==X10Special.SELF) {
// The fieldInstance must be a property.
//Report.report(1, "X10Field_c checking " + fi + " is a property. ");
// The following is going to look for property propertyNames$
// and may throw a MissingDependencyException asking for the field to be set.
if (!fi.isProperty())
throw new Errors.DependentClauseErrorSelfMayAccessOnlyProperties(fi, pos);
}
}
}
}