Fix trigger legality checks to consider inline function bodies#1113
Draft
Fix trigger legality checks to consider inline function bodies#1113
Conversation
When a trigger mentions an {:inline} function, Boogie now checks the
function body (and transitively all called inline functions) for illegal
trigger expressions such as boolean operators (&&, ||, !, ==>, <==>),
equality (==), and arithmetic comparisons (<, <=, >, >=).
Previously, triggers referencing {:inline} functions with illegal bodies
would silently pass Boogie's legality checks, resulting in illegal SMT
triggers being generated.
The fix adds CheckBodyTriggerLegality and CheckExprTriggerLegality helper
methods to FunctionCall that are invoked when rc.TriggerMode is true and
the resolved function has a body. Cycle detection prevents infinite
recursion for mutually recursive inline functions.
Co-authored-by: shazqadeer <7672086+shazqadeer@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Fix trigger legality checks for inlining functions
Fix trigger legality checks to consider inline function bodies
Mar 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Boogie's trigger legality checks were not inspecting the bodies of
{:inline}functions used in triggers. A trigger like{InlinedFunction(z)}would pass validation even if the inlined body contained boolean operators or arithmetic comparisons, resulting in illegal SMT triggers being silently emitted.Root cause: Trigger legality is enforced during resolution via
rc.TriggerMode, but function bodies are resolved without that flag set. WhenFunctionCall.Resolveis called inside a trigger, only the call site was checked—not the body.Fix —
FunctionCall.cs:Func.Body != null) whilerc.TriggerModeis true, recursively check the body for illegal trigger expressionsCheckBodyTriggerLegality/CheckExprTriggerLegalityhelpers that walk the expression tree detecting:&&,||,!,==>,<==>), equality (==), arithmetic comparisons (<,<=,>,>=)HashSet<Function>)FunctionCall(Function f)nodes are also checkedExample — previously undetected, now caught:
Legal inline functions (e.g., bodies with only arithmetic, map selects, or
!=) remain unaffected.Test —
Test/inline/triggerLegality.bpl: Covers direct illegal bodies, legal bodies (no false positives), nested inline chains, and equality in bodies.Original prompt
This section details on the original issue you should resolve
<issue_title>Trigger legality checks do not consider inlining</issue_title>
<issue_description>Boogie ordinarily checks triggers to be legal trigger expressions. For example, operators
&&and<=are not legal in triggers, so Boogie complains about the following program:Alas, the trigger legality checking does not dip into
{:inline}functions. Therefore, if a trigger mentions an{:inline}function and that function has a body that would be illegal in a trigger, then the SMT formulas that Boogie generates will contain illegal triggers.Repro. The following program passes through Boogie without error:
The SMT it generates contains the illegal, inlined trigger:
This trigger is indeed illegal also at the SMT level, as is demonstrated by adding the
/proverWarnings:1flag to Boogie:PS. This bug was detected due to a latent bug in Dafny that generated such illegal triggers: dafny-lang/dafny#1150</issue_description>
Comments on the Issue (you are @copilot in this section)
@shazqadeer @RustanLeino : Are there useful scenarios where calls to inlined functions are useful in triggers? What if we reported an error if that happens? @shazqadeer @RustanLeino : Another ping on my question on this issue. @RustanLeino Yes, there are use scenarios where triggers mention inlined functions. Dafny uses a function `read` for reading the heap:This function was introduced so that it would be easy to switch out the type
Heap. Up until a few years ago, Dafny had instead used:The
readfunction frequently gets produced into triggers that Dafny passes down to Boogie.This is convenient, but it would also be possible for Dafny to expand this function as it's generating Boogie code.</comment_new>
<comment_new>@shazqadeer
I looked at the code implementing trigger legality checks. Currently, the checks are being done during resolution. If we want error reporting to take into account function calls, then the right place to do the checks would be at type checking time when all function calls have been resolved.
The method at the following line is the basis for all this checking:
boogie/Source/Core/AbsyExpr.cs
Line 1644 in fcb674a
The argument
subjectForErrorReportingfor this method is used precisely for reporting errors on triggers.Overall, if we want to do the legal trigger checking during type checking:
subjectForErrorReportingto the method Typecheck.TriggerMode==true.Question: Should we go down this path? If not, we would have to outlaw calls to inlined functions in triggers.</comment_new>
<comment_new>@RustanLeino
What you're describing sounds like an overall improvement to me. I'd argue that doing trigger checking before type checking was a design mistake. In fact, one could even imagine doing trigger checking as a separate pass that happens after type checking. Then, the phases would like like (0) resolution, (1) type checking, (2) trigger checking and other kinds of checking that...
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.