rust_verify/src/automatic_derive.rs
Line | Count | Source |
1 | | use crate::context::Context; |
2 | | use crate::verus_items::RustItem; |
3 | | use rustc_hir::HirId; |
4 | | use rustc_span::Span; |
5 | | use std::sync::Arc; |
6 | | use vir::ast::{ |
7 | | BinaryOp, Expr, ExprX, FunctionX, Mode, Place, PlaceX, SpannedTyped, VirErr, VirErrAs, |
8 | | }; |
9 | | use vir::messages::WarningAllow; |
10 | | |
11 | | /// Traits with special handling |
12 | | #[derive(Clone, Copy, Debug)] |
13 | | pub enum SpecialTrait { |
14 | | Clone, |
15 | | //PartialEq, |
16 | | } |
17 | | |
18 | | /// What to do for a given automatically-derived trait impl |
19 | | #[derive(Debug)] |
20 | | pub enum AutomaticDeriveAction { |
21 | | Special(SpecialTrait), |
22 | | VerifyAsIs, |
23 | | /// Ignore, optionally providing a warning |
24 | | Ignore, |
25 | | } |
26 | | |
27 | 1.38k | pub fn get_action(rust_item: Option<RustItem>) -> AutomaticDeriveAction { |
28 | 1.25k | match rust_item { |
29 | 487 | Some(RustItem::PartialEq | RustItem::Eq) => AutomaticDeriveAction::Ignore, |
30 | 76 | Some(RustItem::Clone) => AutomaticDeriveAction::Special(SpecialTrait::Clone), |
31 | | |
32 | 76 | Some(RustItem::Copy) => AutomaticDeriveAction::VerifyAsIs, |
33 | | |
34 | | Some(RustItem::Hash) |
35 | | | Some(RustItem::Default) |
36 | | | Some(RustItem::Debug) |
37 | | | Some(RustItem::Ord) |
38 | 128 | | Some(RustItem::PartialOrd) => AutomaticDeriveAction::Ignore, |
39 | | |
40 | 613 | Some(_) | None => AutomaticDeriveAction::VerifyAsIs, |
41 | | } |
42 | 1.38k | } |
43 | | |
44 | 434k | pub fn is_automatically_derived(attrs: &[rustc_hir::Attribute]) -> bool { |
45 | 1.11M | for attr in attrs.iter() { |
46 | 294k | match attr { |
47 | 825k | rustc_hir::Attribute::Unparsed(item) => match &item.path.segments[..] { |
48 | 49.0k | [segment] => { |
49 | 49.0k | if segment.as_str() == "automatically_derived" { |
50 | 0 | return true; |
51 | 49.0k | } |
52 | | } |
53 | 776k | _ => {} |
54 | | }, |
55 | | rustc_hir::Attribute::Parsed(rustc_hir::attrs::AttributeKind::AutomaticallyDerived) => { |
56 | 1.74k | return true; |
57 | | } |
58 | 292k | _ => {} |
59 | | } |
60 | | } |
61 | 433k | false |
62 | 434k | } |
63 | | |
64 | 40 | pub fn modify_derived_item<'tcx>( |
65 | 40 | ctxt: &Context<'tcx>, |
66 | 40 | id: rustc_span::def_id::DefId, |
67 | 40 | inputs: &Vec<rustc_middle::ty::Ty>, |
68 | 40 | span: Span, |
69 | 40 | hir_id: HirId, |
70 | 40 | action: &AutomaticDeriveAction, |
71 | 40 | function: &mut FunctionX, |
72 | 40 | ) -> Result<(), VirErr> { |
73 | 40 | let AutomaticDeriveAction::Special(special) = action else { |
74 | 2 | return Ok(()); |
75 | | }; |
76 | 38 | match special { |
77 | | SpecialTrait::Clone => { |
78 | 38 | if &*function.name.path.last_segment() == "clone" { |
79 | 38 | return clone_add_post_condition(ctxt, id, inputs, span, hir_id, function); |
80 | 0 | } |
81 | | } |
82 | | } |
83 | 0 | Ok(()) |
84 | 40 | } |
85 | | |
86 | 38 | fn clone_add_post_condition<'tcx>( |
87 | 38 | ctxt: &Context<'tcx>, |
88 | 38 | mut id: rustc_span::def_id::DefId, |
89 | 38 | inputs: &Vec<rustc_middle::ty::Ty>, |
90 | 38 | span: Span, |
91 | 38 | hir_id: HirId, |
92 | 38 | functionx: &mut FunctionX, |
93 | 38 | ) -> Result<(), VirErr> { |
94 | 38 | if inputs.len() >= 1 { |
95 | | use rustc_middle::ty::{AdtDef, TyKind}; |
96 | 38 | if let TyKind::Ref(_, t, _) = inputs[0].kind() { |
97 | 38 | if let TyKind::Adt(AdtDef(adt_def_data), _) = t.kind() { |
98 | 38 | // It's more convenient to put verifier::allow on the datatype than on the function |
99 | 38 | id = adt_def_data.did; |
100 | 38 | } |
101 | 0 | } |
102 | 0 | } |
103 | 38 | let warn = |msg: &str| { |
104 | 1 | crate::attributes::warning_maybe( |
105 | 1 | ctxt.tcx, |
106 | 1 | id, |
107 | 1 | span, |
108 | 1 | &WarningAllow::AutoderiveCloneWithoutSpec, |
109 | | || msg, |
110 | 1 | |msg| ctxt.diagnostics.borrow_mut().push(VirErrAs::Warning(msg)), |
111 | | ); |
112 | 1 | }; |
113 | 38 | let warn_unexpected = || { |
114 | 0 | warn( |
115 | 0 | "autoderive Clone impl does not take the form Verus expects; continuing, but without adding a specification for the derived Clone impl", |
116 | 0 | ) |
117 | 0 | }; |
118 | 38 | let warn_unsupported = || { |
119 | 1 | warn( |
120 | 1 | "Verus does not (yet) support autoderive Clone impl when the clone is not a copy; continuing, but without adding a specification for the derived Clone impl", |
121 | 1 | ) |
122 | 1 | }; |
123 | | |
124 | 38 | let Some(body) = &functionx.body else { |
125 | 0 | return Ok(()); |
126 | | }; |
127 | | |
128 | | let uses_copy; |
129 | | let self_var; |
130 | | |
131 | 38 | match &body.x { |
132 | 38 | ExprX::Block(_stmts, Some(last_expr)) => match &last_expr.x { |
133 | 37 | ExprX::ReadPlace(pl, _) => match &pl.x { |
134 | 37 | PlaceX::Local(id) if &*id.0 == "self" => { |
135 | 37 | uses_copy = true; |
136 | 37 | self_var = Some(last_expr.clone()); |
137 | 37 | } |
138 | | _ => { |
139 | 0 | warn_unexpected(); |
140 | 0 | return Ok(()); |
141 | | } |
142 | | }, |
143 | 1 | ExprX::Ctor { .. } => { |
144 | 1 | uses_copy = false; |
145 | 1 | self_var = None; |
146 | 1 | } |
147 | | _ => { |
148 | 0 | warn_unexpected(); |
149 | 0 | return Ok(()); |
150 | | } |
151 | | }, |
152 | | _ => { |
153 | 0 | warn_unexpected(); |
154 | 0 | return Ok(()); |
155 | | } |
156 | | } |
157 | | |
158 | 38 | if functionx.ensure.0.len() != 0 { |
159 | 0 | warn_unexpected(); |
160 | 0 | return Ok(()); |
161 | 38 | } |
162 | | |
163 | 38 | if uses_copy { |
164 | 37 | // Add `ensures ret == self` |
165 | 37 | let self_var = self_var.unwrap(); |
166 | 37 | let ret_var = SpannedTyped::new( |
167 | 37 | &self_var.span, |
168 | 37 | &self_var.typ, |
169 | 37 | ExprX::Var(functionx.ret.x.name.clone()), |
170 | 37 | ); |
171 | 37 | let eq_expr = SpannedTyped::new( |
172 | 37 | &self_var.span, |
173 | 37 | &vir::ast_util::bool_typ(), |
174 | 37 | ExprX::Binary(BinaryOp::Eq(Mode::Spec), ret_var.clone(), self_var.clone()), |
175 | 37 | ); |
176 | 37 | |
177 | 37 | let eq_expr = cleanup_span_ids(ctxt, span, hir_id, &eq_expr); |
178 | 37 | functionx.ensure.0 = Arc::new(vec![eq_expr]); |
179 | 37 | } else { |
180 | 1 | warn_unsupported(); |
181 | 1 | } |
182 | | |
183 | 38 | Ok(()) |
184 | 38 | } |
185 | | |
186 | | // TODO better place for this |
187 | 37 | fn cleanup_span_ids<'tcx>(ctxt: &Context<'tcx>, span: Span, hir_id: HirId, expr: &Expr) -> Expr { |
188 | 37 | vir::ast_visitor::map_expr_place_visitor( |
189 | 37 | expr, |
190 | 111 | &|e: &Expr| { |
191 | 111 | let e = ctxt.spans.spanned_typed_new(span, &e.typ, e.x.clone()); |
192 | 111 | let mut erasure_info = ctxt.erasure_info.borrow_mut(); |
193 | 111 | erasure_info.hir_vir_ids.push((hir_id, e.span.id)); |
194 | 111 | Ok(e) |
195 | 111 | }, |
196 | 37 | &|p: &Place| { |
197 | 37 | let p = ctxt.spans.spanned_typed_new(span, &p.typ, p.x.clone()); |
198 | 37 | let mut erasure_info = ctxt.erasure_info.borrow_mut(); |
199 | 37 | erasure_info.hir_vir_ids.push((hir_id, p.span.id)); |
200 | 37 | Ok(p) |
201 | 37 | }, |
202 | | ) |
203 | 37 | .unwrap() |
204 | 37 | } |