Skip to content

Commit 6e59288

Browse files
committed
Auto merge of rust-lang#136531 - jhpratt:rollup-k0y6s6n, r=jhpratt
Rollup of 9 pull requests Successful merges: - rust-lang#128045 (#[contracts::requires(...)] + #[contracts::ensures(...)]) - rust-lang#136263 (rustdoc: clean up a bunch of ts-expected-error declarations in main) - rust-lang#136375 (cg_llvm: Replace some DIBuilder wrappers with LLVM-C API bindings (part 1)) - rust-lang#136392 (bootstrap: add wrapper macros for `feature = "tracing"`-gated `tracing` macros) - rust-lang#136405 (rustdoc-book: Clean up section on `--output-format`) - rust-lang#136497 (Report generic mismatches when calling bodyless trait functions) - rust-lang#136502 (Mark `std::fmt::from_fn` as `#[must_use]`) - rust-lang#136509 (Add tests for nested macro_rules edition behavior) - rust-lang#136526 (mir_build: Rename `thir::cx::Cx` to `ThirBuildCx` and remove `UserAnnotatedTyHelpers`) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 019fc4d + 24f3a50 commit 6e59288

File tree

158 files changed

+2698
-574
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

158 files changed

+2698
-574
lines changed

compiler/rustc_ast/src/ast.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -3348,11 +3348,18 @@ pub struct Impl {
33483348
pub items: ThinVec<P<AssocItem>>,
33493349
}
33503350

3351+
#[derive(Clone, Encodable, Decodable, Debug, Default)]
3352+
pub struct FnContract {
3353+
pub requires: Option<P<Expr>>,
3354+
pub ensures: Option<P<Expr>>,
3355+
}
3356+
33513357
#[derive(Clone, Encodable, Decodable, Debug)]
33523358
pub struct Fn {
33533359
pub defaultness: Defaultness,
33543360
pub generics: Generics,
33553361
pub sig: FnSig,
3362+
pub contract: Option<P<FnContract>>,
33563363
pub body: Option<P<Block>>,
33573364
}
33583365

@@ -3650,7 +3657,7 @@ mod size_asserts {
36503657
static_assert_size!(Block, 32);
36513658
static_assert_size!(Expr, 72);
36523659
static_assert_size!(ExprKind, 40);
3653-
static_assert_size!(Fn, 160);
3660+
static_assert_size!(Fn, 168);
36543661
static_assert_size!(ForeignItem, 88);
36553662
static_assert_size!(ForeignItemKind, 16);
36563663
static_assert_size!(GenericArg, 24);

compiler/rustc_ast/src/mut_visit.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ pub trait MutVisitor: Sized {
143143
walk_flat_map_assoc_item(self, i, ctxt)
144144
}
145145

146+
fn visit_contract(&mut self, c: &mut P<FnContract>) {
147+
walk_contract(self, c);
148+
}
149+
146150
fn visit_fn_decl(&mut self, d: &mut P<FnDecl>) {
147151
walk_fn_decl(self, d);
148152
}
@@ -958,13 +962,16 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
958962
_ctxt,
959963
_ident,
960964
_vis,
961-
Fn { defaultness, generics, body, sig: FnSig { header, decl, span } },
965+
Fn { defaultness, generics, contract, body, sig: FnSig { header, decl, span } },
962966
) => {
963967
// Identifier and visibility are visited as a part of the item.
964968
visit_defaultness(vis, defaultness);
965969
vis.visit_fn_header(header);
966970
vis.visit_generics(generics);
967971
vis.visit_fn_decl(decl);
972+
if let Some(contract) = contract {
973+
vis.visit_contract(contract);
974+
}
968975
if let Some(body) = body {
969976
vis.visit_block(body);
970977
}
@@ -979,6 +986,16 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
979986
}
980987
}
981988

989+
fn walk_contract<T: MutVisitor>(vis: &mut T, contract: &mut P<FnContract>) {
990+
let FnContract { requires, ensures } = contract.deref_mut();
991+
if let Some(pred) = requires {
992+
vis.visit_expr(pred);
993+
}
994+
if let Some(pred) = ensures {
995+
vis.visit_expr(pred);
996+
}
997+
}
998+
982999
fn walk_fn_decl<T: MutVisitor>(vis: &mut T, decl: &mut P<FnDecl>) {
9831000
let FnDecl { inputs, output } = decl.deref_mut();
9841001
inputs.flat_map_in_place(|param| vis.flat_map_param(param));

compiler/rustc_ast/src/visit.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ pub trait Visitor<'ast>: Sized {
188188
fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) -> Self::Result {
189189
walk_closure_binder(self, b)
190190
}
191+
fn visit_contract(&mut self, c: &'ast FnContract) -> Self::Result {
192+
walk_contract(self, c)
193+
}
191194
fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result {
192195
walk_where_predicate(self, p)
193196
}
@@ -800,6 +803,17 @@ pub fn walk_closure_binder<'a, V: Visitor<'a>>(
800803
V::Result::output()
801804
}
802805

806+
pub fn walk_contract<'a, V: Visitor<'a>>(visitor: &mut V, c: &'a FnContract) -> V::Result {
807+
let FnContract { requires, ensures } = c;
808+
if let Some(pred) = requires {
809+
visitor.visit_expr(pred);
810+
}
811+
if let Some(pred) = ensures {
812+
visitor.visit_expr(pred);
813+
}
814+
V::Result::output()
815+
}
816+
803817
pub fn walk_where_predicate<'a, V: Visitor<'a>>(
804818
visitor: &mut V,
805819
predicate: &'a WherePredicate,
@@ -862,12 +876,13 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
862876
_ctxt,
863877
_ident,
864878
_vis,
865-
Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, body },
879+
Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, contract, body },
866880
) => {
867881
// Identifier and visibility are visited as a part of the item.
868882
try_visit!(visitor.visit_fn_header(header));
869883
try_visit!(visitor.visit_generics(generics));
870884
try_visit!(visitor.visit_fn_decl(decl));
885+
visit_opt!(visitor, visit_contract, contract);
871886
visit_opt!(visitor, visit_block, body);
872887
}
873888
FnKind::Closure(binder, coroutine_kind, decl, body) => {

compiler/rustc_ast_lowering/src/expr.rs

+31-4
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
314314
hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label))
315315
}
316316
ExprKind::Ret(e) => {
317-
let e = e.as_ref().map(|x| self.lower_expr(x));
318-
hir::ExprKind::Ret(e)
317+
let expr = e.as_ref().map(|x| self.lower_expr(x));
318+
self.checked_return(expr)
319319
}
320320
ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
321321
ExprKind::Become(sub_expr) => {
@@ -382,6 +382,32 @@ impl<'hir> LoweringContext<'_, 'hir> {
382382
})
383383
}
384384

385+
/// Create an `ExprKind::Ret` that is preceded by a call to check contract ensures clause.
386+
fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> {
387+
let checked_ret = if let Some(Some((span, fresh_ident))) =
388+
self.contract.as_ref().map(|c| c.ensures.as_ref().map(|e| (e.expr.span, e.fresh_ident)))
389+
{
390+
let expr = opt_expr.unwrap_or_else(|| self.expr_unit(span));
391+
Some(self.inject_ensures_check(expr, span, fresh_ident.0, fresh_ident.2))
392+
} else {
393+
opt_expr
394+
};
395+
hir::ExprKind::Ret(checked_ret)
396+
}
397+
398+
/// Wraps an expression with a call to the ensures check before it gets returned.
399+
pub(crate) fn inject_ensures_check(
400+
&mut self,
401+
expr: &'hir hir::Expr<'hir>,
402+
span: Span,
403+
check_ident: Ident,
404+
check_hir_id: HirId,
405+
) -> &'hir hir::Expr<'hir> {
406+
let checker_fn = self.expr_ident(span, check_ident, check_hir_id);
407+
let span = self.mark_span_with_reason(DesugaringKind::Contract, span, None);
408+
self.expr_call(span, checker_fn, std::slice::from_ref(expr))
409+
}
410+
385411
pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock {
386412
self.with_new_scopes(c.value.span, |this| {
387413
let def_id = this.local_def_id(c.id);
@@ -1970,7 +1996,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
19701996
),
19711997
))
19721998
} else {
1973-
self.arena.alloc(self.expr(try_span, hir::ExprKind::Ret(Some(from_residual_expr))))
1999+
let ret_expr = self.checked_return(Some(from_residual_expr));
2000+
self.arena.alloc(self.expr(try_span, ret_expr))
19742001
};
19752002
self.lower_attrs(ret_expr.hir_id, &attrs);
19762003

@@ -2019,7 +2046,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
20192046
let target_id = Ok(catch_id);
20202047
hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr))
20212048
} else {
2022-
hir::ExprKind::Ret(Some(from_yeet_expr))
2049+
self.checked_return(Some(from_yeet_expr))
20232050
}
20242051
}
20252052

compiler/rustc_ast_lowering/src/item.rs

+89-4
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,40 @@ impl<'hir> LoweringContext<'_, 'hir> {
207207
sig: FnSig { decl, header, span: fn_sig_span },
208208
generics,
209209
body,
210+
contract,
210211
..
211212
}) => {
212213
self.with_new_scopes(*fn_sig_span, |this| {
214+
assert!(this.contract.is_none());
215+
if let Some(contract) = contract {
216+
let requires = contract.requires.clone();
217+
let ensures = contract.ensures.clone();
218+
let ensures = ensures.map(|ens| {
219+
// FIXME: this needs to be a fresh (or illegal) identifier to prevent
220+
// accidental capture of a parameter or global variable.
221+
let checker_ident: Ident =
222+
Ident::from_str_and_span("__ensures_checker", ens.span);
223+
let (checker_pat, checker_hir_id) = this.pat_ident_binding_mode_mut(
224+
ens.span,
225+
checker_ident,
226+
hir::BindingMode::NONE,
227+
);
228+
229+
crate::FnContractLoweringEnsures {
230+
expr: ens,
231+
fresh_ident: (checker_ident, checker_pat, checker_hir_id),
232+
}
233+
});
234+
235+
// Note: `with_new_scopes` will reinstall the outer
236+
// item's contract (if any) after its callback finishes.
237+
this.contract.replace(crate::FnContractLoweringInfo {
238+
span,
239+
requires,
240+
ensures,
241+
});
242+
}
243+
213244
// Note: we don't need to change the return type from `T` to
214245
// `impl Future<Output = T>` here because lower_body
215246
// only cares about the input argument patterns in the function
@@ -1054,10 +1085,64 @@ impl<'hir> LoweringContext<'_, 'hir> {
10541085
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
10551086
) -> hir::BodyId {
10561087
self.lower_body(|this| {
1057-
(
1058-
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))),
1059-
body(this),
1060-
)
1088+
let params =
1089+
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));
1090+
let result = body(this);
1091+
1092+
let opt_contract = this.contract.take();
1093+
1094+
// { body }
1095+
// ==>
1096+
// { contract_requires(PRECOND); { body } }
1097+
let Some(contract) = opt_contract else { return (params, result) };
1098+
let result_ref = this.arena.alloc(result);
1099+
let lit_unit = |this: &mut LoweringContext<'_, 'hir>| {
1100+
this.expr(contract.span, hir::ExprKind::Tup(&[]))
1101+
};
1102+
1103+
let precond: hir::Stmt<'hir> = if let Some(req) = contract.requires {
1104+
let lowered_req = this.lower_expr_mut(&req);
1105+
let precond = this.expr_call_lang_item_fn_mut(
1106+
req.span,
1107+
hir::LangItem::ContractCheckRequires,
1108+
&*arena_vec![this; lowered_req],
1109+
);
1110+
this.stmt_expr(req.span, precond)
1111+
} else {
1112+
let u = lit_unit(this);
1113+
this.stmt_expr(contract.span, u)
1114+
};
1115+
1116+
let (postcond_checker, result) = if let Some(ens) = contract.ensures {
1117+
let crate::FnContractLoweringEnsures { expr: ens, fresh_ident } = ens;
1118+
let lowered_ens: hir::Expr<'hir> = this.lower_expr_mut(&ens);
1119+
let postcond_checker = this.expr_call_lang_item_fn(
1120+
ens.span,
1121+
hir::LangItem::ContractBuildCheckEnsures,
1122+
&*arena_vec![this; lowered_ens],
1123+
);
1124+
let checker_binding_pat = fresh_ident.1;
1125+
(
1126+
this.stmt_let_pat(
1127+
None,
1128+
ens.span,
1129+
Some(postcond_checker),
1130+
this.arena.alloc(checker_binding_pat),
1131+
hir::LocalSource::Contract,
1132+
),
1133+
this.inject_ensures_check(result_ref, ens.span, fresh_ident.0, fresh_ident.2),
1134+
)
1135+
} else {
1136+
let u = lit_unit(this);
1137+
(this.stmt_expr(contract.span, u), &*result_ref)
1138+
};
1139+
1140+
let block = this.block_all(
1141+
contract.span,
1142+
arena_vec![this; precond, postcond_checker],
1143+
Some(result),
1144+
);
1145+
(params, this.expr_block(block))
10611146
})
10621147
}
10631148

compiler/rustc_ast_lowering/src/lib.rs

+20
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,19 @@ mod path;
8686

8787
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
8888

89+
#[derive(Debug, Clone)]
90+
struct FnContractLoweringInfo<'hir> {
91+
pub span: Span,
92+
pub requires: Option<ast::ptr::P<ast::Expr>>,
93+
pub ensures: Option<FnContractLoweringEnsures<'hir>>,
94+
}
95+
96+
#[derive(Debug, Clone)]
97+
struct FnContractLoweringEnsures<'hir> {
98+
expr: ast::ptr::P<ast::Expr>,
99+
fresh_ident: (Ident, hir::Pat<'hir>, HirId),
100+
}
101+
89102
struct LoweringContext<'a, 'hir> {
90103
tcx: TyCtxt<'hir>,
91104
resolver: &'a mut ResolverAstLowering,
@@ -100,6 +113,8 @@ struct LoweringContext<'a, 'hir> {
100113
/// Collect items that were created by lowering the current owner.
101114
children: Vec<(LocalDefId, hir::MaybeOwner<'hir>)>,
102115

116+
contract: Option<FnContractLoweringInfo<'hir>>,
117+
103118
coroutine_kind: Option<hir::CoroutineKind>,
104119

105120
/// When inside an `async` context, this is the `HirId` of the
@@ -148,6 +163,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
148163
bodies: Vec::new(),
149164
attrs: SortedMap::default(),
150165
children: Vec::default(),
166+
contract: None,
151167
current_hir_id_owner: hir::CRATE_OWNER_ID,
152168
item_local_id_counter: hir::ItemLocalId::ZERO,
153169
ident_and_label_to_local_id: Default::default(),
@@ -834,12 +850,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
834850
let was_in_loop_condition = self.is_in_loop_condition;
835851
self.is_in_loop_condition = false;
836852

853+
let old_contract = self.contract.take();
854+
837855
let catch_scope = self.catch_scope.take();
838856
let loop_scope = self.loop_scope.take();
839857
let ret = f(self);
840858
self.catch_scope = catch_scope;
841859
self.loop_scope = loop_scope;
842860

861+
self.contract = old_contract;
862+
843863
self.is_in_loop_condition = was_in_loop_condition;
844864

845865
self.current_item = current_item;

compiler/rustc_ast_passes/src/ast_validation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -917,7 +917,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
917917
walk_list!(self, visit_attribute, &item.attrs);
918918
return; // Avoid visiting again.
919919
}
920-
ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, body }) => {
920+
ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, contract: _, body }) => {
921921
self.check_defaultness(item.span, *defaultness);
922922

923923
let is_intrinsic =

compiler/rustc_ast_passes/src/feature_gate.rs

+2
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
548548
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
549549
gate_all!(unsafe_fields, "`unsafe` fields are experimental");
550550
gate_all!(unsafe_binders, "unsafe binder types are experimental");
551+
gate_all!(contracts, "contracts are incomplete");
552+
gate_all!(contracts_internals, "contract internal machinery is for internal use only");
551553

552554
if !visitor.features.never_patterns() {
553555
if let Some(spans) = spans.get(&sym::never_patterns) {

0 commit comments

Comments
 (0)