/*
* 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 polyglot.ast.Expr;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Return_c;
import polyglot.types.CodeDef;
import polyglot.types.ConstructorDef;
import polyglot.types.Context;
import polyglot.types.FunctionDef;
import polyglot.types.InitializerDef;
import polyglot.types.MethodDef;
import polyglot.types.Ref;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.Types;
import polyglot.util.InternalCompilerError;
import polyglot.util.Position;
import polyglot.visit.ContextVisitor;
import x10.errors.Errors;
import x10.types.ClosureDef;
import x10.types.TypeDef;
import x10.types.X10ClassType;
import polyglot.types.Context;
import x10.types.X10MethodDef;
import polyglot.types.TypeSystem;
import x10.types.checker.Converter;
import x10.types.checker.PlaceChecker;
public class X10Return_c extends Return_c {
protected boolean implicit;
public X10Return_c(Position pos, Expr expr, boolean implicit) {
super(pos, expr);
this.implicit = implicit;
}
@Override
public Node typeCheck(ContextVisitor tc) {
TypeSystem ts = (TypeSystem) tc.typeSystem();
Context c = (Context) tc.context();
CodeDef ci = c.currentCode();
if (!implicit && c.inAsyncScope()) { // can return from an at but not from an async
Errors.issue(tc.job(), new Errors.CannotReturnFromAsync(position()), this);
return this;
}
if (ci instanceof ConstructorDef && this.expr() != null) {
Errors.issue(tc.job(), new Errors.CannotReturnValueFromConstructor(position()), this);
return this;
}
X10Return_c n = this;
Type exprType = n.expr() != null ? n.expr().type() : null;
// In the exprType, we may have replaced here by PlaceTerm from the context
// in support of checking references to this object across place boundaries within
// the code for this method.
// Now this value is being returned from this method.
// Replace PlaceTerm from the context with here so that exprType will
// correctly be of ! type in the calling environment (which does not know about PlaceTerm.
if (exprType != null) {
exprType = PlaceChecker.ReplacePlaceTermByHere(exprType, tc.context());
n = (X10Return_c) n.expr(n.expr().type(exprType));
}
// If the return type is not yet known, set it to the type of the value being returned.
if (ci instanceof FunctionDef) {
FunctionDef fi = (FunctionDef) ci;
if (exprType instanceof X10ClassType) {
X10ClassType ct = (X10ClassType) exprType;
if (ct.isAnonymous()) {
if (ct.interfaces().size() > 0)
exprType = ct.interfaces().get(0);
else
exprType = ct.superClass();
}
}
boolean merge = false;
if (fi instanceof X10MethodDef) {
merge = ((X10MethodDef) fi).inferReturnType();
}
if (fi instanceof ClosureDef) {
merge = ((ClosureDef) fi).inferReturnType();
}
Ref<Type> typeRef = (Ref<Type>) fi.returnType();
if (merge) {
if (n.expr() == null) {
if (! typeRef.known()) {
typeRef.update(ts.Void());
}
}
else {
// exprType should only mention variables in scope at the function signature
// For closures, this includes local variables in scope at the closure.
// For methods and closures, this includes formal parameters (incl. this).
exprType = Types.removeLocals(tc.context(), exprType);
if (! typeRef.known()) {
typeRef.update(exprType);
}
else {
// Merge the types
try {
Type t = ts.leastCommonAncestor(typeRef.getCached(), exprType, c);
typeRef.update(t);
} catch (SemanticException e) {
Errors.issue(tc.job(), e, n);
}
}
}
}
/* vj: Commented out -- per X10 1.7 manual a void method or closure body
* cannot have a terminating expression.
* See test
* examples/Constructs/Closures/ClosureReturn5_MustFailCompile.
* if (typeRef.get().isVoid() && expr != null && implicit) {
NodeFactory nf = tc.nodeFactory();
if (expr instanceof Call || expr instanceof New || expr instanceof Assign)
return nf.Block(position(), nf.Eval(expr.position(), expr), nf.Return(position()));
}
*/
// XTENLANG-1939
//// Note that for the code def m(args) = e;
//// the e is translated into a return e;
//// Now we must make sure that if e is of type void, then
//// the return e; is replaced by {eval(e); return;}
//if (n.expr() != null && n.implicit && n.expr().type().isVoid()) {
// NodeFactory nf = tc.nodeFactory();
// return nf.Block(n.position(), nf.Eval(n.expr().position(), n.expr()), nf.Return(n.position()));
//}
if (n.expr() == null && ! typeRef.getCached().isVoid()) {
Errors.issue(tc.job(),
new Errors.MustReturnValueFromNonVoidMethod(n.position()));
}
if (n.expr() != null && typeRef.getCached().isVoid()) {
Errors.issue(tc.job(),
new Errors.CannotReturnValueFromVoidMethod(n.position()));
}
}
if (ci instanceof TypeDef) {
Errors.issue(tc.job(), new SemanticException("Cannot return from this context.", position()), this);
}
if (n.expr() != null) {
if (ci instanceof FunctionDef) {
FunctionDef fi = (FunctionDef) ci;
Type returnType = Types.get(fi.returnType());
// if (fi instanceof X10MemberDef) {
// XRoot classThisVar = ((X10ClassDef) c.currentClassDef()).thisVar();
// XRoot methodThisVar = ((X10MemberDef) fi).thisVar();
// if (classThisVar != null && methodThisVar != null) {
// returnType = X10MethodInstance_c.subst(returnType, new XVar[] { classThisVar }, new XRoot[] { methodThisVar });
// Type t = expr.type();
// t = X10MethodInstance_c.subst(t, new XVar[] { classThisVar }, new XRoot[] { methodThisVar });
// Expr e = X10New_c.attemptCoercion(tc, expr.type(t), returnType);
// n = (X10Return_c) n.expr(e);
// return n.superTypeCheck(tc);
// }
// }
Expr e = Converter.attemptCoercion(tc, n.expr(), returnType);
if (e != null) {
n = (X10Return_c) n.expr(e);
} else {
Errors.issue(tc.job(),
Errors.CannotReturnExpr.make(n.expr(), returnType, tc, n.expr().position()),
this);
}
}
}
return n;
}
}