Skip to content

Commit 633baa8

Browse files
authored
Emit output type constraints for top-level functions too (#2474)
1 parent 80e558c commit 633baa8

4 files changed

Lines changed: 133 additions & 9 deletions

File tree

source/rust_verify_test/tests/fndef_types.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,3 +1959,52 @@ test_verify_one_file! {
19591959
}
19601960
} => Ok(())
19611961
}
1962+
1963+
test_verify_one_file_with_options! {
1964+
#[test] fndef_output_through_assoc_type_gh_issue_2427 ["vstd"] => verus_code! {
1965+
use vstd::prelude::*;
1966+
1967+
pub trait HasItem { type Item; }
1968+
1969+
pub struct Ad<F>(pub F);
1970+
impl<F: FnOnce(u32) -> u32> HasItem for Ad<F> {
1971+
type Item = F::Output;
1972+
}
1973+
1974+
pub struct W<I: HasItem> { pub i: I }
1975+
1976+
impl<I: HasItem> W<I> {
1977+
pub uninterp spec fn index(self) -> int;
1978+
pub uninterp spec fn seq(self) -> Seq<I::Item>;
1979+
1980+
fn touch(&mut self)
1981+
ensures final(self).index() == final(self).seq().len(),
1982+
{
1983+
assume(false);
1984+
}
1985+
}
1986+
1987+
fn foo(x: u32) -> u32 { x }
1988+
1989+
fn use_top_level_fn() {
1990+
let mut y = W { i: Ad(foo) };
1991+
y.touch();
1992+
assert(y.index() == y.seq().len());
1993+
}
1994+
1995+
fn use_closure() {
1996+
let f = |x: u32| -> u32 { x };
1997+
let mut y = W { i: Ad(f) };
1998+
y.touch();
1999+
assert(y.index() == y.seq().len());
2000+
}
2001+
2002+
fn id<T>(x: T) -> T { x }
2003+
2004+
fn use_generic_fn() {
2005+
let mut y = W { i: Ad(id::<u32>) };
2006+
y.touch();
2007+
assert(y.index() == y.seq().len());
2008+
}
2009+
} => Ok(())
2010+
}

