-
Notifications
You must be signed in to change notification settings - Fork 262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Cross-modular override check #3431
Conversation
Fixes 2500. To achieve this, we first give the override axiom the visibility of the overriding function. To ensure we only use the axiom in case we pass the check, we introduce an intermediate visibility level. The ensures clause of the overridden function can reference the underlying function, so we need to substitute the overridden function by the overriding function in the override check. Co-authored-by: Robin Salkeld <[email protected]>
The change here seems to be to use the trait member's height to regulate the use-via-context of the override axiom. The solution appears to work, but I'm unable to state an argument for its correctness. In fact, in trying to do so, I find myself surprised at some of the levels that are being used. For example, the check-well-formedness procedure that is responsible for checking the condition expressed by the consequence axiom is assuming a function height that is 2 higher than the height used to activate the axiom. Here's the playing field, as I see it. For each function
The axioms are expressed with antecedents to make them apply only when the function's parameters have the expected types and the Whenever we generate an axiom, we must make sure there are checks in place that justify the axiom. These checks are collected in two Boogie procedures:
Since these procedures are supposed to check that the properties expressed by the axioms really hold, it is important that the procedures not be given access to the axioms before they are done checking. (This was the problem reported in #2500.) How this is done has changed during the history of the Dafny tool, but let me explain the current form of this enforcement mechanism. The general form of these axioms (except the frame axiom, but I'll ignore it) is axiom
AXIOM_ACTIVATION
==>
(forall args ::
F#CanCall(args) || USE_VIA_CONTEXT
==>
//... what we really wanted to say about the function Each time a function is called, the Dafny verifier checks that the actual argument satisfy the types of the function's formal parameters, that the function's precondition holds, and that the call will terminate (that is, that the termination metric stated by One could imagine that the verifier would use such So, expanding axiom
13 <= FunctionHeight
==>
(forall args ::
F#CanCall(args) || (14 <= FunctionHeight && PRECONDITION)
==>
//... what we really wanted to say about the function The override axiom has a different form, namely: axiom
13 <= FunctionHeight
==>
(forall args ::
//... what we really wanted to say about the function That is, the override axiom is stated at a particular level, without regard to the specific arguments (that is, no Now, back to the PR at hand. The solution is to perform the
From a table like this, one then argues soundness based on Then, it was discovered that this isn't good for overrides, because then there are really two levels at play: the level In the current PR, what I'm observing for an overriding function is
This table looks suspicious to me, since it doesn't satisfy The fact that Finally, it's odd to me that the well-formedness checks are performed at a greater height than the override check. It would make sense to me to say "first, we check that the class gives a function that, by itself, is okay, and then we check that that function has a specification that allows it to be an override for the function in the parent trait". If so, the table should look more like
And to replace the |
Reintroduces #3223, with support for additional use cases. Finally fixes #2500.
This didn't pass before the changes in this PR, as we fail to prove
Cl.Odd.Ens ==> Tr.Odd.Ens
, which is part of the override check forCl.Odd
. It boils down to… Cl.Even … ==> … Tr.Even …
, but because they are in the same SCC, we can’t useTr.Even == Cl.Even
. This fix turns it into… Cl.Even … ==> … Cl.Even …
instead, by lowering all functions (with the correct receiver) from trait to class, instead of just doing it for the function that is override tested.By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.