about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/ast.rs9
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs19
-rw-r--r--compiler/rustc_ast/src/visit.rs17
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs15
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs112
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs20
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs2
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs21
-rw-r--r--compiler/rustc_builtin_macros/src/alloc_error_handler.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/global_allocator.rs1
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs1
-rw-r--r--compiler/rustc_hir/src/hir.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs2
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs22
-rw-r--r--compiler/rustc_parse/src/parser/item.rs13
-rw-r--r--compiler/rustc_parse/src/parser/token_type.rs8
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs5
-rw-r--r--compiler/rustc_resolve/src/late.rs6
-rw-r--r--compiler/rustc_span/src/hygiene.rs3
-rw-r--r--compiler/rustc_span/src/symbol.rs2
21 files changed, 266 insertions, 16 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index b6f331d316c..08621c1c56a 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<P<AssocItem>>,
 }
 
+#[derive(Clone, Encodable, Decodable, Debug, Default)]
+pub struct FnContract {
+    pub requires: Option<P<Expr>>,
+    pub ensures: Option<P<Expr>>,
+}
+
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct Fn {
     pub defaultness: Defaultness,
     pub generics: Generics,
     pub sig: FnSig,
+    pub contract: Option<P<FnContract>>,
     pub body: Option<P<Block>>,
 }
 
@@ -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 7caf7c4c356..70616fe8769 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<FnContract>) {
+        walk_contract(self, c);
+    }
+
     fn visit_fn_decl(&mut self, d: &mut P<FnDecl>) {
         walk_fn_decl(self, d);
     }
@@ -958,13 +962,16 @@ fn walk_fn<T: MutVisitor>(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<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
     }
 }
 
