1use std::sync::Arc;
2
3use rustc_attr_ir::lang_items::LangItem;
4use rustc_attr_ir::target::Target;
5use thin_vec::thin_vec;
6
7use crate::LoweringContext;
8
9impl<'hir> LoweringContext<'_, 'hir> {
10 pub(super) fn lower_contract(
21 &mut self,
22 body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>,
23 contract: &rustc_ast::FnContract,
24 ) -> rustc_hir::Expr<'hir> {
25 let contract_decls = self.lower_decls(contract);
29
30 match (&contract.requires, &contract.ensures) {
31 (Some(req), Some(ens)) => {
32 let precond = self.lower_precond(req);
56 let postcond_checker = self.lower_postcond_checker(ens);
57
58 let contract_check = self.lower_contract_check_with_postcond(
59 contract_decls,
60 Some(precond),
61 postcond_checker,
62 );
63
64 let wrapped_body =
65 self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
66 self.expr_block(wrapped_body)
67 }
68 (None, Some(ens)) => {
69 let postcond_checker = self.lower_postcond_checker(ens);
91 let contract_check =
92 self.lower_contract_check_with_postcond(contract_decls, None, postcond_checker);
93
94 let wrapped_body =
95 self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
96 self.expr_block(wrapped_body)
97 }
98 (Some(req), None) => {
99 let precond = self.lower_precond(req);
113 let precond_check = self.lower_contract_check_just_precond(contract_decls, precond);
114
115 let body = self.arena.alloc(body(self));
116
117 let wrapped_body = self.block_all(
119 body.span,
120 self.arena.alloc_from_iter([precond_check].into_iter()),
121 Some(body),
122 );
123 self.expr_block(wrapped_body)
124 }
125 (None, None) => body(self),
126 }
127 }
128
129 fn lower_decls(&mut self, contract: &rustc_ast::FnContract) -> &'hir [rustc_hir::Stmt<'hir>] {
130 let (decls, decls_tail) = self.lower_stmts(&contract.declarations);
131
132 if let Some(e) = decls_tail {
133 let tail = self.stmt_expr(e.span, *e);
135 self.arena.alloc_from_iter(decls.into_iter().map(|d| *d).chain([tail].into_iter()))
136 } else {
137 decls
138 }
139 }
140
141 fn lower_precond(&mut self, req: &Box<rustc_ast::Expr>) -> rustc_hir::Stmt<'hir> {
143 let lowered_req = self.lower_expr_mut(&req);
144 let req_span = self.mark_span_with_reason(
145 rustc_span::DesugaringKind::Contract,
146 lowered_req.span,
147 Some(Arc::clone(&crate::ALLOW_CONTRACTS)),
148 );
149 let precond = self.expr_call_lang_item_fn_mut(
150 req_span,
151 LangItem::ContractCheckRequires,
152 &*self.arena.alloc_from_iter([lowered_req])arena_vec![self; lowered_req],
153 );
154 self.stmt_expr(req.span, precond)
155 }
156
157 fn lower_postcond_checker(
158 &mut self,
159 ens: &Box<rustc_ast::Expr>,
160 ) -> &'hir rustc_hir::Expr<'hir> {
161 let ens_span = self.lower_span(ens.span);
162 let ens_span = self.mark_span_with_reason(
163 rustc_span::DesugaringKind::Contract,
164 ens_span,
165 Some(Arc::clone(&crate::ALLOW_CONTRACTS)),
166 );
167 let lowered_ens = self.lower_expr_mut(&ens);
168 self.expr_call_lang_item_fn(
169 ens_span,
170 LangItem::ContractBuildCheckEnsures,
171 &*self.arena.alloc_from_iter([lowered_ens])arena_vec![self; lowered_ens],
172 )
173 }
174
175 fn lower_contract_check_just_precond(
176 &mut self,
177 contract_decls: &'hir [rustc_hir::Stmt<'hir>],
178 precond: rustc_hir::Stmt<'hir>,
179 ) -> rustc_hir::Stmt<'hir> {
180 let stmts = self
181 .arena
182 .alloc_from_iter(contract_decls.into_iter().map(|d| *d).chain([precond].into_iter()));
183
184 let then_block_stmts = self.block_all(precond.span, stmts, None);
185 let then_block = self.arena.alloc(self.expr_block(&then_block_stmts));
186
187 let precond_check = rustc_hir::ExprKind::If(
188 self.arena.alloc(self.expr_bool_literal(precond.span, self.tcx.sess.contract_checks())),
189 then_block,
190 None,
191 );
192
193 let precond_check = self.expr(precond.span, precond_check);
194 self.stmt_expr(precond.span, precond_check)
195 }
196
197 fn lower_contract_check_with_postcond(
198 &mut self,
199 contract_decls: &'hir [rustc_hir::Stmt<'hir>],
200 precond: Option<rustc_hir::Stmt<'hir>>,
201 postcond_checker: &'hir rustc_hir::Expr<'hir>,
202 ) -> &'hir rustc_hir::Expr<'hir> {
203 let stmts = self
204 .arena
205 .alloc_from_iter(contract_decls.into_iter().map(|d| *d).chain(precond.into_iter()));
206 let span = match precond {
207 Some(precond) => precond.span,
208 None => postcond_checker.span,
209 };
210
211 let postcond_checker = self.arena.alloc(self.expr_enum_variant_lang_item(
212 postcond_checker.span,
213 LangItem::OptionSome,
214 &*self.arena.alloc_from_iter([*postcond_checker])arena_vec![self; *postcond_checker],
215 ));
216 let then_block_stmts = self.block_all(span, stmts, Some(postcond_checker));
217 let then_block = self.arena.alloc(self.expr_block(&then_block_stmts));
218
219 let none_expr = self.arena.alloc(self.expr_enum_variant_lang_item(
220 postcond_checker.span,
221 LangItem::OptionNone,
222 Default::default(),
223 ));
224 let else_block = self.block_expr(none_expr);
225 let else_block = self.arena.alloc(self.expr_block(else_block));
226
227 let contract_check = rustc_hir::ExprKind::If(
228 self.arena.alloc(self.expr_bool_literal(span, self.tcx.sess.contract_checks())),
229 then_block,
230 Some(else_block),
231 );
232 self.arena.alloc(self.expr(span, contract_check))
233 }
234
235 fn wrap_body_with_contract_check(
236 &mut self,
237 body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>,
238 contract_check: &'hir rustc_hir::Expr<'hir>,
239 postcond_span: rustc_span::Span,
240 ) -> &'hir rustc_hir::Block<'hir> {
241 let check_ident: rustc_span::Ident =
242 rustc_span::Ident::from_str_and_span("__ensures_checker", postcond_span);
243 let (check_hir_id, postcond_decl) = {
244 let (checker_pat, check_hir_id) = self.pat_ident_binding_mode_mut(
246 postcond_span,
247 check_ident,
248 rustc_hir::BindingMode::NONE,
249 );
250 (
251 check_hir_id,
252 self.stmt_let_pat(
253 None,
254 postcond_span,
255 Some(contract_check),
256 self.arena.alloc(checker_pat),
257 rustc_hir::LocalSource::Contract,
258 ),
259 )
260 };
261
262 self.contract_ensures = Some((postcond_span, check_ident, check_hir_id));
265 let body = self.arena.alloc(body(self));
266
267 let body = self.inject_ensures_check(body, postcond_span, check_ident, check_hir_id);
269
270 let wrapped_body = self.block_all(
272 body.span,
273 self.arena.alloc_from_iter([postcond_decl].into_iter()),
274 Some(body),
275 );
276 wrapped_body
277 }
278
279 pub(super) fn checked_return(
282 &mut self,
283 opt_expr: Option<&'hir rustc_hir::Expr<'hir>>,
284 ) -> rustc_hir::ExprKind<'hir> {
285 let checked_ret =
286 if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures {
287 let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span));
288 Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id))
289 } else {
290 opt_expr
291 };
292 rustc_hir::ExprKind::Ret(checked_ret)
293 }
294
295 pub(super) fn inject_ensures_check(
297 &mut self,
298 expr: &'hir rustc_hir::Expr<'hir>,
299 span: rustc_span::Span,
300 cond_ident: rustc_span::Ident,
301 cond_hir_id: rustc_hir::HirId,
302 ) -> &'hir rustc_hir::Expr<'hir> {
303 let ret_ident: rustc_span::Ident = rustc_span::Ident::from_str_and_span("__ret", span);
313
314 let (ret_pat, ret_hir_id) =
316 self.pat_ident_binding_mode_mut(span, ret_ident, rustc_hir::BindingMode::NONE);
317
318 let ret_stmt = self.stmt_let_pat(
319 None,
320 span,
321 Some(expr),
322 self.arena.alloc(ret_pat),
323 rustc_hir::LocalSource::Contract,
324 );
325
326 let ret = self.expr_ident(span, ret_ident, ret_hir_id);
327
328 let cond_fn = self.expr_ident(span, cond_ident, cond_hir_id);
329 let contract_check = self.expr_call_lang_item_fn_mut(
330 span,
331 LangItem::ContractCheckEnsures,
332 self.arena.alloc_from_iter([*cond_fn, *ret])arena_vec![self; *cond_fn, *ret],
333 );
334 let contract_check = self.arena.alloc(contract_check);
335 let call_expr = self.block_expr_block(contract_check);
336
337 let ret = self.expr_ident(span, ret_ident, ret_hir_id);
340 let ret_block = self.block_expr_block(ret);
341
342 let contracts_enabled: rustc_hir::Expr<'_> =
343 self.expr_bool_literal(span, self.tcx.sess.contract_checks());
344 let contract_check = self.arena.alloc(self.expr(
345 span,
346 rustc_hir::ExprKind::If(
347 self.arena.alloc(contracts_enabled),
348 call_expr,
349 Some(ret_block),
350 ),
351 ));
352
353 let attrs: rustc_ast::AttrVec = {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(self.unreachable_code_attr(span));
vec
}thin_vec![self.unreachable_code_attr(span)];
354 self.lower_attrs(contract_check.hir_id, &attrs, span, Target::Expression);
355
356 let ret_block = self.block_all(span, self.arena.alloc_from_iter([ret_stmt])arena_vec![self; ret_stmt], Some(contract_check));
357 self.arena.alloc(self.expr_block(self.arena.alloc(ret_block)))
358 }
359}