bool RewriteMatchingLoop() { if (expr is QuantifierExpr) { QuantifierExpr quantifier = (QuantifierExpr)expr; var l = new List <QuantifierWithTriggers>(); List <Expression> splits = new List <Expression>(); bool rewritten = false; foreach (var q in quantifiers) { if (TriggerUtils.NeedsAutoTriggers(q.quantifier) && TriggerUtils.WantsMatchingLoopRewrite(q.quantifier)) { var matchingLoopRewriter = new MatchingLoopRewriter(); var qq = matchingLoopRewriter.RewriteMatchingLoops(q); splits.Add(qq); l.Add(new QuantifierWithTriggers(qq)); rewritten = true; } else { // don't rewrite the quantifier if we are not auto generate triggers. // This is because rewriting introduces new boundvars and will cause // user provided triggers not mention all boundvars splits.Add(q.quantifier); l.Add(q); } } if (rewritten) { quantifier.SplitQuantifier = splits; quantifiers = l; return(true); } } return(false); }
private static bool ShallowEq(LiteralExpr expr1, LiteralExpr expr2) { if (!TriggerUtils.SameNullity(expr1.Value, expr2.Value) || (expr1.Value != null && !expr1.Value.Equals(expr2.Value))) { return(false); } if (expr1 is StringLiteralExpr && expr2 is StringLiteralExpr) { return(ShallowEq((StringLiteralExpr)expr1, (StringLiteralExpr)expr2)); } else if (expr1 is CharLiteralExpr && expr2 is CharLiteralExpr) { return(ShallowEq((CharLiteralExpr)expr1, (CharLiteralExpr)expr2)); } else if (expr1 is StaticReceiverExpr && expr2 is StaticReceiverExpr) { return(ShallowEq((StaticReceiverExpr)expr1, (StaticReceiverExpr)expr2)); } else { return(expr1.GetType() == expr2.GetType()); // LiteralExpr is not abstract } }
/// <summary> /// Collect triggers from the body of each quantifier, and share them /// between all quantifiers. This method assumes that all quantifiers /// actually come from the same context, and were the result of a split that /// gave them all the same variables. /// </summary> /// <param name="triggersCollector"></param> void CollectAndShareTriggers(TriggersCollector triggersCollector) { List <TriggerTerm> pool = new List <TriggerTerm>(); foreach (var q in quantifiers) { var candidates = triggersCollector.CollectTriggers(q.quantifier).Deduplicate(TriggerTerm.Eq); // filter out the candidates that was "second-class" var filtered = TriggerUtils.Filter(candidates, tr => tr, (tr, _) => !tr.IsTranslatedToFunctionCall(), (tr, _) => { }).ToList(); // if there are only "second-class" candidates, add them back. if (filtered.Count == 0) { filtered = candidates; } pool.AddRange(filtered); } var distinctPool = pool.Deduplicate(TriggerTerm.Eq); foreach (var q in quantifiers) { q.CandidateTerms = distinctPool; // The list of candidate terms is immutable q.Candidates = TriggerUtils.AllNonEmptySubsets(distinctPool, SubsetGenerationPredicate, q.quantifier.BoundVars).Select(set => set.ToTriggerCandidate()).ToList(); } }
public QuantifierExpr RewriteMatchingLoops(QuantifierWithTriggers q) { // rewrite quantifier to avoid matching loops // before: // assert forall i :: 0 <= i < a.Length-1 ==> a[i] <= a[i+1]; // after: // assert forall i,j :: j == i+1 ==> 0 <= i < a.Length-1 ==> a[i] <= a[j]; substMap = new List <Tuple <Expression, IdentifierExpr> >(); foreach (var m in q.LoopingMatches) { var e = m.OriginalExpr; if (TriggersCollector.IsPotentialTriggerCandidate(e) && triggersCollector.IsTriggerKiller(e)) { foreach (var sub in e.SubExpressions) { if (triggersCollector.IsTriggerKiller(sub) && (!TriggersCollector.IsPotentialTriggerCandidate(sub))) { var entry = substMap.Find(x => ExprExtensions.ExpressionEq(sub, x.Item1)); if (entry == null) { var newBv = new BoundVar(sub.tok, "_t#" + substMap.Count, sub.Type); var ie = new IdentifierExpr(sub.tok, newBv.Name); ie.Var = newBv; ie.Type = newBv.Type; substMap.Add(new Tuple <Expression, IdentifierExpr>(sub, ie)); } } } } } var expr = (QuantifierExpr)q.quantifier; if (substMap.Count > 0) { var s = new Translator.ExprSubstituter(substMap); expr = s.Substitute(q.quantifier) as QuantifierExpr; } else { // make a copy of the expr if (expr is ForallExpr) { expr = new ForallExpr(expr.tok, expr.TypeArgs, expr.BoundVars, expr.Range, expr.Term, TriggerUtils.CopyAttributes(expr.Attributes)) { Type = expr.Type, Bounds = expr.Bounds }; } else { expr = new ExistsExpr(expr.tok, expr.TypeArgs, expr.BoundVars, expr.Range, expr.Term, TriggerUtils.CopyAttributes(expr.Attributes)) { Type = expr.Type, Bounds = expr.Bounds }; } } return(expr); }
internal static IEnumerable <Expression> SplitQuantifier(ComprehensionExpr quantifier) { var body = quantifier.Term; var binary = body as BinaryExpr; if (quantifier is ForallExpr) { IEnumerable <Expression> stream; if (binary != null && (binary.Op == BinaryExpr.Opcode.Imp || binary.Op == BinaryExpr.Opcode.Or)) { stream = SplitAndStich(binary, BinaryExpr.Opcode.And); } else { stream = SplitExpr(body, BinaryExpr.Opcode.And); } foreach (var e in stream) { var tok = new NestedToken(quantifier.tok, e.tok); yield return(new ForallExpr(tok, ((ForallExpr)quantifier).TypeArgs, quantifier.BoundVars, quantifier.Range, e, TriggerUtils.CopyAttributes(quantifier.Attributes)) { Type = quantifier.Type, Bounds = quantifier.Bounds }); } } else if (quantifier is ExistsExpr) { IEnumerable <Expression> stream; if (binary != null && binary.Op == BinaryExpr.Opcode.And) { stream = SplitAndStich(binary, BinaryExpr.Opcode.Or); } else { stream = SplitExpr(body, BinaryExpr.Opcode.Or); } foreach (var e in stream) { var tok = new NestedToken(quantifier.tok, e.tok); yield return(new ExistsExpr(tok, ((ExistsExpr)quantifier).TypeArgs, quantifier.BoundVars, quantifier.Range, e, TriggerUtils.CopyAttributes(quantifier.Attributes)) { Type = quantifier.Type, Bounds = quantifier.Bounds }); } } else { yield return(quantifier); } }
private static bool ShallowEq(MemberSelectExpr expr1, MemberSelectExpr expr2) { return(expr1.MemberName == expr2.MemberName && expr1.Member == expr2.Member && TriggerUtils.SameLists(expr1.TypeApplication, expr2.TypeApplication, TypeEq)); }
private static bool ShallowEq(NamedExpr expr1, NamedExpr expr2) { return(expr1.Name == expr2.Name && TriggerUtils.SameNullity(expr1.Contract, expr2.Contract)); }
private static bool ShallowSameAttributes(Attributes attributes1, Attributes attributes2) { return(TriggerUtils.SameLists(attributes1.AsEnumerable(), attributes2.AsEnumerable(), ShallowSameSingleAttribute)); }
internal static IEnumerable <TriggerMatch> SubexpressionsMatchingTrigger(this ComprehensionExpr quantifier, Expression trigger) { return(quantifier.AllSubExpressions(true, true, true) .Select(e => TriggerUtils.PrepareExprForInclusionInTrigger(e).MatchAgainst(trigger, quantifier.BoundVars, e)) .Where(e => e.HasValue).Select(e => e.Value)); }
private TriggerAnnotation Annotate(Expression expr) { TriggerAnnotation cached; if (cache.annotations.TryGetValue(expr, out cached)) { return(cached); } TriggerAnnotation annotation = null; // TODO: Using ApplySuffix fixes the unresolved members problem in GenericSort if (expr is LetExpr) { var le = (LetExpr)expr; if (le.LHSs.All(p => p.Var != null) && le.Exact) { // Inline the let expression before doing trigger selection. annotation = Annotate(Translator.InlineLet(le)); } } if (annotation == null) { expr.SubExpressions.Iter(e => Annotate(e)); if (IsPotentialTriggerCandidate(expr)) { annotation = AnnotatePotentialCandidate(expr); } else if (expr is QuantifierExpr) { annotation = AnnotateQuantifier((QuantifierExpr)expr); } else if (expr is LetExpr) { annotation = AnnotateLetExpr((LetExpr)expr); } else if (expr is IdentifierExpr) { annotation = AnnotateIdentifier((IdentifierExpr)expr); } else if (expr is ApplySuffix) { annotation = AnnotateApplySuffix((ApplySuffix)expr); } else if (expr is MatchExpr) { annotation = AnnotateMatchExpr((MatchExpr)expr); } else if (expr is ComprehensionExpr) { annotation = AnnotateComprehensionExpr((ComprehensionExpr)expr); } else if (expr is ConcreteSyntaxExpression || expr is LiteralExpr || expr is ThisExpr || expr is BoxingCastExpr || expr is MultiSetFormingExpr || expr is SeqConstructionExpr) { annotation = AnnotateOther(expr, false); } else { annotation = AnnotateOther(expr, true); } } TriggerUtils.DebugTriggers("{0} ({1})\n{2}", Printer.ExprToString(expr), expr.GetType(), annotation); cache.annotations[expr] = annotation; return(annotation); }
private void CommitOne(QuantifierWithTriggers q, bool addHeader) { var errorLevel = ErrorLevel.Info; var msg = new StringBuilder(); var indent = addHeader ? " " : ""; bool suppressWarnings = Attributes.Contains(q.quantifier.Attributes, "nowarn"); if (!TriggerUtils.NeedsAutoTriggers(q.quantifier)) // NOTE: split and autotriggers attributes are passed down to Boogie { var extraMsg = TriggerUtils.WantsAutoTriggers(q.quantifier) ? "" : " Note that {:autotriggers false} can cause instabilities. Consider using {:nowarn}, {:matchingloop} (not great either), or a manual trigger instead."; msg.AppendFormat("Not generating triggers for \"{0}\".{1}", Printer.ExprToString(q.quantifier.Term), extraMsg).AppendLine(); } else { if (addHeader) { msg.AppendFormat("For expression \"{0}\":", Printer.ExprToString(q.quantifier.Term)).AppendLine(); } foreach (var candidate in q.Candidates) { q.quantifier.Attributes = new Attributes("trigger", candidate.Terms.Select(t => t.Expr).ToList(), q.quantifier.Attributes); } AddTriggersToMessage("Selected triggers:", q.Candidates, msg, indent); AddTriggersToMessage("Rejected triggers:", q.RejectedCandidates, msg, indent, true); #if QUANTIFIER_WARNINGS var WARN_TAG = ArmadaOptions.O.UnicodeOutput ? "⚠ " : "/!\\ "; var WARN_TAG_OVERRIDE = suppressWarnings ? "(Suppressed warning) " : WARN_TAG; var WARN_LEVEL = suppressWarnings ? ErrorLevel.Info : ErrorLevel.Warning; var WARN = indent + WARN_TAG_OVERRIDE; if (!q.CandidateTerms.Any()) { errorLevel = WARN_LEVEL; msg.Append(WARN).AppendLine("No terms found to trigger on."); } else if (!q.Candidates.Any()) { errorLevel = WARN_LEVEL; msg.Append(WARN).AppendLine("No trigger covering all quantified variables found."); } else if (!q.CouldSuppressLoops && !q.AllowsLoops) { errorLevel = WARN_LEVEL; msg.Append(WARN).AppendLine("Suppressing loops would leave this expression without triggers."); } else if (suppressWarnings) { errorLevel = ErrorLevel.Warning; msg.Append(indent).Append(WARN_TAG).AppendLine("There is no warning here to suppress."); } #endif } if (msg.Length > 0) { var msgStr = msg.ToString().TrimEnd("\r\n ".ToCharArray()); reporter.Message(MessageSource.Rewriter, errorLevel, q.quantifier.tok, msgStr); } }
private static bool SameTriggerCandidate(TriggerCandidate arg1, TriggerCandidate arg2) { return(TriggerUtils.SameLists(arg1.Terms, arg2.Terms, TriggerTerm.Eq)); }
private bool HasSameTriggers(QuantifierWithTriggers one, QuantifierWithTriggers other) { return(TriggerUtils.SameLists(one.Candidates, other.Candidates, SameTriggerCandidate)); }
internal void TrimInvalidTriggers() { Contract.Requires(CandidateTerms != null); Contract.Requires(Candidates != null); Candidates = TriggerUtils.Filter(Candidates, tr => tr, (tr, _) => tr.MentionsAll(quantifier.BoundVars), (tr, _) => { }).ToList(); }
// group split quantifier by what triggers they got, and merged them back into one quantifier. private void CombineSplitQuantifier() { if (quantifiers.Count > 1) { List <QuantifierGroup> groups = new List <QuantifierGroup>(); groups.Add(new QuantifierGroup(quantifiers[0], new List <ComprehensionExpr> { quantifiers[0].quantifier })); for (int i = 1; i < quantifiers.Count; i++) { bool found = false; for (int j = 0; j < groups.Count; j++) { if (HasSameTriggers(quantifiers[i], groups[j].quantifier)) { // belong to the same group groups[j].expressions.Add(quantifiers[i].quantifier); found = true; break; } } if (!found) { // start a new group groups.Add(new QuantifierGroup(quantifiers[i], new List <ComprehensionExpr> { quantifiers[i].quantifier })); } } if (groups.Count == quantifiers.Count) { // have the same number of splits, so no splits are combined. return; } // merge expressions in each group back to one quantifier. List <QuantifierWithTriggers> list = new List <QuantifierWithTriggers>(); List <Expression> splits = new List <Expression>(); foreach (var group in groups) { QuantifierWithTriggers q = group.quantifier; if (q.quantifier is ForallExpr) { ForallExpr quantifier = (ForallExpr)q.quantifier; Expression expr = QuantifiersToExpression(quantifier.tok, BinaryExpr.ResolvedOpcode.And, group.expressions); q.quantifier = new ForallExpr(quantifier.tok, quantifier.TypeArgs, quantifier.BoundVars, quantifier.Range, expr, TriggerUtils.CopyAttributes(quantifier.Attributes)) { Type = quantifier.Type, Bounds = quantifier.Bounds }; } else if (q.quantifier is ExistsExpr) { ExistsExpr quantifier = (ExistsExpr)q.quantifier; Expression expr = QuantifiersToExpression(quantifier.tok, BinaryExpr.ResolvedOpcode.Or, group.expressions); q.quantifier = new ExistsExpr(quantifier.tok, quantifier.TypeArgs, quantifier.BoundVars, quantifier.Range, expr, TriggerUtils.CopyAttributes(quantifier.Attributes)) { Type = quantifier.Type, Bounds = quantifier.Bounds }; } list.Add(q); splits.Add(q.quantifier); } this.quantifiers = list; Contract.Assert(this.expr is QuantifierExpr); // only QuantifierExpr has SplitQuantifier ((QuantifierExpr)this.expr).SplitQuantifier = splits; } }