Skip to content

Commit 7aa5217

Browse files
Merge branch 'main' into for-loop
2 parents e062d0a + 9e351c8 commit 7aa5217

40 files changed

+1724
-268
lines changed

dependencies/syn/src/item.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2237,7 +2237,13 @@ pub mod parsing {
22372237
impl Parse for ItemUnion {
22382238
fn parse(input: ParseStream) -> Result<Self> {
22392239
let attrs = input.call(Attribute::parse_outer)?;
2240+
22402241
let vis = input.parse::<Visibility>()?;
2242+
2243+
if input.peek(Token![tracked]) || input.peek(Token![ghost]) {
2244+
return Err(input.error("a 'union' can only be exec-mode"));
2245+
}
2246+
22412247
let union_token = input.parse::<Token![union]>()?;
22422248
let ident = input.parse::<Ident>()?;
22432249
let generics = input.parse::<Generics>()?;

source/builtin/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,13 @@ pub fn get_variant_field<Adt, Field>(_a: Adt, _variant: &str, _field: &str) -> F
255255
unimplemented!();
256256
}
257257

258+
#[cfg(verus_keep_ghost)]
259+
#[rustc_diagnostic_item = "verus::builtin::get_union_field"]
260+
#[verifier::spec]
261+
pub fn get_union_field<Adt, Field>(_a: Adt, _field: &str) -> Field {
262+
unimplemented!();
263+
}
264+
258265
#[cfg(verus_keep_ghost)]
259266
#[rustc_diagnostic_item = "verus::builtin::assume_"]
260267
#[verifier::proof]

source/builtin_macros/src/syntax.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ use syn_verus::token::{Brace, Bracket, Paren, Semi};
1414
use syn_verus::visit_mut::{
1515
visit_block_mut, visit_expr_loop_mut, visit_expr_mut, visit_expr_while_mut, visit_field_mut,
1616
visit_impl_item_method_mut, visit_item_const_mut, visit_item_enum_mut, visit_item_fn_mut,
17-
visit_item_static_mut, visit_item_struct_mut, visit_local_mut, visit_specification_mut,
18-
visit_trait_item_method_mut, VisitMut,
17+
visit_item_static_mut, visit_item_struct_mut, visit_item_union_mut, visit_local_mut,
18+
visit_specification_mut, visit_trait_item_method_mut, VisitMut,
1919
};
2020
use syn_verus::{
2121
braced, bracketed, parenthesized, parse_macro_input, AttrStyle, Attribute, BareFnArg, BinOp,
2222
Block, DataMode, Decreases, Ensures, Expr, ExprBinary, ExprCall, ExprLit, ExprLoop, ExprTuple,
2323
ExprUnary, ExprWhile, Field, FnArgKind, FnMode, Global, Ident, ImplItem, ImplItemMethod,
2424
Invariant, InvariantEnsures, InvariantNameSet, InvariantNameSetList, Item, ItemConst, ItemEnum,
25-
ItemFn, ItemImpl, ItemMod, ItemStatic, ItemStruct, ItemTrait, Lit, Local, ModeSpec,
25+
ItemFn, ItemImpl, ItemMod, ItemStatic, ItemStruct, ItemTrait, ItemUnion, Lit, Local, ModeSpec,
2626
ModeSpecChecked, Pat, Path, PathArguments, PathSegment, Publish, Recommends, Requires,
2727
ReturnType, Signature, SignatureDecreases, SignatureInvariants, Stmt, Token, TraitItem,
2828
TraitItemMethod, Type, TypeFnSpec, UnOp, Visibility,
@@ -2638,6 +2638,12 @@ impl VisitMut for Visitor {
26382638
self.filter_attrs(&mut item.attrs);
26392639
}
26402640

2641+
fn visit_item_union_mut(&mut self, item: &mut ItemUnion) {
2642+
item.attrs.push(mk_verus_attr(item.span(), quote! { verus_macro }));
2643+
visit_item_union_mut(self, item);
2644+
self.filter_attrs(&mut item.attrs);
2645+
}
2646+
26412647
fn visit_item_struct_mut(&mut self, item: &mut ItemStruct) {
26422648
item.attrs.push(mk_verus_attr(item.span(), quote! { verus_macro }));
26432649
visit_item_struct_mut(self, item);
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use crate::prelude::*;
2+
use core::ops::Try;
3+
use core::ops::ControlFlow;
4+
use core::ops::FromResidual;
5+
use core::convert::Infallible;
6+
7+
verus!{
8+
9+
#[verifier(external_type_specification)]
10+
#[verifier::accept_recursive_types(B)]
11+
#[verifier::reject_recursive_types_in_ground_variants(C)]
12+
pub struct ExControlFlow<B, C>(ControlFlow<B, C>);
13+
14+
#[verifier(external_type_specification)]
15+
#[verifier(external_body)]
16+
pub struct ExInfallible(Infallible);
17+
18+
19+
#[verifier::external_fn_specification]
20+
pub fn ex_result_branch<T, E>(result: Result<T, E>) -> (cf: ControlFlow<<Result<T, E> as Try>::Residual, <Result<T, E> as Try>::Output>)
21+
ensures
22+
cf === match result {
23+
Ok(v) => ControlFlow::Continue(v),
24+
Err(e) => ControlFlow::Break(Err(e)),
25+
},
26+
{
27+
result.branch()
28+
}
29+
30+
#[verifier::external_fn_specification]
31+
pub fn ex_option_branch<T>(option: Option<T>) -> (cf: ControlFlow<<Option<T> as Try>::Residual, <Option<T> as Try>::Output>)
32+
ensures
33+
cf === match option {
34+
Some(v) => ControlFlow::Continue(v),
35+
None => ControlFlow::Break(None),
36+
},
37+
{
38+
option.branch()
39+
}
40+
41+
#[verifier::external_fn_specification]
42+
pub fn ex_option_from_residual<T>(option: Option<Infallible>) -> (option2: Option<T>)
43+
ensures
44+
option.is_none(),
45+
option2.is_none(),
46+
{
47+
Option::from_residual(option)
48+
}
49+
50+
pub spec fn spec_from<S, T>(value: T, ret: S) -> bool;
51+
52+
#[verifier::broadcast_forall]
53+
#[verifier::external_body]
54+
pub proof fn spec_from_blanket_identity<T>(t: T, s: T)
55+
ensures
56+
spec_from::<T, T>(t, s) ==> t == s
57+
{
58+
}
59+
60+
#[verifier::external_fn_specification]
61+
pub fn ex_result_from_residual<T, E, F: From<E>>(result: Result<Infallible, E>)
62+
-> (result2: Result<T, F>)
63+
ensures
64+
match (result, result2) {
65+
(Err(e), Err(e2)) => spec_from::<F, E>(e, e2),
66+
_ => false,
67+
},
68+
{
69+
Result::from_residual(result)
70+
}
71+
72+
}

source/pervasive/std_specs/core.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,9 @@ pub fn ex_intrinsics_unlikely(b: bool) -> (c: bool)
5959
core::intrinsics::unlikely(b)
6060
}
6161

62+
#[verifier::external_type_specification]
63+
#[verifier::external_body]
64+
#[verifier::reject_recursive_types_in_ground_variants(V)]
65+
pub struct ExManuallyDrop<V: ?Sized>(core::mem::ManuallyDrop<V>);
66+
6267
}

source/pervasive/std_specs/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub mod result;
55
pub mod option;
66
pub mod atomic;
77
pub mod bits;
8+
pub mod control_flow;
89

910
#[cfg(feature = "alloc")]
1011
pub mod vec;

source/rust_verify/src/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub fn enable_default_features_and_verus_attr(
114114
"register_tool",
115115
"tuple_trait",
116116
"custom_inner_attributes",
117+
"try_trait_v2",
117118
] {
118119
rustc_args.push("-Z".to_string());
119120
rustc_args.push(format!("crate-attr=feature({})", feature));

source/rust_verify/src/fn_call_to_vir.rs

Lines changed: 137 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use air::ast_util::str_ident;
2121
use rustc_ast::LitKind;
2222
use rustc_hir::def::Res;
2323
use rustc_hir::{Expr, ExprKind, Node, QPath};
24-
use rustc_middle::ty::{GenericArgKind, TyKind};
24+
use rustc_middle::ty::{GenericArg, GenericArgKind, TyKind};
2525
use rustc_span::def_id::DefId;
2626
use rustc_span::source_map::Spanned;
2727
use rustc_span::Span;
@@ -30,10 +30,10 @@ use vir::ast::{
3030
ArithOp, AssertQueryMode, AutospecUsage, BinaryOp, BitwiseOp, BuiltinSpecFun, CallTarget,
3131
ChainedOp, ComputeMode, Constant, ExprX, FieldOpr, FunX, HeaderExpr, HeaderExprX, InequalityOp,
3232
IntRange, IntegerTypeBoundKind, Mode, ModeCoercion, MultiOp, Quant, Typ, TypX, UnaryOp,
33-
UnaryOpr, VarAt, VirErr,
33+
UnaryOpr, VarAt, VariantCheck, VirErr,
3434
};
3535
use vir::ast_util::{const_int_from_string, typ_to_diagnostic_str, types_equal, undecorate_typ};
36-
use vir::def::positional_field_ident;
36+
use vir::def::field_ident_from_rust;
3737

3838
pub(crate) fn fn_call_to_vir<'tcx>(
3939
bctx: &BodyCtxt<'tcx>,
@@ -96,9 +96,6 @@ pub(crate) fn fn_call_to_vir<'tcx>(
9696
),
9797
);
9898
}
99-
Some(RustItem::TryTraitBranch) => {
100-
return err_span(expr.span, "Verus does not yet support the ? operator");
101-
}
10299
Some(RustItem::Clone) => {
103100
// Special case `clone` for standard Rc and Arc types
104101
// (Could also handle it for other types where cloning is the identity
@@ -167,6 +164,8 @@ pub(crate) fn fn_call_to_vir<'tcx>(
167164
// If the resolution is statically known, we record the resolved function for the
168165
// to be used by lifetime_generate.
169166

167+
let node_substs = fix_node_substs(tcx, bctx.types, node_substs, rust_item, &args, expr);
168+
170169
let target_kind = if tcx.trait_of_item(f).is_none() {
171170
vir::ast::CallTargetKind::Static
172171
} else {
@@ -740,6 +739,33 @@ fn verus_item_to_vir<'tcx, 'a>(
740739
variant: str_ident(&variant_name),
741740
field: variant_field.unwrap(),
742741
get_variant: true,
742+
check: VariantCheck::None,
743+
}),
744+
adt_arg,
745+
))
746+
}
747+
ExprItem::GetUnionField => {
748+
record_spec_fn_allow_proof_args(bctx, expr);
749+
assert!(args.len() == 2);
750+
let adt_arg = expr_to_vir(bctx, &args[0], ExprModifier::REGULAR)?;
751+
let field_name = get_string_lit_arg(&args[1], &f_name)?;
752+
753+
let adt_path = check_union_field(
754+
bctx,
755+
expr.span,
756+
args[0],
757+
&field_name,
758+
&bctx.types.expr_ty(expr),
759+
)?;
760+
761+
let field_ident = str_ident(&field_name);
762+
mk_expr(ExprX::UnaryOpr(
763+
UnaryOpr::Field(FieldOpr {
764+
datatype: adt_path,
765+
variant: field_ident.clone(),
766+
field: field_ident_from_rust(&field_ident),
767+
get_variant: true,
768+
check: VariantCheck::None,
743769
}),
744770
adt_arg,
745771
))
@@ -1652,6 +1678,33 @@ fn mk_is_smaller_than<'tcx>(
16521678
return Ok(dec_exp);
16531679
}
16541680