source/vir/src/ast_simplify.rs

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,7 +1001,8 @@ fn add_fndef_axioms_to_function(
10011001
_ctx: &GlobalCtx,
10021002
state: &mut State,
10031003
function: &Function,
1004-
) -> Result<Function, VirErr> {
1004+
fn_once_trait_in_scope: bool,
1005+
) -> Result<(Function, Vec<TraitImpl>, Option<AssocTypeImpl>), VirErr> {
10051006
state.reset_for_function();
10061007

10071008
let params: Vec<_> = function
@@ -1031,10 +1032,64 @@ fn add_fndef_axioms_to_function(
10311032

10321033
let fndef_singleton = SpannedTyped::new(
10331034
&function.span,
1034-
&Arc::new(TypX::FnDef(fun.clone(), typ_args, None)),
1035+
&Arc::new(TypX::FnDef(fun.clone(), typ_args.clone(), None)),
10351036
ExprX::ExecFnByName(fun.clone()),
10361037
);
10371038

1039+
// Emit `FnDef : {Fn, FnMut, FnOnce}<Args>` and `<FnDef as FnOnce<Args>>::Output = Ret`.
1040+
//
1041+
// We emit a TraitImpl for each of the three Fn-family traits (not just Fn), because
1042+
// code that mentions only one through an associated-type projection (e.g. `Map::Item = F::Output`)
1043+
// never creates a Fn term for the Fn-related axioms to trigger on.
1044+
let (trait_impls_out, assoc_type_impl) = if fn_once_trait_in_scope {
1045+
let self_typ = Arc::new(TypX::FnDef(fun.clone(), typ_args.clone(), None));
1046+
let arg_typs: Vec<Typ> = params.iter().map(|p| p.a.clone()).collect();
1047+
let args_tuple_typ = Arc::new(TypX::Datatype(
1048+
Dt::Tuple(arg_typs.len()),
1049+
Arc::new(arg_typs),
1050+
Arc::new(vec![]),
1051+
));
1052+
let trait_typ_args = Arc::new(vec![self_typ, args_tuple_typ]);
1053+
1054+
let mk_impl_path = |kind: ClosureKind| {
1055+
Arc::new(crate::ast::PathX {
1056+
krate: CrateId::Internal,
1057+
segments: Arc::new(vec![crate::def::impl_fndef(&function.x.name, kind)]),
1058+
})
1059+
};
1060+
1061+
let mut trait_impls_out: Vec<TraitImpl> = Vec::new();
1062+
for kind in [ClosureKind::Fn, ClosureKind::FnMut, ClosureKind::FnOnce] {
1063+
let trait_implx = crate::ast::TraitImplX {
1064+
impl_path: mk_impl_path(kind),
1065+
typ_params: function.x.typ_params.clone(),
1066+
typ_bounds: function.x.typ_bounds.clone(),
1067+
trait_path: kind.trait_path(),
1068+
trait_typ_args: trait_typ_args.clone(),
1069+
trait_typ_arg_impls: Spanned::new(function.span.clone(), Arc::new(vec![])),
1070+
owning_module: None,
1071+
auto_imported: true,
1072+
external_trait_blanket: false,
1073+
};
1074+
trait_impls_out.push(Spanned::new(function.span.clone(), trait_implx));
1075+
}
1076+
1077+
let assoc_typ_implx = crate::ast::AssocTypeImplX {
1078+
name: Arc::new("Output".to_string()),
1079+
impl_path: mk_impl_path(ClosureKind::FnOnce),
1080+
typ_params: function.x.typ_params.clone(),
1081+
typ_bounds: function.x.typ_bounds.clone(),
1082+
trait_path: ClosureKind::FnOnce.trait_path(),
1083+
trait_typ_args,
1084+
typ: function.x.ret.x.typ.clone(),
1085+
impl_paths: Arc::new(vec![]),
1086+
};
1087+
1088+
(trait_impls_out, Some(Spanned::new(function.span.clone(), assoc_typ_implx)))
1089+
} else {
1090+
(Vec::new(), None)
1091+
};
1092+
10381093
let mut fndef_axioms = vec![];
10391094

10401095
// Don't need to repeat the 'requires' for a trait impl fn because requires can't change
@@ -1087,7 +1142,7 @@ fn add_fndef_axioms_to_function(
10871142
let mut functionx = function.x.clone();
10881143
assert!(functionx.fndef_axioms.is_none());
10891144
functionx.fndef_axioms = Some(Arc::new(fndef_axioms));
1090-
Ok(Spanned::new(function.span.clone(), functionx))
1145+
Ok((Spanned::new(function.span.clone(), functionx), trait_impls_out, assoc_type_impl))
10911146
}
10921147

10931148
fn simplify_function(
@@ -1349,13 +1404,24 @@ pub fn simplify_krate(ctx: &mut GlobalCtx, krate: &Krate) -> Result<Krate, VirEr
13491404
let mut assoc_type_impls =
13501405
vec_map_result(&assoc_type_impls, |a| simplify_assoc_type_impl(&mut state, a))?;
13511406

1352-
let functions = vec_map_result(&functions, |f: &Function| {
1407+
let fn_once_trait_path = ClosureKind::FnOnce.trait_path();
1408+
let fn_once_trait_in_scope = traits.iter().any(|t| t.x.name == fn_once_trait_path);
1409+
1410+
let mut new_functions: Vec<Function> = Vec::with_capacity(functions.len());
1411+
for f in functions.iter() {
13531412
if need_fndef_axiom(&state.fndef_typs, f) {
1354-
add_fndef_axioms_to_function(ctx, &mut state, f)
1413+
let (f2, tis, ai) =
1414+
add_fndef_axioms_to_function(ctx, &mut state, f, fn_once_trait_in_scope)?;
1415+
trait_impls.extend(tis);
1416+
if let Some(ai) = ai {
1417+
assoc_type_impls.push(ai);
1418+
}
1419+
new_functions.push(f2);
13551420
} else {
1356-
Ok(f.clone())
1421+
new_functions.push(f.clone());
13571422
}
1358-
})?;
1423+
}
1424+
let functions = new_functions;
13591425

13601426
// Add a generic datatype to represent each tuple arity
13611427
// Iterate in sorted order to get consistent output

source/vir/src/def.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ const PREFIX_SPEC_FN_TYPE: &str = "fun%";
6969
const PREFIX_IMPL_IDENT: &str = "impl&%";
7070
pub(crate) const PREFIX_IMPL_TUPLE: &str = "impl_tuple&%";
7171
pub(crate) const PREFIX_IMPL_CLOSURE: &str = "impl_closure&%";
72+
pub(crate) const PREFIX_IMPL_FNDEF: &str = "impl_fndef&%";
7273
const PREFIX_PROJECT: &str = "proj%";
7374
const PREFIX_PROJECT_DECORATION: &str = "proj%%";
7475
pub(crate) const PREFIX_DEFAULT_TYP_PARAM: &str = "def_typ_param%";
@@ -644,6 +645,12 @@ pub(crate) fn impl_closure(kind: ClosureKind, id: usize) -> Ident {
644645
Arc::new(format!("{}{}{}", PREFIX_IMPL_CLOSURE, kind, id))
645646
}
646647

648+
pub(crate) fn impl_fndef(fun: &Fun, kind: ClosureKind) -> Ident {
649+
let joined =
650+
fun.path.segments.iter().map(|s| s.as_str()).collect::<Vec<_>>().join(PATH_SEPARATOR);
651+
Arc::new(format!("{}{}{}", PREFIX_IMPL_FNDEF, kind, joined))
652+
}
653+
647654
impl NameCtxt {
648655
pub fn projection(&self, decoration: bool, trait_path: &Path, name: &Ident) -> Ident {
649656
let proj = if decoration { PREFIX_PROJECT_DECORATION } else { PREFIX_PROJECT };

source/vstd/resource/map.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,8 +1306,10 @@ impl<K, V> GhostPointsTo<K, V> {
13061306
self.lemma_map_view();
13071307
self.submap.agree(auth);
13081308
assert(self.submap@ <= auth@);
1309-
assert(self.submap@.contains_key(self.key()));
1310-
assert(self.submap@.contains_pair(self.key(), self.value()));
1309+
assert(self.submap@.dom().contains(self.key()));
1310+
assert(auth@.dom().contains(self.key()));
1311+
assert(self.submap@[self.key()] == self.value());
1312+
assert(auth@[self.key()] == self.value());
13111313
}
13121314

13131315
/// We can combine two [`GhostPointsTo`]s into a [`GhostSubmap`]

0 commit comments

Comments
 (0)