Skip to content

Commit e8d43b8

Browse files
authored
Use constrain_type function to set return value type in ensures cla… (#1799)
To support opaque types, this changes the encoding of `ensure`s clauses to avoid opaque types in closure arguments.
1 parent eff6e63 commit e8d43b8

11 files changed

Lines changed: 301 additions & 30 deletions

File tree

source/builtin/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,13 @@ pub fn with_triggers<A, B>(_triggers_tuples: A, body: B) -> B {
239239
body
240240
}
241241

242+
#[cfg(verus_keep_ghost)]
243+
#[rustc_diagnostic_item = "verus::verus_builtin::constrain_type"]
244+
#[verifier::spec]
245+
pub fn constrain_type<T>(_x: T, _y: T) -> bool {
246+
true
247+
}
248+
242249
// example: forall with three triggers [f(x), g(y)], [h(x, y)], [m(y, x)]:
243250
// forall(|x: int, y: int| with_triggers!([f(x), g(y)], [h(x, y)], [m(y, x)] => body))
244251
#[macro_export]

source/builtin_macros/src/attr_rewrite.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ pub(crate) fn rewrite_verus_spec_on_fun_or_loop(
376376
}
377377

378378
// Update function signature based on verus_spec.
379-
let spec_stmts = syntax::sig_specs_attr(erase, spec_attr, &mut fun.sig);
379+
let spec_stmts = syntax::sig_specs_attr(erase, spec_attr, &mut fun.sig, false, false);
380380

381381
// Create const proxy function if it is a const function.
382382
if fun.sig.constness.is_some() {
@@ -414,7 +414,7 @@ pub(crate) fn rewrite_verus_spec_on_fun_or_loop(
414414
}.into();
415415
}
416416
let mut signature = closure_to_fn_sig(&closure);
417-
let spec_stmts = syntax::sig_specs_attr(erase, spec_attr, &mut signature);
417+
let spec_stmts = syntax::sig_specs_attr(erase, spec_attr, &mut signature, false, true);
418418
let body = &closure.body;
419419
let new_body = quote_spanned!(closure.body.span() =>
420420
#(#spec_stmts)*
@@ -439,7 +439,7 @@ pub(crate) fn rewrite_verus_spec_on_fun_or_loop(
439439
);
440440
}
441441

442-
let spec_stmts = syntax::sig_specs_attr(erase, spec_attr, &mut method.sig);
442+
let spec_stmts = syntax::sig_specs_attr(erase, spec_attr, &mut method.sig, true, false);
443443
let new_stmts = spec_stmts.into_iter().map(|s| parse2(quote! { #s }).unwrap());
444444
let mut spec_fun_opt = syntax_trait::split_trait_method_syn(&method, erase.erase());
445445
let spec_fun = spec_fun_opt.as_mut().unwrap_or(&mut method);

source/builtin_macros/src/syntax.rs

Lines changed: 242 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ use proc_macro2::TokenTree;
77
use quote::ToTokens;
88
use quote::format_ident;
99
use quote::{quote, quote_spanned};
10+
use syn::token::Comma;
1011
use verus_syn::BroadcastUse;
1112
use verus_syn::DefaultEnsures;
1213
use verus_syn::ExprBlock;
1314
use verus_syn::ExprForLoop;
15+
use verus_syn::Generics;
1416
use verus_syn::parse::{Parse, ParseStream};
1517
use verus_syn::parse_quote_spanned;
1618
use verus_syn::punctuated::Punctuated;
@@ -508,6 +510,11 @@ impl Visitor {
508510
ret_pat: Option<(Pat, TType)>,
509511
final_ret_pat: Option<Pat>, // Some(pat) if different from ret_pat,
510512
_span: Span,
513+
is_impl_fn: bool, // is the function a ImplItemFn or TraitImplFn
514+
is_closure: bool, // some closures also use this function to handle
515+
ident: impl ToTokens, // function name.
516+
generics: Option<impl ToTokens>,
517+
inputs: (Option<impl ToTokens>, impl ToTokens), // optional self and args
511518
) -> Vec<Stmt> {
512519
let requires = self.take_ghost(&mut spec.requires);
513520
let recommends = self.take_ghost(&mut spec.recommends);
@@ -518,6 +525,8 @@ impl Visitor {
518525
let opens_invariants = self.take_ghost(&mut spec.invariants);
519526
let unwind = self.take_ghost(&mut spec.unwind);
520527

528+
let (self_token_op, args) = inputs;
529+
521530
let ensures = merge_default_ensures(ensures, default_ensures);
522531

523532
let mut spec_stmts = Vec::new();
@@ -611,12 +620,46 @@ impl Visitor {
611620
)
612621
}
613622
}
614-
spec_stmts.push(Stmt::Expr(
615-
Expr::Verbatim(
616-
quote_spanned_builtin!(verus_builtin, token.span => #verus_builtin::ensures(|#p: #ty| [#exprs])),
617-
),
618-
Some(Semi { spans: [token.span] }),
619-
));
623+
if is_closure {
624+
// closures cannot return impl xxx so it's safe to
625+
spec_stmts.push(Stmt::Expr(
626+
Expr::Verbatim(
627+
quote_spanned_builtin!(verus_builtin, token.span => #verus_builtin::ensures(|#p: #ty| [#exprs])),
628+
),
629+
Some(Semi { spans: [token.span] }),
630+
));
631+
} else {
632+
let constrain_type = {
633+
let generics_token = {
634+
match generics {
635+
Some(generics) => {
636+
Some(quote_spanned!(token.span => ::#generics))
637+
}
638+
None => None,
639+
}
640+
};
641+
let receiver_token = {
642+
match (is_impl_fn, self_token_op) {
643+
(true, None) => Some(quote_spanned!(token.span => Self::)),
644+
(true, Some(self_token)) => {
645+
Some(quote_spanned!(token.span => #self_token.))
646+
}
647+
(false, None) => None,
648+
(false, Some(self_token)) => {
649+
Some(quote_spanned!(token.span => #self_token.))
650+
}
651+
}
652+
};
653+
quote_spanned_builtin!(verus_builtin, token.span => #verus_builtin::constrain_type(#p, #receiver_token#ident#generics_token(#args)))
654+
};
655+
let contrain_typ_expr = Expr::Verbatim(constrain_type);
656+
spec_stmts.push(Stmt::Expr(
657+
Expr::Verbatim(
658+
quote_spanned_builtin!(verus_builtin, token.span => #verus_builtin::ensures(|#p| [#contrain_typ_expr, #exprs])),
659+
),
660+
Some(Semi { spans: [token.span] }),
661+
));
662+
}
620663
} else {
621664
spec_stmts.push(Stmt::Expr(
622665
Expr::Verbatim(
@@ -746,6 +789,7 @@ impl Visitor {
746789
sig: &mut Signature,
747790
semi_token: Option<Token![;]>,
748791
is_trait: bool,
792+
is_impl_fn: bool,
749793
) -> Vec<Stmt> {
750794
let mut stmts: Vec<Stmt> = Vec::new();
751795
let mut unwrap_ghost_tracked: Vec<Stmt> = Vec::new();
@@ -931,7 +975,24 @@ impl Visitor {
931975
self.inside_ghost += 1; // for requires, ensures, etc.
932976

933977
let sig_span = sig.span().clone();
934-
let spec_stmts = self.take_sig_specs(&mut sig.spec, ret_pat, None, sig_span);
978+
979+
if let Some((p, _)) = &ret_pat {
980+
if let Some(err_stmt) = check_verus_return_ident(p, &sig.inputs) {
981+
stmts.push(err_stmt);
982+
}
983+
}
984+
985+
let spec_stmts = self.take_sig_specs(
986+
&mut sig.spec,
987+
ret_pat,
988+
None,
989+
sig_span,
990+
is_impl_fn,
991+
false,
992+
sig.ident.clone(),
993+
verus_generic_to_tokens(&sig.generics),
994+
verus_inputs_to_tokens(&sig.inputs),
995+
);
935996
if !self.erase_ghost.erase() {
936997
stmts.extend(spec_stmts);
937998
}
@@ -1624,6 +1685,7 @@ impl Visitor {
16241685
&mut item_fn.sig,
16251686
Some(semi),
16261687
false,
1688+
false,
16271689
);
16281690

16291691
if self.rustdoc && matches!(vstd_kind(), VstdKind::IsVstd) {
@@ -3795,8 +3857,14 @@ impl VisitMut for Visitor {
37953857
if self.rustdoc {
37963858
crate::rustdoc::process_item_fn(fun);
37973859
}
3798-
let stmts =
3799-
self.visit_fn(&mut fun.attrs, Some(&fun.vis), &mut fun.sig, fun.semi_token, false);
3860+
let stmts = self.visit_fn(
3861+
&mut fun.attrs,
3862+
Some(&fun.vis),
3863+
&mut fun.sig,
3864+
fun.semi_token,
3865+
false,
3866+
false,
3867+
);
38003868
fun.block.stmts.splice(0..0, stmts);
38013869
fun.semi_token = None;
38023870
let is_external_code = has_external_code(&fun.attrs);
@@ -3820,6 +3888,7 @@ impl VisitMut for Visitor {
38203888
&mut method.sig,
38213889
method.semi_token,
38223890
false,
3891+
true,
38233892
);
38243893
method.block.stmts.splice(0..0, stmts);
38253894
method.semi_token = None;
@@ -3836,7 +3905,7 @@ impl VisitMut for Visitor {
38363905
fn visit_trait_item_fn_mut(&mut self, method: &mut TraitItemFn) {
38373906
let is_spec_method = method.sig.ident.to_string().starts_with(VERUS_SPEC);
38383907
let mut stmts =
3839-
self.visit_fn(&mut method.attrs, None, &mut method.sig, method.semi_token, true);
3908+
self.visit_fn(&mut method.attrs, None, &mut method.sig, method.semi_token, true, true);
38403909
if let Some(block) = &mut method.default {
38413910
block.stmts.splice(0..0, stmts);
38423911
} else if self.erase_ghost.keep() && is_spec_method {
@@ -4658,10 +4727,115 @@ fn take_sig_with_spec(
46584727
};
46594728
spec_stmts
46604729
}
4730+
4731+
pub(crate) fn verus_inputs_to_tokens(
4732+
inputs: &Punctuated<FnArg, Token![,]>,
4733+
) -> (Option<TokenStream>, TokenStream) {
4734+
let mut arg_tokens = TokenStream::new();
4735+
let mut args: Punctuated<verus_syn::Expr, Comma> = Punctuated::new();
4736+
let mut self_token = None;
4737+
for input in inputs.iter() {
4738+
match (&input.tracked, &input.kind) {
4739+
(_, FnArgKind::Receiver(receiver)) => {
4740+
self_token = Some(receiver.self_token.clone().to_token_stream());
4741+
}
4742+
(_, FnArgKind::Typed(pat_type)) => match &*pat_type.pat {
4743+
Pat::Ident(pat_ident) => {
4744+
args.push(Expr::Verbatim(pat_ident.ident.to_token_stream()));
4745+
}
4746+
_ => {
4747+
args.push(Expr::Verbatim(quote_spanned!(input.span() =>
4748+
compile_error!("verus! macro error: input of the function is not an Ident"))));
4749+
}
4750+
},
4751+
}
4752+
}
4753+
args.to_tokens(&mut arg_tokens);
4754+
(self_token, arg_tokens)
4755+
}
4756+
4757+
pub(crate) fn inputs_to_tokens(
4758+
inputs: &syn::punctuated::Punctuated<syn::FnArg, syn::Token![,]>,
4759+
) -> (Option<TokenStream>, TokenStream) {
4760+
let mut ret = TokenStream::new();
4761+
let mut args: Punctuated<verus_syn::Expr, Comma> = Punctuated::new();
4762+
let mut self_token = None;
4763+
for input in inputs.iter() {
4764+
match input {
4765+
syn::FnArg::Receiver(receiver) => {
4766+
self_token = Some(receiver.self_token.clone().to_token_stream());
4767+
}
4768+
syn::FnArg::Typed(pat_type) => match &*pat_type.pat {
4769+
syn::Pat::Ident(pat_ident) => {
4770+
args.push(Expr::Verbatim(pat_ident.ident.to_token_stream()));
4771+
}
4772+
_ => {
4773+
args.push(Expr::Verbatim(quote_spanned!(input.span() =>
4774+
compile_error!("verus! macro error: input of the function is not an Ident"))));
4775+
}
4776+
},
4777+
}
4778+
}
4779+
args.to_tokens(&mut ret);
4780+
(self_token, ret)
4781+
}
4782+
4783+
pub(crate) fn verus_generic_to_tokens(generic: &Generics) -> Option<TokenStream> {
4784+
if generic.lt_token.is_none() {
4785+
return None;
4786+
}
4787+
let mut ret = TokenStream::new();
4788+
generic.lt_token.to_tokens(&mut ret);
4789+
let mut params: Punctuated<verus_syn::GenericArgument, Comma> = Punctuated::new();
4790+
for gen_arg in generic.params.iter() {
4791+
match gen_arg {
4792+
verus_syn::GenericParam::Lifetime(_) => {}
4793+
verus_syn::GenericParam::Type(type_param) => {
4794+
params.push(verus_syn::GenericArgument::Type(Type::Verbatim(
4795+
type_param.ident.to_token_stream(),
4796+
)));
4797+
}
4798+
verus_syn::GenericParam::Const(const_param) => {
4799+
params.push(verus_syn::GenericArgument::Const(Expr::Verbatim(
4800+
const_param.ident.to_token_stream(),
4801+
)));
4802+
}
4803+
}
4804+
}
4805+
params.to_tokens(&mut ret);
4806+
generic.gt_token.to_tokens(&mut ret);
4807+
Some(ret)
4808+
}
4809+
4810+
pub(crate) fn generic_to_tokens(generic: &syn::Generics) -> Option<TokenStream> {
4811+
if generic.lt_token.is_none() {
4812+
return None;
4813+
}
4814+
let mut ret = TokenStream::new();
4815+
generic.lt_token.to_tokens(&mut ret);
4816+
let mut params: Punctuated<Ident, Comma> = Punctuated::new();
4817+
for gen_args in generic.params.iter() {
4818+
match gen_args {
4819+
syn::GenericParam::Lifetime(_) => {}
4820+
syn::GenericParam::Type(type_param) => {
4821+
params.push(type_param.ident.clone());
4822+
}
4823+
syn::GenericParam::Const(const_param) => {
4824+
params.push(const_param.ident.clone());
4825+
}
4826+
}
4827+
}
4828+
params.to_tokens(&mut ret);
4829+
generic.gt_token.to_tokens(&mut ret);
4830+
Some(ret)
4831+
}
4832+
46614833
pub(crate) fn sig_specs_attr(
46624834
erase_ghost: EraseGhost,
46634835
spec_attr: SignatureSpecAttr,
46644836
sig: &mut syn::Signature,
4837+
is_impl_fn: bool,
4838+
is_closure: bool,
46654839
) -> Vec<Stmt> {
46664840
let SignatureSpecAttr { ret_pat, mut spec } = spec_attr;
46674841
let mut spec_stmts = vec![];
@@ -4697,8 +4871,25 @@ pub(crate) fn sig_specs_attr(
46974871
assign_to: false,
46984872
rustdoc: env_rustdoc(),
46994873
};
4874+
4875+
if let Some((p, _)) = &ret_pat {
4876+
if let Some(err_stmt) = check_return_ident(p, &sig.inputs) {
4877+
spec_stmts.push(err_stmt);
4878+
}
4879+
}
4880+
47004881
let sig_span = sig.span().clone();
4701-
spec_stmts.extend(visitor.take_sig_specs(&mut spec, ret_pat, final_ret_pat, sig_span));
4882+
spec_stmts.extend(visitor.take_sig_specs(
4883+
&mut spec,
4884+
ret_pat,
4885+
final_ret_pat,
4886+
sig_span,
4887+
is_impl_fn,
4888+
is_closure,
4889+
sig.ident.clone(),
4890+
generic_to_tokens(&sig.generics),
4891+
inputs_to_tokens(&sig.inputs),
4892+
));
47024893
spec_stmts
47034894
}
47044895

@@ -5227,3 +5418,43 @@ fn get_ex_ident_mangle_path(qself: &Option<verus_syn::QSelf>, path: &Path) -> Id
52275418

52285419
return Ident::new(&s, path.span());
52295420
}
5421+
5422+
/// In VIR there's the same check, but Rustc will complain first, and throw out
5423+
/// some errors about "constrain_type", which ar confusing and the users should not see.
5424+
/// Instead we give an early error with nice error msg here.
5425+
fn check_return_ident(
5426+
ret_pat: &Pat,
5427+
input_args: &syn::punctuated::Punctuated<syn::FnArg, Comma>,
5428+
) -> Option<Stmt> {
5429+
for input in input_args {
5430+
if let syn::FnArg::Typed(pt) = &input {
5431+
if pt.pat.to_token_stream().to_string() == ret_pat.to_token_stream().to_string() {
5432+
return Some(stmt_with_semi!(
5433+
input.span() =>
5434+
compile_error!("parameter name cannot be the same as the return value name")
5435+
));
5436+
}
5437+
}
5438+
}
5439+
None
5440+
}
5441+
5442+
/// In VIR there's the same check, but Rustc will complain first, and throw out
5443+
/// some errors about "constrain_type", which ar confusing and the users should not see.
5444+
/// Instead we give an early error with nice error msg here.
5445+
fn check_verus_return_ident(
5446+
ret_pat: &Pat,
5447+
input_args: &Punctuated<FnArg, verus_syn::token::Comma>,
5448+
) -> Option<Stmt> {
5449+
for input in input_args {
5450+
if let FnArgKind::Typed(pt) = &input.kind {
5451+
if pt.pat.to_token_stream().to_string() == ret_pat.to_token_stream().to_string() {
5452+
return Some(stmt_with_semi!(
5453+
input.span() =>
5454+
compile_error!("parameter name cannot be the same as the return value name")
5455+
));
5456+
}
5457+
}
5458+
}
5459+
None
5460+
}

0 commit comments

Comments
 (0)