-
Notifications
You must be signed in to change notification settings - Fork 268
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
feat: Add resolver plugin that automatically creates test wrappers for contracts #2235
Comments
Here's a proposal for the next step we might take. Expected Use
Wrapper DetailsIt’s possible that the contract on a method could be difficult to prove due to SMT solver limitations, such as the following. method HardCastMethod(i: nat) returns (res: nat)
requires i < 64
ensures res == (64 - i as bv64) as int
{
return (64 - i as bv8) as int;
} Or, alternatively, it could be difficult to prove because it’s written in a different language, such as the following. method {:extern} GenerateBytes(i: int32) returns (res: seq<uint8>)
requires i >= 0
ensures |res| == i as int In either case, we will use the same approach to test the contract contract before and after calling the underlying method, such as the following. method HardCastMethod_checked(i: nat) returns (res: nat)
requires i < 64
ensures res == (64 - i as bv64) as int
{
expect i < 64;
res := HardCastMethod(i);
expect res == (64 - i as bv64) as int;
}
method {:extern} GenerateBytes_checked(i: int32) returns (res: seq<uint8>)
requires i >= 0
ensures |res| == i as int
{
expect i >= 0;
res := GenerateBytes(i);
expect |res| == i as int;
} Functions would behave similarly, but with one additional factor. Consider this functional equivalent of the above method. function HardCastFunction(i: nat): (res: nat)
requires i < 64
ensures res == (64 - i as bv64) as int
{
(64 - i as bv8) as int
} It could have a similar checked wrapper using function HardCastFunction_checked(i: nat): (res: nat)
requires i < 64
ensures res == (64 - i as bv64) as int
{
HardCastFunction(i)
} by method {
expect i < 64;
res := HardCastFunction(i);
expect res == (64 - i as bv64) as int;
} Otherwise, functions would behave identically to methods. Supporting this approach suggests continuing to allow First Iteration PlanLimitations
Implementation Plan
Side BenefitsBecause contract-checking wrappers will exist as separate but directly-callable code in the target language, anyone who wants to use them from property-based tests, fuzzing harnesses, or bounded-model-checking harnesses can do so, and that allows us to experiment with the utility of those connections before automating them. Later StagesAfter this first iteration, there are a number of improvements that may be valuable. These could include:
|
Loving this direction. My only ask is that you're very explicit in the documentation that My only other ask is that you rename |
Yep, that sounds great. I'm not fully confident that these are the right modes, either, but they at least give us something to start experimenting with. And I edited it to fully spell out |
Could you say something about what error messages are generated if you use non-compilable expression in your ensures clauses combined with What's the use-case of |
I think a message something along the lines of I have two use cases in mind for
|
…runtime (#2712) Add a flag to allow certain function and method contracts to be tested at runtime. For the moment, it focuses on `{:extern}` code, but could be applied to other code in the future. Some details of the design are described in the comment thread of issue #2235. It is usable with the following: ``` /testContracts:<AllExterns|ExternsInTests> Enable run-time testing of compiled function or method contracts in certain situations, currently focused on :extern code. AllExterns - Check contracts on every call to a function or method marked :extern, regardless of where it occurs. Externs - Check contracts on every call to a function or method marked :extern when it occurs in one marked :test. ``` Fixes #2235.
The Dafny reference manual, in the documentation for the
expect
statement, suggests that it can be used as a way to dynamically check the contract of a method.However, doing this manually in Dafny as-is introduces the possibility that the
expect
statement and theensures
clause could get out of sync. Instead, we could have a plugin that would automatically generate a wrapper to check theensures
clauses (and possibly evenrequires
clauses) of a method usingexpect
, guaranteeing that the dynamic checks would always match the stated contracts.Further comments on this issue will elaborate a proposed design.
The text was updated successfully, but these errors were encountered: