diff --git a/src/main/scala/inox/ast/CallGraph.scala b/src/main/scala/inox/ast/CallGraph.scala index 12d68dd0fb28c4d5ad8b3879c8b60d29d300d513..a79b0ecaa122e1d6980a0cfaf686ee1e8dd38d77 100644 --- a/src/main/scala/inox/ast/CallGraph.scala +++ b/src/main/scala/inox/ast/CallGraph.scala @@ -96,7 +96,7 @@ trait CallGraph { val (c1, c2) = (functionComponent(a), functionComponent(b)) if (c1.isEmpty && c2.isEmpty) a.id.uniqueName.compare(b.id.uniqueName) else if (c1.isEmpty) -1 - else if (c2.isEmpty) -1 + else if (c2.isEmpty) +1 else componentOrdering.compare(c1, c2) } } diff --git a/src/main/scala/inox/ast/SymbolOps.scala b/src/main/scala/inox/ast/SymbolOps.scala index 7a007f1143f27affd0a68bb5daa49c94113c8511..9ebfc07ba4ee0b3f05aa0e601599cef593d758eb 100644 --- a/src/main/scala/inox/ast/SymbolOps.scala +++ b/src/main/scala/inox/ast/SymbolOps.scala @@ -5,6 +5,8 @@ package ast import utils._ +import scala.collection.mutable.{Map => MutableMap} + /** Provides functions to manipulate [[Expressions.Expr]] in cases where * a symbol table is available (and required: see [[ExprOps]] for * simpler tree manipulations). @@ -88,8 +90,8 @@ trait SymbolOps { self: TypeOps => fixpoint(postMap(rec))(expr) } - private val typedIds: scala.collection.mutable.Map[Type, List[Identifier]] = - scala.collection.mutable.Map.empty.withDefaultValue(List.empty) + private val typedIds: MutableMap[Type, List[Identifier]] = + MutableMap.empty.withDefaultValue(List.empty) /** Normalizes identifiers in an expression to enable some notion of structural * equality between expressions on which usual equality doesn't make sense @@ -106,64 +108,71 @@ trait SymbolOps { self: TypeOps => */ def normalizeStructure(args: Seq[ValDef], expr: Expr, onlySimple: Boolean = true): (Seq[ValDef], Expr, Map[Variable, Expr]) = synchronized { - val vars = args.map(_.toVariable).toSet - - class Normalizer extends SelfTreeTransformer { - var subst: Map[Variable, Expr] = Map.empty - var varSubst: Map[Identifier, Identifier] = Map.empty - var remainingIds: Map[Type, List[Identifier]] = typedIds.toMap - - def getId(e: Expr): Identifier = { - val tpe = bestRealType(e.getType) - val newId = remainingIds.get(tpe) match { - case Some(x :: xs) => - remainingIds += tpe -> xs - x - case _ => - val x = FreshIdentifier("x", true) - typedIds(tpe) = typedIds(tpe) :+ x - x - } - subst += Variable(newId, tpe) -> e - newId - } - override def transform(id: Identifier, tpe: Type): (Identifier, Type) = subst.get(Variable(id, tpe)) match { - case Some(Variable(newId, tpe)) => (newId, tpe) - case Some(_) => scala.sys.error("Should never happen!") - case None => varSubst.get(id) match { - case Some(newId) => (newId, tpe) - case None => - val newId = getId(Variable(id, tpe)) - varSubst += id -> newId - (newId, tpe) - } + val subst: MutableMap[Variable, Expr] = MutableMap.empty + val varSubst: MutableMap[Identifier, Identifier] = MutableMap.empty + + // Note: don't use clone here, we want to drop the `withDefaultValue` feature of [[typeIds]] + val remainingIds: MutableMap[Type, List[Identifier]] = MutableMap.empty ++ typedIds.toMap + + def getId(e: Expr): Identifier = { + val tpe = bestRealType(e.getType) + val newId = remainingIds.get(tpe) match { + case Some(x :: xs) => + remainingIds += tpe -> xs + x + case _ => + val x = FreshIdentifier("x", true) + typedIds(tpe) = typedIds(tpe) :+ x + x } + subst += Variable(newId, tpe) -> e + newId + } - override def transform(e: Expr): Expr = e match { - case expr if (!onlySimple || isSimple(expr)) && (variablesOf(expr) & vars).isEmpty => - Variable(getId(expr), expr.getType) - case f: Forall => - val (args, body, newSubst) = normalizeStructure(f.args, f.body, onlySimple) - subst ++= newSubst - Forall(args, body) - case l: Lambda => - val (args, body, newSubst) = normalizeStructure(l.args, l.body, onlySimple) - subst ++= newSubst - Lambda(args, body) - case _ => super.transform(e) + def transformId(id: Identifier, tpe: Type): (Identifier, Type) = subst.get(Variable(id, tpe)) match { + case Some(Variable(newId, tpe)) => (newId, tpe) + case Some(_) => scala.sys.error("Should never happen!") + case None => varSubst.get(id) match { + case Some(newId) => (newId, tpe) + case None => + val newId = getId(Variable(id, tpe)) + varSubst += id -> newId + (newId, tpe) } } - val n = new Normalizer - // this registers the argument images into n.subst - val bindings = args map n.transform - val normalized = n.transform(expr) + def rec(vars: Set[Variable], body: Expr): Expr = { + + class Normalizer extends SelfTreeTransformer { + override def transform(id: Identifier, tpe: Type): (Identifier, Type) = transformId(id, tpe) + + override def transform(e: Expr): Expr = e match { + case expr if (!onlySimple || isSimple(expr)) && (variablesOf(expr) & vars).isEmpty => + Variable(getId(expr), expr.getType) + case f: Forall => + val newBody = rec(vars ++ f.args.map(_.toVariable), f.body) + Forall(f.args.map(vd => vd.copy(id = varSubst(vd.id))), newBody) + case l: Lambda => + val newBody = rec(vars ++ l.args.map(_.toVariable), l.body) + Lambda(l.args.map(vd => vd.copy(id = varSubst(vd.id))), newBody) + case _ => super.transform(e) + } + } + + val n = new Normalizer + + // this registers the argument images into subst + vars foreach n.transform + n.transform(body) + } - val freeVars = variablesOf(normalized) -- bindings.map(_.toVariable) - val bodySubst = n.subst.filter(p => freeVars(p._1)) + val newExpr = rec(args.map(_.toVariable).toSet, expr) + val bindings = args.map(vd => vd.copy(id = varSubst(vd.id))) + val freeVars = variablesOf(newExpr) -- bindings.map(_.toVariable) + val bodySubst = subst.filter(p => freeVars(p._1)).toMap - (bindings, normalized, bodySubst) + (bindings, newExpr, bodySubst) } def normalizeStructure(lambda: Lambda): (Lambda, Map[Variable, Expr]) = { diff --git a/src/main/scala/inox/solvers/unrolling/LambdaTemplates.scala b/src/main/scala/inox/solvers/unrolling/LambdaTemplates.scala index ad9640ddcc12047532445d3743891218dddec67f..16510afc7fe73c9c88d31b674bb3ccbb259a6e45 100644 --- a/src/main/scala/inox/solvers/unrolling/LambdaTemplates.scala +++ b/src/main/scala/inox/solvers/unrolling/LambdaTemplates.scala @@ -14,6 +14,7 @@ trait LambdaTemplates { self: Templates => import lambdasManager._ import datatypesManager._ + import quantificationsManager._ /** Represents a POTENTIAL application of a first-class function in the unfolding procedure * @@ -83,7 +84,7 @@ trait LambdaTemplates { self: Templates => clauses, blockers, applications, matchers, lambdas, quantifications, structure, - lambda, lambdaString + lambda, lambdaString, false ) } } @@ -177,7 +178,7 @@ trait LambdaTemplates { self: Templates => val substituter = mkSubstituter(substMap.mapValues(_.encoded)) val deps = dependencies.map(substituter) - val key = (lambda, pathVar._2, deps) + val key = (lambda, blockerPath(pathVar._2), deps) val sortedDeps = exprOps.variablesOf(lambda).toSeq.sortBy(_.id.uniqueName) val locals = sortedDeps zip deps @@ -190,6 +191,10 @@ trait LambdaTemplates { self: Templates => } override def hashCode: Int = key.hashCode + + def subsumes(that: LambdaStructure): Boolean = { + key._1 == that.key._1 && key._3 == that.key._3 && key._2.subsetOf(that.key._2) + } } class LambdaTemplate private ( @@ -208,7 +213,8 @@ trait LambdaTemplates { self: Templates => val quantifications: Seq[QuantificationTemplate], val structure: LambdaStructure, val lambda: Lambda, - private[unrolling] val stringRepr: () => String) extends Template { + private[unrolling] val stringRepr: () => String, + private val isConcrete: Boolean) extends Template { val args = arguments.map(_._2) val tpe = bestRealType(ids._1.getType).asInstanceOf[FunctionType] @@ -224,10 +230,11 @@ trait LambdaTemplates { self: Templates => lambdas.map(_.substitute(substituter, msubst)), quantifications.map(_.substitute(substituter, msubst)), structure.substitute(substituter, msubst), - lambda, stringRepr) + lambda, stringRepr, isConcrete) /** This must be called right before returning the clauses in [[structure.instantiation]]! */ def concretize(idT: Encoded): LambdaTemplate = { + assert(!isConcrete, "Can't concretize concrete lambda template") val substituter = mkSubstituter(Map(ids._2 -> idT) ++ (closures.map(_._2) zip structure.locals.map(_._2))) new LambdaTemplate( ids._1 -> idT, @@ -238,7 +245,7 @@ trait LambdaTemplates { self: Templates => matchers.map { case (b, ms) => b -> ms.map(_.substitute(substituter, Map.empty)) }, lambdas.map(_.substitute(substituter, Map.empty)), quantifications.map(_.substitute(substituter, Map.empty)), - structure, lambda, stringRepr) + structure, lambda, stringRepr, true) } override def instantiate(blocker: Encoded, args: Seq[Arg]): Clauses = { @@ -262,6 +269,11 @@ trait LambdaTemplates { self: Templates => mkSubstituter(Map(vT -> caller) ++ (asT zip args))(app) } + def mkFlatApp(caller: Encoded, tpe: FunctionType, args: Seq[Encoded]): Encoded = tpe.to match { + case ft: FunctionType => mkFlatApp(mkApp(caller, tpe, args.take(tpe.from.size)), ft, args.drop(tpe.from.size)) + case _ => mkApp(caller, tpe, args) + } + def registerFunction(b: Encoded, tpe: FunctionType, f: Encoded): Clauses = { val ft = bestRealType(tpe).asInstanceOf[FunctionType] freeFunctions += ft -> (freeFunctions(ft) + (b -> f)) @@ -296,48 +308,50 @@ trait LambdaTemplates { self: Templates => } def instantiateLambda(template: LambdaTemplate): (Encoded, Clauses) = { - byType(template.tpe).get(template.structure) match { - case Some(template) => - (template.ids._2, Seq.empty) - - case None => - val idT = encodeSymbol(template.ids._1) - val newTemplate = template.concretize(idT) + byType(template.tpe).get(template.structure).map { + t => (t.ids._2, Seq.empty) + }.orElse { + byType(template.tpe).collectFirst { + case (s, t) if s subsumes template.structure => (t.ids._2, Seq.empty) + } + }.getOrElse { + val idT = encodeSymbol(template.ids._1) + val newTemplate = template.concretize(idT) - val orderingClauses = newTemplate.structure.locals.flatMap { - case (v, dep) => registerClosure(newTemplate.start, idT -> newTemplate.tpe, dep -> v.tpe) - } + val orderingClauses = newTemplate.structure.locals.flatMap { + case (v, dep) => registerClosure(newTemplate.start, idT -> newTemplate.tpe, dep -> v.tpe) + } - val extClauses = for ((oldB, freeF) <- freeBlockers(newTemplate.tpe) if canEqual(freeF, idT)) yield { - val nextB = encodeSymbol(Variable(FreshIdentifier("b_or", true), BooleanType)) - val ext = mkOr(mkAnd(newTemplate.start, mkEquals(idT, freeF)), nextB) - mkEquals(oldB, ext) - } + val extClauses = for ((oldB, freeF) <- freeBlockers(newTemplate.tpe) if canEqual(freeF, idT)) yield { + val nextB = encodeSymbol(Variable(FreshIdentifier("b_or", true), BooleanType)) + val ext = mkOr(mkAnd(newTemplate.start, mkEquals(idT, freeF)), nextB) + mkEquals(oldB, ext) + } - val arglessEqClauses = if (newTemplate.tpe.from.nonEmpty) Seq.empty[Encoded] else { - for ((b,f) <- freeFunctions(newTemplate.tpe) if canEqual(idT, f)) yield { - val (tmplApp, fApp) = (mkApp(idT, newTemplate.tpe, Seq.empty), mkApp(f, newTemplate.tpe, Seq.empty)) - mkImplies(mkAnd(b, newTemplate.start, mkEquals(tmplApp, fApp)), mkEquals(idT, f)) - } + val arglessEqClauses = if (newTemplate.tpe.from.nonEmpty) Seq.empty[Encoded] else { + for ((b,f) <- freeFunctions(newTemplate.tpe) if canEqual(idT, f)) yield { + val (tmplApp, fApp) = (mkApp(idT, newTemplate.tpe, Seq.empty), mkApp(f, newTemplate.tpe, Seq.empty)) + mkImplies(mkAnd(b, newTemplate.start, mkEquals(tmplApp, fApp)), mkEquals(idT, f)) } + } - // make sure we have sound equality between the new lambda and previously defined ones - val clauses = newTemplate.structure.instantiation ++ - equalityClauses(newTemplate) ++ - orderingClauses ++ - extClauses ++ - arglessEqClauses + // make sure we have sound equality between the new lambda and previously defined ones + val clauses = newTemplate.structure.instantiation ++ + equalityClauses(newTemplate) ++ + orderingClauses ++ + extClauses ++ + arglessEqClauses - byID += idT -> newTemplate - byType += newTemplate.tpe -> (byType(newTemplate.tpe) + (newTemplate.structure -> newTemplate)) + byID += idT -> newTemplate + byType += newTemplate.tpe -> (byType(newTemplate.tpe) + (newTemplate.structure -> newTemplate)) - val gen = nextGeneration(currentGeneration) - for (app @ (_, App(caller, _, args, _)) <- applications(newTemplate.tpe)) { - val cond = mkAnd(newTemplate.start, mkEquals(idT, caller)) - registerAppBlocker(gen, app, Left(newTemplate), cond, args) - } + val gen = nextGeneration(currentGeneration) + for (app @ (_, App(caller, _, args, _)) <- applications(newTemplate.tpe)) { + val cond = mkAnd(newTemplate.start, mkEquals(idT, caller)) + registerAppBlocker(gen, app, Left(newTemplate), cond, args) + } - (idT, clauses) + (idT, clauses) } } diff --git a/src/main/scala/inox/solvers/unrolling/QuantificationTemplates.scala b/src/main/scala/inox/solvers/unrolling/QuantificationTemplates.scala index 46d591a37568a05be17bfe8acd31a820b455188f..4e5bed2d032faf1933f3297bf4e5fe288d71e36d 100644 --- a/src/main/scala/inox/solvers/unrolling/QuantificationTemplates.scala +++ b/src/main/scala/inox/solvers/unrolling/QuantificationTemplates.scala @@ -81,8 +81,8 @@ trait QuantificationTemplates { self: Templates => * * Instantiations of unknown polarity quantification with body {{{p}}} follows the schema: * {{{ - * b ==> (q == (q2 && inst) - * b ==> (inst == (guard ==> p)) + * q == (q2 && inst) + * inst == (guard ==> p) * }}} * It is useful to keep the two clauses separate so that satisfying assignments can be * constructed where only certain `inst` variables are falsified. This is used to enable @@ -103,7 +103,7 @@ trait QuantificationTemplates { self: Templates => val matchers: Matchers, val lambdas: Seq[LambdaTemplate], val quantifications: Seq[QuantificationTemplate], - val key: (Seq[ValDef], Expr, Seq[Encoded]), + val key: (Encoded, Seq[ValDef], Expr, Seq[Encoded]), val body: Expr, stringRepr: () => String) { @@ -122,7 +122,7 @@ trait QuantificationTemplates { self: Templates => matchers.map { case (b, ms) => substituter(b) -> ms.map(_.substitute(substituter, msubst)) }, lambdas.map(_.substitute(substituter, msubst)), quantifications.map(_.substitute(substituter, msubst)), - (key._1, key._2, key._3.map(substituter)), + (substituter(key._1), key._2, key._3, key._4.map(substituter)), body, stringRepr) private lazy val str : String = stringRepr() @@ -197,7 +197,8 @@ trait QuantificationTemplates { self: Templates => extraGuarded merge guardedExprs, extraEqs ++ equations, lambdas, quantifications, substMap = substMap ++ extraSubst) - val key = templateKey(proposition.args, proposition.body, substMap) + val tk = templateKey(proposition.args, proposition.body, substMap) + val key = (pathVar._2, tk._1, tk._2, tk._3) (optVar, new QuantificationTemplate( pathVar, polarity, quantifiers, condVars, exprVars, condTree, clauses, @@ -218,8 +219,8 @@ trait QuantificationTemplates { self: Templates => val handledSubsts = new IncrementalMap[Quantification, Set[(Set[Encoded], Map[Encoded,Arg])]] val ignoredGrounds = new IncrementalMap[Int, Set[Quantification]] - val lambdaAxioms = new IncrementalSet[(LambdaStructure, Seq[(Variable, Encoded)])] - val templates = new IncrementalMap[(Seq[ValDef], Expr, Seq[Encoded]), Map[Encoded, Encoded]] + val lambdaAxioms = new IncrementalSet[LambdaStructure] + val templates = new IncrementalMap[(Encoded, Seq[ValDef], Expr, Seq[Encoded]), (QuantificationTemplate, Map[Encoded, Encoded])] val incrementals: Seq[IncrementalState] = Seq(quantifications, lambdaAxioms, templates, ignoredMatchers, handledMatchers, ignoredSubsts, handledSubsts, ignoredGrounds) @@ -338,14 +339,14 @@ trait QuantificationTemplates { self: Templates => @inline private def matcherKey(m: Matcher): MatcherKey = matcherKey(m.key) - private def correspond(k1: MatcherKey, k2: MatcherKey): Option[Boolean] = (k1, k2) match { + protected def correspond(k1: MatcherKey, k2: MatcherKey): Option[Boolean] = (k1, k2) match { case (`k2`, _) => Some(true) case (tp1: TypedKey, tp2: TypedKey) if tp1.tpe == tp2.tpe => Some(false) case _ => None } @inline - private def correspond(m1: Matcher, m2: Matcher): Option[Boolean] = + protected def correspond(m1: Matcher, m2: Matcher): Option[Boolean] = correspond(matcherKey(m1), matcherKey(m2)) private trait BlockedSet[Element] extends Iterable[(Set[Encoded], Element)] with IncrementalState { @@ -691,6 +692,14 @@ trait QuantificationTemplates { self: Templates => } } + for (tmpl <- lambdas.map(t => byID(substituter(t.ids._2)))) { + instantiation ++= instantiateAxiom(tmpl) + } + + for ((_, apps) <- applications; App(caller, _, _, _) <- apps; tmpl <- byID.get(substituter(caller))) { + instantiation ++= instantiateAxiom(tmpl) + } + instantiation.toSeq } @@ -821,63 +830,54 @@ trait QuantificationTemplates { self: Templates => } def instantiateAxiom(template: LambdaTemplate): Clauses = { - val quantifiers = template.arguments.map { p => p._1.freshen -> encodeSymbol(p._1) } - - val app = mkApplication(template.ids._1, quantifiers.map(_._1)) - val appT = mkEncoder(quantifiers.toMap + template.ids)(app) - val selfMatcher = Matcher(Left(template.ids._2 -> template.tpe), quantifiers.map(p => Left(p._2)), appT) - - val blocker = Variable(FreshIdentifier("blocker", true), BooleanType) - val blockerT = encodeSymbol(blocker) - - val guard = Variable(FreshIdentifier("guard", true), BooleanType) - val guardT = encodeSymbol(guard) - - val enablingClause = mkEquals(mkAnd(guardT, template.start), blockerT) - - /* Compute Axiom's unique key to avoid redudant instantiations */ + if (lambdaAxioms(template.structure) || lambdaAxioms.exists(s => s.subsumes(template.structure))) { + Seq.empty + } else { + lambdaAxioms += template.structure + val quantifiers = template.arguments + val app = mkFlatApp(template.ids._2, template.tpe, quantifiers.map(_._2)) + val matcher = Matcher(Left(template.ids._2 -> template.tpe), quantifiers.map(p => Left(p._2)), app) + + val guard = encodeSymbol(Variable(FreshIdentifier("guard", true), BooleanType)) + val substituter = mkSubstituter(Map(template.start -> guard)) + + val body: Expr = { + def rec(caller: Expr, body: Expr): Expr = body match { + case Lambda(args, body) => rec(Application(caller, args.map(_.toVariable)), body) + case _ => Equals(caller, body) + } + rec(template.ids._1, template.structure.lambda) + } - def flattenLambda(e: Expr): (Seq[ValDef], Expr) = e match { - case Lambda(args, body) => - val (recArgs, recBody) = flattenLambda(body) - (args ++ recArgs, recBody) - case _ => (Seq.empty, e) + val tk = QuantificationTemplate.templateKey( + quantifiers.map(_._1.toVal), + body, + template.structure.locals.toMap + template.ids + ) + val key = (template.pathVar._2, tk._1, tk._2, tk._3) + + instantiateQuantification(new QuantificationTemplate( + template.pathVar, + Positive(guard), + quantifiers, + template.condVars, + template.exprVars, + template.condTree, + template.clauses map substituter, + template.blockers.map { case (b, fis) => substituter(b) -> fis.map(_.substitute(substituter, Map.empty)) }, + template.applications.map { case (b, fas) => substituter(b) -> fas.map(_.substitute(substituter, Map.empty)) }, + template.matchers.map { case (b, ms) => + substituter(b) -> ms.map(_.substitute(substituter, Map.empty)) + } merge Map(guard -> Set(matcher)), + template.lambdas.map(_.substitute(substituter, Map.empty)), + template.quantifications.map(_.substitute(substituter, Map.empty)), + key, body, template.stringRepr))._2 // mapping is guaranteed empty!! } - - val (structArgs, structBody) = flattenLambda(template.structure.lambda) - assert(quantifiers.size == structArgs.size, "Expecting lambda templates to contain flattened lamdbas") - - val lambdaBody = exprOps.replaceFromSymbols((structArgs zip quantifiers.map(_._1)).toMap, structBody) - val quantBody = Equals(app, lambdaBody) - - val substMap = template.structure.locals.toMap + template.ids - - val key = QuantificationTemplate.templateKey(quantifiers.map(_._1.toVal), quantBody, substMap) - - val substituter = mkSubstituter((template.args zip quantifiers.map(_._2)).toMap + (template.start -> blockerT)) - val msubst = Map.empty[Encoded, Matcher] - - instantiateQuantification(new QuantificationTemplate( - template.pathVar, - Positive(guardT), - quantifiers, - template.condVars + (blocker -> blockerT), - template.exprVars, - template.condTree, - (template.clauses map substituter) :+ enablingClause, - template.blockers.map { case (b, fis) => substituter(b) -> fis.map(_.substitute(substituter, msubst)) }, - template.applications.map { case (b, fas) => substituter(b) -> fas.map(_.substitute(substituter, msubst)) }, - template.matchers.map { case (b, ms) => - substituter(b) -> ms.map(_.substitute(substituter, msubst)) - } merge Map(blockerT -> Set(selfMatcher)), - template.lambdas.map(_.substitute(substituter, msubst)), - template.quantifications.map(_.substitute(substituter, msubst)), - key, quantBody, template.stringRepr))._2 // mapping is guaranteed empty!! } def instantiateQuantification(template: QuantificationTemplate): (Map[Encoded, Encoded], Clauses) = { templates.get(template.key) match { - case Some(map) => + case Some((_, map)) => (map, Seq.empty) case None => @@ -908,9 +908,8 @@ trait QuantificationTemplates { self: Templates => clauses ++= substClauses // this will call `instantiateMatcher` on all matchers in `template.matchers` - val instClauses = Template.instantiate(template.clauses, + clauses ++= Template.instantiate(template.clauses, template.blockers, template.applications, template.matchers, substMap) - clauses ++= instClauses Map(insts._2 -> instT) @@ -931,7 +930,8 @@ trait QuantificationTemplates { self: Templates => clauses ++= quantification.instantiate(bs, m) } - val freshSubst = mkSubstituter(template.quantifiers.map(p => p._2 -> encodeSymbol(p._1)).toMap) + val freshQuantifiers = template.quantifiers.map(p => encodeSymbol(p._1)) + val freshSubst = mkSubstituter((template.quantifiers.map(_._2) zip freshQuantifiers).toMap) for ((b,ms) <- template.matchers; m <- ms) { clauses ++= instantiateMatcher(Set.empty[Encoded], m, false) // it is very rare that such instantiations are actually required, so we defer them @@ -943,7 +943,7 @@ trait QuantificationTemplates { self: Templates => Map(qs._2 -> qT) } - templates += template.key -> mapping + templates += template.key -> ((template, mapping)) (mapping, clauses.toSeq) } }