* returned, otherwise a replacement Decls object is cached and returned.
* @return { d: Decls | d.size = decls.size &&
* all i: [0..d.size) | d.declarations[i] = decls.declarations[i].accept(this) }
*/
public Expression visit(ProjectExpression project) {
Expression ret = lookup(project);
if (ret!=null) return ret;
final Expression expr = project.expression().accept(this);
final IntExpression[] cols = new IntExpression[project.arity()];
boolean allSame = expr==project.expression();
for(int i = 0, arity = project.arity(); i < arity; i++) {
cols[i] = project.column(i).accept(this);
allSame = allSame && (cols[i]==project.column(i));
}
ret = allSame ? project : expr.project(cols);
return cache(project, ret);
}