/*
* 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;
/**
* Immutable representation of a local variable access.
* Introduced to add X10 specific type checks. A local variable accessed
* in a deptype must be final.
*
* @author vj
*/
import polyglot.ast.Id;
import polyglot.ast.Local_c;
import polyglot.ast.Node;
import polyglot.types.CodeDef;
import polyglot.types.LocalInstance;
import polyglot.types.Name;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.Types;
import polyglot.types.VarDef;
import polyglot.util.Position;
import polyglot.visit.ContextVisitor;
import x10.constraint.XFailure;
import x10.constraint.XTerm;
import x10.constraint.XConstraint;
import x10.errors.Errors;
import polyglot.types.Context;
import x10.types.X10LocalInstance;
import x10.types.X10ProcedureDef;
import polyglot.types.TypeSystem;
import x10.types.X10LocalDef_c;
import x10.types.X10LocalDef;
import x10.types.constraints.CConstraint;
import x10.types.constraints.ConstraintManager;
import x10.types.constraints.XConstrainedTerm;
public class X10Local_c extends Local_c {
public X10Local_c(Position pos, Id name) {
super(pos, name);
}
public void checkLocalAccess(LocalInstance li, ContextVisitor tc) {
Context context = tc.context();
final Name liName = name.id();
// if the local is defined in an outer class, then it must be final
// shared was removed from the language: you cannot access var in a closure
// Note that an async is similar to a closure (we create a dummy closure)
boolean isInClosure = false;
if (context.isLocalExcludingAsyncAt(liName)) {
// ok
} else if (!context.isLocal(liName)) {
// this local is defined in an outer class
isInClosure = true;
if (!li.flags().isFinal())
Errors.issue(tc.job(), new Errors.LocalVariableAccessedFromInnerClass(liName, this.position()));
} else {
// if the access is in an async and the local-var is not local, then we must ensure that the scoping looks like this: var ... (no async) ... finish ... async
// algorithm: we go up the context (going outwards) looking for a finish
// (setting flag foundFinish to true when we find a finish, and to false when we find an async)
// when we get to the var definition, then foundFinish must be true.
if (!context.isSequentialAccess(true,liName))
Errors.issue(tc.job(), new Errors.LocalVariableCannotBeCapturedInAsync(liName, this.position()));
}
if (!isInClosure) {
// we check that usages inside an "at" are at the origin place if it is a "var" (for "val" we're fine, except when it's a write)
final X10LocalDef_c localDef_c = (X10LocalDef_c) li.def();
XTerm origin = localDef_c.placeTerm();
// origin maybe null when typechecking a method to get the return type (see XTENLANG-1902)
// but we will type check that method again later (with correct placeTerm)
if (origin!=null) { // origin = PlaceChecker.here();
final XConstrainedTerm placeTerm = context.currentPlaceTerm();
final XTerm currentPlace = placeTerm.term();
XConstraint constraint = ConstraintManager.getConstraintSystem().makeConstraint();;
boolean isOk = false;
constraint.addBinding(origin,currentPlace);
if (placeTerm.constraint().entails(constraint)) {
//ok origin == currentPlace
isOk = true;
}
if (!isOk)
Errors.issue(tc.job(), new Errors.LocalVariableAccessedAtDifferentPlace(liName, this.position()));
}
// initialization in an "at" is also considered async-init by the backend
if (context.localHasAt(liName))
((X10LocalDef)li.def()).setAsyncInit();
}
}
public Node typeCheck(ContextVisitor tc) {
Context context = tc.context();
final Name liName = name.id();
LocalInstance li = localInstance();
if (!((X10LocalInstance) li).isValid()) {
li = findAppropriateLocal(tc, liName);
}
if (context.inAssignment()) {
// local is assigned to
li = (LocalInstance) li.lval(true);
}
context.recordCapturedVariable(li);
if (!li.flags().isFinal()) {
checkLocalAccess(li, tc);
}
X10Local_c result = (X10Local_c) localInstance(li).type(li.type());
try {
VarDef dli = context.varWhoseTypeIsBeingElaborated();
if (context.inDepType()) {
li = result.localInstance();
if (! (li.def().equals(dli)) && ! li.flags().isFinal()) {
throw new Errors.LocalVariableNotAllowedInContainer(liName, position());
}
}
// Add in self==x to local variable x.
Type type = ((X10LocalInstance) li).rightType();
result = (X10Local_c) result.type(type);
// Fold in the method's guard.
// %%% FIXME: move method guard into context.currentConstraint
CodeDef ci = context.currentCode();
if (ci instanceof X10ProcedureDef) {
X10ProcedureDef pi = (X10ProcedureDef) ci;
CConstraint c = Types.get(pi.guard());
if (c != null) {
TypeSystem xts = (TypeSystem) tc.typeSystem();
// Substitute self for x (this local) in the guard.
// C_Var var = new TypeTranslator(xts).trans(localInstance());
// Promise p = c.intern(var);
// System.out.println("before = " + c);
// c = c.substitute(p.term(), C_Special.Self);
// System.out.println("after = " + c);
// Add the guard into the constraint for this type.
Type t = result.type();
CConstraint dep = Types.xclause(t);
if (dep==null) {
dep = c.copy();
} else {
dep = dep.copy();
dep.addIn(c);
}
if (! dep.consistent()) {
throw new Errors.InconsistentType(t, position());
}
t = Types.xclause(Types.baseType(t), dep);
return result.type(t);
}
}
} catch (SemanticException z) {
Errors.issue(tc.job(), z, this);
}
return result;
}
public static X10LocalInstance findAppropriateLocal(ContextVisitor tc, Name name) {
Context context = (Context) tc.context();
SemanticException error = null;
try {
return (X10LocalInstance) context.findLocal(name);
} catch (SemanticException e) {
error = e;
}
// If not returned yet, fake the local instance.
TypeSystem xts = tc.typeSystem();
X10LocalInstance li = xts.createFakeLocal(name, error);
return li;
}
}