00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include "theory_array.h"
00031 #include "array_proof_rules.h"
00032 #include "typecheck_exception.h"
00033 #include "parser_exception.h"
00034 #include "smtlib_exception.h"
00035 #include "theory_core.h"
00036 #include "command_line_flags.h"
00037
00038
00039 using namespace std;
00040 using namespace CVCL;
00041
00042
00043
00044
00045
00046
00047 Theorem TheoryArray::renameExpr(const Expr& e) {
00048 Theorem thm = getCommonRules()->varIntroSkolem(e);
00049 DebugAssert(thm.isRewrite() && thm.getRHS().isSkolem(),
00050 "thm = "+thm.toString());
00051 theoryCore()->addToVarDB(thm.getRHS());
00052 return thm;
00053 }
00054
00055
00056
00057
00058
00059
00060
00061 TheoryArray::TheoryArray(TheoryCore* core)
00062 : Theory(core, "Arrays"), d_reads(core->getCM()->getCurrentContext()),
00063 d_applicationsInModel(core->getFlags()["applications"].getBool())
00064 {
00065 d_rules = createProofRules();
00066
00067
00068 getEM()->newKind(ARRAY, "ARRAY", true);
00069 getEM()->newKind(READ, "READ");
00070 getEM()->newKind(WRITE, "WRITE");
00071 getEM()->newKind(ARRAY_LITERAL, "ARRAY_LITERAL");
00072
00073 vector<int> kinds;
00074 kinds.push_back(ARRAY);
00075 kinds.push_back(READ);
00076 kinds.push_back(WRITE);
00077
00078 kinds.push_back(ARRAY_LITERAL);
00079
00080 registerTheory(this, kinds);
00081 }
00082
00083
00084
00085 TheoryArray::~TheoryArray() {
00086 if(d_rules != NULL) delete d_rules;
00087 }
00088
00089
00090 Theorem TheoryArray::rewrite(const Expr& e)
00091
00092 IF_DEBUG(
00093 {
00094 TRACE("rewrite array", "rewrite[Array](", e, ") {");
00095 Theorem res(rewriteDebug(e));
00096 TRACE("rewrite array", "rewrite[Array] => ", res.getExpr(), " }");
00097 return res;
00098 }
00099
00100 Theorem TheoryArray::rewriteDebug(const Expr& e)
00101 )
00102
00103 {
00104 Theorem thm;
00105 if (isRead(e)) {
00106 switch(e[0].getKind()) {
00107 case WRITE:
00108 thm = d_rules->rewriteReadWrite(e);
00109 return transitivityRule(thm, simplify(thm.getRHS()));
00110 case ARRAY_LITERAL: {
00111 Theorem res = d_rules->readArrayLiteral(e);
00112
00113 res = transitivityRule(res, simplify(res.getRHS()));
00114 IF_DEBUG(debugger.counter("Read array literals")++);
00115 return res;
00116 }
00117 default: {
00118 const Theorem& rep = e.getRep();
00119 if (rep.isNull()) return reflexivityRule(e);
00120 else return symmetryRule(rep);
00121 }
00122 }
00123 }
00124 else if (!e.isTerm()) {
00125 if (e.isEq() && e[0].isAtomic() && e[1].isAtomic() && isWrite(e[0])) {
00126 Expr left = e[0], right = e[1];
00127 int leftWrites = 1, rightWrites = 0;
00128
00129
00130 Expr e1 = left[0];
00131 while (isWrite(e1)) { leftWrites++; e1 = e1[0]; }
00132
00133 Expr e2 = right;
00134 while (isWrite(e2)) { rightWrites++; e2 = e2[0]; }
00135
00136 if (rightWrites == 0) {
00137 if (e1 == e2) {
00138 thm = d_rules->rewriteSameStore(e, leftWrites);
00139 return transitivityRule(thm, simplify(thm.getRHS()));
00140 }
00141 else {
00142 e.setRewriteNormal();
00143 return reflexivityRule(e);
00144 }
00145 }
00146
00147 if (leftWrites > rightWrites) {
00148 thm = getCommonRules()->rewriteUsingSymmetry(e);
00149 thm = transitivityRule(thm, d_rules->rewriteWriteWrite(thm.getRHS()));
00150 }
00151 else thm = d_rules->rewriteWriteWrite(e);
00152 return transitivityRule(thm, simplify(thm.getRHS()));
00153 }
00154 e.setRewriteNormal();
00155 return reflexivityRule(e);
00156 }
00157 else if (!e.isAtomic()) {
00158 e.setRewriteNormal();
00159 return reflexivityRule(e);
00160 }
00161 else if (isWrite(e)) {
00162 Expr e1 = e[2].getType().isBool() ? e[2].iffExpr(Expr(READ, e[0], e[1])) :
00163 e[2].eqExpr(Expr(READ, e[0], e[1]));
00164 thm = simplify(e1);
00165 if (thm.getRHS().isTrue()) {
00166 return d_rules->rewriteRedundantWrite1(
00167 getCommonRules()->iffTrueElim(thm), e);
00168 }
00169 if (isWrite(e[0])) {
00170 if (e[0][1] == e[1]) {
00171 return d_rules->rewriteRedundantWrite2(e);
00172 }
00173 if (e[0][1] > e[1]) {
00174 thm = d_rules->interchangeIndices(e);
00175 return transitivityRule(thm, simplify(thm.getRHS()));
00176 }
00177 }
00178 return reflexivityRule(e);
00179 }
00180
00181
00182
00183
00184
00185 return reflexivityRule(e);
00186 }
00187
00188
00189 void TheoryArray::setup(const Expr& e)
00190 {
00191 if (e.isNot() || e.isEq()) return;
00192 DebugAssert(e.isAtomic(), "e should be atomic");
00193 for (int k = 0; k < e.arity(); ++k) {
00194 e[k].addToNotify(this, e);
00195 }
00196 if (isRead(e)) {
00197 Theorem thm = reflexivityRule(e);
00198 e.setSig(thm);
00199 e.setRep(thm);
00200
00201 TRACE("model", "TheoryArray: add array read ", e, "");
00202 d_reads.push_back(e);
00203 }
00204 else if (isWrite(e)) {
00205
00206 Expr store = e[0];
00207 while (isWrite(store)) store = store[0];
00208 Expr r = simplifyExpr(Expr(READ, store, e[1]));
00209 theoryCore()->setupTerm(r, this);
00210 DebugAssert(r.isAtomic(), "Should be atomic");
00211 r.addToNotify(this, e);
00212 }
00213 }
00214
00215
00216 void TheoryArray::update(const Theorem& e, const Expr& d)
00217 {
00218 int k, ar(d.arity());
00219
00220 if (isRead(d)) {
00221 const Theorem& dEQdsig = d.getSig();
00222 if (!dEQdsig.isNull()) {
00223 Expr dsig = dEQdsig.getRHS();
00224 Theorem thm = updateHelper(d);
00225 Expr sigNew = thm.getRHS();
00226 if (sigNew == dsig) return;
00227 dsig.setRep(Theorem());
00228 if (isWrite(sigNew[0])) {
00229 thm = transitivityRule(thm, d_rules->rewriteReadWrite(sigNew));
00230 Theorem renameTheorem = renameExpr(d);
00231 enqueueFact(transitivityRule(symmetryRule(renameTheorem), thm));
00232 d.setSig(Theorem());
00233 enqueueFact(renameTheorem);
00234 }
00235 else {
00236 const Theorem& repEQsigNew = sigNew.getRep();
00237 if (!repEQsigNew.isNull()) {
00238 d.setSig(Theorem());
00239 enqueueFact(transitivityRule(repEQsigNew, symmetryRule(thm)));
00240 }
00241 else {
00242 for (k = 0; k < ar; ++k) {
00243 if (sigNew[k] != dsig[k]) {
00244 sigNew[k].addToNotify(this, d);
00245 }
00246 }
00247 d.setSig(thm);
00248 sigNew.setRep(thm);
00249 }
00250 }
00251 }
00252 }
00253 else {
00254 DebugAssert(isWrite(d), "Expected write: d = "+d.toString());
00255 if (find(d).getRHS() == d) {
00256 Theorem thm = updateHelper(d);
00257 Expr sig0, sigNew;
00258 do {
00259 sig0 = thm.getRHS();
00260 thm = transitivityRule(thm, rewrite(sig0));
00261 sigNew = thm.getRHS();
00262 } while (sig0 != sigNew);
00263
00264 if (d == sigNew) {
00265
00266 e.getRHS().addToNotify(this, d);
00267 }
00268 else if (sigNew.isAtomic()) {
00269 enqueueEquality(thm);
00270 }
00271 else {
00272 Theorem renameTheorem = renameExpr(d);
00273 enqueueFact(transitivityRule(symmetryRule(renameTheorem), thm));
00274 enqueueEquality(renameTheorem);
00275 }
00276 }
00277 }
00278 }
00279
00280
00281 Theorem TheoryArray::solve(const Theorem& eThm)
00282 {
00283 const Expr& e = eThm.getExpr();
00284 DebugAssert(e.isEq(), "TheoryArray::solve("+e.toString()+")");
00285 if (isWrite(e[0])) {
00286 DebugAssert(!isWrite(e[1]), "Should have been rewritten: e = "
00287 +e.toString());
00288 return symmetryRule(eThm);
00289 }
00290 return eThm;
00291 }
00292
00293
00294 void TheoryArray::checkType(const Expr& e)
00295 {
00296 switch (e.getKind()) {
00297 case ARRAY: {
00298 if (e.arity() != 2) throw Exception
00299 ("ARRAY type should have two arguments");
00300 Type t1(e[0]);
00301 if (t1.isBool()) throw Exception
00302 ("Array index types must be non-Boolean");
00303 if (t1.isFunction()) throw Exception
00304 ("Array index types cannot be functions");
00305 Type t2(e[1]);
00306 if (t2.isBool()) throw Exception
00307 ("Array value types must be non-Boolean");
00308 if (t2.isFunction()) throw Exception
00309 ("Array value types cannot be functions");
00310 break;
00311 }
00312 default:
00313 DebugAssert(false, "Unexpected kind in TheoryArray::checkType"
00314 +getEM()->getKindName(e.getKind()));
00315 }
00316
00317 }
00318
00319
00320 void TheoryArray::computeType(const Expr& e)
00321 {
00322 switch (e.getKind()) {
00323 case READ: {
00324 DebugAssert(e.arity() == 2,"");
00325 Type arrType = e[0].getType();
00326 Type idxType = getBaseType(e[1]);
00327 if (!isArray(arrType))
00328 throw TypecheckException
00329 ("Expected an ARRAY type in\n\n "
00330 +e[0].toString()+"\n\nBut received this:\n\n "
00331 +arrType.toString()+"\n\nIn the expression:\n\n "
00332 +e.toString());
00333 if(getBaseType(arrType[0]) != idxType) {
00334 throw TypecheckException
00335 ("The type of index expression:\n\n "
00336 +idxType.toString()
00337 +"\n\nDoes not match the ARRAY index type:\n\n "
00338 +arrType[0].toString()
00339 +"\n\nIn the expression: "+e.toString());
00340 }
00341 e.setType(arrType[1]);
00342 break;
00343 }
00344 case WRITE: {
00345 DebugAssert(e.arity() == 3,"");
00346 Type arrType = e[0].getType();
00347 Type idxType = getBaseType(e[1]);
00348 Type valType = getBaseType(e[2]);
00349
00350 bool idxCorrect = getBaseType(arrType[0]) == idxType;
00351 bool valCorrect = getBaseType(arrType[1]) == valType;
00352 if (!isArray(arrType))
00353 throw TypecheckException
00354 ("Expected an ARRAY type in\n\n "
00355 +e[0].toString()+"\n\nBut received this:\n\n "
00356 +arrType.toString()+"\n\nIn the expression:\n\n "
00357 +e.toString());
00358 if(!idxCorrect) {
00359 throw TypecheckException
00360 ("The type of index expression:\n\n "
00361 +idxType.toString()
00362 +"\n\nDoes not match the ARRAY's type index:\n\n "
00363 +arrType[0].toString()
00364 +"\n\nIn the expression: "+e.toString());
00365 }
00366 if(!valCorrect) {
00367 throw TypecheckException
00368 ("The type of value expression:\n\n "
00369 +idxType.toString()
00370 +"\n\nDoes not match the ARRAY's value type:\n\n "
00371 +arrType[1].toString()
00372 +"\n\nIn the expression: "+e.toString());
00373 }
00374 e.setType(arrType);
00375 break;
00376 }
00377 case ARRAY_LITERAL: {
00378 DebugAssert(e.isClosure(), "TheoryArray::computeType("+e.toString()+")");
00379 DebugAssert(e.getVars().size()==1,
00380 "TheoryArray::computeType("+e.toString()+")");
00381 Type ind(e.getVars()[0].getType());
00382 e.setType(arrayType(ind, e.getBody().getType()));
00383 break;
00384 }
00385 default:
00386 DebugAssert(false,"Unexpected type");
00387 break;
00388 }
00389 }
00390
00391
00392 Type TheoryArray::computeBaseType(const Type& t) {
00393 const Expr& e = t.getExpr();
00394 DebugAssert(e.getKind()==ARRAY && e.arity() == 2,
00395 "TheoryArray::computeBaseType("+t.toString()+")");
00396 vector<Expr> kids;
00397 for(Expr::iterator i=e.begin(), iend=e.end(); i!=iend; ++i) {
00398 kids.push_back(getBaseType(Type(*i)).getExpr());
00399 }
00400 return Type(Expr(e.getOp(), kids));
00401 }
00402
00403
00404 void TheoryArray::computeModelTerm(const Expr& e, std::vector<Expr>& v) {
00405 switch(e.getKind()) {
00406 case WRITE:
00407
00408
00409
00410 v.push_back(e[0]);
00411 v.push_back(e[1]);
00412 v.push_back(e[2]);
00413 break;
00414 case READ:
00415
00416
00417 v.push_back(e[1]);
00418
00419
00420 default:
00421
00422 if(e.getType().getExpr().getKind() == ARRAY) {
00423 for(CDList<Expr>::const_iterator i=d_reads.begin(), iend=d_reads.end();
00424 i!=iend; ++i) {
00425 DebugAssert(isRead(*i) && (*i).arity()==2, "Bad array read: "
00426 +(*i).toString());
00427 if((*i)[0] == e) {
00428
00429
00430 v.push_back(*i);
00431
00432
00433
00434
00435 v.push_back((*i)[1]);
00436 }
00437 }
00438 }
00439 break;
00440 }
00441 }
00442
00443
00444 void TheoryArray::computeModel(const Expr& e, std::vector<Expr>& v) {
00445 static unsigned count(0);
00446
00447 switch(e.getKind()) {
00448 case WRITE: {
00449
00450 Expr res(getModelValue(e[0]).getRHS());
00451 Expr ind(getEM()->newBoundVarExpr("arr_var", int2string(count++)));
00452 Type tp(e.getType());
00453 DebugAssert(isArray(tp) && tp.arity()==2,
00454 "TheoryArray::computeModel(WRITE)");
00455 ind.setType(tp[0]);
00456 res = rewrite(Expr(READ, res, ind)).getRHS();
00457 Expr indVal(getModelValue(e[1]).getRHS());
00458 Expr updVal(getModelValue(e[2]).getRHS());
00459 res = (ind.eqExpr(indVal)).iteExpr(updVal, res);
00460 res = arrayLiteral(ind, res);
00461 assignValue(e, res);
00462 v.push_back(e);
00463 break;
00464 }
00465
00466
00467
00468
00469
00470
00471
00472
00473 default: {
00474
00475 v.push_back(e);
00476
00477 ExprHashMap<Expr> reads;
00478
00479 DebugAssert(getBaseType(e).getExpr().getKind() == ARRAY, "");
00480
00481 for(CDList<Expr>::const_iterator i=d_reads.begin(), iend=d_reads.end();
00482 i!=iend; ++i) {
00483 TRACE("model", "TheoryArray::computeModel: read = ", *i, "");
00484 DebugAssert(isRead(*i) && (*i).arity()==2, "Bad array read: "
00485 +(*i).toString());
00486 if((*i)[0] == e) {
00487
00488 Theorem asst(getModelValue((*i)[1]));
00489 Expr var;
00490 if(asst.getLHS()!=asst.getRHS()) {
00491 vector<Theorem> thms;
00492 vector<unsigned> changed;
00493 thms.push_back(asst);
00494 changed.push_back(1);
00495 Theorem subst(substitutivityRule(*i, changed, thms));
00496 assignValue(transitivityRule(symmetryRule(subst),
00497 getModelValue(*i)));
00498 var = subst.getRHS();
00499 } else
00500 var = *i;
00501 if(d_applicationsInModel) v.push_back(var);
00502
00503 Expr val(getModelValue(var).getRHS());
00504 DebugAssert(!val.isNull(), "TheoryArray::computeModel(): Variable "
00505 +var.toString()+" has a Null value");
00506 reads[var] = val;
00507 }
00508 }
00509
00510 if(reads.size()==0) {
00511 assignValue(reflexivityRule(e));
00512 } else {
00513
00514 Expr ind(getEM()->newBoundVarExpr("arr_var", int2string(count++)));
00515 Type tp(e.getType());
00516 DebugAssert(isArray(tp) && tp.arity()==2,
00517 "TheoryArray::computeModel(WRITE)");
00518 ind.setType(tp[0]);
00519 ExprHashMap<Expr>::iterator i=reads.begin(), iend=reads.end();
00520 DebugAssert(i!=iend,
00521 "TheoryArray::computeModel(): expected the reads "
00522 "table be non-empty");
00523
00524 Expr res((*i).second);
00525 ++i;
00526 DebugAssert(!res.isNull(), "TheoryArray::computeModel()");
00527 for(; i!=iend; ++i) {
00528
00529
00530
00531 if((*i).second == res) continue;
00532
00533 Expr cond = ind.eqExpr((*i).first[1]);
00534 res = cond.iteExpr((*i).second, res);
00535 }
00536
00537 res = arrayLiteral(ind, res);
00538 assignValue(e, res);
00539 }
00540 break;
00541 }
00542 }
00543 }
00544
00545
00546 Expr TheoryArray::computeTCC(const Expr& e)
00547 {
00548 Expr tcc(Theory::computeTCC(e));
00549 switch (e.getKind()) {
00550 case READ:
00551 DebugAssert(e.arity() == 2,"");
00552 return tcc.andExpr(getTypePred(e[0].getType()[0], e[1]));
00553 case WRITE: {
00554 DebugAssert(e.arity() == 3,"");
00555 Type arrType = e[0].getType();
00556 return rewriteAnd(getTypePred(arrType[0], e[1]).andExpr
00557 (getTypePred(arrType[1], e[2])).andExpr(tcc)).getRHS();
00558 }
00559 case ARRAY_LITERAL: {
00560 return tcc;
00561 }
00562 default:
00563 DebugAssert(false,"TheoryArray::computeTCC: Unexpected expression: "
00564 +e.toString());
00565 return tcc;
00566 }
00567 }
00568
00569
00570
00571
00572
00573
00574
00575 ExprStream& TheoryArray::print(ExprStream& os, const Expr& e) {
00576 switch(os.lang()) {
00577 case PRESENTATION_LANG:
00578 switch(e.getKind()) {
00579 case READ:
00580 if(e.arity() == 1)
00581 os << "[" << push << e[0] << push << "]";
00582 else
00583 os << e[0] << "[" << push << e[1] << push << "]";
00584 break;
00585 case WRITE:
00586 os << "(" << push << e[0] << space << "WITH ["
00587 << push << e[1] << "] := " << push << e[2] << push << ")";
00588 break;
00589 case ARRAY:
00590 os << "ARRAY" << space << e[0] << space << "OF" << space << e[1];
00591 break;
00592 case ARRAY_LITERAL:
00593 if(e.isClosure()) {
00594 const vector<Expr>& vars = e.getVars();
00595 const Expr& body = e.getBody();
00596 os << "(" << push << "ARRAY" << space << "(" << push;
00597 bool first(true);
00598 for(size_t i=0; i<vars.size(); ++i) {
00599 if(first) first=false;
00600 else os << push << "," << pop << space;
00601 os << vars[i];
00602 if(vars[i].isVar())
00603 os << ":" << space << pushdag << vars[i].getType() << popdag;
00604 }
00605 os << push << "):" << pop << pop << space << body << push << ")";
00606 } else
00607 e.printAST(os);
00608 break;
00609 default:
00610
00611
00612 e.printAST(os);
00613 }
00614 break;
00615 case SMTLIB_LANG:
00616 d_theoryUsed = true;
00617 switch(e.getKind()) {
00618 case READ:
00619 if(e.arity() == 2)
00620 os << "(" << push << "select" << space << e[0]
00621 << space << e[1] << push << ")";
00622 else
00623 e.printAST(os);
00624 break;
00625 case WRITE:
00626 if(e.arity() == 3)
00627 os << "(" << push << "store" << space << e[0]
00628 << space << e[1] << space << e[2] << push << ")";
00629 else
00630 e.printAST(os);
00631 break;
00632 case ARRAY:
00633 os << "Array";
00634 break;
00635 default:
00636 throw SmtlibException("TheoryArray::print: default not supported");
00637
00638
00639 e.printAST(os);
00640 }
00641 break;
00642 case LISP_LANG:
00643 switch(e.getKind()) {
00644 case READ:
00645 if(e.arity() == 2)
00646 os << "(" << push << "READ" << space << e[0]
00647 << space << e[1] << push << ")";
00648 else
00649 e.printAST(os);
00650 break;
00651 case WRITE:
00652 if(e.arity() == 3)
00653 os << "(" << push << "WRITE" << space << e[0]
00654 << space << e[1] << space << e[2] << push << ")";
00655 else
00656 e.printAST(os);
00657 break;
00658 case ARRAY:
00659 os << "(" << push << "ARRAY" << space << e[0]
00660 << space << e[1] << push << ")";
00661 break;
00662 default:
00663
00664
00665 e.printAST(os);
00666 }
00667 break;
00668 default:
00669
00670
00671 e.printAST(os);
00672 }
00673 return os;
00674 }
00675
00676
00677
00678
00679
00680 Expr
00681 TheoryArray::parseExprOp(const Expr& e) {
00682 TRACE("parser", "TheoryArray::parseExprOp(", e, ")");
00683
00684
00685 if(RAW_LIST != e.getKind()) return e;
00686
00687 DebugAssert(e.arity() > 0,
00688 "TheoryArray::parseExprOp:\n e = "+e.toString());
00689
00690 const Expr& c1 = e[0][0];
00691 int kind = getEM()->getKind(c1.getString());
00692 switch(kind) {
00693 case READ:
00694 if(!e.arity() == 3)
00695 throw ParserException("Bad array access expression: "+e.toString());
00696 return Expr(READ, parseExpr(e[1]), parseExpr(e[2]));
00697 case WRITE:
00698 if(!e.arity() == 4)
00699 throw ParserException("Bad array update expression: "+e.toString());
00700 return Expr(WRITE, parseExpr(e[1]), parseExpr(e[2]), parseExpr(e[3]));
00701 case ARRAY: {
00702 vector<Expr> k;
00703 Expr::iterator i = e.begin(), iend=e.end();
00704
00705
00706 ++i;
00707
00708 for(; i!=iend; ++i)
00709 k.push_back(parseExpr(*i));
00710 return Expr(kind, k, e.getEM());
00711 break;
00712 }
00713 case ARRAY_LITERAL: {
00714 if(!(e.arity() == 3 && e[1].getKind() == RAW_LIST && e[1].arity() == 2))
00715 throw ParserException("Bad ARRAY literal expression: "+e.toString());
00716 const Expr& varPair = e[1];
00717 if(varPair.getKind() != RAW_LIST)
00718 throw ParserException("Bad variable declaration block in ARRAY "
00719 "literal expression: "+varPair.toString()+
00720 "\n e = "+e.toString());
00721 if(varPair[0].getKind() != ID)
00722 throw ParserException("Bad variable declaration in ARRAY"
00723 "literal expression: "+varPair.toString()+
00724 "\n e = "+e.toString());
00725 Type varTp(parseExpr(varPair[1]));
00726 vector<Expr> var;
00727 var.push_back(addBoundVar(varPair[0][0].getString(), varTp));
00728 Expr body(parseExpr(e[2]));
00729
00730 return getEM()->newClosureExpr(ARRAY_LITERAL, var, body);
00731 break;
00732 }
00733 default:
00734 DebugAssert(false,
00735 "TheoryArray::parseExprOp: wrong type: "
00736 + e.toString());
00737 break;
00738 }
00739 return e;
00740 }
00741
00742 namespace CVCL {
00743
00744 Expr arrayLiteral(const Expr& ind, const Expr& body) {
00745 vector<Expr> vars;
00746 vars.push_back(ind);
00747 return body.getEM()->newClosureExpr(ARRAY_LITERAL, vars, body);
00748 }
00749
00750 }