-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
[move-prover] algorithm for progressive instantiation #9056
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need progressive instantiation? As far as I can tell from the paper, a concept like this is not needed, is there something wrong in the paper?
The need for finding all instantiations of
This paragraph does not specify how to collect each concrete instantiation of Previously we have been using the cartesian product as a proxy. For example, if we have a function This progressive instantiation algorithm is to solve this issue while also ensuring that we find a complete set for all possible instantiations of |
/land |
This algorithm deals with finding a complete set of instantiation combinations for all type parameters when unifying two types. // problem definition The algorithm is encapsulated in `struct TypeInstantiationDerivation` and is not conceptually hard to understand: Suppose we aim to unify `T1 [X0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, where `T1` and `T2` are types while `X`s and `Y`s are type parameters that show up in `T1` and `T2`, respectively. We want to find all instantiations to `<X0, X1, ..., Xm>` such that for each instantiation `(x0, x1, ..., xm)`, there exists a valid instantiation `(y0, y1, ..., yn)` which makes `T1` and `T2` equivelant, i.e., `T1<x0, x1, ..., xm> == T2<y0, y1, ..., yn>`. We put all these instantiation in a set denoted as `|(x0, x1, ..., xm)|` and this algorithm is about finding this set of instantiations. // algorithm description The algorithm works by finding all instantiations for `X0` first, and then progress to `X1`, `X2`, ..., until finishing `Xn`. - unify `T1 [X0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X0`, denoted as `|x0|` - for each `x0 in |x0|`: - refine `T1` with `x0` - unify `T1 [X0 := x0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X1`, denoted as `|x1|` - for each `x1 in |x1|`: - refine `T1` with `x1` - unify `T1 [X0 := x0, X1 := x1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X2`, denoted as `|x2|` - for each `x2` in `|x2|`: - ...... The process continues until we reach the end of `Xn`. After which, the algorithm should have collected all the legal instantiation combinations for type parameters `<X0, X1, ..., Xm>`. // other notes - The implementation has a bit of fine-tuning rooted by the fact that sometimes we want to treat a type parameter as a variable (i.e., participate in type unification) while in other cases, we want to treat a type parameter as a concrete type (i.e., do not participate in type unification). - We also have a fine-tuning on whether we treat a type parameter that does not have any valid instantiations as an error or remains as a concrete type parameter. This is rooted by the differentation of type parameters in function vs type parameters in a global invariant. Essentially, all type parameters in a global invariant must be instantiated in order for the invariant to be instrumented. But not all function type paramters need to be instantiated. - This is not the most efficient algorithm, especially when we have a large number of type parameters. But a vast majority of Move code we have seen so far have at most one type parameter, so in this commit, we trade-off efficiency with simplicity. Closes: diem#9056
Cluster Test Result
Repro cmd:
🎉 Land-blocking cluster test passed! 👌 |
59819e1
to
2a96d32
Compare
This algorithm deals with finding a complete set of instantiation combinations for all type parameters when unifying two types. // problem definition The algorithm is encapsulated in `struct TypeInstantiationDerivation` and is not conceptually hard to understand: Suppose we aim to unify `T1 [X0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, where `T1` and `T2` are types while `X`s and `Y`s are type parameters that show up in `T1` and `T2`, respectively. We want to find all instantiations to `<X0, X1, ..., Xm>` such that for each instantiation `(x0, x1, ..., xm)`, there exists a valid instantiation `(y0, y1, ..., yn)` which makes `T1` and `T2` equivelant, i.e., `T1<x0, x1, ..., xm> == T2<y0, y1, ..., yn>`. We put all these instantiation in a set denoted as `|(x0, x1, ..., xm)|` and this algorithm is about finding this set of instantiations. // algorithm description The algorithm works by finding all instantiations for `X0` first, and then progress to `X1`, `X2`, ..., until finishing `Xn`. - unify `T1 [X0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X0`, denoted as `|x0|` - for each `x0 in |x0|`: - refine `T1` with `x0` - unify `T1 [X0 := x0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X1`, denoted as `|x1|` - for each `x1 in |x1|`: - refine `T1` with `x1` - unify `T1 [X0 := x0, X1 := x1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X2`, denoted as `|x2|` - for each `x2` in `|x2|`: - ...... The process continues until we reach the end of `Xn`. After which, the algorithm should have collected all the legal instantiation combinations for type parameters `<X0, X1, ..., Xm>`. // other notes - The implementation has a bit of fine-tuning rooted by the fact that sometimes we want to treat a type parameter as a variable (i.e., participate in type unification) while in other cases, we want to treat a type parameter as a concrete type (i.e., do not participate in type unification). - We also have a fine-tuning on whether we treat a type parameter that does not have any valid instantiations as an error or remains as a concrete type parameter. This is rooted by the differentation of type parameters in function vs type parameters in a global invariant. Essentially, all type parameters in a global invariant must be instantiated in order for the invariant to be instrumented. But not all function type paramters need to be instantiated. - This is not the most efficient algorithm, especially when we have a large number of type parameters. But a vast majority of Move code we have seen so far have at most one type parameter, so in this commit, we trade-off efficiency with simplicity. Closes: diem#9056
This algorithm deals with finding a complete set of instantiation combinations for all type parameters when unifying two types. // problem definition The algorithm is encapsulated in `struct TypeInstantiationDerivation` and is not conceptually hard to understand: Suppose we aim to unify `T1 [X0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, where `T1` and `T2` are types while `X`s and `Y`s are type parameters that show up in `T1` and `T2`, respectively. We want to find all instantiations to `<X0, X1, ..., Xm>` such that for each instantiation `(x0, x1, ..., xm)`, there exists a valid instantiation `(y0, y1, ..., yn)` which makes `T1` and `T2` equivelant, i.e., `T1<x0, x1, ..., xm> == T2<y0, y1, ..., yn>`. We put all these instantiation in a set denoted as `|(x0, x1, ..., xm)|` and this algorithm is about finding this set of instantiations. // algorithm description The algorithm works by finding all instantiations for `X0` first, and then progress to `X1`, `X2`, ..., until finishing `Xn`. - unify `T1 [X0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X0`, denoted as `|x0|` - for each `x0 in |x0|`: - refine `T1` with `x0` - unify `T1 [X0 := x0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X1`, denoted as `|x1|` - for each `x1 in |x1|`: - refine `T1` with `x1` - unify `T1 [X0 := x0, X1 := x1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X2`, denoted as `|x2|` - for each `x2` in `|x2|`: - ...... The process continues until we reach the end of `Xn`. After which, the algorithm should have collected all the legal instantiation combinations for type parameters `<X0, X1, ..., Xm>`. // other notes - The implementation has a bit of fine-tuning rooted by the fact that sometimes we want to treat a type parameter as a variable (i.e., participate in type unification) while in other cases, we want to treat a type parameter as a concrete type (i.e., do not participate in type unification). - We also have a fine-tuning on whether we treat a type parameter that does not have any valid instantiations as an error or remains as a concrete type parameter. This is rooted by the differentation of type parameters in function vs type parameters in a global invariant. Essentially, all type parameters in a global invariant must be instantiated in order for the invariant to be instrumented. But not all function type paramters need to be instantiated. - This is not the most efficient algorithm, especially when we have a large number of type parameters. But a vast majority of Move code we have seen so far have at most one type parameter, so in this commit, we trade-off efficiency with simplicity. Closes: #9056
This algorithm deals with finding a complete set of instantiation combinations for all type parameters when unifying two types. // problem definition The algorithm is encapsulated in `struct TypeInstantiationDerivation` and is not conceptually hard to understand: Suppose we aim to unify `T1 [X0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, where `T1` and `T2` are types while `X`s and `Y`s are type parameters that show up in `T1` and `T2`, respectively. We want to find all instantiations to `<X0, X1, ..., Xm>` such that for each instantiation `(x0, x1, ..., xm)`, there exists a valid instantiation `(y0, y1, ..., yn)` which makes `T1` and `T2` equivelant, i.e., `T1<x0, x1, ..., xm> == T2<y0, y1, ..., yn>`. We put all these instantiation in a set denoted as `|(x0, x1, ..., xm)|` and this algorithm is about finding this set of instantiations. // algorithm description The algorithm works by finding all instantiations for `X0` first, and then progress to `X1`, `X2`, ..., until finishing `Xn`. - unify `T1 [X0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X0`, denoted as `|x0|` - for each `x0 in |x0|`: - refine `T1` with `x0` - unify `T1 [X0 := x0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X1`, denoted as `|x1|` - for each `x1 in |x1|`: - refine `T1` with `x1` - unify `T1 [X0 := x0, X1 := x1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X2`, denoted as `|x2|` - for each `x2` in `|x2|`: - ...... The process continues until we reach the end of `Xn`. After which, the algorithm should have collected all the legal instantiation combinations for type parameters `<X0, X1, ..., Xm>`. // other notes - The implementation has a bit of fine-tuning rooted by the fact that sometimes we want to treat a type parameter as a variable (i.e., participate in type unification) while in other cases, we want to treat a type parameter as a concrete type (i.e., do not participate in type unification). - We also have a fine-tuning on whether we treat a type parameter that does not have any valid instantiations as an error or remains as a concrete type parameter. This is rooted by the differentation of type parameters in function vs type parameters in a global invariant. Essentially, all type parameters in a global invariant must be instantiated in order for the invariant to be instrumented. But not all function type paramters need to be instantiated. - This is not the most efficient algorithm, especially when we have a large number of type parameters. But a vast majority of Move code we have seen so far have at most one type parameter, so in this commit, we trade-off efficiency with simplicity. Closes: #9056
This algorithm deals with finding a complete set of instantiation combinations for all type parameters when unifying two types. // problem definition The algorithm is encapsulated in `struct TypeInstantiationDerivation` and is not conceptually hard to understand: Suppose we aim to unify `T1 [X0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, where `T1` and `T2` are types while `X`s and `Y`s are type parameters that show up in `T1` and `T2`, respectively. We want to find all instantiations to `<X0, X1, ..., Xm>` such that for each instantiation `(x0, x1, ..., xm)`, there exists a valid instantiation `(y0, y1, ..., yn)` which makes `T1` and `T2` equivelant, i.e., `T1<x0, x1, ..., xm> == T2<y0, y1, ..., yn>`. We put all these instantiation in a set denoted as `|(x0, x1, ..., xm)|` and this algorithm is about finding this set of instantiations. // algorithm description The algorithm works by finding all instantiations for `X0` first, and then progress to `X1`, `X2`, ..., until finishing `Xn`. - unify `T1 [X0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X0`, denoted as `|x0|` - for each `x0 in |x0|`: - refine `T1` with `x0` - unify `T1 [X0 := x0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X1`, denoted as `|x1|` - for each `x1 in |x1|`: - refine `T1` with `x1` - unify `T1 [X0 := x0, X1 := x1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X2`, denoted as `|x2|` - for each `x2` in `|x2|`: - ...... The process continues until we reach the end of `Xn`. After which, the algorithm should have collected all the legal instantiation combinations for type parameters `<X0, X1, ..., Xm>`. // other notes - The implementation has a bit of fine-tuning rooted by the fact that sometimes we want to treat a type parameter as a variable (i.e., participate in type unification) while in other cases, we want to treat a type parameter as a concrete type (i.e., do not participate in type unification). - We also have a fine-tuning on whether we treat a type parameter that does not have any valid instantiations as an error or remains as a concrete type parameter. This is rooted by the differentation of type parameters in function vs type parameters in a global invariant. Essentially, all type parameters in a global invariant must be instantiated in order for the invariant to be instrumented. But not all function type paramters need to be instantiated. - This is not the most efficient algorithm, especially when we have a large number of type parameters. But a vast majority of Move code we have seen so far have at most one type parameter, so in this commit, we trade-off efficiency with simplicity. Closes: #9056
This algorithm deals with finding a complete set of instantiation combinations for all type parameters when unifying two types. // problem definition The algorithm is encapsulated in `struct TypeInstantiationDerivation` and is not conceptually hard to understand: Suppose we aim to unify `T1 [X0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, where `T1` and `T2` are types while `X`s and `Y`s are type parameters that show up in `T1` and `T2`, respectively. We want to find all instantiations to `<X0, X1, ..., Xm>` such that for each instantiation `(x0, x1, ..., xm)`, there exists a valid instantiation `(y0, y1, ..., yn)` which makes `T1` and `T2` equivelant, i.e., `T1<x0, x1, ..., xm> == T2<y0, y1, ..., yn>`. We put all these instantiation in a set denoted as `|(x0, x1, ..., xm)|` and this algorithm is about finding this set of instantiations. // algorithm description The algorithm works by finding all instantiations for `X0` first, and then progress to `X1`, `X2`, ..., until finishing `Xn`. - unify `T1 [X0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X0`, denoted as `|x0|` - for each `x0 in |x0|`: - refine `T1` with `x0` - unify `T1 [X0 := x0, X1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X1`, denoted as `|x1|` - for each `x1 in |x1|`: - refine `T1` with `x1` - unify `T1 [X0 := x0, X1 := x1, ..., Xm]` vs `T2 [Y0, Y1, ..., Yn]`, get all possible substitutions for `X2`, denoted as `|x2|` - for each `x2` in `|x2|`: - ...... The process continues until we reach the end of `Xn`. After which, the algorithm should have collected all the legal instantiation combinations for type parameters `<X0, X1, ..., Xm>`. // other notes - The implementation has a bit of fine-tuning rooted by the fact that sometimes we want to treat a type parameter as a variable (i.e., participate in type unification) while in other cases, we want to treat a type parameter as a concrete type (i.e., do not participate in type unification). - We also have a fine-tuning on whether we treat a type parameter that does not have any valid instantiations as an error or remains as a concrete type parameter. This is rooted by the differentation of type parameters in function vs type parameters in a global invariant. Essentially, all type parameters in a global invariant must be instantiated in order for the invariant to be instrumented. But not all function type paramters need to be instantiated. - This is not the most efficient algorithm, especially when we have a large number of type parameters. But a vast majority of Move code we have seen so far have at most one type parameter, so in this commit, we trade-off efficiency with simplicity. Closes: #9056
This algorithm deals with finding a complete set of instantiation
combinations for all type parameters when unifying two types.
// problem definition
The algorithm is encapsulated in
struct TypeInstantiationDerivation
and is not conceptually hard to understand:
Suppose we aim to unify
T1 [X0, X1, ..., Xm]
vsT2 [Y0, Y1, ..., Yn]
,where
T1
andT2
are types whileX
s andY
s are type parametersthat show up in
T1
andT2
, respectively.We want to find all instantiations to
<X0, X1, ..., Xm>
such thatfor each instantiation
(x0, x1, ..., xm)
, there exists a validinstantiation
(y0, y1, ..., yn)
which makesT1
andT2
equivelant,i.e.,
T1<x0, x1, ..., xm> == T2<y0, y1, ..., yn>
.We put all these instantiation in a set denoted as
|(x0, x1, ..., xm)|
and this algorithm is about finding this set of instantiations.
// algorithm description
The algorithm works by finding all instantiations for
X0
first, andthen progress to
X1
,X2
, ..., until finishingXn
.T1 [X0, X1, ..., Xm]
vsT2 [Y0, Y1, ..., Yn]
, get allpossible substitutions for
X0
, denoted as|x0|
x0 in |x0|
:T1
withx0
T1 [X0 := x0, X1, ..., Xm]
vsT2 [Y0, Y1, ..., Yn]
, getall possible substitutions for
X1
, denoted as|x1|
x1 in |x1|
:T1
withx1
T1 [X0 := x0, X1 := x1, ..., Xm]
vsT2 [Y0, Y1, ..., Yn]
,get all possible substitutions for
X2
, denoted as|x2|
x2
in|x2|
:The process continues until we reach the end of
Xn
. After which, thealgorithm should have collected all the legal instantiation combinations
for type parameters
<X0, X1, ..., Xm>
.// other notes
The implementation has a bit of fine-tuning rooted by the fact that
sometimes we want to treat a type parameter as a variable (i.e.,
participate in type unification) while in other cases, we want to
treat a type parameter as a concrete type (i.e., do not participate in
type unification).
We also have a fine-tuning on whether we treat a type parameter that
does not have any valid instantiations as an error or remains as a
concrete type parameter. This is rooted by the differentation of type
parameters in function vs type parameters in a global invariant.
Essentially, all type parameters in a global invariant must be
instantiated in order for the invariant to be instrumented. But not
all function type paramters need to be instantiated.
This is not the most efficient algorithm, especially when we have a
large number of type parameters. But a vast majority of Move code we
have seen so far have at most one type parameter, so in this commit,
we trade-off efficiency with simplicity.
Motivation
Work item needed for global invariant instrumentation
Have you read the Contributing Guidelines on pull requests?
Yes
Test Plan