Termination check: handle recursion through proof trees and add recursive types check#2006
Termination check: handle recursion through proof trees and add recursive types check#2006
Conversation
|
Ok, many thanks for that! There is a lot of things to review, and I need to think about this, because there is a lot of potential for unsoundness. |
4718c2c to
e032a4d
Compare
I came up with an argument for that. You can split every trait in two: its associated types on one side, and its methods and supertrait constraints on the other side. Once associated type declarations are viewed as standalone constructs, they depend on nothing. That's why there is no need to track dependencies between ADTs and associated type declarations (which are different from associated trait instances, for which I do track dependencies). The only remaining dependencies from types to traits are via trait objects, which are basically not supported at the moment. |
|
Cannot associated types depend on associated types of subtraits? |
|
I don't see how. Another way to think about it is that associated types are like extra type parameters (as they would actually be in Rocq), with differences only relevant to type inference. trait Tr {
type A;
}
struct S<T: Tr>(T::A);
// is like
trait Tr<A> {
}
struct S<A, T: Tr<A>>(A); |
b449dfd to
64422e4
Compare
6603c32 to
7b072b6
Compare
7b072b6 to
2d02416
Compare
Close #1232
#[trusted(ghost)](more fine grained than#[trusted] #[check(ghost)], i.e., it still enables Coma translation of program functions)#[trusted(terminates)](idem, and can also be used on types)#[trusted(positive(T, U))]to postulate "strictly positive" type arguments (can also be used inextern_spec!)Some related things that are not handled in this PR:
struct Never(Box<Never>), Reject empty types before Why3 rejects them. #881)Finally, I put traits in the same dependency graph as types, but there are currently no edges between traits and types. When checking traits, I only find dependencies to other traits in trait bounds (for instance,
trait Tr0 where T1: Tr2<T3>has an edgeTr0 -> Tr2, andT1andT3are ignored). And when checking types, there are no edges due to bounds (instruct T<A: Tr>(A),Tris ignored; but we will track dependencies to trait impls wheneverTis instantiated) or trait objectsdyn Tr(because they are unsupported). Can you find a counterexample where that breaks soundness?