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