Supporting dyn soundly #1047
Replies: 10 comments 3 replies
-
I think there is a sound refinement of this that is useful: with a the axiom for where the precondition One way to think about this is to (conceptually) split the dictionary into two dictionaries, a |
Beta Was this translation helpful? Give feedback.
-
|
One additional consideration is that in Rust, for an object-safe Here, the code is able to call
With these, code can still use In addition, we have to make sure that For this, I propose making the SMT encoding of trait bounds slightly more aware of (As an alternative, we might consider having the syntax macro add |
Beta Was this translation helpful? Give feedback.
-
|
@tjhance: consider "call credits", for higher order recursive functions. Also consider termination credits. |
Beta Was this translation helpful? Give feedback.
-
|
@Chris-Hawblitzel: we have a sound design for |
Beta Was this translation helpful? Give feedback.
-
|
A use case that also motivates this: in the Anvil project we have to define a struct that needs to take a sequence of different types that all implement a trait T. Right now our solution is: (1) define an opaque type and use that type in the struct, (2) define (uninterpretable) functions to convert between each type to/from the opaque type, (3) define axioms for each type that any object of this type remains unchanged after a RTT of the conversion --- similar to marshal and unmarshal. |
Beta Was this translation helpful? Give feedback.
-
|
Another use case for dyn: @y1ca1 was looking for support that would allow writing certain combinators (in https://github.com/secure-foundations/vest) that need a form of deferring. I believe the specific sort of |
Beta Was this translation helpful? Give feedback.
-
|
We have another use case in Anvil that the user of the Anvil framework wants to provide some function |
Beta Was this translation helpful? Give feedback.
-
|
Potential soundness thing to watch out for: dyn traits effectively have automatic impls According to the linked discussion there, the main problem in Rust only occurs when the traits have associated types. I think it'll be problematic for us even without associated types, since conflicting trait impls can have inconsistent spec functions. When we implement dyn, we should make sure Verus's conflict checker is able to catch the issue. |
Beta Was this translation helpful? Give feedback.
-
|
@tjhance @Chris-Hawblitzel: It may be easier to support only |
Beta Was this translation helpful? Give feedback.
-
|
Another cool thing we can have with This API set enables opaque types for OSTD users to define arbitrary frame types, such as page cache frames, buffer frames, etc. Under the hood, we utilize the pointer metadata to implement our own smart pointers. It seems to be another complex problem for Verus if we don't allow any cheats. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
We've been discussing
dynfor Verus recently, and I wanted to write down some notes aboutdyn's soundness when used in specifications and proofs. I thinkdynshould be sound, but there's at least one thing we have to be careful about: operations ondynshould be consideredexecorproofmode operations, notspecmode operations. Otherwise, there would be a danger of fabricating fake implementations of impossible trait bounds (this would be similar to Dafny issue dafny-lang/dafny#851 ). I believe this should be a straightforward restriction that fits in naturally with Verus's mode system.To see the potential issue in more detail, consider our model encoding of traits as dictionary datatypes, where we model a trait like this:
with a dictionary datatype like this:
In this model, a bound
A: Ton typeA:is treated like a dictionary parameter:
It's important that program cannot fabricate dictionaries, because not all dictionary types can be implemented. For example, if a trait has a method
proof fn bad() ensures false;, then no dictionary value can implement this method, and if we accidentally assume a dictionary implementing this method, the program could callbadand prove false. So in Verus mode terminology, we have to treat dictionaries as having modeproof/tracked, not modespec/ghost, since Verus allows fabrication of values for modespec/ghostbut prohibits fabrication for modeproof/tracked:This matters for
dyn, because the typedyn Tis essentially an existential type or dependent product type that contains a typeSelf, a dictionaryDictionary_T<Self>, and a value of typeSelf:Here, it's important that we treat the bound
Dictionary_T<Self>asproof/tracked, not asspec/ghost. This means that aDyn_Tvalue is only useful if it has modeexecorproof/tracked. If the program created aspec/ghostDyn_Tvalue, we would have to assume that it had been fabricated (e.g. usingarbitrary()) and we should not allow its dictionary to be used to instantiate boundsA: T. Therefore, the mode system should insist that any operations (in particular, method invocations) on adyn Tvalue require thedyn Tvalue to have modeexecorproof/tracked. This should be required even forspecmethods onT, since we intend to support ensures clauses onspecmethods and we already support polymorphic broadcast lemmas (with trait bounds) triggered byspecfunctions.(We could consider an exception for traits that consist entirely of spec methods and have no ensures clauses; I haven't thought much about this and I don't know if it would be useful. A more general exception would be if the program could prove the trait dictionary was an inhabited type, though again, I don't know if this is actually useful.)
Beta Was this translation helpful? Give feedback.
All reactions