1681+
pub(crate) fn fix_node_substs<'tcx, 'a>(
1682+
tcx: rustc_middle::ty::TyCtxt<'tcx>,
1683+
types: &'tcx rustc_middle::ty::TypeckResults<'tcx>,
1684+
node_substs: &'tcx rustc_middle::ty::List<rustc_middle::ty::GenericArg<'tcx>>,
1685+
rust_item: Option<RustItem>,
1686+
args: &'a [&'tcx Expr<'tcx>],
1687+
expr: &'a Expr<'tcx>,
1688+
) -> &'tcx rustc_middle::ty::List<rustc_middle::ty::GenericArg<'tcx>> {
1689+
match rust_item {
1690+
Some(RustItem::TryTraitBranch) => {
1691+
// I don't understand why, but in this case, node_substs is empty instead
1692+
// of having the type argument. Let's fix it here.
1693+
// `branch` has type `fn branch(self) -> ...`
1694+
// so we can get the Self argument from the first argument.
1695+
let generic_arg = GenericArg::from(types.expr_ty_adjusted(&args[0]));
1696+
tcx.mk_args(&[generic_arg])
1697+
}
1698+
Some(RustItem::ResidualTraitFromResidual) => {
1699+
// `fn from_residual(residual: R) -> Self;`
1700+
let generic_arg0 = GenericArg::from(types.expr_ty(expr));
1701+
let generic_arg1 = GenericArg::from(types.expr_ty_adjusted(&args[0]));
1702+
tcx.mk_args(&[generic_arg0, generic_arg1])
1703+
}
1704+
_ => node_substs,
1705+
}
1706+
}
1707+
16551708
fn mk_typ_args<'tcx>(
16561709
bctx: &BodyCtxt<'tcx>,
16571710
substs: &rustc_middle::ty::List<rustc_middle::ty::GenericArg<'tcx>>,
@@ -1771,11 +1824,6 @@ fn check_variant_field<'tcx>(
17711824
}
17721825
};
17731826

