diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 8e73df63ef54f..757026bc1c006 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -3316,11 +3316,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
>,
}
@@ -3618,7 +3625,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 aa88e8369d5b6..e4721b2c8a120 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);
}
@@ -973,6 +977,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));
@@ -1205,8 +1219,11 @@ impl WalkItemKind for ItemKind {
ItemKind::Const(item) => {
visit_const_item(item, vis);
}
- ItemKind::Fn(box Fn { defaultness, generics, sig, body }) => {
+ ItemKind::Fn(box Fn { defaultness, generics, sig, contract, body }) => {
visit_defaultness(vis, defaultness);
+ if let Some(contract) = contract {
+ vis.visit_contract(contract)
+ };
vis.visit_fn(
FnKind::Fn(FnCtxt::Free, ident, sig, visibility, generics, body),
span,
@@ -1329,8 +1346,11 @@ impl WalkItemKind for AssocItemKind {
AssocItemKind::Const(item) => {
visit_const_item(item, visitor);
}
- AssocItemKind::Fn(box Fn { defaultness, generics, sig, body }) => {
+ AssocItemKind::Fn(box Fn { defaultness, generics, sig, contract, body }) => {
visit_defaultness(visitor, defaultness);
+ if let Some(contract) = contract {
+ visitor.visit_contract(contract);
+ }
visitor.visit_fn(
FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, visibility, generics, body),
span,
@@ -1476,8 +1496,11 @@ impl WalkItemKind for ForeignItemKind {
visitor.visit_ty(ty);
visit_opt(expr, |expr| visitor.visit_expr(expr));
}
- ForeignItemKind::Fn(box Fn { defaultness, generics, sig, body }) => {
+ ForeignItemKind::Fn(box Fn { defaultness, generics, sig, contract, body }) => {
visit_defaultness(visitor, defaultness);
+ if let Some(contract) = contract {
+ visitor.visit_contract(contract);
+ }
visitor.visit_fn(
FnKind::Fn(FnCtxt::Foreign, ident, sig, visibility, generics, body),
span,
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 1d6d7330757de..c8e35816a79cc 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -65,7 +65,15 @@ impl BoundKind {
#[derive(Copy, Clone, Debug)]
pub enum FnKind<'a> {
/// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
- Fn(FnCtxt, &'a Ident, &'a FnSig, &'a Visibility, &'a Generics, &'a Option>),
+ Fn(
+ FnCtxt,
+ &'a Ident,
+ &'a FnSig,
+ &'a Visibility,
+ &'a Generics,
+ &'a Option
>,
+ &'a Option
>,
+ ),
/// E.g., `|x, y| body`.
Closure(&'a ClosureBinder, &'a Option, &'a FnDecl, &'a Expr),
@@ -74,7 +82,7 @@ pub enum FnKind<'a> {
impl<'a> FnKind<'a> {
pub fn header(&self) -> Option<&'a FnHeader> {
match *self {
- FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header),
+ FnKind::Fn(_, _, sig, _, _, _, _) => Some(&sig.header),
FnKind::Closure(..) => None,
}
}
@@ -88,7 +96,7 @@ impl<'a> FnKind<'a> {
pub fn decl(&self) -> &'a FnDecl {
match self {
- FnKind::Fn(_, _, sig, _, _, _) => &sig.decl,
+ FnKind::Fn(_, _, sig, _, _, _, _) => &sig.decl,
FnKind::Closure(_, _, decl, _) => decl,
}
}
@@ -188,6 +196,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)
}
@@ -374,8 +385,8 @@ impl WalkItemKind for ItemKind {
try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_expr, expr);
}
- ItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => {
- let kind = FnKind::Fn(FnCtxt::Free, ident, sig, vis, generics, body);
+ ItemKind::Fn(box Fn { defaultness: _, generics, sig, contract, body }) => {
+ let kind = FnKind::Fn(FnCtxt::Free, ident, sig, vis, generics, contract, body);
try_visit!(visitor.visit_fn(kind, span, id));
}
ItemKind::Mod(_unsafety, mod_kind) => match mod_kind {
@@ -715,8 +726,8 @@ impl WalkItemKind for ForeignItemKind {
try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_expr, expr);
}
- ForeignItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => {
- let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, generics, body);
+ ForeignItemKind::Fn(box Fn { defaultness: _, generics, sig, contract, body }) => {
+ let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, generics, contract, body);
try_visit!(visitor.visit_fn(kind, span, id));
}
ForeignItemKind::TyAlias(box TyAlias {
@@ -800,6 +811,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,
@@ -858,11 +880,20 @@ pub fn walk_fn_decl<'a, V: Visitor<'a>>(
pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Result {
match kind {
- FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span: _ }, _vis, generics, body) => {
+ FnKind::Fn(
+ _ctxt,
+ _ident,
+ FnSig { header, decl, span: _ },
+ _vis,
+ 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) => {
@@ -892,8 +923,9 @@ impl WalkItemKind for AssocItemKind {
try_visit!(visitor.visit_ty(ty));
visit_opt!(visitor, visit_expr, expr);
}
- AssocItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => {
- let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, generics, body);
+ AssocItemKind::Fn(box Fn { defaultness: _, generics, sig, contract, body }) => {
+ let kind =
+ FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, generics, contract, body);
try_visit!(visitor.visit_fn(kind, span, id));
}
AssocItemKind::Type(box TyAlias {
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index a76ca6772e52e..9ccadc74fc333 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -314,7 +314,20 @@ 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));
+ let mut e = e.as_ref().map(|x| self.lower_expr(x));
+ 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 checker_fn = self.expr_ident(span, fresh_ident.0, fresh_ident.2);
+ let args = if let Some(e) = e {
+ std::slice::from_ref(e)
+ } else {
+ std::slice::from_ref(self.expr_unit(span))
+ };
+ e = Some(self.expr_call(span, checker_fn, args));
+ }
hir::ExprKind::Ret(e)
}
ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
@@ -2125,7 +2138,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.arena.alloc(self.expr_call_mut(span, e, args))
}
- fn expr_call_lang_item_fn_mut(
+ pub(super) fn expr_call_lang_item_fn_mut(
&mut self,
span: Span,
lang_item: hir::LangItem,
@@ -2135,7 +2148,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.expr_call_mut(span, path, args)
}
- fn expr_call_lang_item_fn(
+ pub(super) fn expr_call_lang_item_fn(
&mut self,
span: Span,
lang_item: hir::LangItem,
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 6fce911693863..7bc0cecde9d01 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -207,9 +207,42 @@ 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 = if let Some(ens) = ensures {
+ // 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,
+ );
+
+ Some(crate::FnContractLoweringEnsures {
+ expr: ens,
+ fresh_ident: (checker_ident, checker_pat, checker_hir_id),
+ })
+ } else {
+ None
+ };
+
+ // 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