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.26k | pub fn get_action(rust_item: Option<RustItem>) -> AutomaticDeriveAction { |
28 | 1.17k | match rust_item { |
29 | 483 | Some(RustItem::PartialEq | RustItem::Eq) => AutomaticDeriveAction::Ignore, |
30 | 40 | Some(RustItem::Clone) => AutomaticDeriveAction::Special(SpecialTrait::Clone), |
31 | | |
32 | 40 | 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 | 573 | Some(_) | None => AutomaticDeriveAction::VerifyAsIs, |
41 | | } |
42 | 1.26k | } |
43 | | |
44 | 399k | pub fn is_automatically_derived(attrs: &[rustc_hir::Attribute]) -> bool { |
45 | 1.03M | for attr in attrs.iter() { |
46 | 284k | match attr { |
47 | 748k | rustc_hir::Attribute::Unparsed(item) => match &item.path.segments[..] { |
48 | 42.9k | [segment] => { |
49 | 42.9k | if segment.as_str() == "automatically_derived" { |
50 | 0 | return true; |
51 | 42.9k | } |
52 | | } |
53 | 705k | _ => {} |
54 | | }, |
55 | | rustc_hir::Attribute::Parsed( |
56 | | rustc_hir::attrs::AttributeKind::AutomaticallyDerived(_), |
57 | | ) => { |
58 | 1.63k | return true; |
59 | | } |
60 | 283k | _ => {} |
61 | | } |
62 | | } |
63 | 397k | false |
64 | 399k | } |
65 | | |
66 | 22 | pub fn modify_derived_item<'tcx>( |
67 | 22 | ctxt: &Context<'tcx>, |
68 | 22 | id: rustc_span::def_id::DefId, |
69 | 22 | inputs: &Vec<rustc_middle::ty::Ty>, |
70 | 22 | span: Span, |
71 | 22 | hir_id: HirId, |
72 | 22 | action: &AutomaticDeriveAction, |
73 | 22 | function: &mut FunctionX, |
74 | 22 | ) -> Result<(), VirErr> { |
75 | 22 | let AutomaticDeriveAction::Special(special) = action else { |
76 | 2 | return Ok(()); |
77 | | }; |
78 | 20 | match special { |
79 | | SpecialTrait::Clone => { |
80 | 20 | if &*function.name.path.last_segment() == "clone" { |
81 | 20 | return clone_add_post_condition(ctxt, id, inputs, span, hir_id, function); |
82 | 0 | } |
83 | | } |
84 | | } |
85 | 0 | Ok(()) |
86 | 22 | } |
87 | | |
88 | 20 | fn clone_add_post_condition<'tcx>( |
89 | 20 | ctxt: &Context<'tcx>, |
90 | 20 | mut id: rustc_span::def_id::DefId, |
91 | 20 | inputs: &Vec<rustc_middle::ty::Ty>, |
92 | 20 | span: Span, |
93 | 20 | hir_id: HirId, |
94 | 20 | functionx: &mut FunctionX, |
95 | 20 | ) -> Result<(), VirErr> { |
96 | 20 | if inputs.len() >= 1 { |
97 | | use rustc_middle::ty::{AdtDef, TyKind}; |
98 | 20 | if let TyKind::Ref(_, t, _) = inputs[0].kind() { |
99 | 20 | if let TyKind::Adt(AdtDef(adt_def_data), _) = t.kind() { |
100 | 20 | // It's more convenient to put verifier::allow on the datatype than on the function |
101 | 20 | id = adt_def_data.did; |
102 | 20 | } |
103 | 0 | } |
104 | 0 | } |
105 | 20 | let warn = |msg: &str| { |
106 | 1 | crate::attributes::warning_maybe( |
107 | 1 | ctxt.tcx, |
108 | 1 | id, |
109 | 1 | span, |
110 | 1 | &WarningAllow::AutoderiveCloneWithoutSpec, |
111 | | || msg, |
112 | 1 | |msg| ctxt.diagnostics.borrow_mut().push(VirErrAs::Warning(msg)), |
113 | | ); |
114 | 1 | }; |
115 | 20 | let warn_unexpected = || { |
116 | 0 | warn( |
117 | 0 | "autoderive Clone impl does not take the form Verus expects; continuing, but without adding a specification for the derived Clone impl", |
118 | 0 | ) |
119 | 0 | }; |
120 | 20 | let warn_unsupported = || { |
121 | 1 | warn( |
122 | 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", |
123 | 1 | ) |
124 | 1 | }; |
125 | | |
126 | 20 | let Some(body) = &functionx.body else { |
127 | 0 | return Ok(()); |
128 | | }; |
129 | | |
130 | | let uses_copy; |
131 | | let self_var; |
132 | | |
133 | 20 | match &body.x { |
134 | 20 | ExprX::Block(_stmts, Some(last_expr)) => match &last_expr.x { |
135 | 19 | ExprX::ReadPlace(pl, _) => match &pl.x { |
136 | 19 | PlaceX::Local(id) if &*id.0 == "self" => { |
137 | 19 | uses_copy = true; |
138 | 19 | self_var = Some(last_expr.clone()); |
139 | 19 | } |
140 | | _ => { |
141 | 0 | warn_unexpected(); |
142 | 0 | return Ok(()); |
143 | | } |
144 | | }, |
145 | 1 | ExprX::Ctor { .. } => { |
146 | 1 | uses_copy = false; |
147 | 1 | self_var = None; |
148 | 1 | } |
149 | | _ => { |
150 | 0 | warn_unexpected(); |
151 | 0 | return Ok(()); |
152 | | } |
153 | | }, |
154 | | _ => { |
155 | 0 | warn_unexpected(); |
156 | 0 | return Ok(()); |
157 | | } |
158 | | } |
159 | | |
160 | 20 | if functionx.ensure.0.len() != 0 { |
161 | 0 | warn_unexpected(); |
162 | 0 | return Ok(()); |
163 | 20 | } |
164 | | |
165 | 20 | if uses_copy { |
166 | 19 | // Add `ensures ret == self` |
167 | 19 | let self_var = self_var.unwrap(); |
168 | 19 | let ret_var = SpannedTyped::new( |
169 | 19 | &self_var.span, |
170 | 19 | &self_var.typ, |
171 | 19 | ExprX::Var(functionx.ret.x.name.clone()), |
172 | 19 | ); |
173 | 19 | let eq_expr = SpannedTyped::new( |
174 | 19 | &self_var.span, |
175 | 19 | &vir::ast_util::bool_typ(), |
176 | 19 | ExprX::Binary(BinaryOp::Eq(Mode::Spec), ret_var.clone(), self_var.clone()), |
177 | 19 | ); |
178 | 19 | |
179 | 19 | let eq_expr = cleanup_span_ids(ctxt, span, hir_id, &eq_expr); |
180 | 19 | functionx.ensure.0 = Arc::new(vec![eq_expr]); |
181 | 19 | } else { |
182 | 1 | warn_unsupported(); |
183 | 1 | } |
184 | | |
185 | 20 | Ok(()) |
186 | 20 | } |
187 | | |
188 | | // TODO better place for this |
189 | 19 | fn cleanup_span_ids<'tcx>(ctxt: &Context<'tcx>, span: Span, hir_id: HirId, expr: &Expr) -> Expr { |
190 | 19 | vir::ast_visitor::map_expr_place_visitor( |
191 | 19 | expr, |
192 | 57 | &|e: &Expr| { |
193 | 57 | let e = ctxt.spans.spanned_typed_new(span, &e.typ, e.x.clone()); |
194 | 57 | let mut erasure_info = ctxt.erasure_info.borrow_mut(); |
195 | 57 | erasure_info.hir_vir_ids.push((hir_id, e.span.id)); |
196 | 57 | Ok(e) |
197 | 57 | }, |
198 | 19 | &|p: &Place| { |
199 | 19 | let p = ctxt.spans.spanned_typed_new(span, &p.typ, p.x.clone()); |
200 | 19 | let mut erasure_info = ctxt.erasure_info.borrow_mut(); |
201 | 19 | erasure_info.hir_vir_ids.push((hir_id, p.span.id)); |
202 | 19 | Ok(p) |
203 | 19 | }, |
204 | | ) |
205 | 19 | .unwrap() |
206 | 19 | } |