Skip to content

Commit addc3c6

Browse files
committed
style fix
1 parent 5169471 commit addc3c6

3 files changed

Lines changed: 62 additions & 86 deletions

File tree

tests/ui/fail/loop_invariant_trait_self.rs

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,6 @@
22
//@compile-flags: -C debug-assertions=off
33
//@rustc-env: THRUST_SOLVER=tests/thrust-pcsat-wrapper THRUST_SOLVER_TIMEOUT_SECS=60
44

5-
// Same shape as the `pass` counterpart, but the invariant asserts the predicate
6-
// `Self::step(c, it, c)` outright. `step` relates a state to its *successor*
7-
// (`dist.start == self.start + 1`), so it can never hold with `dist == c`: the
8-
// invariant is not inductive and verification fails with `Unsat`.
9-
105
#[thrust_macros::requires(true)]
116
#[thrust_macros::ensures(true)]
127
#[thrust::trusted]
@@ -15,53 +10,50 @@ fn rand() -> i64 {
1510
}
1611

1712
#[thrust_macros::context]
18-
trait Foo {
19-
type Item;
20-
13+
trait Gauge {
2114
#[thrust_macros::predicate]
22-
fn step(self, item: Self::Item, dist: Self) -> bool;
15+
fn invariant(x: i32) -> bool;
16+
17+
fn update(&mut self) -> i32;
2318

2419
#[thrust_macros::invariant_context]
25-
fn run(self, item: Self::Item)
26-
where
27-
Self: Sized,
28-
{
29-
let c = self;
30-
let it = item;
20+
fn run(&mut self) -> i32 {
21+
let mut state = 0;
3122
while rand() == 0 {
32-
thrust_macros::invariant!(|c: Self, it: Self::Item| Self::step(c, it, c));
23+
state = self.update();
24+
thrust_macros::invariant!(|state: i32| Self::invariant(state));
3325
}
34-
let _last = c;
35-
let _last_item = it;
26+
state
3627
}
3728
}
3829

3930
#[derive(PartialEq)]
40-
struct Range {
41-
start: i64,
42-
end: i64,
31+
struct Counter {
32+
value: i32,
4333
}
4434

45-
impl thrust_models::Model for Range {
46-
type Ty = Range;
35+
impl thrust_models::Model for Counter {
36+
type Ty = Counter;
4737
}
4838

4939
#[thrust_macros::context]
50-
impl Foo for Range {
51-
type Item = i64;
52-
40+
impl Gauge for Counter {
5341
#[thrust_macros::predicate]
54-
fn step(self, item: Self::Item, dist: Self) -> bool {
55-
// self.end == dist.end && self.start == item && self.start + 1 == dist.start
56-
"(and
57-
(= (tuple_proj<Int-Int>.1 self_) (tuple_proj<Int-Int>.1 dist))
58-
(= (tuple_proj<Int-Int>.0 self_) item)
59-
(= (+ (tuple_proj<Int-Int>.0 self_) 1) (tuple_proj<Int-Int>.0 dist))
60-
)";
61-
true
42+
fn invariant(x: i32) -> bool {
43+
"(>= x 0)"; true
44+
}
45+
46+
fn update(&mut self) -> i32 {
47+
if self.value < 0 {
48+
self.value *= -1;
49+
} else {
50+
self.value -= 1;
51+
}
52+
self.value
6253
}
6354
}
6455

6556
fn main() {
66-
Range { start: 0, end: 5 }.run(0);
57+
let mut c = Counter { value: 0 };
58+
assert!(c.run() >= 0);
6759
}

tests/ui/pass/loop_invariant_trait_self.rs

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@
22
//@compile-flags: -C debug-assertions=off
33
//@rustc-env: THRUST_SOLVER=tests/thrust-pcsat-wrapper THRUST_SOLVER_TIMEOUT_SECS=60
44

5-
// A loop invariant in a *trait default method* may refer to `Self`, the trait's
6-
// associated types (`Self::Item`) and its predicates (`Self::step`). The
7-
// invariant is rewritten into a free `formula_fn` in which `Self` becomes a
8-
// synthetic generic, so that generic must inherit the host `Self: Foo` bound for
9-
// the associated type / predicate to remain resolvable.
10-
115
#[thrust_macros::requires(true)]
126
#[thrust_macros::ensures(true)]
137
#[thrust::trusted]
@@ -16,53 +10,50 @@ fn rand() -> i64 {
1610
}
1711

1812
#[thrust_macros::context]
19-
trait Foo {
20-
type Item;
21-
13+
trait Gauge {
2214
#[thrust_macros::predicate]
23-
fn step(self, item: Self::Item, dist: Self) -> bool;
15+
fn invariant(x: i32) -> bool;
16+
17+
fn update(&mut self) -> i32;
2418

2519
#[thrust_macros::invariant_context]
26-
fn run(self, item: Self::Item)
27-
where
28-
Self: Sized,
29-
{
30-
let c = self;
31-
let it = item;
20+
fn run(&mut self) -> i32 {
21+
let mut state = 0;
3222
while rand() == 0 {
33-
thrust_macros::invariant!(|c: Self, it: Self::Item| Self::step(c, it, c) || true);
23+
state = self.update();
24+
thrust_macros::invariant!(|state: i32| Self::invariant(state));
3425
}
35-
let _last = c;
36-
let _last_item = it;
26+
state
3727
}
3828
}
3929

4030
#[derive(PartialEq)]
41-
struct Range {
42-
start: i64,
43-
end: i64,
31+
struct Counter {
32+
value: i32,
4433
}
4534

46-
impl thrust_models::Model for Range {
47-
type Ty = Range;
35+
impl thrust_models::Model for Counter {
36+
type Ty = Counter;
4837
}
4938

5039
#[thrust_macros::context]
51-
impl Foo for Range {
52-
type Item = i64;
53-
40+
impl Gauge for Counter {
5441
#[thrust_macros::predicate]
55-
fn step(self, item: Self::Item, dist: Self) -> bool {
56-
// self.end == dist.end && self.start == item && self.start + 1 == dist.start
57-
"(and
58-
(= (tuple_proj<Int-Int>.1 self_) (tuple_proj<Int-Int>.1 dist))
59-
(= (tuple_proj<Int-Int>.0 self_) item)
60-
(= (+ (tuple_proj<Int-Int>.0 self_) 1) (tuple_proj<Int-Int>.0 dist))
61-
)";
62-
true
42+
fn invariant(x: i32) -> bool {
43+
"(>= x 0)"; true
44+
}
45+
46+
fn update(&mut self) -> i32 {
47+
if self.value < 0 {
48+
self.value *= -1;
49+
} else {
50+
self.value += 1;
51+
}
52+
self.value
6353
}
6454
}
6555

6656
fn main() {
67-
Range { start: 0, end: 5 }.run(0);
57+
let mut c = Counter { value: 0 };
58+
assert!(c.run() >= 0);
6859
}

thrust-macros/src/invariant.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,19 +117,6 @@ impl Context {
117117
FormulaFnTypeLowering::new(&self.sig)
118118
}
119119
}
120-
121-
/// When the host is a trait, the bound `synth: Trait<..>` mirroring the
122-
/// implicit `Self: Trait` bound of a trait method, so the synthetic
123-
/// stand-in for `Self` can still name the trait's associated types and call
124-
/// its predicates.
125-
fn self_trait_bound(&self, synth: &syn::Ident) -> Option<WherePredicate> {
126-
let FnOuterItem::ItemTrait(item_trait) = self.outer.as_ref()? else {
127-
return None;
128-
};
129-
let trait_ident = &item_trait.ident;
130-
let (_, ty_generics, _) = item_trait.generics.split_for_impl();
131-
Some(syn::parse_quote!(#synth: #trait_ident #ty_generics))
132-
}
133120
}
134121

135122
/// Expands a predicate closure into a `#[thrust::formula_fn]` plus a marker
@@ -186,6 +173,8 @@ fn expand_invariant(
186173
// in expression position).
187174
if crate::tokens_contain_ident(&closure.to_token_stream(), "Self") {
188175
let synth: syn::Ident = format_ident!("__ThrustSelf");
176+
def_wheres.push(syn::parse_quote!(#synth: ?Sized));
177+
189178
let mut rewriter = SelfRewriter { synth: &synth };
190179
for param in &mut fn_params {
191180
rewriter.visit_fn_arg_mut(param);
@@ -199,8 +188,12 @@ fn expand_invariant(
199188
// Mirror the host's implicit `Self: Trait` bound onto the synthetic
200189
// generic so trait associated types (`Self::Item`) and predicates
201190
// (`Self::step`) remain resolvable on it.
202-
if let Some(bound) = context.and_then(|context| context.self_trait_bound(&synth)) {
203-
def_wheres.push(bound);
191+
if let Some(FnOuterItem::ItemTrait(item_trait)) =
192+
context.and_then(|context| context.outer.as_ref())
193+
{
194+
let trait_ident = &item_trait.ident;
195+
let (_, ty_generics, _) = item_trait.generics.split_for_impl();
196+
def_wheres.push(syn::parse_quote!(#synth: #trait_ident #ty_generics));
204197
}
205198
turbofish_args.push(quote!(Self));
206199

0 commit comments

Comments
 (0)