/** {@inheritDoc} */
@Override public Expr visit(ExprQt x) throws Err {
TempList<Decl> decls = new TempList<Decl>(x.decls.size());
boolean isMetaSig=false, isMetaField=false;
for(Decl d: x.decls) {
Expr exp = visitThis(d.expr).resolve_as_set(warns);
if (exp.mult==0 && exp.type().arity()==1) exp = ExprUnary.Op.ONEOF.make(null, exp);
if (exp.errors.isEmpty()) {
if (exp.type().isSubtypeOf(rootmodule.metaSig().plus(rootmodule.metaField()).type())) {
isMetaSig = exp.type().intersects(rootmodule.metaSig().type());
isMetaField = exp.type().intersects(rootmodule.metaField().type());
}
}
// Below is a special case to allow more fine-grained typechecking when we see "all x:field$" or "some x:field$"
boolean some = (x.op==ExprQt.Op.SOME), compre = (x.op==ExprQt.Op.COMPREHENSION);
if (x.decls.size()==1 && d.names.size()==1 && isOneOf(exp) && (x.op==ExprQt.Op.ALL || some || compre) && (isMetaSig || isMetaField)) {
ExprVar v = (ExprVar)(d.names.get(0));
// Prevent warnings
List<ErrorWarning> saved = warns;
warns = null;
// Now duplicate the body for each possible Meta Atom binding
Expr answer = null;
if (isMetaSig) for(PrimSig child: rootmodule.metaSig().children()) if (child.type().intersects(exp.type())) {
put(v.label, child);
Expr sub = visitThis(x.sub);
remove(v.label);
if (compre) answer = child.in(exp).and(sub).ite(child, Sig.NONE).plus(answer);
else if (some) answer = child.in(exp).and(sub).or(answer);
else answer = child.in(exp).implies(sub).and(answer);
}
if (isMetaField) for(PrimSig child: rootmodule.metaField().children()) if (child.type().intersects(exp.type())) {
put(v.label, child);
Expr sub = visitThis(x.sub);
remove(v.label);
if (compre) answer = child.in(exp).and(sub).ite(child, Sig.NONE).plus(answer);
else if (some) answer = child.in(exp).and(sub).or(answer);
else answer = child.in(exp).implies(sub).and(answer);
}
if (answer==null) answer = (compre ? Sig.NONE : (some ? ExprConstant.FALSE : ExprConstant.TRUE)); else answer = answer.resolve(answer.type(), null);
// Now, wrap the body in an ExprLet expression to prevent any more warnings by outer code
ExprVar combinedAnswer = ExprVar.make(Pos.UNKNOWN, "", answer.type());
Expr returnValue = ExprLet.make(Pos.UNKNOWN, combinedAnswer, answer, combinedAnswer);
// Restore the "warns" array, then return the answer
warns = saved;
return returnValue;
}
// Above is a special case to allow more fine-grained typechecking when we see "all x:field$" or "some x:field$"
TempList<ExprVar> n = new TempList<ExprVar>(d.names.size());
for(ExprHasName v: d.names) n.add(ExprVar.make(v.pos, v.label, exp.type()));
Decl dd = new Decl(d.isPrivate, d.disjoint, d.disjoint2, n.makeConst(), exp);
for(ExprHasName newname: dd.names) put(newname.label, newname);
decls.add(dd);
}
Expr sub = visitThis(x.sub);
if (x.op==ExprQt.Op.SUM) sub=sub.resolve_as_int(warns); else sub=sub.resolve_as_formula(warns);
for(Decl d: decls.makeConst()) for(ExprHasName v: d.names) remove(v.label);
return x.op.make(x.pos, x.closingBracket, decls.makeConst(), sub);
}