Line | Count | Source |
1 | | //! VIR-AST -> VIR-AST transformation to simplify away some complicated features |
2 | | |
3 | | use crate::ast::CrateId; |
4 | | use crate::ast::Quant; |
5 | | use crate::ast::Typs; |
6 | | use crate::ast::VarBinder; |
7 | | use crate::ast::VarBinderX; |
8 | | use crate::ast::VarBinders; |
9 | | use crate::ast::VarIdent; |
10 | | use crate::ast::{ |
11 | | AssocTypeImpl, AutospecUsage, BinaryOp, Binder, BoundsCheck, BuiltinSpecFun, ByRef, CallTarget, |
12 | | ChainedOp, ClosureKind, Constant, CtorPrintStyle, CtorUpdateTail, Datatype, |
13 | | DatatypeTransparency, DatatypeX, Dt, Expr, ExprX, Exprs, Field, FieldOpr, Fun, Function, |
14 | | FunctionKind, Ident, IntRange, ItemKind, Krate, KrateX, LogicalOp, Mode, MultiOp, Path, |
15 | | Pattern, PatternBinding, PatternX, Place, PlaceX, SpannedTyped, Stmt, StmtX, TraitImpl, Typ, |
16 | | TypX, UnaryOp, UnaryOpr, Variant, VariantCheck, VirErr, Visibility, |
17 | | }; |
18 | | use crate::ast_util::{ |
19 | | conjoin, mk_eq, mk_implies, place_to_spec_expr, typ_args_for_datatype_typ, undecorate_typ, |
20 | | unit_typ, wrap_in_trigger, |
21 | | }; |
22 | | use crate::ast_visitor::VisitorScopeMap; |
23 | | use crate::context::GlobalCtx; |
24 | | use crate::def::dummy_param_name; |
25 | | use crate::def::is_dummy_param_name; |
26 | | use crate::def::{ |
27 | | Spanned, impl_fndef_path, positional_field_ident, prefix_tuple_param, prefix_tuple_variant, |
28 | | user_local_name, |
29 | | }; |
30 | | use crate::messages::Span; |
31 | | use crate::messages::{error, internal_error}; |
32 | | use crate::sst_util::subst_typ_for_datatype; |
33 | | use crate::util::vec_map_result; |
34 | | use air::ast_util::ident_binder; |
35 | | use air::scope_map::ScopeMap; |
36 | | use std::collections::{HashMap, HashSet}; |
37 | | use std::sync::Arc; |
38 | | |
39 | | struct ClosureDatatype { |
40 | | enclosing_fun: Fun, |
41 | | args: Typs, |
42 | | output: Typ, |
43 | | kind: ClosureKind, |
44 | | path: Path, |
45 | | } |
46 | | |
47 | | struct State { |
48 | | // Counter to generate temporary variables |
49 | | next_var: u64, |
50 | | // Rename parameters to simplify their names |
51 | | rename_vars: HashMap<VarIdent, VarIdent>, |
52 | | // Rename parameters to simplify their names |
53 | | rename_vars_reverse: HashMap<VarIdent, VarIdent>, |
54 | | // Name of a datatype to represent each tuple arity |
55 | | tuple_typs: HashSet<usize>, |
56 | | // Name of a datatype to represent each closure |
57 | | closure_typs: HashMap<usize, ClosureDatatype>, |
58 | | // Functions for which the corresponding FnDef type is used |
59 | | fndef_typs: HashSet<Fun>, |
60 | | } |
61 | | |
62 | | impl State { |
63 | 3.16k | fn new() -> Self { |
64 | 3.16k | State { |
65 | 3.16k | next_var: 0, |
66 | 3.16k | rename_vars: HashMap::new(), |
67 | 3.16k | rename_vars_reverse: HashMap::new(), |
68 | 3.16k | tuple_typs: HashSet::new(), |
69 | 3.16k | closure_typs: HashMap::new(), |
70 | 3.16k | fndef_typs: HashSet::new(), |
71 | 3.16k | } |
72 | 3.16k | } |
73 | | |
74 | 262k | fn reset_for_function(&mut self) { |
75 | 262k | self.next_var = 0; |
76 | 262k | self.rename_vars = HashMap::new(); |
77 | 262k | } |
78 | | |
79 | 91.7k | fn next_temp(&mut self) -> VarIdent { |
80 | 91.7k | self.next_var += 1; |
81 | 91.7k | crate::def::simplify_temp_var(self.next_var) |
82 | 91.7k | } |
83 | | |
84 | 799k | fn tuple_type_name(&mut self, arity: usize) -> Dt { |
85 | 799k | self.tuple_typs.insert(arity); |
86 | 799k | Dt::Tuple(arity) |
87 | 799k | } |
88 | | |
89 | 2.20k | fn closure_type_name( |
90 | 2.20k | &mut self, |
91 | 2.20k | fun: Fun, |
92 | 2.20k | typs: Typs, |
93 | 2.20k | typ: Typ, |
94 | 2.20k | kind: ClosureKind, |
95 | 2.20k | id: usize, |
96 | 2.20k | ) -> Path { |
97 | 2.20k | let e = self.closure_typs.entry(id).or_insert(ClosureDatatype { |
98 | 2.20k | enclosing_fun: fun.clone(), |
99 | 2.20k | args: typs, |
100 | 2.20k | output: typ, |
101 | 2.20k | kind: kind, |
102 | 2.20k | path: crate::def::prefix_closure_type(id), |
103 | 2.20k | }); |
104 | 2.20k | assert!(&e.enclosing_fun == &fun); |
105 | 2.20k | e.path.clone() |
106 | 2.20k | } |
107 | | } |
108 | | |
109 | | struct LocalCtxt { |
110 | | span: Span, |
111 | | typ_params: Vec<Ident>, |
112 | | fun: Option<Fun>, |
113 | | } |
114 | | |
115 | | /// Should only return true if this expression is guaranteed constant |
116 | | /// (i.e., does not depend on evaluation order, i.e., does not depend on any mutable variable) |
117 | 150 | fn is_small_expr(expr: &Expr) -> bool { |
118 | 150 | match &expr.x { |
119 | 31 | ExprX::Const(_) => true, |
120 | 2 | ExprX::Unary(UnaryOp::Not | UnaryOp::Clip { .. }, e) => is_small_expr(e), |
121 | 0 | ExprX::UnaryOpr(UnaryOpr::Box(_) | UnaryOpr::Unbox(_), _) => panic!("unexpected box"), |
122 | 117 | _ => false, |
123 | | } |
124 | 150 | } |
125 | | |
126 | | /// Create a temporary and return: |
127 | | /// - A Stmt that assigns the given `expr` to the temporary |
128 | | /// - The name of the temporary |
129 | 91.4k | fn temp_var(state: &mut State, expr: &Expr) -> (Stmt, VarIdent) { |
130 | 91.4k | let temp = state.next_temp(); |
131 | 91.4k | let name = temp.clone(); |
132 | 91.4k | let pattern = PatternX::simple_var(name, &expr.span, &expr.typ); |
133 | 91.4k | let decl = StmtX::Decl { |
134 | 91.4k | pattern, |
135 | 91.4k | mode: None, |
136 | 91.4k | init: Some(PlaceX::spec_temporary(expr.clone())), |
137 | 91.4k | els: None, |
138 | 91.4k | assert_irrefutable: false, |
139 | 91.4k | }; |
140 | 91.4k | let temp_decl = Spanned::new(expr.span.clone(), decl); |
141 | 91.4k | (temp_decl, temp) |
142 | 91.4k | } |
143 | | |
144 | 82.3k | fn temp_expr(state: &mut State, expr: &Expr) -> (Stmt, Expr) { |
145 | 82.3k | let (temp_decl, var_ident) = temp_var(state, expr); |
146 | 82.3k | (temp_decl, SpannedTyped::new(&expr.span, &expr.typ, ExprX::Var(var_ident))) |
147 | 82.3k | } |
148 | | |
149 | 148 | fn small_or_temp(state: &mut State, expr: &Expr) -> (Vec<Stmt>, Expr) { |
150 | 148 | if is_small_expr(&expr) { |
151 | 31 | (vec![], expr.clone()) |
152 | | } else { |
153 | 117 | let (ts, te) = temp_expr(state, expr); |
154 | 117 | (vec![ts], te) |
155 | | } |
156 | 148 | } |
157 | | |
158 | 45 | fn pattern_to_decls_with_no_initializer(pattern: &Pattern, stmts: &mut Vec<Stmt>) { |
159 | 45 | match &pattern.x { |
160 | 15 | PatternX::Wildcard(_) => {} |
161 | 10 | PatternX::Var(binding) | PatternX::Binding { binding, sub_pat: _ } => { |
162 | 10 | let v_patternx = PatternX::Var(PatternBinding { |
163 | 10 | name: binding.name.clone(), |
164 | 10 | user_mut: None, |
165 | 10 | by_ref: ByRef::No, |
166 | 10 | typ: binding.typ.clone(), |
167 | 10 | copy: false, |
168 | 10 | }); |
169 | 10 | let v_pattern = SpannedTyped::new(&pattern.span, &binding.typ, v_patternx); |
170 | 10 | stmts.push(Spanned::new( |
171 | 10 | pattern.span.clone(), |
172 | 10 | StmtX::Decl { |
173 | 10 | pattern: v_pattern, |
174 | 10 | mode: None, // mode doesn't matter anymore |
175 | 10 | init: None, |
176 | 10 | els: None, |
177 | 10 | assert_irrefutable: false, |
178 | 10 | }, |
179 | | )); |
180 | | |
181 | 10 | match &pattern.x { |
182 | 0 | PatternX::Binding { sub_pat, .. } => { |
183 | 0 | pattern_to_decls_with_no_initializer(sub_pat, stmts); |
184 | 0 | } |
185 | 10 | _ => {} |
186 | | } |
187 | | } |
188 | 12 | PatternX::Constructor(_path, _variant, patterns) => { |
189 | 20 | for binder in patterns.iter() { |
190 | 20 | pattern_to_decls_with_no_initializer(&binder.a, stmts); |
191 | 20 | } |
192 | | } |
193 | 4 | PatternX::Or(pat1, _pat2) => { |
194 | 4 | pattern_to_decls_with_no_initializer(&pat1, stmts); |
195 | 4 | } |
196 | 4 | PatternX::Expr(_) => {} |
197 | 0 | PatternX::Range(_, _) => {} |
198 | 0 | PatternX::ImmutRef(p) | PatternX::MutRef(p) => { |
199 | 0 | pattern_to_decls_with_no_initializer(p, stmts); |
200 | 0 | } |
201 | | } |
202 | 45 | } |
203 | | |
204 | 1.41M | fn rename_var(state: &State, scope_map: &VisitorScopeMap, x: &VarIdent) -> VarIdent { |
205 | 1.41M | if let Some(rename) = state.rename_vars.get(x) { |
206 | 1.10M | if scope_map[x].is_outer_param_or_ret { |
207 | 1.10M | return rename.clone(); |
208 | 0 | } |
209 | 311k | } |
210 | 311k | x.clone() |
211 | 1.41M | } |
212 | | |
213 | 1.84M | fn simplify_one_place( |
214 | 1.84M | _ctx: &GlobalCtx, |
215 | 1.84M | state: &mut State, |
216 | 1.84M | scope_map: &VisitorScopeMap, |
217 | 1.84M | place: &Place, |
218 | 1.84M | ) -> Result<Place, VirErr> { |
219 | 1.84M | match &place.x { |
220 | 1.40M | PlaceX::Local(x) => Ok(place.new_x(PlaceX::Local(rename_var(state, scope_map, x)))), |
221 | 448k | _ => Ok(place.clone()), |
222 | | } |
223 | 1.84M | } |
224 | | |
225 | | /// Returns a "pure place", i.e., a Place with no-side effects, and which is rooted |
226 | | /// at a Local (rather than a Temporary). |
227 | 21.7k | fn place_to_pure_place(state: &mut State, place: &Place) -> (Vec<Stmt>, Place) { |
228 | 21.7k | let (mut stmts, place, wf) = place_to_pure_place_rec(state, place); |
229 | 21.7k | stmts.extend(wf); |
230 | 21.7k | (stmts, place) |
231 | 21.7k | } |
232 | | |
233 | | /// Returns (Stmts, place, wf) |
234 | | /// wf are the remaining obligations to prove that accessing the place is safe |
235 | | /// (i.e., those not already icnluded in Stmts) |
236 | 23.8k | fn place_to_pure_place_rec(state: &mut State, place: &Place) -> (Vec<Stmt>, Place, Vec<Stmt>) { |
237 | 23.8k | match &place.x { |
238 | 1.63k | PlaceX::Field(field_opr, p) => { |
239 | 1.63k | let (stmts, p1, mut wf) = place_to_pure_place_rec(state, p); |
240 | 1.63k | match field_opr.check { |
241 | 1.62k | VariantCheck::None => {} |
242 | 19 | VariantCheck::Union => { |
243 | 19 | let p1_expr = place_to_spec_expr(&p1); |
244 | 19 | let assert_stmt = |
245 | 19 | crate::place_preconditions::field_check(&place.span, &p1_expr, field_opr); |
246 | 19 | wf.push(assert_stmt); |
247 | 19 | } |
248 | | // Handled later, at SST lowering - no AST-level encoding for this timing. |
249 | 0 | VariantCheck::Recommends => {} |
250 | | } |
251 | | // Preserve Recommends for that later pass; Union is already discharged above. |
252 | 1.63k | let check = if field_opr.check == VariantCheck::Recommends { |
253 | 0 | VariantCheck::Recommends |
254 | | } else { |
255 | 1.63k | VariantCheck::None |
256 | | }; |
257 | 1.63k | let field_opr = FieldOpr { check, ..field_opr.clone() }; |
258 | 1.63k | let p2 = |
259 | 1.63k | SpannedTyped::new(&place.span, &place.typ, PlaceX::Field(field_opr.clone(), p1)); |
260 | 1.63k | (stmts, p2, wf) |
261 | | } |
262 | 164 | PlaceX::DerefMut(p) => { |
263 | 164 | let (stmts, p1, wf) = place_to_pure_place_rec(state, p); |
264 | 164 | let p2 = SpannedTyped::new(&place.span, &place.typ, PlaceX::DerefMut(p1)); |
265 | 164 | (stmts, p2, wf) |
266 | | } |
267 | 6 | PlaceX::ModeUnwrap(p, mwm) => { |
268 | 6 | let (stmts, p1, wf) = place_to_pure_place_rec(state, p); |
269 | 6 | let p2 = SpannedTyped::new(&place.span, &place.typ, PlaceX::ModeUnwrap(p1, *mwm)); |
270 | 6 | (stmts, p2, wf) |
271 | | } |
272 | 12.6k | PlaceX::Local(_l) => (vec![], place.clone(), vec![]), |
273 | 9.12k | PlaceX::Temporary(expr) => { |
274 | 9.12k | let (ts, var_ident) = temp_var(state, expr); |
275 | 9.12k | let p = SpannedTyped::new(&place.span, &place.typ, PlaceX::Local(var_ident)); |
276 | 9.12k | (vec![ts], p, vec![]) |
277 | | } |
278 | 230 | PlaceX::WithExpr(expr, p) => { |
279 | 230 | let (mut stmts, p1, wf) = place_to_pure_place_rec(state, p); |
280 | 230 | stmts.insert(0, Spanned::new(place.span.clone(), StmtX::Expr(expr.clone()))); |
281 | 230 | (stmts, p1, wf) |
282 | | } |
283 | 64 | PlaceX::Index(p, idx, kind, bounds_check) => { |
284 | 64 | let (mut stmts, p1, mut wf) = place_to_pure_place_rec(state, p); |
285 | 64 | let (idx_decl, idx_expr) = temp_expr(state, idx); |
286 | 64 | stmts.push(idx_decl); |
287 | | |
288 | 64 | match bounds_check { |
289 | 0 | BoundsCheck::Allow => {} |
290 | | BoundsCheck::Error => { |
291 | 64 | if kind.getting_len_requires_read() { |
292 | 16 | stmts.extend(wf); |
293 | 16 | wf = vec![]; |
294 | 48 | } |
295 | 64 | let p1_expr = place_to_spec_expr(&p1); |
296 | 64 | let assert_stmt = crate::place_preconditions::index_bound( |
297 | 64 | &place.span, |
298 | 64 | &p1_expr, |
299 | 64 | &idx_expr, |
300 | 64 | *kind, |
301 | | ); |
302 | 64 | stmts.push(assert_stmt); |
303 | | } |
304 | | } |
305 | | |
306 | 64 | let p = SpannedTyped::new( |
307 | 64 | &place.span, |
308 | 64 | &place.typ, |
309 | 64 | PlaceX::Index(p1, idx_expr, *kind, BoundsCheck::Allow), |
310 | | ); |
311 | | |
312 | 64 | (stmts, p, wf) |
313 | | } |
314 | | PlaceX::UserDefinedTypInvariantObligation(..) => { |
315 | 0 | panic!("Verus internal error: unexpected UserDefinedTypInvariantObligation"); |
316 | | } |
317 | | } |
318 | 23.8k | } |
319 | | |
320 | | // note that this gets called *bottom up* |
321 | | // that is, if node A is the parent of children B and C, |
322 | | // then simplify_one_expr is called first on B and C, and then on A |
323 | | |
324 | 4.25M | fn simplify_one_expr( |
325 | 4.25M | ctx: &GlobalCtx, |
326 | 4.25M | state: &mut State, |
327 | 4.25M | scope_map: &VisitorScopeMap, |
328 | 4.25M | expr: &Expr, |
329 | 4.25M | ) -> Result<Expr, VirErr> { |
330 | | use crate::ast::CallTargetKind; |
331 | 4.25M | match &expr.x { |
332 | 9.93k | ExprX::Var(x) => Ok(expr.new_x(ExprX::Var(rename_var(state, scope_map, x)))), |
333 | 1.76k | ExprX::VarAt(x, at) => Ok(expr.new_x(ExprX::VarAt(rename_var(state, scope_map, x), *at))), |
334 | 15.8k | ExprX::Assign { place, .. } |
335 | 3.36k | | ExprX::BorrowMut(place) |
336 | 11.1k | | ExprX::TwoPhaseBorrowMut(place) |
337 | 6 | | ExprX::BorrowMutTracked(place) => { |
338 | | // This check is no longer needed for soundness because ast_to_sst infers |
339 | | // mutability rather than relying on the user annotations. |
340 | | // Nonetheless, this check lets us pick up a few situations that wouldn't |
341 | | // get caught by borrowck (ghost variables). |
342 | | // However, much of the time it _does_ get caught by borrowck. |
343 | | // This check is in ast_simplify because it's after borrowck |
344 | | // (borrowck errors look nicer). |
345 | 30.3k | if !crate::ast_util::place_has_deref_mut(place) |
346 | 20.2k | && let Some(local) = crate::ast_util::place_get_local(place) |
347 | | { |
348 | 20.1k | let PlaceX::Local(x) = &local.x else { unreachable!() }; |
349 | 20.1k | let x = match state.rename_vars_reverse.get(x) { |
350 | 20.0k | None => x, |
351 | 132 | Some(y) => y, |
352 | | }; |
353 | 20.1k | match scope_map.get(x) { |
354 | | None => { |
355 | 0 | return Err(error( |
356 | 0 | &expr.span, |
357 | 0 | "Verus Internal Error: cannot find this variable", |
358 | 0 | )); |
359 | | } |
360 | 20.1k | Some(entry) if entry.user_mut == Some(false) && entry.init => { |
361 | 2 | let name = user_local_name(x); |
362 | 2 | return Err(error( |
363 | 2 | &expr.span, |
364 | 2 | format!("variable `{name:}` is not marked mutable"), |
365 | 2 | )); |
366 | | } |
367 | 20.1k | _ => {} |
368 | | } |
369 | 10.1k | } |
370 | 30.3k | Ok(expr.clone()) |
371 | | } |
372 | 540 | ExprX::ConstVar(x, autospec) => { |
373 | 540 | let call_target_attrs = crate::ast::CallTargetAttrs { |
374 | 540 | autospec: *autospec, |
375 | 540 | const_var: true, |
376 | 540 | assume_external_allowed: false, |
377 | 540 | }; |
378 | 540 | let call = ExprX::Call { |
379 | 540 | target: CallTarget::Fun( |
380 | 540 | CallTargetKind::Static, |
381 | 540 | x.clone(), |
382 | 540 | Arc::new(vec![]), |
383 | 540 | Arc::new(vec![]), |
384 | 540 | call_target_attrs, |
385 | 540 | ), |
386 | 540 | args: Arc::new(vec![]), |
387 | 540 | post_args: None, |
388 | 540 | body: None, |
389 | 540 | }; |
390 | 540 | Ok(SpannedTyped::new(&expr.span, &expr.typ, call)) |
391 | | } |
392 | | ExprX::Call { |
393 | 776k | target: CallTarget::Fun(kind, tgt, typs, impl_paths, attrs), |
394 | 776k | args, |
395 | 776k | post_args, |
396 | 776k | body, |
397 | | } => { |
398 | 776k | assert!(attrs.autospec == AutospecUsage::Final); |
399 | | |
400 | 776k | let is_trait_impl = match kind { |
401 | 650k | CallTargetKind::Static => false, |
402 | 42 | CallTargetKind::ProofFn(..) => false, |
403 | 62.8k | CallTargetKind::Dynamic => true, |
404 | 62.7k | CallTargetKind::DynamicResolved { .. } => true, |
405 | 16 | CallTargetKind::ExternalTraitDefault => true, |
406 | | }; |
407 | 776k | let args = if typs.len() == 0 && args.len() == 0 && !is_trait_impl { |
408 | | // To simplify the AIR/SMT encoding, add a dummy argument to any function with 0 arguments |
409 | 3.16k | let typ = Arc::new(TypX::Int(IntRange::Int)); |
410 | | use num_traits::Zero; |
411 | 3.16k | let argx = ExprX::Const(Constant::Int(num_bigint::BigInt::zero())); |
412 | 3.16k | let arg = SpannedTyped::new(&expr.span, &typ, argx); |
413 | 3.16k | Arc::new(vec![arg]) |
414 | | } else { |
415 | 772k | args.clone() |
416 | | }; |
417 | | |
418 | 776k | let call = ExprX::Call { |
419 | 776k | target: CallTarget::Fun( |
420 | 776k | kind.clone(), |
421 | 776k | tgt.clone(), |
422 | 776k | typs.clone(), |
423 | 776k | impl_paths.clone(), |
424 | 776k | attrs.clone(), |
425 | 776k | ), |
426 | 776k | args, |
427 | 776k | post_args: post_args.clone(), |
428 | 776k | body: body.clone(), |
429 | 776k | }; |
430 | 776k | Ok(SpannedTyped::new(&expr.span, &expr.typ, call)) |
431 | | } |
432 | 565 | ExprX::Ctor(name, variant, partial_binders, Some(update)) => { |
433 | 565 | let CtorUpdateTail { place, taken_fields: _ } = update; |
434 | 565 | let (stmts, update) = place_to_pure_place(state, place); |
435 | | // not really spec but that doesn't matter at this point |
436 | 565 | let update = place_to_spec_expr(&update); |
437 | 565 | let mut decls: Vec<Stmt> = Vec::new(); |
438 | 565 | let mut binders: Vec<Binder<Expr>> = Vec::new(); |
439 | 565 | if stmts.len() == 0 { |
440 | 433 | for binder in partial_binders.iter() { |
441 | 433 | binders.push(binder.clone()); |
442 | 433 | } |
443 | | } else { |
444 | | // Because of Rust's order of evaluation here, |
445 | | // we have to put binders in temp vars, too. |
446 | 148 | for binder in partial_binders.iter() { |
447 | 148 | let (temp_decl_inner, e) = small_or_temp(state, &binder.a); |
448 | 148 | decls.extend(temp_decl_inner.into_iter()); |
449 | 148 | binders.push(binder.map_a(|_| e)); |
450 | | } |
451 | 138 | decls.extend(stmts.into_iter()); |
452 | | } |
453 | | |
454 | 565 | let path = match name { |
455 | 565 | Dt::Path(p) => p, |
456 | | Dt::Tuple(_) => { |
457 | 0 | return Err(internal_error( |
458 | 0 | &expr.span, |
459 | 0 | "ExprX::Ctor with update and tuple type", |
460 | 0 | )); |
461 | | } |
462 | | }; |
463 | | |
464 | 565 | let (typ_positives, variants) = &ctx.datatypes[path]; |
465 | 565 | let fields = &crate::ast_util::get_variant(&variants, variant).fields; |
466 | 565 | let typ_args = typ_args_for_datatype_typ(&expr.typ); |
467 | | // replace ..update |
468 | | // with f1: update.f1, f2: update.f2, ... |
469 | 1.19k | for field in fields.iter() { |
470 | 1.30k | if binders.iter().find(|b| b.name == field.name).is_none() { |
471 | 611 | let op = UnaryOpr::Field(FieldOpr { |
472 | 611 | datatype: name.clone(), |
473 | 611 | variant: variant.clone(), |
474 | 611 | field: field.name.clone(), |
475 | 611 | get_variant: false, |
476 | 611 | check: VariantCheck::None, |
477 | 611 | }); |
478 | 611 | let exprx = ExprX::UnaryOpr(op, update.clone()); |
479 | 611 | let ty = subst_typ_for_datatype(&typ_positives, typ_args, &field.a.0); |
480 | 611 | let field_exp = SpannedTyped::new(&expr.span, &ty, exprx); |
481 | 611 | binders.push(ident_binder(&field.name, &field_exp)); |
482 | 611 | } |
483 | | } |
484 | 565 | let ctorx = ExprX::Ctor(name.clone(), variant.clone(), Arc::new(binders), None); |
485 | 565 | let ctor = SpannedTyped::new(&expr.span, &expr.typ, ctorx); |
486 | 565 | if decls.len() == 0 { |
487 | 427 | Ok(ctor) |
488 | | } else { |
489 | 138 | let block = ExprX::Block(Arc::new(decls), Some(ctor)); |
490 | 138 | Ok(SpannedTyped::new(&expr.span, &expr.typ, block)) |
491 | | } |
492 | | } |
493 | 32 | ExprX::ShrRefStructWrap(e1, e2, _t1, t2, variant, field) => { |
494 | | // Simplify as `Struct { field: e1, .. e2 }` |
495 | 32 | let datatype_typ = undecorate_typ(&t2); |
496 | 32 | let (dt, _typ_args) = match &*datatype_typ { |
497 | 32 | TypX::Datatype(dt, typ_args, ..) => (dt, typ_args), |
498 | 0 | _ => panic!("ShrRefStructWrap expects datatype"), |
499 | | }; |
500 | 32 | let Dt::Path(path) = dt else { panic!("ShrRefStructWrap expects Dt::Path") }; |
501 | 32 | let partial_binders = |
502 | 32 | Arc::new(vec![Arc::new(air::ast::BinderX { name: field.clone(), a: e1.clone() })]); |
503 | 32 | let place = SpannedTyped::new(&e2.span, &e2.typ, PlaceX::Temporary(e2.clone())); |
504 | | // taken_fields is ignored by this point |
505 | 32 | let upd = CtorUpdateTail { place: place, taken_fields: Arc::new(vec![]) }; |
506 | 32 | let variant = if **variant == "" { |
507 | 30 | let (_, variants) = &ctx.datatypes[path]; |
508 | 30 | assert!(variants.len() == 1); |
509 | 30 | variants[0].name.clone() |
510 | | } else { |
511 | 2 | variant.clone() |
512 | | }; |
513 | 32 | let ctor = ExprX::Ctor(dt.clone(), variant.clone(), partial_binders, Some(upd)); |
514 | 32 | let ctor = SpannedTyped::new(&expr.span, &expr.typ, ctor); |
515 | 32 | simplify_one_expr(ctx, state, scope_map, &ctor) |
516 | | } |
517 | 11.0k | ExprX::Unary(UnaryOp::CoerceMode { .. }, expr0) => Ok(expr0.clone()), |
518 | 26.5k | ExprX::Multi(MultiOp::Chained(ops), args) => { |
519 | | use crate::ast::IeeeFloatBinaryOp; |
520 | 26.5k | assert!(args.len() == ops.len() + 1); |
521 | 26.5k | let mut stmts: Vec<Stmt> = Vec::new(); |
522 | 26.5k | let mut es: Vec<Expr> = Vec::new(); |
523 | 26.5k | let mut is_float = false; |
524 | | // Execute each argument in order; no short-circuiting |
525 | 82.1k | for i in 0..args.len() { |
526 | 82.1k | let t = crate::ast_util::undecorate_typ(&args[i].typ); |
527 | 82.1k | if matches!(*t, TypX::Float(_)) { |
528 | 18 | is_float = true; |
529 | 82.1k | } |
530 | 82.1k | let (decl, e) = temp_expr(state, &args[i]); |
531 | 82.1k | stmts.push(decl); |
532 | 82.1k | es.push(e); |
533 | | } |
534 | 26.5k | let mut conjunction: Expr = es[0].clone(); |
535 | 55.6k | for i in 0..ops.len() { |
536 | 55.6k | let op = match (is_float, ops[i]) { |
537 | 53.2k | (false, ChainedOp::Inequality(a)) => BinaryOp::Inequality(a), |
538 | 12 | (true, ChainedOp::Inequality(a)) => { |
539 | 12 | BinaryOp::IeeeFloat(IeeeFloatBinaryOp::InEq(a)) |
540 | | } |
541 | 2.30k | (_, ChainedOp::MultiEq) => BinaryOp::Eq(Mode::Spec), |
542 | | }; |
543 | 55.6k | let left = es[i].clone(); |
544 | 55.6k | let right = es[i + 1].clone(); |
545 | 55.6k | let span = left.span.clone(); |
546 | 55.6k | let binary = SpannedTyped::new(&span, &expr.typ, ExprX::Binary(op, left, right)); |
547 | 55.6k | if i == 0 { |
548 | 26.5k | conjunction = binary; |
549 | 29.0k | } else { |
550 | 29.0k | let exprx = ExprX::Logical(LogicalOp::And, conjunction, binary); |
551 | 29.0k | conjunction = SpannedTyped::new(&span, &expr.typ, exprx); |
552 | 29.0k | } |
553 | | } |
554 | 26.5k | if stmts.len() == 0 { |
555 | 0 | Ok(conjunction) |
556 | | } else { |
557 | 26.5k | let block = ExprX::Block(Arc::new(stmts), Some(conjunction)); |
558 | 26.5k | Ok(SpannedTyped::new(&expr.span, &expr.typ, block)) |
559 | | } |
560 | | } |
561 | 13.9k | ExprX::Match(place, arms1, assert_irrefutable) => { |
562 | 13.9k | let (temp_decl, place) = place_to_pure_place(state, place); |
563 | | |
564 | | // Translate into If expression |
565 | 13.9k | let t_bool = Arc::new(TypX::Bool); |
566 | 13.9k | let mut if_expr: Option<Expr> = None; |
567 | 29.6k | for arm in arms1.iter().rev() { |
568 | 29.6k | let mut decls: Vec<Stmt> = Vec::new(); |
569 | 29.6k | let has_guard = arm.x.has_guard(); |
570 | | |
571 | 29.6k | let test_pattern = crate::patterns::pattern_to_exprs( |
572 | 29.6k | ctx, |
573 | 29.6k | &place, |
574 | 29.6k | &arm.x.pattern, |
575 | 29.6k | has_guard, |
576 | 29.6k | &mut decls, |
577 | 1 | )?; |
578 | | |
579 | 29.6k | let test = if !has_guard { |
580 | 29.5k | test_pattern |
581 | | } else { |
582 | 62 | assert!(!crate::patterns::pattern_has_or(&arm.x.pattern)); |
583 | | |
584 | 62 | let mut guard = arm.x.guard.clone(); |
585 | 62 | guard = SpannedTyped::new( |
586 | 62 | &guard.span, |
587 | 62 | &guard.typ, |
588 | 62 | ExprX::MatchGuardFreeze(place.clone(), guard.clone()), |
589 | | ); |
590 | 62 | let test_exp = ExprX::Logical(LogicalOp::And, test_pattern, guard); |
591 | 62 | let test = SpannedTyped::new(&arm.x.pattern.span, &t_bool, test_exp); |
592 | 62 | let block = ExprX::Block(Arc::new(decls.clone()), Some(test)); |
593 | 62 | SpannedTyped::new(&arm.x.pattern.span, &t_bool, block) |
594 | | }; |
595 | | |
596 | 29.6k | let block = ExprX::Block(Arc::new(decls), Some(arm.x.body.clone())); |
597 | 29.6k | let body = SpannedTyped::new(&arm.x.pattern.span, &expr.typ, block); |
598 | 29.6k | if let Some(prev) = if_expr { |
599 | 15.6k | // if pattern && guard then body else prev |
600 | 15.6k | let ifx = ExprX::If(test.clone(), body, Some(prev)); |
601 | 15.6k | if_expr = Some(SpannedTyped::new(&test.span, &expr.typ.clone(), ifx)); |
602 | 15.6k | } else if *assert_irrefutable { |
603 | 1.75k | if has_guard { |
604 | 0 | return Err(error(&arm.x.guard.span, "if-guard on final match arm")); |
605 | 1.75k | } |
606 | 1.75k | let assertion = SpannedTyped::new( |
607 | 1.75k | &arm.x.pattern.span, |
608 | 1.75k | &unit_typ(), |
609 | 1.75k | ExprX::AssertAssume { |
610 | 1.75k | is_assume: false, |
611 | 1.75k | expr: test, |
612 | 1.75k | msg: Some(irrefut_failure_msg(&arm.x.pattern.span)), |
613 | 1.75k | }, |
614 | | ); |
615 | 1.75k | let block = SpannedTyped::new( |
616 | 1.75k | &body.span, |
617 | 1.75k | &body.typ, |
618 | 1.75k | ExprX::Block( |
619 | 1.75k | Arc::new(vec![Spanned::new( |
620 | 1.75k | assertion.span.clone(), |
621 | 1.75k | StmtX::Expr(assertion.clone()), |
622 | 1.75k | )]), |
623 | 1.75k | Some(body.clone()), |
624 | 1.75k | ), |
625 | | ); |
626 | 1.75k | if_expr = Some(block); |
627 | 12.1k | } else { |
628 | 12.1k | // last arm is unconditional |
629 | 12.1k | if_expr = Some(body); |
630 | 12.1k | } |
631 | | } |
632 | 13.9k | if let Some(if_expr) = if_expr { |
633 | 13.9k | let if_expr = if temp_decl.len() != 0 { |
634 | 6.57k | let block = ExprX::Block(Arc::new(temp_decl), Some(if_expr)); |
635 | 6.57k | SpannedTyped::new(&expr.span, &expr.typ, block) |
636 | | } else { |
637 | 7.37k | if_expr |
638 | | }; |
639 | 13.9k | Ok(if_expr) |
640 | | } else { |
641 | 1 | Err(error(&expr.span, "not yet implemented: zero-arm match expressions")) |
642 | | } |
643 | | } |
644 | 29.6k | ExprX::Ghost { alloc_wrapper: _, tracked: _, expr: expr1 } => Ok(expr1.clone()), |
645 | | ExprX::NonSpecClosure { |
646 | 307 | params, |
647 | 307 | proof_fn_modes, |
648 | 307 | body, |
649 | 307 | requires, |
650 | 307 | ensures, |
651 | 307 | ret, |
652 | 307 | external_spec, |
653 | | } => { |
654 | 307 | assert!(external_spec.is_none()); |
655 | | |
656 | 307 | let closure_var_ident = state.next_temp(); |
657 | 307 | let closure_var = SpannedTyped::new( |
658 | 307 | &expr.span, |
659 | 307 | &expr.typ.clone(), |
660 | 307 | ExprX::Var(closure_var_ident.clone()), |
661 | | ); |
662 | | |
663 | 307 | let external_spec_expr = |
664 | 307 | exec_closure_spec(state, &expr.span, &closure_var, params, ret, requires, ensures)?; |
665 | 307 | let external_spec = Some((closure_var_ident, external_spec_expr)); |
666 | | |
667 | 307 | Ok(SpannedTyped::new( |
668 | 307 | &expr.span, |
669 | 307 | &expr.typ, |
670 | 307 | ExprX::NonSpecClosure { |
671 | 307 | params: params.clone(), |
672 | 307 | proof_fn_modes: proof_fn_modes.clone(), |
673 | 307 | body: body.clone(), |
674 | 307 | requires: requires.clone(), |
675 | 307 | ensures: ensures.clone(), |
676 | 307 | ret: ret.clone(), |
677 | 307 | external_spec, |
678 | 307 | }, |
679 | 307 | )) |
680 | | } |
681 | 3.35M | _ => Ok(expr.clone()), |
682 | | } |
683 | 4.25M | } |
684 | | |
685 | 5.39k | fn tuple_get_field_expr( |
686 | 5.39k | state: &mut State, |
687 | 5.39k | span: &Span, |
688 | 5.39k | typ: &Typ, |
689 | 5.39k | tuple_expr: &Expr, |
690 | 5.39k | tuple_arity: usize, |
691 | 5.39k | field: usize, |
692 | 5.39k | ) -> Expr { |
693 | 5.39k | let datatype = state.tuple_type_name(tuple_arity); |
694 | | |
695 | 5.39k | let variant = prefix_tuple_variant(tuple_arity); |
696 | 5.39k | let field = positional_field_ident(field); |
697 | 5.39k | let op = UnaryOpr::Field(FieldOpr { |
698 | 5.39k | datatype, |
699 | 5.39k | variant, |
700 | 5.39k | field, |
701 | 5.39k | get_variant: false, |
702 | 5.39k | check: VariantCheck::None, |
703 | 5.39k | }); |
704 | 5.39k | let field_expr = SpannedTyped::new(span, typ, ExprX::UnaryOpr(op, tuple_expr.clone())); |
705 | 5.39k | field_expr |
706 | 5.39k | } |
707 | | |
708 | 1.75k | fn irrefut_failure_msg(pattern_span: &Span) -> crate::messages::Message { |
709 | 1.75k | error(pattern_span, "unable to prove this pattern will successfully match") |
710 | 1.75k | } |
711 | | |
712 | 300k | fn simplify_one_stmt(ctx: &GlobalCtx, state: &mut State, stmt: &Stmt) -> Result<Vec<Stmt>, VirErr> { |
713 | 300k | match &stmt.x { |
714 | 11.5k | StmtX::Decl { pattern, mode: _, init: None, els: None, assert_irrefutable } => { |
715 | 11.5k | assert!(!assert_irrefutable); |
716 | 11.5k | match &pattern.x { |
717 | | PatternX::Var(PatternBinding { |
718 | | by_ref: ByRef::No, |
719 | | name: _, |
720 | | user_mut: _, |
721 | | typ: _, |
722 | | copy: _, |
723 | 11.5k | }) => Ok(vec![stmt.clone()]), |
724 | | _ => { |
725 | 21 | let mut stmts: Vec<Stmt> = Vec::new(); |
726 | 21 | pattern_to_decls_with_no_initializer(pattern, &mut stmts); |
727 | 21 | Ok(stmts) |
728 | | } |
729 | | } |
730 | | } |
731 | 0 | StmtX::Decl { pattern, mode: _, init: None, els: Some(_), .. } => Err(error( |
732 | 0 | &pattern.span, |
733 | 0 | "Verus Internal Error: Decl with else-block but no initializer", |
734 | 0 | )), |
735 | 63.5k | StmtX::Decl { pattern, mode: _, init: Some(_init), els: None, assert_irrefutable: _ } |
736 | 7.18k | if matches!( |
737 | 63.5k | pattern.x, |
738 | | PatternX::Var(PatternBinding { |
739 | | by_ref: ByRef::No, |
740 | | name: _, |
741 | | user_mut: _, |
742 | | typ: _, |
743 | | copy: _ |
744 | | }) |
745 | | ) => |
746 | | { |
747 | 56.3k | Ok(vec![stmt.clone()]) |
748 | | } |
749 | 7.21k | StmtX::Decl { pattern, mode: _, init: Some(init), els, assert_irrefutable } => { |
750 | 7.21k | let (mut stmts, place) = place_to_pure_place(state, init); |
751 | 7.21k | let mut stmts2: Vec<Stmt> = vec![]; |
752 | 7.21k | let pattern_check = |
753 | 7.21k | crate::patterns::pattern_to_exprs(ctx, &place, pattern, false, &mut stmts2)?; |
754 | 7.21k | if let Some(els) = &els { |
755 | 34 | assert!(!assert_irrefutable); |
756 | 34 | let checkx = ExprX::Unary(UnaryOp::Not, pattern_check.clone()); |
757 | 34 | let check = SpannedTyped::new(&pattern_check.span, &pattern_check.typ, checkx); |
758 | 34 | let neverx = ExprX::NeverToAny(els.clone()); |
759 | 34 | let never = SpannedTyped::new(&els.span, &unit_typ(), neverx); |
760 | 34 | let ifx = ExprX::If(check.clone(), never, None); |
761 | 34 | let ife = SpannedTyped::new(&stmt.span, &unit_typ(), ifx); |
762 | 34 | let ifstmtx = StmtX::Expr(ife); |
763 | 34 | let ifstmt = Spanned::new(stmt.span.clone(), ifstmtx); |
764 | 34 | stmts.push(ifstmt); |
765 | 7.18k | } else if *assert_irrefutable { |
766 | 1 | stmts.push(Spanned::new( |
767 | 1 | stmt.span.clone(), |
768 | 1 | StmtX::Expr(SpannedTyped::new( |
769 | 1 | &stmt.span, |
770 | 1 | &unit_typ(), |
771 | 1 | ExprX::AssertAssume { |
772 | 1 | is_assume: false, |
773 | 1 | expr: pattern_check, |
774 | 1 | msg: Some(irrefut_failure_msg(&pattern.span)), |
775 | 1 | }, |
776 | 1 | )), |
777 | 1 | )); |
778 | 7.17k | } |
779 | 7.21k | stmts.extend(stmts2); |
780 | 7.21k | Ok(stmts) |
781 | | } |
782 | 225k | StmtX::Expr(_) => Ok(vec![stmt.clone()]), |
783 | | } |
784 | 300k | } |
785 | | |
786 | 15.4M | fn simplify_one_typ(local: &LocalCtxt, state: &mut State, typ: &Typ) -> Result<Typ, VirErr> { |
787 | 15.4M | match &**typ { |
788 | 770k | TypX::Datatype(Dt::Tuple(i), ..) => { |
789 | 770k | state.tuple_type_name(*i); |
790 | 770k | Ok(typ.clone()) |
791 | | } |
792 | 2.20k | TypX::AnonymousClosure(typs, typ, kind, id) => { |
793 | 2.20k | let Some(fun) = local.fun.clone() else { |
794 | 0 | return Err(error( |
795 | 0 | &local.span, |
796 | 0 | format!("Verus Internal Error: found AnonymousClosure type outside function"), |
797 | 0 | )); |
798 | | }; |
799 | 2.20k | let path = |
800 | 2.20k | Dt::Path(state.closure_type_name(fun, typs.clone(), typ.clone(), *kind, *id)); |
801 | 2.20k | let typ_args: Vec<Typ> = local |
802 | 2.20k | .typ_params |
803 | 2.20k | .iter() |
804 | 2.20k | .map(|name| Arc::new(TypX::TypParam(name.clone()))) |
805 | 2.20k | .collect(); |
806 | 2.20k | Ok(Arc::new(TypX::Datatype(path, Arc::new(typ_args), Arc::new(vec![])))) |
807 | | } |
808 | 2.77k | TypX::FnDef(fun, _typs, resolved) => { |
809 | 2.77k | state.fndef_typs.insert(fun.clone()); |
810 | 2.77k | if let Some(resolved_fun) = resolved { |
811 | 140 | state.fndef_typs.insert(resolved_fun.clone()); |
812 | 2.63k | } |
813 | 2.77k | Ok(typ.clone()) |
814 | | } |
815 | 5.67M | TypX::TypParam(x) => { |
816 | 5.67M | if !local.typ_params.contains(&x) { |
817 | 0 | return Err(error( |
818 | 0 | &local.span, |
819 | 0 | format!("type parameter {} used before being declared", x), |
820 | 0 | )); |
821 | 5.67M | } |
822 | 5.67M | Ok(typ.clone()) |
823 | | } |
824 | 8.98M | _ => Ok(typ.clone()), |
825 | | } |
826 | 15.4M | } |
827 | | |
828 | | // TODO: a lot of this closure stuff could get its own file |
829 | | // rename to apply to all fn types, not just closure types |
830 | | |
831 | 5.31k | fn closure_trait_call_typ_args(state: &mut State, fn_val: &Expr, params: &VarBinders<Typ>) -> Typs { |
832 | 5.31k | let path = state.tuple_type_name(params.len()); |
833 | | |
834 | 5.41k | let param_typs: Vec<Typ> = params.iter().map(|p| p.a.clone()).collect(); |
835 | 5.31k | let tup_typ = Arc::new(TypX::Datatype(path, Arc::new(param_typs), Arc::new(vec![]))); |
836 | | |
837 | 5.31k | Arc::new(vec![fn_val.typ.clone(), tup_typ]) |
838 | 5.31k | } |
839 | | |
840 | 938 | fn mk_closure_req_call( |
841 | 938 | state: &mut State, |
842 | 938 | span: &Span, |
843 | 938 | params: &VarBinders<Typ>, |
844 | 938 | fn_val: &Expr, |
845 | 938 | arg_tuple: &Expr, |
846 | 938 | ) -> Expr { |
847 | 938 | let bool_typ = Arc::new(TypX::Bool); |
848 | 938 | SpannedTyped::new( |
849 | 938 | span, |
850 | 938 | &bool_typ, |
851 | 938 | ExprX::Call { |
852 | 938 | target: CallTarget::BuiltinSpecFun( |
853 | 938 | BuiltinSpecFun::ClosureReq, |
854 | 938 | closure_trait_call_typ_args(state, fn_val, params), |
855 | 938 | Arc::new(vec![]), |
856 | 938 | ), |
857 | 938 | args: Arc::new(vec![fn_val.clone(), arg_tuple.clone()]), |
858 | 938 | post_args: None, |
859 | 938 | body: None, |
860 | 938 | }, |
861 | | ) |
862 | 938 | } |
863 | | |
864 | 4.37k | fn mk_closure_ens_call( |
865 | 4.37k | state: &mut State, |
866 | 4.37k | span: &Span, |
867 | 4.37k | params: &VarBinders<Typ>, |
868 | 4.37k | fn_val: &Expr, |
869 | 4.37k | arg_tuple: &Expr, |
870 | 4.37k | ret_arg: &Expr, |
871 | 4.37k | builtin_spec_fun: BuiltinSpecFun, |
872 | 4.37k | ) -> Expr { |
873 | 4.37k | let bool_typ = Arc::new(TypX::Bool); |
874 | 4.37k | SpannedTyped::new( |
875 | 4.37k | span, |
876 | 4.37k | &bool_typ, |
877 | 4.37k | ExprX::Call { |
878 | 4.37k | target: CallTarget::BuiltinSpecFun( |
879 | 4.37k | builtin_spec_fun, |
880 | 4.37k | closure_trait_call_typ_args(state, fn_val, params), |
881 | 4.37k | Arc::new(vec![]), |
882 | 4.37k | ), |
883 | 4.37k | args: Arc::new(vec![fn_val.clone(), arg_tuple.clone(), ret_arg.clone()]), |
884 | 4.37k | post_args: None, |
885 | 4.37k | body: None, |
886 | 4.37k | }, |
887 | | ) |
888 | 4.37k | } |
889 | | |
890 | 5.31k | fn exec_closure_spec_param( |
891 | 5.31k | state: &mut State, |
892 | 5.31k | span: &Span, |
893 | 5.31k | params: &VarBinders<Typ>, |
894 | 5.31k | ) -> (VarIdent, Expr) { |
895 | 5.41k | let param_typs: Vec<Typ> = params.iter().map(|p| p.a.clone()).collect(); |
896 | 5.31k | let tuple_path = state.tuple_type_name(params.len()); |
897 | 5.31k | let tuple_typ = Arc::new(TypX::Datatype(tuple_path, Arc::new(param_typs), Arc::new(vec![]))); |
898 | 5.31k | let tuple_ident = crate::def::closure_param_var(); |
899 | 5.31k | let tuple_var = SpannedTyped::new(span, &tuple_typ, ExprX::Var(tuple_ident.clone())); |
900 | 5.31k | (tuple_ident, tuple_var) |
901 | 5.31k | } |
902 | | |
903 | 938 | fn exec_closure_spec_requires( |
904 | 938 | state: &mut State, |
905 | 938 | span: &Span, |
906 | 938 | closure_var: &Expr, |
907 | 938 | params: &VarBinders<Typ>, |
908 | 938 | requires: &Exprs, |
909 | 938 | ) -> Result<Expr, VirErr> { |
910 | | // For requires: |
911 | | |
912 | | // If the closure has `|a0, a1, a2| requires f(a0, a1, a2)` |
913 | | // then we emit a spec of the form |
914 | | // |
915 | | // forall x :: f(x.0, x.1, x.2) ==> closure.requires(x) |
916 | | // |
917 | | // with `closure.requires(x)` as the trigger |
918 | | |
919 | | // (Since the user doesn't have the option to specify a trigger here, |
920 | | // we need to use the most general one, and that means we need to |
921 | | // quantify over a tuple.) |
922 | | |
923 | 938 | let (tuple_ident, tuple_var) = exec_closure_spec_param(state, span, params); |
924 | 938 | let tuple_typ = tuple_var.typ.clone(); |
925 | | |
926 | 938 | let reqs = conjoin(span, requires); |
927 | | |
928 | | // Supply 'let' statements of the form 'let a0 = x.0; let a1 = x.1; ...' etc. |
929 | | |
930 | 938 | let mut decls: Vec<Stmt> = Vec::new(); |
931 | 1.03k | for (i, p) in params.iter().enumerate() { |
932 | 1.03k | let typ = &p.a; |
933 | 1.03k | let pattern = PatternX::simple_var(p.name.clone(), span, typ); |
934 | 1.03k | let tuple_field = tuple_get_field_expr(state, span, typ, &tuple_var, params.len(), i); |
935 | 1.03k | let decl = StmtX::Decl { |
936 | 1.03k | pattern, |
937 | 1.03k | mode: None, |
938 | 1.03k | init: Some(PlaceX::spec_temporary(tuple_field)), |
939 | 1.03k | els: None, |
940 | 1.03k | assert_irrefutable: false, |
941 | 1.03k | }; |
942 | 1.03k | decls.push(Spanned::new(span.clone(), decl)); |
943 | 1.03k | } |
944 | | |
945 | 938 | let reqs_body = |
946 | 938 | SpannedTyped::new(&reqs.span, &reqs.typ, ExprX::Block(Arc::new(decls), Some(reqs.clone()))); |
947 | | |
948 | 938 | let closure_req_call = |
949 | 938 | wrap_in_trigger(&mk_closure_req_call(state, span, params, closure_var, &tuple_var)); |
950 | | |
951 | 938 | let bool_typ = Arc::new(TypX::Bool); |
952 | 938 | let req_quant_body = mk_implies(span, &reqs_body, &closure_req_call); |
953 | | |
954 | 938 | let forall = Quant { quant: air::ast::Quant::Forall }; |
955 | 938 | let binders = Arc::new(vec![Arc::new(VarBinderX { name: tuple_ident, a: tuple_typ })]); |
956 | 938 | let req_forall = |
957 | 938 | SpannedTyped::new(span, &bool_typ, ExprX::Quant(forall, binders, req_quant_body)); |
958 | | |
959 | 938 | Ok(req_forall) |
960 | 938 | } |
961 | | |
962 | 4.36k | fn exec_closure_spec_ensures( |
963 | 4.36k | state: &mut State, |
964 | 4.36k | span: &Span, |
965 | 4.36k | closure_var: &Expr, |
966 | 4.36k | params: &VarBinders<Typ>, |
967 | 4.36k | ret: &VarBinder<Typ>, |
968 | 4.36k | ensures: &Vec<Expr>, |
969 | 4.36k | default_ens: bool, |
970 | 4.36k | ) -> Result<Expr, VirErr> { |
971 | | // For ensures: |
972 | | |
973 | | // If the closure has `|a0, a1, a2| ensures |b| f(a0, a1, a2, b)` |
974 | | // then we emit a spec of the form |
975 | | // |
976 | | // forall x, y :: closure.ensures(x, y) ==> f(x.0, x.1, x.2, y) |
977 | | // |
978 | | // with `closure.ensures(x)` as the trigger |
979 | | |
980 | 4.36k | let (tuple_ident, tuple_var) = exec_closure_spec_param(state, span, params); |
981 | 4.36k | let tuple_typ = tuple_var.typ.clone(); |
982 | | |
983 | 4.36k | let ret_ident = ret.clone(); |
984 | 4.36k | let ret_var = SpannedTyped::new(span, &ret.a, ExprX::Var(ret_ident.name.clone())); |
985 | | |
986 | 4.36k | let enss = conjoin(span, ensures); |
987 | | |
988 | | // Supply 'let' statements of the form 'let a0 = x.0; let a1 = x.1; ...' etc. |
989 | | |
990 | 4.36k | let mut decls: Vec<Stmt> = Vec::new(); |
991 | 4.36k | for (i, p) in params.iter().enumerate() { |
992 | 4.36k | let typ = &p.a; |
993 | 4.36k | let pattern = PatternX::simple_var(p.name.clone(), span, typ); |
994 | 4.36k | let tuple_field = tuple_get_field_expr(state, span, typ, &tuple_var, params.len(), i); |
995 | 4.36k | let decl = StmtX::Decl { |
996 | 4.36k | pattern, |
997 | 4.36k | mode: None, |
998 | 4.36k | init: Some(PlaceX::spec_temporary(tuple_field)), |
999 | 4.36k | els: None, |
1000 | 4.36k | assert_irrefutable: false, |
1001 | 4.36k | }; |
1002 | 4.36k | decls.push(Spanned::new(span.clone(), decl)); |
1003 | 4.36k | } |
1004 | | |
1005 | 4.36k | let enss_body = |
1006 | 4.36k | SpannedTyped::new(&enss.span, &enss.typ, ExprX::Block(Arc::new(decls), Some(enss.clone()))); |
1007 | | |
1008 | 4.36k | let closure_ens_call = wrap_in_trigger(&mk_closure_ens_call( |
1009 | 4.36k | state, |
1010 | 4.36k | span, |
1011 | 4.36k | params, |
1012 | 4.36k | closure_var, |
1013 | 4.36k | &tuple_var, |
1014 | 4.36k | &ret_var, |
1015 | 4.36k | if default_ens { BuiltinSpecFun::DefaultEns } else { BuiltinSpecFun::ClosureEns }, |
1016 | | )); |
1017 | | |
1018 | 4.36k | let bool_typ = Arc::new(TypX::Bool); |
1019 | 4.36k | let ens_quant_body = mk_implies(span, &closure_ens_call, &enss_body); |
1020 | | |
1021 | 4.36k | let forall = Quant { quant: air::ast::Quant::Forall }; |
1022 | 4.36k | let binders = |
1023 | 4.36k | Arc::new(vec![Arc::new(VarBinderX { name: tuple_ident, a: tuple_typ }), ret.clone()]); |
1024 | 4.36k | let ens_forall = |
1025 | 4.36k | SpannedTyped::new(span, &bool_typ, ExprX::Quant(forall, binders, ens_quant_body)); |
1026 | | |
1027 | 4.36k | Ok(ens_forall) |
1028 | 4.36k | } |
1029 | | |
1030 | 307 | fn exec_closure_spec( |
1031 | 307 | state: &mut State, |
1032 | 307 | span: &Span, |
1033 | 307 | closure_var: &Expr, |
1034 | 307 | params: &VarBinders<Typ>, |
1035 | 307 | ret: &VarBinder<Typ>, |
1036 | 307 | requires: &Exprs, |
1037 | 307 | ensures: &Exprs, |
1038 | 307 | ) -> Result<Expr, VirErr> { |
1039 | 307 | let req_forall = exec_closure_spec_requires(state, span, closure_var, params, requires)?; |
1040 | | |
1041 | 307 | if ensures.len() > 0 { |
1042 | 137 | let ens_forall = |
1043 | 137 | exec_closure_spec_ensures(state, span, closure_var, params, ret, ensures, false)?; |
1044 | 137 | Ok(conjoin(span, &vec![req_forall, ens_forall])) |
1045 | | } else { |
1046 | 170 | Ok(req_forall) |
1047 | | } |
1048 | 307 | } |
1049 | | |
1050 | 735k | pub(crate) fn need_fndef_axiom(fndef_typs: &HashSet<Fun>, f: &Function) -> bool { |
1051 | 735k | if fndef_typs.contains(&f.x.name) { |
1052 | 3.99k | return true; |
1053 | 731k | } |
1054 | 731k | match &f.x.kind { |
1055 | 263k | FunctionKind::TraitMethodImpl { method, .. } => fndef_typs.contains(method), |
1056 | 468k | _ => false, |
1057 | | } |
1058 | 735k | } |
1059 | | |
1060 | 9.34k | fn add_fndef_axioms_to_function( |
1061 | 9.34k | _ctx: &GlobalCtx, |
1062 | 9.34k | state: &mut State, |
1063 | 9.34k | function: &Function, |
1064 | 9.34k | fn_once_trait_in_scope: bool, |
1065 | 9.34k | ) -> Result<(Function, Vec<TraitImpl>, Option<AssocTypeImpl>), VirErr> { |
1066 | 9.34k | state.reset_for_function(); |
1067 | | |
1068 | 9.34k | let params: Vec<_> = function |
1069 | 9.34k | .x |
1070 | 9.34k | .params |
1071 | 9.34k | .iter() |
1072 | 10.9k | .filter(|p| !is_dummy_param_name(&p.x.name)) |
1073 | 10.9k | .map(|p| Arc::new(VarBinderX { name: p.x.name.clone(), a: p.x.typ.clone() })) |
1074 | 9.34k | .collect(); |
1075 | 9.34k | let params = Arc::new(params); |
1076 | | |
1077 | 9.34k | let (fun, typ_args, is_trait_method_impl, inherit) = match &function.x.kind { |
1078 | 8.71k | FunctionKind::TraitMethodImpl { method, trait_typ_args, inherit_body_from, .. } => { |
1079 | 8.71k | (method, trait_typ_args.clone(), true, inherit_body_from.is_some()) |
1080 | | } |
1081 | | _ => { |
1082 | 631 | let typ_args: Vec<_> = function |
1083 | 631 | .x |
1084 | 631 | .typ_params |
1085 | 631 | .iter() |
1086 | 932 | .map(|tp| Arc::new(TypX::TypParam(tp.clone()))) |
1087 | 631 | .collect(); |
1088 | 631 | let typ_args = Arc::new(typ_args); |
1089 | 631 | (&function.x.name, typ_args, false, false) |
1090 | | } |
1091 | | }; |
1092 | | |
1093 | 9.34k | let fndef_singleton = SpannedTyped::new( |
1094 | 9.34k | &function.span, |
1095 | 9.34k | &Arc::new(TypX::FnDef(fun.clone(), typ_args.clone(), None)), |
1096 | 9.34k | ExprX::ExecFnByName(fun.clone()), |
1097 | | ); |
1098 | | |
1099 | | // Emit `FnDef : {Fn, FnMut, FnOnce}<Args>` and `<FnDef as FnOnce<Args>>::Output = Ret`. |
1100 | | // |
1101 | | // We emit a TraitImpl for each of the three Fn-family traits (not just Fn), because |
1102 | | // code that mentions only one through an associated-type projection (e.g. `Map::Item = F::Output`) |
1103 | | // never creates a Fn term for the Fn-related axioms to trigger on. |
1104 | 9.34k | let (trait_impls_out, assoc_type_impl) = if fn_once_trait_in_scope { |
1105 | 9.28k | let self_typ = Arc::new(TypX::FnDef(fun.clone(), typ_args.clone(), None)); |
1106 | 10.8k | let arg_typs: Vec<Typ> = params.iter().map(|p| p.a.clone()).collect(); |
1107 | 9.28k | let tuple_dt = state.tuple_type_name(arg_typs.len()); |
1108 | 9.28k | let args_tuple_typ = |
1109 | 9.28k | Arc::new(TypX::Datatype(tuple_dt, Arc::new(arg_typs), Arc::new(vec![]))); |
1110 | 9.28k | let trait_typ_args = Arc::new(vec![self_typ, args_tuple_typ]); |
1111 | | |
1112 | 9.28k | let mut trait_impls_out: Vec<TraitImpl> = Vec::new(); |
1113 | 27.8k | for kind in [ClosureKind::Fn, ClosureKind::FnMut, ClosureKind::FnOnce] { |
1114 | 27.8k | let trait_implx = crate::ast::TraitImplX { |
1115 | 27.8k | impl_path: impl_fndef_path(&function.x.name, kind), |
1116 | 27.8k | typ_params: function.x.typ_params.clone(), |
1117 | 27.8k | typ_bounds: function.x.typ_bounds.clone(), |
1118 | 27.8k | trait_path: kind.trait_path(), |
1119 | 27.8k | trait_typ_args: trait_typ_args.clone(), |
1120 | 27.8k | trait_typ_arg_impls: Spanned::new(function.span.clone(), Arc::new(vec![])), |
1121 | 27.8k | owning_module: None, |
1122 | 27.8k | auto_imported: true, |
1123 | 27.8k | external_trait_blanket: false, |
1124 | 27.8k | }; |
1125 | 27.8k | trait_impls_out.push(Spanned::new(function.span.clone(), trait_implx)); |
1126 | 27.8k | } |
1127 | | |
1128 | 9.28k | let assoc_typ_implx = crate::ast::AssocTypeImplX { |
1129 | 9.28k | name: Arc::new("Output".to_string()), |
1130 | 9.28k | impl_path: impl_fndef_path(&function.x.name, ClosureKind::FnOnce), |
1131 | 9.28k | typ_params: function.x.typ_params.clone(), |
1132 | 9.28k | typ_bounds: function.x.typ_bounds.clone(), |
1133 | 9.28k | trait_path: ClosureKind::FnOnce.trait_path(), |
1134 | 9.28k | trait_typ_args, |
1135 | 9.28k | typ: function.x.ret.x.typ.clone(), |
1136 | 9.28k | impl_paths: Arc::new(vec![]), |
1137 | 9.28k | }; |
1138 | | |
1139 | 9.28k | (trait_impls_out, Some(Spanned::new(function.span.clone(), assoc_typ_implx))) |
1140 | | } else { |
1141 | 61 | (Vec::new(), None) |
1142 | | }; |
1143 | | |
1144 | 9.34k | let mut fndef_axioms = vec![]; |
1145 | | |
1146 | | // Don't need to repeat the 'requires' for a trait impl fn because requires can't change |
1147 | 9.34k | if !is_trait_method_impl { |
1148 | 631 | let req_forall = exec_closure_spec_requires( |
1149 | 631 | state, |
1150 | 631 | &function.span, |
1151 | 631 | &fndef_singleton, |
1152 | 631 | ¶ms, |
1153 | 631 | &function.x.require, |
1154 | 0 | )?; |
1155 | 631 | fndef_axioms.push(req_forall); |
1156 | 8.71k | } |
1157 | | |
1158 | 9.34k | let ret = Arc::new(VarBinderX { |
1159 | 9.34k | name: function.x.ret.x.name.clone(), |
1160 | 9.34k | a: function.x.ret.x.typ.clone(), |
1161 | 9.34k | }); |
1162 | 9.34k | let (mut closure_enss, default_enss) = function.x.ensure.clone(); |
1163 | 9.34k | if inherit { |
1164 | 10 | assert!(closure_enss.len() + default_enss.len() == 0); |
1165 | 10 | let (_, tuple_var) = exec_closure_spec_param(state, &function.span, ¶ms); |
1166 | 10 | let ret_var = SpannedTyped::new(&function.span, &ret.a, ExprX::Var(ret.name.clone())); |
1167 | 10 | let default_expr = mk_closure_ens_call( |
1168 | 10 | state, |
1169 | 10 | &function.span, |
1170 | 10 | ¶ms, |
1171 | 10 | &fndef_singleton, |
1172 | 10 | &tuple_var, |
1173 | 10 | &ret_var, |
1174 | 10 | BuiltinSpecFun::DefaultEns, |
1175 | | ); |
1176 | 10 | closure_enss = Arc::new(vec![default_expr]); |
1177 | 9.33k | } |
1178 | 18.6k | for (default_ens, enss) in [(false, closure_enss), (true, default_enss)] { |
1179 | 18.6k | if enss.len() > 0 { |
1180 | 4.23k | let ens_forall = exec_closure_spec_ensures( |
1181 | 4.23k | state, |
1182 | 4.23k | &function.span, |
1183 | 4.23k | &fndef_singleton, |
1184 | 4.23k | ¶ms, |
1185 | 4.23k | &ret, |
1186 | 4.23k | &*enss, |
1187 | 4.23k | default_ens, |
1188 | 0 | )?; |
1189 | 4.23k | fndef_axioms.push(ens_forall); |
1190 | 14.4k | } |
1191 | | } |
1192 | | |
1193 | 9.34k | let mut functionx = function.x.clone(); |
1194 | 9.34k | assert!(functionx.fndef_axioms.is_none()); |
1195 | 9.34k | functionx.fndef_axioms = Some(Arc::new(fndef_axioms)); |
1196 | 9.34k | Ok((Spanned::new(function.span.clone(), functionx), trait_impls_out, assoc_type_impl)) |
1197 | 9.34k | } |
1198 | | |
1199 | 252k | fn simplify_function( |
1200 | 252k | ctx: &GlobalCtx, |
1201 | 252k | state: &mut State, |
1202 | 252k | function: &Function, |
1203 | 252k | ) -> Result<Function, VirErr> { |
1204 | 252k | state.reset_for_function(); |
1205 | 252k | let mut functionx = function.x.clone(); |
1206 | | |
1207 | 252k | if let Some(r) = functionx.returns.clone() { |
1208 | 5.93k | functionx.returns = None; |
1209 | | |
1210 | 5.93k | if functionx.ens_has_return { |
1211 | 5.93k | let var = SpannedTyped::new( |
1212 | 5.93k | &r.span, |
1213 | 5.93k | &functionx.ret.x.typ, |
1214 | 5.93k | ExprX::Var(functionx.ret.x.name.clone()), |
1215 | 5.93k | ); |
1216 | 5.93k | let eq = mk_eq(&r.span, &var, &r); |
1217 | 5.93k | Arc::make_mut(&mut functionx.ensure.0).push(eq); |
1218 | 5.93k | } else { |
1219 | 0 | // For a unit return type, any returns clause is tautological so we |
1220 | 0 | // can just skip appending to the postconditions. |
1221 | 0 | } |
1222 | 246k | } |
1223 | | |
1224 | 252k | let local = LocalCtxt { |
1225 | 252k | span: function.span.clone(), |
1226 | 252k | typ_params: (*functionx.typ_params).clone(), |
1227 | 252k | fun: Some(functionx.name.clone()), |
1228 | 252k | }; |
1229 | | |
1230 | 252k | let is_trait_impl = matches!(functionx.kind, FunctionKind::TraitMethodImpl { .. }); |
1231 | | |
1232 | | // If possible, rename parameters to drop the rustc id |
1233 | 252k | let mut param_ids: HashSet<Ident> = HashSet::new(); |
1234 | 252k | let mut rename_ok = true; |
1235 | 393k | for p in functionx.params.iter() { |
1236 | 393k | let x = &p.x.name.0; |
1237 | 393k | if param_ids.contains(x) { |
1238 | 0 | rename_ok = false; |
1239 | 393k | } |
1240 | 393k | param_ids.insert(x.clone()); |
1241 | | } |
1242 | 252k | let mut param_names: Vec<VarIdent> = Vec::new(); |
1243 | 393k | for param in functionx.params.iter() { |
1244 | 393k | let prev = param.x.name.clone(); |
1245 | 393k | let name = if rename_ok { |
1246 | 393k | let name = VarIdent(prev.0.clone(), crate::ast::VarIdentDisambiguate::VirParam); |
1247 | 393k | state.rename_vars.insert(prev, name.clone()).map(|_| panic!("rename params")); |
1248 | 393k | name |
1249 | | } else { |
1250 | 0 | prev |
1251 | | }; |
1252 | 393k | param_names.push(name); |
1253 | | } |
1254 | 252k | let ret_name = if rename_ok && !param_ids.contains(&functionx.ret.x.name.0) { |
1255 | 252k | let prev = functionx.ret.x.name.clone(); |
1256 | 252k | let name = VarIdent(prev.0.clone(), crate::ast::VarIdentDisambiguate::VirParam); |
1257 | 252k | state.rename_vars.insert(prev, name.clone()).map(|_| panic!("rename ret")); |
1258 | 252k | name |
1259 | | } else { |
1260 | 0 | functionx.ret.x.name.clone() |
1261 | | }; |
1262 | | |
1263 | 645k | for (a, b) in state.rename_vars.iter() { |
1264 | 645k | state.rename_vars_reverse.insert(b.clone(), a.clone()); |
1265 | 645k | } |
1266 | | |
1267 | | // To simplify the AIR/SMT encoding, add a dummy argument to any function with 0 arguments |
1268 | 252k | if functionx.typ_params.len() == 0 |
1269 | 119k | && functionx.params.len() == 0 |
1270 | 20.3k | && !matches!(functionx.item_kind, ItemKind::Const) |
1271 | 20.2k | && !matches!(functionx.item_kind, ItemKind::Static) |
1272 | 20.2k | && !functionx.attrs.broadcast_forall |
1273 | 17.1k | && !is_trait_impl |
1274 | 4.33k | { |
1275 | 4.33k | let paramx = crate::ast::ParamX { |
1276 | 4.33k | name: dummy_param_name(), |
1277 | 4.33k | typ: Arc::new(TypX::Int(IntRange::Int)), |
1278 | 4.33k | mode: Mode::Spec, |
1279 | 4.33k | user_mut: false, |
1280 | 4.33k | unwrapped_info: None, |
1281 | 4.33k | }; |
1282 | 4.33k | param_names.push(paramx.name.clone()); |
1283 | 4.33k | let param = Spanned::new(function.span.clone(), paramx); |
1284 | 4.33k | functionx.params = Arc::new(vec![param]); |
1285 | 248k | } |
1286 | | |
1287 | 252k | let function = Spanned::new(function.span.clone(), functionx); |
1288 | 252k | let mut map: VisitorScopeMap = ScopeMap::new(); |
1289 | 252k | let function = crate::ast_visitor::map_function_visitor_env( |
1290 | 252k | &function, |
1291 | 252k | &mut map, |
1292 | 252k | state, |
1293 | 4.25M | &|state, map, expr| simplify_one_expr(ctx, state, map, expr), |
1294 | 300k | &|state, _, stmt| simplify_one_stmt(ctx, state, stmt), |
1295 | 14.2M | &|state, typ| simplify_one_typ(&local, state, typ), |
1296 | 1.84M | &|state, map, place| simplify_one_place(ctx, state, map, place), |
1297 | 5 | )?; |
1298 | 252k | let mut functionx = function.x.clone(); |
1299 | 252k | assert!(functionx.params.len() == param_names.len()); |
1300 | 252k | functionx.params = Arc::new( |
1301 | 252k | functionx |
1302 | 252k | .params |
1303 | 252k | .iter() |
1304 | 252k | .zip(param_names.iter()) |
1305 | 397k | .map(|(p, x)| p.new_x(crate::ast::ParamX { name: x.clone(), ..p.x.clone() })) |
1306 | 252k | .collect(), |
1307 | | ); |
1308 | 252k | functionx.ret = |
1309 | 252k | functionx.ret.new_x(crate::ast::ParamX { name: ret_name, ..functionx.ret.x.clone() }); |
1310 | | |
1311 | 252k | Ok(Spanned::new(function.span.clone(), functionx)) |
1312 | 252k | } |
1313 | | |
1314 | 14.1k | fn simplify_datatype(state: &mut State, datatype: &Datatype) -> Result<Datatype, VirErr> { |
1315 | 14.1k | let mut local = LocalCtxt { span: datatype.span.clone(), typ_params: Vec::new(), fun: None }; |
1316 | 14.1k | for (x, _strict_pos) in datatype.x.typ_params.iter() { |
1317 | 13.6k | local.typ_params.push(x.clone()); |
1318 | 13.6k | } |
1319 | 49.8k | crate::ast_visitor::map_datatype_visitor_env(datatype, state, &|state, typ| { |
1320 | 49.8k | simplify_one_typ(&local, state, typ) |
1321 | 49.8k | }) |
1322 | 14.1k | } |
1323 | | |
1324 | 175k | fn simplify_trait_impl(state: &mut State, imp: &TraitImpl) -> Result<TraitImpl, VirErr> { |
1325 | 175k | let mut local = LocalCtxt { span: imp.span.clone(), typ_params: Vec::new(), fun: None }; |
1326 | 175k | for x in imp.x.typ_params.iter() { |
1327 | 119k | local.typ_params.push(x.clone()); |
1328 | 119k | } |
1329 | 761k | crate::ast_visitor::map_trait_impl_visitor_env(imp, state, &|state, typ| { |
1330 | 761k | simplify_one_typ(&local, state, typ) |
1331 | 761k | }) |
1332 | 175k | } |
1333 | | |
1334 | 69.9k | fn simplify_assoc_type_impl( |
1335 | 69.9k | state: &mut State, |
1336 | 69.9k | assoc: &AssocTypeImpl, |
1337 | 69.9k | ) -> Result<AssocTypeImpl, VirErr> { |
1338 | 69.9k | let mut local = LocalCtxt { span: assoc.span.clone(), typ_params: Vec::new(), fun: None }; |
1339 | 69.9k | for x in assoc.x.typ_params.iter() { |
1340 | 25.4k | local.typ_params.push(x.clone()); |
1341 | 25.4k | } |
1342 | 355k | crate::ast_visitor::map_assoc_type_impl_visitor_env(assoc, state, &|state, typ| { |
1343 | 355k | simplify_one_typ(&local, state, typ) |
1344 | 355k | }) |
1345 | 69.9k | } |
1346 | | |
1347 | | /* |
1348 | | fn mk_fun_decl( |
1349 | | span: &Span, |
1350 | | path: &Path, |
1351 | | typ_params: &Idents, |
1352 | | params: &Params, |
1353 | | ret: &Param, |
1354 | | ) -> Function { |
1355 | | let mut attrs: crate::ast::FunctionAttrsX = Default::default(); |
1356 | | attrs.no_auto_trigger = true; |
1357 | | Spanned::new( |
1358 | | span.clone(), |
1359 | | FunctionX { |
1360 | | name: Arc::new(FunX { path: path.clone() }), |
1361 | | visibility: Visibility { owning_module: None, restricted_to: None }, |
1362 | | mode: Mode::Spec, |
1363 | | fuel: 0, |
1364 | | typ_params: typ_params.clone(), |
1365 | | typ_bounds: Arc::new(vec![]), |
1366 | | params: params.clone(), |
1367 | | ret: ret.clone(), |
1368 | | require: Arc::new(vec![]), |
1369 | | ensure: Arc::new(vec![]), |
1370 | | decrease: None, |
1371 | | is_const: false, |
1372 | | is_abstract: false, |
1373 | | attrs: Arc::new(attrs), |
1374 | | body: None, |
1375 | | }, |
1376 | | ) |
1377 | | } |
1378 | | */ |
1379 | | |
1380 | 13.0k | fn add_tuple_auto_impl( |
1381 | 13.0k | ctx: &mut GlobalCtx, |
1382 | 13.0k | traits: &Vec<crate::ast::Trait>, |
1383 | 13.0k | trait_impls: &mut Vec<TraitImpl>, |
1384 | 13.0k | arity: usize, |
1385 | 13.0k | needs_bounds: bool, |
1386 | 13.0k | trait_path: Path, |
1387 | 13.0k | ) { |
1388 | | use crate::ast::{GenericBound, GenericBoundX}; |
1389 | | use crate::def::impl_tuple; |
1390 | | |
1391 | 139k | if !traits.iter().any(|t| &t.x.name == &trait_path) { |
1392 | 9.02k | return; |
1393 | 3.98k | } |
1394 | | // Rust doesn't seem to give us the TraitImpl for tuples for the traits |
1395 | | // Tuple, Clone, Copy, Send, Sync, Unpin, so we make it ourselves: |
1396 | 8.46k | let typ_params: Vec<Ident> = (0..arity).map(|i| prefix_tuple_param(i)).collect(); |
1397 | 3.98k | let typ_args: Vec<Typ> = |
1398 | 8.46k | typ_params.iter().map(|x| Arc::new(TypX::TypParam(x.clone()))).collect(); |
1399 | 3.98k | let mut bounds: Vec<GenericBound> = Vec::new(); |
1400 | 3.98k | if needs_bounds { |
1401 | 5.27k | for i in 0..arity { |
1402 | 5.27k | let id = crate::ast::TraitId::Path(trait_path.clone()); |
1403 | 5.27k | let typs = Arc::new(vec![typ_args[i].clone()]); |
1404 | 5.27k | bounds.push(Arc::new(GenericBoundX::Trait(id, typs))); |
1405 | 5.27k | } |
1406 | 2.25k | } |
1407 | 3.98k | let self_ty = Arc::new(TypX::Datatype(Dt::Tuple(arity), Arc::new(typ_args), Arc::new(vec![]))); |
1408 | 3.98k | let impl_path = Arc::new(crate::ast::PathX { |
1409 | 3.98k | krate: CrateId::Internal, |
1410 | 3.98k | segments: Arc::new(vec![impl_tuple(trait_path.segments.last().unwrap(), arity)]), |
1411 | 3.98k | }); |
1412 | 3.98k | let trait_implx = crate::ast::TraitImplX { |
1413 | 3.98k | impl_path, |
1414 | 3.98k | typ_params: Arc::new(typ_params), |
1415 | 3.98k | typ_bounds: Arc::new(bounds), |
1416 | 3.98k | trait_path, |
1417 | 3.98k | trait_typ_args: Arc::new(vec![self_ty]), |
1418 | 3.98k | trait_typ_arg_impls: Spanned::new(ctx.no_span.clone(), Arc::new(vec![])), |
1419 | 3.98k | owning_module: None, |
1420 | 3.98k | auto_imported: true, |
1421 | 3.98k | external_trait_blanket: false, |
1422 | 3.98k | }; |
1423 | 3.98k | trait_impls.push(Spanned::new(ctx.no_span.clone(), trait_implx)); |
1424 | 13.0k | } |
1425 | | |
1426 | 3.16k | pub fn simplify_krate(ctx: &mut GlobalCtx, krate: &Krate) -> Result<Krate, VirErr> { |
1427 | | let KrateX { |
1428 | 3.16k | functions, |
1429 | 3.16k | reveal_groups, |
1430 | 3.16k | datatypes, |
1431 | 3.16k | opaque_types, |
1432 | 3.16k | traits, |
1433 | 3.16k | trait_impls, |
1434 | 3.16k | assoc_type_impls, |
1435 | 3.16k | modules: module_ids, |
1436 | 3.16k | external_fns, |
1437 | 3.16k | external_types, |
1438 | 3.16k | path_as_rust_names, |
1439 | 3.16k | arch, |
1440 | 3.16k | } = &**krate; |
1441 | 3.16k | let mut state = State::new(); |
1442 | | |
1443 | | // Always add this because unit values might be added later, after ast_simplify. |
1444 | 3.16k | state.tuple_type_name(0); |
1445 | | |
1446 | 14.1k | let mut datatypes = vec_map_result(&datatypes, |d| simplify_datatype(&mut state, d))?; |
1447 | 3.16k | ctx.datatypes = Arc::new( |
1448 | 3.16k | datatypes |
1449 | 3.16k | .iter() |
1450 | 14.1k | .map(|d| (d.x.name.expect_path(), (d.x.typ_params.clone(), d.x.variants.clone()))) |
1451 | 3.16k | .collect(), |
1452 | | ); |
1453 | 252k | let functions = vec_map_result(functions, |f| simplify_function(ctx, &mut state, f))?; |
1454 | 175k | let mut trait_impls = vec_map_result(&trait_impls, |t| simplify_trait_impl(&mut state, t))?; |
1455 | 3.15k | let mut assoc_type_impls = |
1456 | 69.9k | vec_map_result(&assoc_type_impls, |a| simplify_assoc_type_impl(&mut state, a))?; |
1457 | | |
1458 | 3.15k | let fn_once_trait_path = ClosureKind::FnOnce.trait_path(); |
1459 | 8.78k | let fn_once_trait_in_scope = traits.iter().any(|t| t.x.name == fn_once_trait_path); |
1460 | | |
1461 | 3.15k | let mut new_functions: Vec<Function> = Vec::with_capacity(functions.len()); |
1462 | 252k | for f in functions.iter() { |
1463 | 252k | if need_fndef_axiom(&state.fndef_typs, f) { |
1464 | 9.34k | let (f2, tis, ai) = |
1465 | 9.34k | add_fndef_axioms_to_function(ctx, &mut state, f, fn_once_trait_in_scope)?; |
1466 | 9.34k | trait_impls.extend(tis); |
1467 | 9.34k | if let Some(ai) = ai { |
1468 | 9.28k | assoc_type_impls.push(ai); |
1469 | 9.28k | } |
1470 | 9.34k | new_functions.push(f2); |
1471 | 243k | } else { |
1472 | 243k | new_functions.push(f.clone()); |
1473 | 243k | } |
1474 | | } |
1475 | 3.15k | let functions = new_functions; |
1476 | | |
1477 | | // Add a generic datatype to represent each tuple arity |
1478 | | // Iterate in sorted order to get consistent output |
1479 | 3.15k | let mut tuples: Vec<usize> = state.tuple_typs.into_iter().collect(); |
1480 | 3.15k | tuples.sort(); |
1481 | 4.33k | for arity in tuples { |
1482 | 4.33k | let visibility = Visibility { restricted_to: None }; |
1483 | 4.33k | let transparency = DatatypeTransparency::WhenVisible(visibility.clone()); |
1484 | 4.33k | let acc = crate::ast::AcceptRecursiveType::RejectInGround; |
1485 | 4.33k | let typ_params = Arc::new((0..arity).map(|i| (prefix_tuple_param(i), acc)).collect()); |
1486 | 4.33k | let mut fields: Vec<Field> = Vec::new(); |
1487 | 4.33k | for i in 0..arity { |
1488 | 3.73k | let typ = Arc::new(TypX::TypParam(prefix_tuple_param(i))); |
1489 | 3.73k | let vis = Visibility { restricted_to: None }; |
1490 | 3.73k | // Note: the mode is irrelevant at this stage, so we arbitrarily use Mode::Exec |
1491 | 3.73k | fields.push(ident_binder(&positional_field_ident(i), &(typ, Mode::Exec, vis))); |
1492 | 3.73k | } |
1493 | 4.33k | let variant = Variant { |
1494 | 4.33k | name: prefix_tuple_variant(arity), |
1495 | 4.33k | fields: Arc::new(fields), |
1496 | 4.33k | ctor_style: CtorPrintStyle::Tuple, |
1497 | 4.33k | }; |
1498 | 4.33k | let variants = Arc::new(vec![variant]); |
1499 | 4.33k | let datatypex = DatatypeX { |
1500 | 4.33k | name: Dt::Tuple(arity), |
1501 | 4.33k | proxy: None, |
1502 | 4.33k | visibility, |
1503 | 4.33k | owning_module: None, |
1504 | 4.33k | transparency, |
1505 | 4.33k | typ_params, |
1506 | 4.33k | typ_bounds: Arc::new(vec![]), |
1507 | 4.33k | variants, |
1508 | 4.33k | mode: Mode::Exec, |
1509 | 4.33k | ext_equal: arity > 0, |
1510 | 4.33k | user_defined_invariant_fn: None, |
1511 | 4.33k | sized_constraint: if arity == 0 { |
1512 | 3.15k | None |
1513 | | } else { |
1514 | 1.17k | Some(Arc::new(TypX::TypParam(prefix_tuple_param(arity - 1)))) |
1515 | | }, |
1516 | | destructor: false, |
1517 | | }; |
1518 | 4.33k | datatypes.push(Spanned::new(ctx.no_span.clone(), datatypex)); |
1519 | | |
1520 | 4.33k | let tuple_path = crate::path![CrateId::Core => "marker", "Tuple"]; |
1521 | 4.33k | let clone_path = crate::path![CrateId::Core => "clone", "Clone"]; |
1522 | 4.33k | let copy_path = crate::path![CrateId::Core => "marker", "Copy"]; |
1523 | | //let send_path = crate::path![CrateId::Core => "marker", "Send"]; |
1524 | | //let sync_path = crate::path![CrateId::Core => "marker", "Sync"]; |
1525 | | //let unpin_path = crate::path![CrateId::Core => "marker", "Unpin"]; |
1526 | 4.33k | add_tuple_auto_impl(ctx, &traits, &mut trait_impls, arity, false, tuple_path); |
1527 | 4.33k | add_tuple_auto_impl(ctx, &traits, &mut trait_impls, arity, true, clone_path); |
1528 | 4.33k | add_tuple_auto_impl(ctx, &traits, &mut trait_impls, arity, true, copy_path); |
1529 | | // TODO when when we have full support for auto traits: |
1530 | | //add_tuple_auto_impl(ctx, &traits, &mut trait_impls, arity, true, send_path); |
1531 | | //add_tuple_auto_impl(ctx, &traits, &mut trait_impls, arity, true, sync_path); |
1532 | | //add_tuple_auto_impl(ctx, &traits, &mut trait_impls, arity, true, unpin_path); |
1533 | | } |
1534 | | |
1535 | 3.15k | let mut closures: Vec<_> = state.closure_typs.into_iter().collect(); |
1536 | 3.15k | closures.sort_by_key(|kv| kv.0); |
1537 | 3.15k | for (id, closure) in closures { |
1538 | | // Right now, we translate the closure type into an a global datatype. |
1539 | | // |
1540 | | // However, I'm pretty sure an anonymous closure can't actually be referenced |
1541 | | // from outside the item that defines it (Rust seems to represent it as an |
1542 | | // "opaque type" if it escapes through an existential type, which Verus currently |
1543 | | // doesn't support anyway.) |
1544 | | // So in principle, we could make the type private to the item and not emit any |
1545 | | // global declarations for it. |
1546 | | |
1547 | | // Also, note that Rust already prohibits a closure type from depending on itself |
1548 | | // (not even via reference types, which would be allowed for other types). |
1549 | | // As such, we don't have to worry about any kind of recursion-checking: |
1550 | | // a closure type cannot possibly be involved in any type cycle. |
1551 | | // (In principle, the closure should depend negatively on its param and return types, |
1552 | | // since they are arguments to the 'requires' and 'ensures' predicates, but thanks |
1553 | | // to Rust's restrictions, we don't have to do any additional checks.) |
1554 | | |
1555 | 227 | let visibility = Visibility { restricted_to: None }; |
1556 | 227 | let transparency = DatatypeTransparency::Never; |
1557 | | |
1558 | 227 | let variants = Arc::new(vec![]); |
1559 | | |
1560 | 3.62k | let function = functions.iter().find(|f| f.x.name == closure.enclosing_fun).unwrap(); |
1561 | | |
1562 | 227 | let typ_params: crate::ast::TypPositives = Arc::new( |
1563 | 227 | function |
1564 | 227 | .x |
1565 | 227 | .typ_params |
1566 | 227 | .iter() |
1567 | 227 | .map(|tb| (tb.clone(), crate::ast::AcceptRecursiveType::Accept)) |
1568 | 227 | .collect(), |
1569 | | ); |
1570 | 227 | let datatypex = DatatypeX { |
1571 | 227 | name: Dt::Path(closure.path.clone()), |
1572 | 227 | proxy: None, |
1573 | 227 | visibility, |
1574 | 227 | owning_module: None, |
1575 | 227 | transparency, |
1576 | 227 | typ_params: typ_params.clone(), |
1577 | 227 | typ_bounds: function.x.typ_bounds.clone(), |
1578 | 227 | variants, |
1579 | 227 | mode: Mode::Exec, |
1580 | 227 | ext_equal: false, |
1581 | 227 | user_defined_invariant_fn: None, |
1582 | 227 | sized_constraint: None, |
1583 | 227 | destructor: false, |
1584 | 227 | }; |
1585 | 227 | datatypes.push(Spanned::new(ctx.no_span.clone(), datatypex)); |
1586 | | |
1587 | | // Add a trait bound, `ClosureType: {Fn, FnMut, FnOnce}`, and the corresponding |
1588 | | // `<ClosureType as FnOnce<Args>>::Output = ReturnType` associated-type impl. |
1589 | | |
1590 | 227 | let typ_args: Typs = Arc::new( |
1591 | 227 | function.x.typ_params.iter().map(|tb| Arc::new(TypX::TypParam(tb.clone()))).collect(), |
1592 | | ); |
1593 | 227 | let self_typ = |
1594 | 227 | Arc::new(TypX::Datatype(Dt::Path(closure.path.clone()), typ_args, Arc::new(vec![]))); |
1595 | 227 | let args_tuple_typ = Arc::new(TypX::Datatype( |
1596 | 227 | Dt::Tuple(closure.args.len()), |
1597 | 227 | closure.args.clone(), |
1598 | 227 | Arc::new(vec![]), |
1599 | 227 | )); |
1600 | 227 | let impl_path = Arc::new(crate::ast::PathX { |
1601 | 227 | krate: CrateId::Internal, |
1602 | 227 | segments: Arc::new(vec![crate::def::impl_closure(closure.kind, id)]), |
1603 | 227 | }); |
1604 | 227 | let trait_typ_args = Arc::new(vec![self_typ.clone(), args_tuple_typ.clone()]); |
1605 | 227 | let trait_implx = crate::ast::TraitImplX { |
1606 | 227 | impl_path: impl_path.clone(), |
1607 | 227 | typ_params: function.x.typ_params.clone(), |
1608 | 227 | typ_bounds: function.x.typ_bounds.clone(), |
1609 | 227 | trait_path: closure.kind.trait_path(), |
1610 | 227 | trait_typ_args: trait_typ_args.clone(), |
1611 | 227 | trait_typ_arg_impls: Spanned::new(ctx.no_span.clone(), Arc::new(vec![])), |
1612 | 227 | owning_module: None, |
1613 | 227 | auto_imported: true, |
1614 | 227 | external_trait_blanket: false, |
1615 | 227 | }; |
1616 | 227 | trait_impls.push(Spanned::new(ctx.no_span.clone(), trait_implx)); |
1617 | | |
1618 | | // The `Output` associated type is defined on the `FnOnce` trait (and |
1619 | | // inherited by `Fn` and `FnMut`), so we always use the `FnOnce` trait |
1620 | | // path for the associated-type impl, regardless of the closure's kind. |
1621 | 227 | let fn_once_trait_path = crate::ast::ClosureKind::FnOnce.trait_path(); |
1622 | 419 | if traits.iter().any(|t| &t.x.name == &fn_once_trait_path) { |
1623 | 136 | let assoc_typ_implx = crate::ast::AssocTypeImplX { |
1624 | 136 | name: Arc::new("Output".to_string()), |
1625 | 136 | impl_path, |
1626 | 136 | typ_params: function.x.typ_params.clone(), |
1627 | 136 | typ_bounds: function.x.typ_bounds.clone(), |
1628 | 136 | trait_path: fn_once_trait_path, |
1629 | 136 | trait_typ_args, |
1630 | 136 | typ: closure.output.clone(), |
1631 | 136 | impl_paths: Arc::new(vec![]), |
1632 | 136 | }; |
1633 | 136 | assoc_type_impls.push(Spanned::new(ctx.no_span.clone(), assoc_typ_implx)); |
1634 | 136 | } |
1635 | | } |
1636 | | |
1637 | 3.15k | let traits = traits.clone(); |
1638 | 3.15k | let module_ids = module_ids.clone(); |
1639 | 3.15k | let external_fns = external_fns.clone(); |
1640 | 3.15k | let external_types = external_types.clone(); |
1641 | 3.15k | let krate = Arc::new(KrateX { |
1642 | 3.15k | functions, |
1643 | 3.15k | reveal_groups: reveal_groups.clone(), |
1644 | 3.15k | datatypes, |
1645 | 3.15k | opaque_types: opaque_types.clone(), |
1646 | 3.15k | traits, |
1647 | 3.15k | trait_impls, |
1648 | 3.15k | assoc_type_impls, |
1649 | 3.15k | modules: module_ids, |
1650 | 3.15k | external_fns, |
1651 | 3.15k | external_types, |
1652 | 3.15k | path_as_rust_names: path_as_rust_names.clone(), |
1653 | 3.15k | arch: arch.clone(), |
1654 | 3.15k | }); |
1655 | 3.15k | *ctx = crate::context::GlobalCtx::new( |
1656 | 3.15k | &krate, |
1657 | 3.15k | ctx.crate_name.clone(), |
1658 | 3.15k | ctx.no_span.clone(), |
1659 | 3.15k | ctx.rlimit, |
1660 | 3.15k | ctx.interpreter_log.clone(), |
1661 | 3.15k | ctx.func_call_graph_log.clone(), |
1662 | 3.15k | ctx.warning_ctx.clone(), |
1663 | 3.15k | ctx.solver.clone(), |
1664 | | true, |
1665 | 3.15k | ctx.check_api_safety, |
1666 | 3.15k | ctx.axiom_usage_info, |
1667 | 3.15k | ctx.no_bv_simplify, |
1668 | 3.15k | ctx.report_long_running, |
1669 | 0 | )?; |
1670 | 3.15k | Ok(krate) |
1671 | 3.16k | } |
1672 | | |
1673 | 4.04k | pub fn merge_krates(krates: Vec<Krate>) -> Result<Krate, VirErr> { |
1674 | 4.04k | let mut krates = krates.into_iter(); |
1675 | 4.04k | let mut kratex: KrateX = (*krates.next().expect("at least one crate")).clone(); |
1676 | 4.04k | for k in krates { |
1677 | | let KrateX { |
1678 | 1.62k | functions, |
1679 | 1.62k | reveal_groups, |
1680 | 1.62k | datatypes, |
1681 | 1.62k | opaque_types, |
1682 | 1.62k | traits, |
1683 | 1.62k | trait_impls, |
1684 | 1.62k | assoc_type_impls, |
1685 | 1.62k | modules, |
1686 | 1.62k | external_fns, |
1687 | 1.62k | external_types, |
1688 | 1.62k | path_as_rust_names, |
1689 | 1.62k | arch, |
1690 | 1.62k | } = &*k; |
1691 | 1.62k | kratex.functions.extend(functions.clone()); |
1692 | 1.62k | kratex.reveal_groups.extend(reveal_groups.clone()); |
1693 | 1.62k | kratex.datatypes.extend(datatypes.clone()); |
1694 | 1.62k | kratex.opaque_types.extend(opaque_types.clone()); |
1695 | 1.62k | kratex.traits.extend(traits.clone()); |
1696 | 1.62k | kratex.trait_impls.extend(trait_impls.clone()); |
1697 | 1.62k | kratex.assoc_type_impls.extend(assoc_type_impls.clone()); |
1698 | 1.62k | kratex.modules.extend(modules.clone()); |
1699 | 1.62k | kratex.external_fns.extend(external_fns.clone()); |
1700 | 1.62k | kratex.external_types.extend(external_types.clone()); |
1701 | 1.62k | kratex.path_as_rust_names.extend(path_as_rust_names.clone()); |
1702 | | kratex.arch.word_bits = { |
1703 | 1.62k | let word_bits = match (arch.word_bits, kratex.arch.word_bits) { |
1704 | 0 | (crate::ast::ArchWordBits::Exactly(l), crate::ast::ArchWordBits::Exactly(r)) => { |
1705 | 0 | if l != r { |
1706 | 0 | return Err(crate::messages::error_bare( |
1707 | 0 | "all crates must have compatible arch_word_bits (set via `global size_of usize`", |
1708 | 0 | )); |
1709 | | } else { |
1710 | 0 | crate::ast::ArchWordBits::Exactly(l) |
1711 | | } |
1712 | | } |
1713 | 1.59k | (crate::ast::ArchWordBits::Either32Or64, other) |
1714 | 1.62k | | (other, crate::ast::ArchWordBits::Either32Or64) => other, |
1715 | | }; |
1716 | 1.62k | if let crate::ast::ArchWordBits::Exactly(e) = &word_bits { |
1717 | 28 | assert!(*e == 32 || *e == 64); |
1718 | 1.59k | } |
1719 | 1.62k | word_bits |
1720 | | }; |
1721 | | } |
1722 | 4.04k | Ok(Arc::new(kratex)) |
1723 | 4.04k | } |