diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index b6f331d316cc9..08621c1c56a14 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -3348,11 +3348,18 @@ pub struct Impl {
pub items: ThinVec
>,
}
+#[derive(Clone, Encodable, Decodable, Debug, Default)]
+pub struct FnContract {
+ pub requires: Option
>,
+ pub ensures: Option
>,
+}
+
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct Fn {
pub defaultness: Defaultness,
pub generics: Generics,
pub sig: FnSig,
+ pub contract: Option
>,
pub body: Option
>,
}
@@ -3650,7 +3657,7 @@ mod size_asserts {
static_assert_size!(Block, 32);
static_assert_size!(Expr, 72);
static_assert_size!(ExprKind, 40);
- static_assert_size!(Fn, 160);
+ static_assert_size!(Fn, 168);
static_assert_size!(ForeignItem, 88);
static_assert_size!(ForeignItemKind, 16);
static_assert_size!(GenericArg, 24);
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 7caf7c4c35687..70616fe87691b 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -143,6 +143,10 @@ pub trait MutVisitor: Sized {
walk_flat_map_assoc_item(self, i, ctxt)
}
+ fn visit_contract(&mut self, c: &mut P) {
+ walk_contract(self, c);
+ }
+
fn visit_fn_decl(&mut self, d: &mut P) {
walk_fn_decl(self, d);
}
@@ -958,13 +962,16 @@ fn walk_fn(vis: &mut T, kind: FnKind<'_>) {
_ctxt,
_ident,
_vis,
- Fn { defaultness, generics, body, sig: FnSig { header, decl, span } },
+ Fn { defaultness, generics, contract, body, sig: FnSig { header, decl, span } },
) => {
// Identifier and visibility are visited as a part of the item.
visit_defaultness(vis, defaultness);
vis.visit_fn_header(header);
vis.visit_generics(generics);
vis.visit_fn_decl(decl);
+ if let Some(contract) = contract {
+ vis.visit_contract(contract);
+ }
if let Some(body) = body {
vis.visit_block(body);
}
@@ -979,6 +986,16 @@ fn walk_fn(vis: &mut T, kind: FnKind<'_>) {
}
}
+fn walk_contract(vis: &mut T, contract: &mut P) {
+ let FnContract { requires, ensures } = contract.deref_mut();
+ if let Some(pred) = requires {
+ vis.visit_expr(pred);
+ }
+ if let Some(pred) = ensures {
+ vis.visit_expr(pred);
+ }
+}
+
fn walk_fn_decl(vis: &mut T, decl: &mut P) {
let FnDecl { inputs, output } = decl.deref_mut();
inputs.flat_map_in_place(|param| vis.flat_map_param(param));
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 232fd546de9a3..714b074f930c2 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -188,6 +188,9 @@ pub trait Visitor<'ast>: Sized {
fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) -> Self::Result {
walk_closure_binder(self, b)
}
+ fn visit_contract(&mut self, c: &'ast FnContract) -> Self::Result {
+ walk_contract(self, c)
+ }
fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result {
walk_where_predicate(self, p)
}
@@ -800,6 +803,17 @@ pub fn walk_closure_binder<'a, V: Visitor<'a>>(
V::Result::output()
}
+pub fn walk_contract<'a, V: Visitor<'a>>(visitor: &mut V, c: &'a FnContract) -> V::Result {
+ let FnContract { requires, ensures } = c;
+ if let Some(pred) = requires {
+ visitor.visit_expr(pred);
+ }
+ if let Some(pred) = ensures {
+ visitor.visit_expr(pred);
+ }
+ V::Result::output()
+}
+
pub fn walk_where_predicate<'a, V: Visitor<'a>>(
visitor: &mut V,
predicate: &'a WherePredicate,
@@ -862,12 +876,13 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
_ctxt,
_ident,
_vis,
- Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, body },
+ Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, contract, body },
) => {
// Identifier and visibility are visited as a part of the item.
try_visit!(visitor.visit_fn_header(header));
try_visit!(visitor.visit_generics(generics));
try_visit!(visitor.visit_fn_decl(decl));
+ visit_opt!(visitor, visit_contract, contract);
visit_opt!(visitor, visit_block, body);
}
FnKind::Closure(binder, coroutine_kind, decl, body) => {
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 1267281f73ebe..8125da361b814 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -314,8 +314,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label))
}
ExprKind::Ret(e) => {
- let e = e.as_ref().map(|x| self.lower_expr(x));
- hir::ExprKind::Ret(e)
+ let expr = e.as_ref().map(|x| self.lower_expr(x));
+ self.checked_return(expr)
}
ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
ExprKind::Become(sub_expr) => {
@@ -382,6 +382,32 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
}
+ /// Create an `ExprKind::Ret` that is preceded by a call to check contract ensures clause.
+ fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> {
+ let checked_ret = if let Some(Some((span, fresh_ident))) =
+ self.contract.as_ref().map(|c| c.ensures.as_ref().map(|e| (e.expr.span, e.fresh_ident)))
+ {
+ let expr = opt_expr.unwrap_or_else(|| self.expr_unit(span));
+ Some(self.inject_ensures_check(expr, span, fresh_ident.0, fresh_ident.2))
+ } else {
+ opt_expr
+ };
+ hir::ExprKind::Ret(checked_ret)
+ }
+
+ /// Wraps an expression with a call to the ensures check before it gets returned.
+ pub(crate) fn inject_ensures_check(
+ &mut self,
+ expr: &'hir hir::Expr<'hir>,
+ span: Span,
+ check_ident: Ident,
+ check_hir_id: HirId,
+ ) -> &'hir hir::Expr<'hir> {
+ let checker_fn = self.expr_ident(span, check_ident, check_hir_id);
+ let span = self.mark_span_with_reason(DesugaringKind::Contract, span, None);
+ self.expr_call(span, checker_fn, std::slice::from_ref(expr))
+ }
+
pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock {
self.with_new_scopes(c.value.span, |this| {
let def_id = this.local_def_id(c.id);
@@ -1970,7 +1996,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
),
))
} else {
- self.arena.alloc(self.expr(try_span, hir::ExprKind::Ret(Some(from_residual_expr))))
+ let ret_expr = self.checked_return(Some(from_residual_expr));
+ self.arena.alloc(self.expr(try_span, ret_expr))
};
self.lower_attrs(ret_expr.hir_id, &attrs);
@@ -2019,7 +2046,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let target_id = Ok(catch_id);
hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr))
} else {
- hir::ExprKind::Ret(Some(from_yeet_expr))
+ self.checked_return(Some(from_yeet_expr))
}
}
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 74870d741504c..c96110fee619b 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -207,9 +207,40 @@ impl<'hir> LoweringContext<'_, 'hir> {
sig: FnSig { decl, header, span: fn_sig_span },
generics,
body,
+ contract,
..
}) => {
self.with_new_scopes(*fn_sig_span, |this| {
+ assert!(this.contract.is_none());
+ if let Some(contract) = contract {
+ let requires = contract.requires.clone();
+ let ensures = contract.ensures.clone();
+ let ensures = ensures.map(|ens| {
+ // FIXME: this needs to be a fresh (or illegal) identifier to prevent
+ // accidental capture of a parameter or global variable.
+ let checker_ident: Ident =
+ Ident::from_str_and_span("__ensures_checker", ens.span);
+ let (checker_pat, checker_hir_id) = this.pat_ident_binding_mode_mut(
+ ens.span,
+ checker_ident,
+ hir::BindingMode::NONE,
+ );
+
+ crate::FnContractLoweringEnsures {
+ expr: ens,
+ fresh_ident: (checker_ident, checker_pat, checker_hir_id),
+ }
+ });
+
+ // Note: `with_new_scopes` will reinstall the outer
+ // item's contract (if any) after its callback finishes.
+ this.contract.replace(crate::FnContractLoweringInfo {
+ span,
+ requires,
+ ensures,
+ });
+ }
+
// Note: we don't need to change the return type from `T` to
// `impl Future