+fn walk_contract<T: MutVisitor>(vis: &mut T, contract: &mut P<FnContract>) {
+    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<T: MutVisitor>(vis: &mut T, decl: &mut P<FnDecl>) {
     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 232fd546de9..714b074f930 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 1267281f73e..19433047595 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()),
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 74870d74150..4679ccdddbb 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<Output = T>` here because lower_body
                     // only cares about the input argument patterns in the function
@@ -1054,10 +1087,81 @@ impl<'hir> LoweringContext<'_, 'hir> {
         body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
     ) -> hir::BodyId {
         self.lower_body(|this| {
-            (
-                this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))),
-                body(this),
-            )
+            let params =
+                this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));
+            let result = body(this);
+
+            let opt_contract = this.contract.take();
+
+            // { body }
+            // ==>
+            // { rustc_contract_requires(PRECOND); { body } }
+            let result: hir::Expr<'hir> = if let Some(contract) = opt_contract {
+                let lit_unit = |this: &mut LoweringContext<'_, 'hir>| {
+                    this.expr(contract.span, hir::ExprKind::Tup(&[]))
+                };
+
+                let precond: hir::Stmt<'hir> = if let Some(req) = contract.requires {
+                    let lowered_req = this.lower_expr_mut(&req);
+                    let precond = this.expr_call_lang_item_fn_mut(
+                        req.span,
+                        hir::LangItem::ContractCheckRequires,
+                        &*arena_vec![this; lowered_req],
+                    );
+                    this.stmt_expr(req.span, precond)
+                } else {
+                    let u = lit_unit(this);
+                    this.stmt_expr(contract.span, u)
+                };
+
+                let (postcond_checker, result) = if let Some(ens) = contract.ensures {
+                    let crate::FnContractLoweringEnsures { expr: ens, fresh_ident } = ens;
+                    let lowered_ens: hir::Expr<'hir> = this.lower_expr_mut(&ens);
+                    let postcond_checker = this.expr_call_lang_item_fn(
+                        ens.span,
+                        hir::LangItem::ContractBuildCheckEnsures,
+                        &*arena_vec![this; lowered_ens],
+                    );
+                    let checker_binding_pat = fresh_ident.1;
+                    (
+                        this.stmt_let_pat(
+                            None,
+                            ens.span,
+                            Some(postcond_checker),
+                            this.arena.alloc(checker_binding_pat),
+                            hir::LocalSource::Contract,
+                        ),
+                        {
+                            let checker_fn =
+                                this.expr_ident(ens.span, fresh_ident.0, fresh_ident.2);
+                            let span = this.mark_span_with_reason(
+                                DesugaringKind::Contract,
+                                ens.span,
+                                None,
+                            );
+                            this.expr_call_mut(
+                                span,
+                                checker_fn,
+                                std::slice::from_ref(this.arena.alloc(result)),
+                            )
+                        },
+                    )
+                } else {
+                    let u = lit_unit(this);
+                    (this.stmt_expr(contract.span, u), result)
+                };
+
+                let block = this.block_all(
+                    contract.span,
+                    arena_vec![this; precond, postcond_checker],
+                    Some(this.arena.alloc(result)),
+                );
+                this.expr_block(block)
+            } else {
+                result
+            };
+
+            (params, result)
         })
     }
 
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 893da930855..2715b3d6215 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -86,6 +86,19 @@ mod path;
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
+#[derive(Debug, Clone)]
+struct FnContractLoweringInfo<'hir> {
+    pub span: Span,
+    pub requires: Option<ast::ptr::P<ast::Expr>>,
+    pub ensures: Option<FnContractLoweringEnsures<'hir>>,
+}
+
+#[derive(Debug, Clone)]
+struct FnContractLoweringEnsures<'hir> {
+    expr: ast::ptr::P<ast::Expr>,
+    fresh_ident: (Ident, hir::Pat<'hir>, HirId),
+}
+
 struct LoweringContext<'a, 'hir> {
     tcx: TyCtxt<'hir>,
     resolver: &'a mut ResolverAstLowering,
@@ -100,6 +113,8 @@ struct LoweringContext<'a, 'hir> {
     /// Collect items that were created by lowering the current owner.
     children: Vec<(LocalDefId, hir::MaybeOwner<'hir>)>,
 
+    contract: Option<FnContractLoweringInfo<'hir>>,
+
     coroutine_kind: Option<hir::CoroutineKind>,
 
     /// When inside an `async` context, this is the `HirId` of the
@@ -148,6 +163,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             bodies: Vec::new(),
             attrs: SortedMap::default(),
             children: Vec::default(),
+            contract: None,
             current_hir_id_owner: hir::CRATE_OWNER_ID,
             item_local_id_counter: hir::ItemLocalId::ZERO,
             ident_and_label_to_local_id: Default::default(),
@@ -834,12 +850,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let was_in_loop_condition = self.is_in_loop_condition;
         self.is_in_loop_condition = false;
 
+        let old_contract = self.contract.take();
+
         let catch_scope = self.catch_scope.take();
         let loop_scope = self.loop_scope.take();
         let ret = f(self);
         self.catch_scope = catch_scope;
         self.loop_scope = loop_scope;
 
+        self.contract = old_contract;
+
         self.is_in_loop_condition = was_in_loop_condition;
 
         self.current_item = current_item;
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index ea1f4a6559a..0049c5b4823 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -917,7 +917,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 walk_list!(self, visit_attribute, &item.attrs);
                 return; // Avoid visiting again.
             }
-            ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, body }) => {
+            ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, contract: _, body }) => {
                 self.check_defaultness(item.span, *defaultness);
 
                 let is_intrinsic =
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index 4cfcaa95233..c10b5ad34e1 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -650,13 +650,17 @@ impl<'a> State<'a> {
         attrs: &[ast::Attribute],
         func: &ast::Fn,
     ) {
-        let ast::Fn { defaultness, generics, sig, body } = func;
+        let ast::Fn { defaultness, generics, sig, contract, body } = func;
         if body.is_some() {
             self.head("");
         }
         self.print_visibility(vis);
         self.print_defaultness(*defaultness);
         self.print_fn(&sig.decl, sig.header, Some(name), generics);
+        if let Some(contract) = &contract {
+            self.nbsp();
+            self.print_contract(contract);
+        }
         if let Some(body) = body {
             self.nbsp();
             self.print_block_with_attrs(body, attrs);
@@ -665,6 +669,21 @@ impl<'a> State<'a> {
         }
     }
 
+    fn print_contract(&mut self, contract: &ast::FnContract) {
+        if let Some(pred) = &contract.requires {
+            self.word("rustc_requires");
+            self.popen();
+            self.print_expr(pred, FixupContext::default());
+            self.pclose();
+        }
+        if let Some(pred) = &contract.ensures {
+            self.word("rustc_ensures");
+            self.popen();
+            self.print_expr(pred, FixupContext::default());
+            self.pclose();
+        }
+    }
+
     pub(crate) fn print_fn(
         &mut self,
         decl: &ast::FnDecl,
diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
index d2b4e1ca824..cffc4978601 100644
--- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
+++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
@@ -85,6 +85,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
         defaultness: ast::Defaultness::Final,
         sig,
         generics: Generics::default(),
+        contract: None,
         body,
     }));
 
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 755a733286c..0631c5a80fc 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -1034,6 +1034,7 @@ impl<'a> MethodDef<'a> {
                 defaultness,
                 sig,
                 generics: fn_generics,
+                contract: None,
                 body: Some(body_block),
             })),
             tokens: None,
diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs
index 8388e9dcafb..8fdbbf8e704 100644
--- a/compiler/rustc_builtin_macros/src/global_allocator.rs
+++ b/compiler/rustc_builtin_macros/src/global_allocator.rs
@@ -81,6 +81,7 @@ impl AllocFnFactory<'_, '_> {
             defaultness: ast::Defaultness::Final,
             sig,
             generics: Generics::default(),
+            contract: None,
             body,
         }));
         let item = self.cx.item(
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index 31b068bd33d..472e16e62d5 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -344,6 +344,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
         defaultness,
         sig,
         generics: ast::Generics::default(),
+        contract: None,
         body: Some(main_body),
     }));
 
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index af2f86b67e0..8bc09f631cf 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2598,6 +2598,8 @@ pub enum LocalSource {
     /// A desugared `expr = expr`, where the LHS is a tuple, struct, array or underscore expression.
     /// The span is that of the `=` sign.
     AssignDesugar(Span),
+    /// A contract `#[ensures(..)]` attribute injects a let binding for the check that runs at point of return.
+    Contract,
 }
 
 /// Hints at the original code for a `match _ { .. }`.
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index a519e177fbc..f7bc21cc526 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -269,6 +269,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // diverging expression (e.g. it arose from desugaring of `try { return }`),
             // we skip issuing a warning because it is autogenerated code.
             ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {}