1774-
let variant_opt = adt.variants().iter().find(|v| v.ident(tcx).as_str() == variant_name);
1775-
let Some(variant) = variant_opt else {
1776-
return err_span(span, format!("no variant `{variant_name:}` for this datatype"));
1777-
};
1778-
17791827
let vir_adt_ty = mid_ty_to_vir(tcx, &bctx.ctxt.verus_items, bctx.fun_id, span, &ty, false)?;
17801828
let adt_path = match &*vir_adt_ty {
17811829
TypX::Datatype(path, _, _) => path.clone(),
@@ -1784,9 +1832,34 @@ fn check_variant_field<'tcx>(
17841832
}
17851833
};
17861834

1835+
if adt.is_union() {
1836+
if field_name_typ.is_some() {
1837+
// Don't use get_variant_field with unions
1838+
return err_span(
1839+
span,
1840+
format!("this datatype is a union; consider `get_union_field` instead"),
1841+
);
1842+
}
1843+
let variant = adt.non_enum_variant();
1844+
let field_opt = variant.fields.iter().find(|f| f.ident(tcx).as_str() == variant_name);
1845+
if field_opt.is_none() {
1846+
return err_span(span, format!("no field `{variant_name:}` for this union"));
1847+
}
1848+
1849+
return Ok((adt_path, None));
1850+
}
1851+
1852+
// Enum case:
1853+
1854+
let variant_opt = adt.variants().iter().find(|v| v.ident(tcx).as_str() == variant_name);
1855+
let Some(variant) = variant_opt else {
1856+
return err_span(span, format!("no variant `{variant_name:}` for this datatype"));
1857+
};
1858+
17871859
match field_name_typ {
17881860
None => Ok((adt_path, None)),
17891861
Some((field_name, expected_field_typ)) => {
1862+
// The 'get_variant_field' case
17901863
let field_opt = variant.fields.iter().find(|f| f.ident(tcx).as_str() == field_name);
17911864
let Some(field) = field_opt else {
17921865
return err_span(span, format!("no field `{field_name:}` for this variant"));
@@ -1807,18 +1880,65 @@ fn check_variant_field<'tcx>(
18071880
return err_span(span, "field has the wrong type");
18081881
}
18091882

1810-
let field_ident = if field_name.as_str().bytes().nth(0).unwrap().is_ascii_digit() {
1811-
let i = field_name.parse::<usize>().unwrap();
1812-
positional_field_ident(i)
1813-
} else {
1814-
str_ident(&field_name)
1815-
};
1883+
let field_ident = field_ident_from_rust(&field_name);
18161884

18171885
Ok((adt_path, Some(field_ident)))
18181886
}
18191887
}
18201888
}
18211889

1890+
fn check_union_field<'tcx>(
1891+
bctx: &BodyCtxt<'tcx>,
1892+
span: Span,
1893+
adt_arg: &'tcx Expr<'tcx>,
1894+
field_name: &String,
1895+
expected_field_typ: &rustc_middle::ty::Ty<'tcx>,
1896+
) -> Result<vir::ast::Path, VirErr> {
1897+
let tcx = bctx.ctxt.tcx;
1898+
1899+
let ty = bctx.types.expr_ty_adjusted(adt_arg);
1900+
let ty = match ty.kind() {
1901+
rustc_middle::ty::TyKind::Ref(_, t, rustc_ast::Mutability::Not) => t,
1902+
_ => &ty,
1903+
};
1904+
let (adt, substs) = match ty.kind() {
1905+
rustc_middle::ty::TyKind::Adt(adt, substs) => (adt, substs),
1906+
_ => {
1907+
return err_span(span, format!("expected type to be datatype"));
1908+
}
1909+
};
1910+
1911+
if !adt.is_union() {
1912+
return err_span(span, format!("get_union_field expects a union type"));
1913+
}
1914+
1915+
let variant = adt.non_enum_variant();
1916+
1917+
let field_opt = variant.fields.iter().find(|f| f.ident(tcx).as_str() == field_name);
1918+
let Some(field) = field_opt else {
1919+
return err_span(span, format!("no field `{field_name:}` for this union"));
1920+
};
1921+
1922+
let field_ty = field.ty(tcx, substs);
1923+
let vir_field_ty =
1924+
mid_ty_to_vir(tcx, &bctx.ctxt.verus_items, bctx.fun_id, span, &field_ty, false)?;
1925+
let vir_expected_field_ty =
1926+
mid_ty_to_vir(tcx, &bctx.ctxt.verus_items, bctx.fun_id, span, &expected_field_typ, false)?;
1927+
if !types_equal(&vir_field_ty, &vir_expected_field_ty) {
1928+
return err_span(span, "field has the wrong type");
1929+
}
1930+
1931+
let vir_adt_ty = mid_ty_to_vir(tcx, &bctx.ctxt.verus_items, bctx.fun_id, span, &ty, false)?;
1932+
let adt_path = match &*vir_adt_ty {
1933+
TypX::Datatype(path, _, _) => path.clone(),
1934+
_ => {
1935+
return err_span(span, format!("expected type to be datatype"));
1936+
}
1937+
};
1938+
1939+
Ok(adt_path)
1940+
}
1941+
18221942
fn record_compilable_operator<'tcx>(bctx: &BodyCtxt<'tcx>, expr: &Expr, op: CompilableOperator) {
18231943
let resolved_call = ResolvedCall::CompilableOperator(op);
18241944
let mut erasure_info = bctx.ctxt.erasure_info.borrow_mut();

0 commit comments

Comments
 (0)