Skip to content

Commit 69482e8

Browse files
committed
Auto merge of #136851 - jhpratt:rollup-ftijn95, r=jhpratt
Rollup of 11 pull requests Successful merges: - #136606 (Fix long lines which rustfmt fails to format) - #136663 (Stabilize `NonZero::count_ones`) - #136672 (library: doc: core::alloc::Allocator: trivial typo fix) - #136704 (Improve examples for file locking) - #136721 (cg_llvm: Reduce visibility of some items outside the `llvm` module) - #136813 (rustc_target: Add the fp16 target feature for AArch32) - #136830 (fix i686-unknown-hurd-gnu x87 footnote) - #136832 (Fix platform support table for i686-unknown-uefi) - #136835 (Stop using span hack for contracts feature gating) - #136837 (Overhaul how contracts are lowered on fn-like bodies) - #136839 (fix ensure_monomorphic_enough) r? `@ghost` `@rustbot` modify labels: rollup
2 parents c182ce9 + 2130310 commit 69482e8

File tree

27 files changed

+241
-336
lines changed

27 files changed

+241
-336
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+11-10
Original file line numberDiff line numberDiff line change
@@ -379,16 +379,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
379379
})
380380
}
381381

382-
/// Create an `ExprKind::Ret` that is preceded by a call to check contract ensures clause.
382+
/// Create an `ExprKind::Ret` that is optionally wrapped by a call to check
383+
/// a contract ensures clause, if it exists.
383384
fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> {
384-
let checked_ret = if let Some(Some((span, fresh_ident))) =
385-
self.contract.as_ref().map(|c| c.ensures.as_ref().map(|e| (e.expr.span, e.fresh_ident)))
386-
{
387-
let expr = opt_expr.unwrap_or_else(|| self.expr_unit(span));
388-
Some(self.inject_ensures_check(expr, span, fresh_ident.0, fresh_ident.2))
389-
} else {
390-
opt_expr
391-
};
385+
let checked_ret =
386+
if let Some((check_span, check_ident, check_hir_id)) = self.contract_ensures {
387+
let expr = opt_expr.unwrap_or_else(|| self.expr_unit(check_span));
388+
Some(self.inject_ensures_check(expr, check_span, check_ident, check_hir_id))
389+
} else {
390+
opt_expr
391+
};
392392
hir::ExprKind::Ret(checked_ret)
393393
}
394394

@@ -1090,7 +1090,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
10901090
} else {
10911091
None
10921092
};
1093-
let body_id = this.lower_fn_body(decl, |this| {
1093+
// FIXME(contracts): Support contracts on closures?
1094+
let body_id = this.lower_fn_body(decl, None, |this| {
10941095
this.coroutine_kind = coroutine_kind;
10951096
let e = this.lower_expr_mut(body);
10961097
coroutine_kind = this.coroutine_kind;

compiler/rustc_ast_lowering/src/item.rs

+80-86
Original file line numberDiff line numberDiff line change
@@ -211,36 +211,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
211211
..
212212
}) => {
213213
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-
244214
// Note: we don't need to change the return type from `T` to
245215
// `impl Future<Output = T>` here because lower_body
246216
// only cares about the input argument patterns in the function
@@ -254,6 +224,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
254224
coroutine_kind,
255225
body.as_deref(),
256226
attrs,
227+
contract.as_deref(),
257228
);
258229

259230
let itctx = ImplTraitContext::Universal;
@@ -803,6 +774,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
803774
(generics, kind, expr.is_some())
804775
}
805776
AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => {
777+
// FIXME(contracts): Deny contract here since it won't apply to
778+
// any impl method or callees.
806779
let names = self.lower_fn_params_to_names(&sig.decl);
807780
let (generics, sig) = self.lower_method_sig(
808781
generics,
@@ -814,7 +787,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
814787
);
815788
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
816789
}
817-
AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => {
790+
AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), contract, .. }) => {
818791
let body_id = self.lower_maybe_coroutine_body(
819792
sig.span,
820793
i.span,
@@ -823,6 +796,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
823796
sig.header.coroutine_kind,
824797
Some(body),
825798
attrs,
799+
contract.as_deref(),
826800
);
827801
let (generics, sig) = self.lower_method_sig(
828802
generics,
@@ -931,7 +905,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
931905
hir::ImplItemKind::Const(ty, body)
932906
},
933907
),
934-
AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => {
908+
AssocItemKind::Fn(box Fn { sig, generics, body, contract, .. }) => {
935909
let body_id = self.lower_maybe_coroutine_body(
936910
sig.span,
937911
i.span,
@@ -940,6 +914,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
940914
sig.header.coroutine_kind,
941915
body.as_deref(),
942916
attrs,
917+
contract.as_deref(),
943918
);
944919
let (generics, sig) = self.lower_method_sig(
945920
generics,
@@ -1088,72 +1063,89 @@ impl<'hir> LoweringContext<'_, 'hir> {
10881063
pub(super) fn lower_fn_body(
10891064
&mut self,
10901065
decl: &FnDecl,
1066+
contract: Option<&FnContract>,
10911067
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
10921068
) -> hir::BodyId {
10931069
self.lower_body(|this| {
10941070
let params =
10951071
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));
1096-
let result = body(this);
1097-
1098-
let opt_contract = this.contract.take();
10991072

1073+
// Optionally lower the fn contract, which turns:
1074+
//
11001075
// { body }
1101-
// ==>
1102-
// { contract_requires(PRECOND); { body } }
1103-
let Some(contract) = opt_contract else { return (params, result) };
1104-
let result_ref = this.arena.alloc(result);
1105-
let lit_unit = |this: &mut LoweringContext<'_, 'hir>| {
1106-
this.expr(contract.span, hir::ExprKind::Tup(&[]))
1107-
};
1108-
1109-
let precond: hir::Stmt<'hir> = if let Some(req) = contract.requires {
1110-
let lowered_req = this.lower_expr_mut(&req);
1111-
let precond = this.expr_call_lang_item_fn_mut(
1112-
req.span,
1113-
hir::LangItem::ContractCheckRequires,
1114-
&*arena_vec![this; lowered_req],
1115-
);
1116-
this.stmt_expr(req.span, precond)
1117-
} else {
1118-
let u = lit_unit(this);
1119-
this.stmt_expr(contract.span, u)
1120-
};
1121-
1122-
let (postcond_checker, result) = if let Some(ens) = contract.ensures {
1123-
let crate::FnContractLoweringEnsures { expr: ens, fresh_ident } = ens;
1124-
let lowered_ens: hir::Expr<'hir> = this.lower_expr_mut(&ens);
1125-
let postcond_checker = this.expr_call_lang_item_fn(
1126-
ens.span,
1127-
hir::LangItem::ContractBuildCheckEnsures,
1128-
&*arena_vec![this; lowered_ens],
1129-
);
1130-
let checker_binding_pat = fresh_ident.1;
1131-
(
1132-
this.stmt_let_pat(
1076+
//
1077+
// into:
1078+
//
1079+
// { contract_requires(PRECOND); let __postcond = |ret_val| POSTCOND; postcond({ body }) }
1080+
if let Some(contract) = contract {
1081+
let precond = if let Some(req) = &contract.requires {
1082+
// Lower the precondition check intrinsic.
1083+
let lowered_req = this.lower_expr_mut(&req);
1084+
let precond = this.expr_call_lang_item_fn_mut(
1085+
req.span,
1086+
hir::LangItem::ContractCheckRequires,
1087+
&*arena_vec![this; lowered_req],
1088+
);
1089+
Some(this.stmt_expr(req.span, precond))
1090+
} else {
1091+
None
1092+
};
1093+
let (postcond, body) = if let Some(ens) = &contract.ensures {
1094+
let ens_span = this.lower_span(ens.span);
1095+
// Set up the postcondition `let` statement.
1096+
let check_ident: Ident =
1097+
Ident::from_str_and_span("__ensures_checker", ens_span);
1098+
let (checker_pat, check_hir_id) = this.pat_ident_binding_mode_mut(
1099+
ens_span,
1100+
check_ident,
1101+
hir::BindingMode::NONE,
1102+
);
1103+
let lowered_ens = this.lower_expr_mut(&ens);
1104+
let postcond_checker = this.expr_call_lang_item_fn(
1105+
ens_span,
1106+
hir::LangItem::ContractBuildCheckEnsures,
1107+
&*arena_vec![this; lowered_ens],
1108+
);
1109+
let postcond = this.stmt_let_pat(
11331110
None,
1134-
ens.span,
1111+
ens_span,
11351112
Some(postcond_checker),
1136-
this.arena.alloc(checker_binding_pat),
1113+
this.arena.alloc(checker_pat),
11371114
hir::LocalSource::Contract,
1138-
),
1139-
this.inject_ensures_check(result_ref, ens.span, fresh_ident.0, fresh_ident.2),
1140-
)
1141-
} else {
1142-
let u = lit_unit(this);
1143-
(this.stmt_expr(contract.span, u), &*result_ref)
1144-
};
1115+
);
11451116

1146-
let block = this.block_all(
1147-
contract.span,
1148-
arena_vec![this; precond, postcond_checker],
1149-
Some(result),
1150-
);
1151-
(params, this.expr_block(block))
1117+
// Install contract_ensures so we will intercept `return` statements,
1118+
// then lower the body.
1119+
this.contract_ensures = Some((ens_span, check_ident, check_hir_id));
1120+
let body = this.arena.alloc(body(this));
1121+
1122+
// Finally, inject an ensures check on the implicit return of the body.
1123+
let body = this.inject_ensures_check(body, ens_span, check_ident, check_hir_id);
1124+
(Some(postcond), body)
1125+
} else {
1126+
let body = &*this.arena.alloc(body(this));
1127+
(None, body)
1128+
};
1129+
// Flatten the body into precond, then postcond, then wrapped body.
1130+
let wrapped_body = this.block_all(
1131+
body.span,
1132+
this.arena.alloc_from_iter([precond, postcond].into_iter().flatten()),
1133+
Some(body),
1134+
);
1135+
(params, this.expr_block(wrapped_body))
1136+
} else {
1137+
(params, body(this))
1138+
}
11521139
})
11531140
}
11541141

1155-
fn lower_fn_body_block(&mut self, decl: &FnDecl, body: &Block) -> hir::BodyId {
1156-
self.lower_fn_body(decl, |this| this.lower_block_expr(body))
1142+
fn lower_fn_body_block(
1143+
&mut self,
1144+
decl: &FnDecl,
1145+
body: &Block,
1146+
contract: Option<&FnContract>,
1147+
) -> hir::BodyId {
1148+
self.lower_fn_body(decl, contract, |this| this.lower_block_expr(body))
11571149
}
11581150

11591151
pub(super) fn lower_const_body(&mut self, span: Span, expr: Option<&Expr>) -> hir::BodyId {
@@ -1179,12 +1171,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
11791171
coroutine_kind: Option<CoroutineKind>,
11801172
body: Option<&Block>,
11811173
attrs: &'hir [hir::Attribute],
1174+
contract: Option<&FnContract>,
11821175
) -> hir::BodyId {
11831176
let Some(body) = body else {
11841177
// Functions without a body are an error, except if this is an intrinsic. For those we
11851178
// create a fake body so that the entire rest of the compiler doesn't have to deal with
11861179
// this as a special case.
1187-
return self.lower_fn_body(decl, |this| {
1180+
return self.lower_fn_body(decl, contract, |this| {
11881181
if attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic) {
11891182
let span = this.lower_span(span);
11901183
let empty_block = hir::Block {
@@ -1209,8 +1202,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
12091202
};
12101203
let Some(coroutine_kind) = coroutine_kind else {
12111204
// Typical case: not a coroutine.
1212-
return self.lower_fn_body_block(decl, body);
1205+
return self.lower_fn_body_block(decl, body, contract);
12131206
};
1207+
// FIXME(contracts): Support contracts on async fn.
12141208
self.lower_body(|this| {
12151209
let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
12161210
decl,

compiler/rustc_ast_lowering/src/lib.rs

+4-17
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,6 @@ pub mod stability;
8888

8989
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
9090

91-
#[derive(Debug, Clone)]
92-
struct FnContractLoweringInfo<'hir> {
93-
pub span: Span,
94-
pub requires: Option<ast::ptr::P<ast::Expr>>,
95-
pub ensures: Option<FnContractLoweringEnsures<'hir>>,
96-
}
97-
98-
#[derive(Debug, Clone)]
99-
struct FnContractLoweringEnsures<'hir> {
100-
expr: ast::ptr::P<ast::Expr>,
101-
fresh_ident: (Ident, hir::Pat<'hir>, HirId),
102-
}
103-
10491
struct LoweringContext<'a, 'hir> {
10592
tcx: TyCtxt<'hir>,
10693
resolver: &'a mut ResolverAstLowering,
@@ -115,7 +102,7 @@ struct LoweringContext<'a, 'hir> {
115102
/// Collect items that were created by lowering the current owner.
116103
children: Vec<(LocalDefId, hir::MaybeOwner<'hir>)>,
117104

118-
contract: Option<FnContractLoweringInfo<'hir>>,
105+
contract_ensures: Option<(Span, Ident, HirId)>,
119106

120107
coroutine_kind: Option<hir::CoroutineKind>,
121108

@@ -165,7 +152,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
165152
bodies: Vec::new(),
166153
attrs: SortedMap::default(),
167154
children: Vec::default(),
168-
contract: None,
155+
contract_ensures: None,
169156
current_hir_id_owner: hir::CRATE_OWNER_ID,
170157
item_local_id_counter: hir::ItemLocalId::ZERO,
171158
ident_and_label_to_local_id: Default::default(),
@@ -852,15 +839,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
852839
let was_in_loop_condition = self.is_in_loop_condition;
853840
self.is_in_loop_condition = false;
854841

855-
let old_contract = self.contract.take();
842+
let old_contract = self.contract_ensures.take();
856843

857844
let catch_scope = self.catch_scope.take();
858845
let loop_scope = self.loop_scope.take();
859846
let ret = f(self);
860847
self.catch_scope = catch_scope;
861848
self.loop_scope = loop_scope;
862849

863-
self.contract = old_contract;
850+
self.contract_ensures = old_contract;
864851

865852
self.is_in_loop_condition = was_in_loop_condition;
866853

0 commit comments

Comments
 (0)