+            // Likewise, do not lint unreachable code injected via contracts desugaring.
+            ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::Contract) => {}
             ExprKind::Call(callee, _) => self.warn_if_unreachable(expr.hir_id, callee.span, "call"),
             ExprKind::MethodCall(segment, ..) => {
                 self.warn_if_unreachable(expr.hir_id, segment.ident.span, "call")
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index b1b84b0b701..d5ac8d1588d 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -297,6 +297,28 @@ impl<'a> Parser<'a> {
         })
     }
 
+    /// Parses a rustc-internal fn contract
+    /// (`rustc_contract_requires(WWW) rustc_contract_ensures(ZZZ)`)
+    pub(super) fn parse_contract(
+        &mut self,
+    ) -> PResult<'a, Option<rustc_ast::ptr::P<ast::FnContract>>> {
+        let requires = if self.eat_keyword_noexpect(exp!(RustcContractRequires).kw) {
+            Some(self.parse_expr()?)
+        } else {
+            None
+        };
+        let ensures = if self.eat_keyword_noexpect(exp!(RustcContractEnsures).kw) {
+            Some(self.parse_expr()?)
+        } else {
+            None
+        };
+        if requires.is_none() && ensures.is_none() {
+            Ok(None)
+        } else {
+            Ok(Some(rustc_ast::ptr::P(ast::FnContract { requires, ensures })))
+        }
+    }
+
     /// Parses an optional where-clause.
     ///
     /// ```ignore (only-for-syntax-highlight)
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index f3e56be9f6e..dbdc31f06a2 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -213,9 +213,12 @@ impl<'a> Parser<'a> {
             self.parse_use_item()?
         } else if self.check_fn_front_matter(check_pub, case) {
             // FUNCTION ITEM
-            let (ident, sig, generics, body) =
+            let (ident, sig, generics, contract, body) =
                 self.parse_fn(attrs, fn_parse_mode, lo, vis, case)?;
-            (ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body })))
+            (
+                ident,
+                ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, contract, body })),
+            )
         } else if self.eat_keyword(exp!(Extern)) {
             if self.eat_keyword(exp!(Crate)) {
                 // EXTERN CRATE
@@ -2372,7 +2375,7 @@ impl<'a> Parser<'a> {
         sig_lo: Span,
         vis: &Visibility,
         case: Case,
-    ) -> PResult<'a, (Ident, FnSig, Generics, Option<P<Block>>)> {
+    ) -> PResult<'a, (Ident, FnSig, Generics, Option<P<FnContract>>, Option<P<Block>>)> {
         let fn_span = self.token.span;
         let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn`
         let ident = self.parse_ident()?; // `foo`
@@ -2398,6 +2401,8 @@ impl<'a> Parser<'a> {
         // inside `parse_fn_body()`.
         let fn_params_end = self.prev_token.span.shrink_to_hi();
 
+        let contract = self.parse_contract()?;
+
         generics.where_clause = self.parse_where_clause()?; // `where T: Ord`
 
         // `fn_params_end` is needed only when it's followed by a where clause.
@@ -2409,7 +2414,7 @@ impl<'a> Parser<'a> {
         let body =
             self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?;
         let fn_sig_span = sig_lo.to(sig_hi);
-        Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body))
+        Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, contract, body))
     }
 
     /// Provide diagnostics when function body is not found
diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs
index 73f3ac001c8..50f03e72f82 100644
--- a/compiler/rustc_parse/src/parser/token_type.rs
+++ b/compiler/rustc_parse/src/parser/token_type.rs
@@ -108,6 +108,8 @@ pub enum TokenType {
     KwRef,
     KwReturn,
     KwReuse,
+    KwRustcContractEnsures,
+    KwRustcContractRequires,
     KwSafe,
     KwSelfUpper,
     KwStatic,
@@ -242,6 +244,8 @@ impl TokenType {
             KwRef,
             KwReturn,
             KwReuse,
+            KwRustcContractEnsures,
+            KwRustcContractRequires,
             KwSafe,
             KwSelfUpper,
             KwStatic,
@@ -314,6 +318,8 @@ impl TokenType {
             TokenType::KwRef => Some(kw::Ref),
             TokenType::KwReturn => Some(kw::Return),
             TokenType::KwReuse => Some(kw::Reuse),
+            TokenType::KwRustcContractEnsures => Some(kw::RustcContractEnsures),
+            TokenType::KwRustcContractRequires => Some(kw::RustcContractRequires),
             TokenType::KwSafe => Some(kw::Safe),
             TokenType::KwSelfUpper => Some(kw::SelfUpper),
             TokenType::KwStatic => Some(kw::Static),
@@ -544,6 +550,8 @@ macro_rules! exp {
     (Ref)            => { exp!(@kw, Ref,        KwRef) };
     (Return)         => { exp!(@kw, Return,     KwReturn) };
     (Reuse)          => { exp!(@kw, Reuse,      KwReuse) };
+    (RustcContractEnsures)  => { exp!(@kw, RustcContractEnsures, KwRustcContractEnsures) };
+    (RustcContractRequires) => { exp!(@kw, RustcContractRequires, KwRustcContractRequires) };
     (Safe)           => { exp!(@kw, Safe,       KwSafe) };
     (SelfUpper)      => { exp!(@kw, SelfUpper,  KwSelfUpper) };
     (Static)         => { exp!(@kw, Static,     KwStatic) };
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index 16c0a345f87..db607dbb419 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -174,10 +174,13 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
                 _ctxt,
                 _ident,
                 _vis,
-                Fn { sig: FnSig { header, decl, span: _ }, generics, body, .. },
+                Fn { sig: FnSig { header, decl, span: _ }, generics, contract, body, .. },
             ) if let Some(coroutine_kind) = header.coroutine_kind => {
                 self.visit_fn_header(header);
                 self.visit_generics(generics);
+                if let Some(contract) = contract {
+                    self.visit_contract(contract);
+                }
 
                 // For async functions, we need to create their inner defs inside of a
                 // closure to match their desugared representation. Besides that,
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 4842cbd556c..e37e7e98ee7 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1019,7 +1019,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
             // Create a label rib for the function.
             this.with_label_rib(RibKind::FnOrCoroutine, |this| {
                 match fn_kind {
-                    FnKind::Fn(_, _, _, Fn { sig, generics, body, .. }) => {
+                    FnKind::Fn(_, _, _, Fn { sig, generics, contract, body, .. }) => {
                         this.visit_generics(generics);
 
                         let declaration = &sig.decl;
@@ -1046,6 +1046,10 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
                             },
                         );
 
+                        if let Some(contract) = contract {
+                            this.visit_contract(contract);
+                        }
+
                         if let Some(body) = body {
                             // Ignore errors in function bodies if this is rustdoc
                             // Be sure not to set this until the function signature has been resolved.
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index a5826137181..dbbbb5077cb 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -1163,6 +1163,8 @@ pub enum DesugaringKind {
     WhileLoop,
     /// `async Fn()` bound modifier
     BoundModifier,
+    /// Calls to contract checks (`#[requires]` to precond, `#[ensures]` to postcond)
+    Contract,
 }
 
 impl DesugaringKind {
@@ -1179,6 +1181,7 @@ impl DesugaringKind {
             DesugaringKind::ForLoop => "`for` loop",
             DesugaringKind::WhileLoop => "`while` loop",
             DesugaringKind::BoundModifier => "trait bound modifier",
+            DesugaringKind::Contract => "contract check",
         }
     }
 }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index df424b3312a..1c15ca7d11a 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -118,6 +118,8 @@ symbols! {
         MacroRules:         "macro_rules",
         Raw:                "raw",
         Reuse:              "reuse",
+        RustcContractEnsures:  "rustc_contract_ensures",
+        RustcContractRequires: "rustc_contract_requires",
         Safe:               "safe",
         Union:              "union",
         Yeet:               "yeet",