Skip to content

Commit 519c326

Browse files
committed
intermediate step: express contracts as part of function header and lower it into the previously added contract lang items.
1 parent 58b9e67 commit 519c326

File tree

28 files changed

+463
-53
lines changed

28 files changed

+463
-53
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3335,11 +3335,18 @@ pub struct Impl {
33353335
pub items: ThinVec<P<AssocItem>>,
33363336
}
33373337

3338+
#[derive(Clone, Encodable, Decodable, Debug, Default)]
3339+
pub struct FnContract {
3340+
pub requires: Option<P<Expr>>,
3341+
pub ensures: Option<P<Expr>>,
3342+
}
3343+
33383344
#[derive(Clone, Encodable, Decodable, Debug)]
33393345
pub struct Fn {
33403346
pub defaultness: Defaultness,
33413347
pub generics: Generics,
33423348
pub sig: FnSig,
3349+
pub contract: Option<P<FnContract>>,
33433350
pub body: Option<P<Block>>,
33443351
}
33453352

@@ -3637,7 +3644,7 @@ mod size_asserts {
36373644
static_assert_size!(Block, 32);
36383645
static_assert_size!(Expr, 72);
36393646
static_assert_size!(ExprKind, 40);
3640-
static_assert_size!(Fn, 160);
3647+
static_assert_size!(Fn, 168);
36413648
static_assert_size!(ForeignItem, 88);
36423649
static_assert_size!(ForeignItemKind, 16);
36433650
static_assert_size!(GenericArg, 24);

compiler/rustc_ast/src/mut_visit.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@ pub trait MutVisitor: Sized {
144144
walk_flat_map_assoc_item(self, i, ctxt)
145145
}
146146

147+
fn visit_contract(&mut self, c: &mut P<FnContract>) {
148+
walk_contract(self, c);
149+
}
150+
147151
fn visit_fn_decl(&mut self, d: &mut P<FnDecl>) {
148152
walk_fn_decl(self, d);
149153
}
@@ -969,6 +973,16 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
969973
}
970974
}
971975

976+
fn walk_contract<T: MutVisitor>(vis: &mut T, contract: &mut P<FnContract>) {
977+
let FnContract { requires, ensures } = contract.deref_mut();
978+
if let Some(pred) = requires {
979+
vis.visit_expr(pred);
980+
}
981+
if let Some(pred) = ensures {
982+
vis.visit_expr(pred);
983+
}
984+
}
985+
972986
fn walk_fn_decl<T: MutVisitor>(vis: &mut T, decl: &mut P<FnDecl>) {
973987
let FnDecl { inputs, output } = decl.deref_mut();
974988
inputs.flat_map_in_place(|param| vis.flat_map_param(param));
@@ -1200,8 +1214,11 @@ impl WalkItemKind for ItemKind {
12001214
ItemKind::Const(item) => {
12011215
visit_const_item(item, vis);
12021216
}
1203-
ItemKind::Fn(box Fn { defaultness, generics, sig, body }) => {
1217+
ItemKind::Fn(box Fn { defaultness, generics, sig, contract, body }) => {
12041218
visit_defaultness(vis, defaultness);
1219+
if let Some(contract) = contract {
1220+
vis.visit_contract(contract)
1221+
};
12051222
vis.visit_fn(
12061223
FnKind::Fn(FnCtxt::Free, ident, sig, visibility, generics, body),
12071224
span,
@@ -1319,8 +1336,11 @@ impl WalkItemKind for AssocItemKind {
13191336
AssocItemKind::Const(item) => {
13201337
visit_const_item(item, visitor);
13211338
}
1322-
AssocItemKind::Fn(box Fn { defaultness, generics, sig, body }) => {
1339+
AssocItemKind::Fn(box Fn { defaultness, generics, sig, contract, body }) => {
13231340
visit_defaultness(visitor, defaultness);
1341+
if let Some(contract) = contract {
1342+
visitor.visit_contract(contract);
1343+
}
13241344
visitor.visit_fn(
13251345
FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, visibility, generics, body),
13261346
span,
@@ -1466,8 +1486,11 @@ impl WalkItemKind for ForeignItemKind {
14661486
visitor.visit_ty(ty);
14671487
visit_opt(expr, |expr| visitor.visit_expr(expr));
14681488
}
1469-
ForeignItemKind::Fn(box Fn { defaultness, generics, sig, body }) => {
1489+
ForeignItemKind::Fn(box Fn { defaultness, generics, sig, contract, body }) => {
14701490
visit_defaultness(visitor, defaultness);
1491+
if let Some(contract) = contract {
1492+
visitor.visit_contract(contract);
1493+
}
14711494
visitor.visit_fn(
14721495
FnKind::Fn(FnCtxt::Foreign, ident, sig, visibility, generics, body),
14731496
span,

compiler/rustc_ast/src/visit.rs

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,15 @@ impl BoundKind {
6666
#[derive(Copy, Clone, Debug)]
6767
pub enum FnKind<'a> {
6868
/// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
69-
Fn(FnCtxt, &'a Ident, &'a FnSig, &'a Visibility, &'a Generics, &'a Option<P<Block>>),
69+
Fn(
70+
FnCtxt,
71+
&'a Ident,
72+
&'a FnSig,
73+
&'a Visibility,
74+
&'a Generics,
75+
&'a Option<P<FnContract>>,
76+
&'a Option<P<Block>>,
77+
),
7078

7179
/// E.g., `|x, y| body`.
7280
Closure(&'a ClosureBinder, &'a Option<CoroutineKind>, &'a FnDecl, &'a Expr),
@@ -75,7 +83,7 @@ pub enum FnKind<'a> {
7583
impl<'a> FnKind<'a> {
7684
pub fn header(&self) -> Option<&'a FnHeader> {
7785
match *self {
78-
FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header),
86+
FnKind::Fn(_, _, sig, _, _, _, _) => Some(&sig.header),
7987
FnKind::Closure(..) => None,
8088
}
8189
}
@@ -89,7 +97,7 @@ impl<'a> FnKind<'a> {
8997

9098
pub fn decl(&self) -> &'a FnDecl {
9199
match self {
92-
FnKind::Fn(_, _, sig, _, _, _) => &sig.decl,
100+
FnKind::Fn(_, _, sig, _, _, _, _) => &sig.decl,
93101
FnKind::Closure(_, _, decl, _) => decl,
94102
}
95103
}
@@ -189,6 +197,9 @@ pub trait Visitor<'ast>: Sized {
189197
fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) -> Self::Result {
190198
walk_closure_binder(self, b)
191199
}
200+
fn visit_contract(&mut self, c: &'ast FnContract) -> Self::Result {
201+
walk_contract(self, c)
202+
}
192203
fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result {
193204
walk_where_predicate(self, p)
194205
}
@@ -375,8 +386,8 @@ impl WalkItemKind for ItemKind {
375386
try_visit!(visitor.visit_ty(ty));
376387
visit_opt!(visitor, visit_expr, expr);
377388
}
378-
ItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => {
379-
let kind = FnKind::Fn(FnCtxt::Free, ident, sig, vis, generics, body);
389+
ItemKind::Fn(box Fn { defaultness: _, generics, sig, contract, body }) => {
390+
let kind = FnKind::Fn(FnCtxt::Free, ident, sig, vis, generics, contract, body);
380391
try_visit!(visitor.visit_fn(kind, span, id));
381392
}
382393
ItemKind::Mod(_unsafety, mod_kind) => match mod_kind {
@@ -708,8 +719,8 @@ impl WalkItemKind for ForeignItemKind {
708719
try_visit!(visitor.visit_ty(ty));
709720
visit_opt!(visitor, visit_expr, expr);
710721
}
711-
ForeignItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => {
712-
let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, generics, body);
722+
ForeignItemKind::Fn(box Fn { defaultness: _, generics, sig, contract, body }) => {
723+
let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, generics, contract, body);
713724
try_visit!(visitor.visit_fn(kind, span, id));
714725
}
715726
ForeignItemKind::TyAlias(box TyAlias {
@@ -793,6 +804,17 @@ pub fn walk_closure_binder<'a, V: Visitor<'a>>(
793804
V::Result::output()
794805
}
795806

807+
pub fn walk_contract<'a, V: Visitor<'a>>(visitor: &mut V, c: &'a FnContract) -> V::Result {
808+
let FnContract { requires, ensures } = c;
809+
if let Some(pred) = requires {
810+
visitor.visit_expr(pred);
811+
}
812+
if let Some(pred) = ensures {
813+
visitor.visit_expr(pred);
814+
}
815+
V::Result::output()
816+
}
817+
796818
pub fn walk_where_predicate<'a, V: Visitor<'a>>(
797819
visitor: &mut V,
798820
predicate: &'a WherePredicate,
@@ -851,11 +873,20 @@ pub fn walk_fn_decl<'a, V: Visitor<'a>>(
851873

852874
pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Result {
853875
match kind {
854-
FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span: _ }, _vis, generics, body) => {
876+
FnKind::Fn(
877+
_ctxt,
878+
_ident,
879+
FnSig { header, decl, span: _ },
880+
_vis,
881+
generics,
882+
contract,
883+
body,
884+
) => {
855885
// Identifier and visibility are visited as a part of the item.
856886
try_visit!(visitor.visit_fn_header(header));
857887
try_visit!(visitor.visit_generics(generics));
858888
try_visit!(visitor.visit_fn_decl(decl));
889+
visit_opt!(visitor, visit_contract, contract);
859890
visit_opt!(visitor, visit_block, body);
860891
}
861892
FnKind::Closure(binder, coroutine_kind, decl, body) => {
@@ -885,8 +916,9 @@ impl WalkItemKind for AssocItemKind {
885916
try_visit!(visitor.visit_ty(ty));
886917
visit_opt!(visitor, visit_expr, expr);
887918
}
888-
AssocItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => {
889-
let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, generics, body);
919+
AssocItemKind::Fn(box Fn { defaultness: _, generics, sig, contract, body }) => {
920+
let kind =
921+
FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, generics, contract, body);
890922
try_visit!(visitor.visit_fn(kind, span, id));
891923
}
892924
AssocItemKind::Type(box TyAlias {

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
336336
hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label))
337337
}
338338
ExprKind::Ret(e) => {
339-
let e = e.as_ref().map(|x| self.lower_expr(x));
339+
let mut e = e.as_ref().map(|x| self.lower_expr(x));
340+
if let Some(Some((span, fresh_ident))) = self
341+
.contract
342+
.as_ref()
343+
.map(|c| c.ensures.as_ref().map(|e| (e.expr.span, e.fresh_ident)))
344+
{
345+
let checker_fn = self.expr_ident(span, fresh_ident.0, fresh_ident.2);
346+
let args = if let Some(e) = e {
347+
std::slice::from_ref(e)
348+
} else {
349+
std::slice::from_ref(self.expr_unit(span))
350+
};
351+
e = Some(self.expr_call(span, checker_fn, args));
352+
}
340353
hir::ExprKind::Ret(e)
341354
}
342355
ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
@@ -2116,7 +2129,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
21162129
self.arena.alloc(self.expr_call_mut(span, e, args))
21172130
}
21182131

2119-
fn expr_call_lang_item_fn_mut(
2132+
pub(super) fn expr_call_lang_item_fn_mut(
21202133
&mut self,
21212134
span: Span,
21222135
lang_item: hir::LangItem,
@@ -2126,7 +2139,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
21262139
self.expr_call_mut(span, path, args)
21272140
}
21282141

2129-
fn expr_call_lang_item_fn(
2142+
pub(super) fn expr_call_lang_item_fn(
21302143
&mut self,
21312144
span: Span,
21322145
lang_item: hir::LangItem,

compiler/rustc_ast_lowering/src/item.rs

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,38 @@ impl<'hir> LoweringContext<'_, 'hir> {
205205
sig: FnSig { decl, header, span: fn_sig_span },
206206
generics,
207207
body,
208+
contract,
208209
..
209210
}) => {
211+
if let Some(contract) = contract {
212+
assert!(self.contract.is_none());
213+
let requires = contract.requires.clone();
214+
let ensures = contract.ensures.clone();
215+
let ensures = if let Some(ens) = ensures {
216+
// FIXME: this needs to be a fresh (or illegal) identifier to prevent
217+
// accidental capture of a parameter or global variable.
218+
let checker_ident: Ident =
219+
Ident::from_str_and_span("__ensures_checker", ens.span);
220+
let (checker_pat, checker_hir_id) = self.pat_ident_binding_mode_mut(
221+
ens.span,
222+
checker_ident,
223+
hir::BindingMode::NONE,
224+
);
225+
226+
Some(crate::FnContractLoweringEnsures {
227+
expr: ens,
228+
fresh_ident: (checker_ident, checker_pat, checker_hir_id),
229+
})
230+
} else {
231+
None
232+
};
233+
234+
self.contract.replace(crate::FnContractLoweringInfo {
235+
span,
236+
requires,
237+
ensures,
238+
});
239+
}
210240
self.with_new_scopes(*fn_sig_span, |this| {
211241
// Note: we don't need to change the return type from `T` to
212242
// `impl Future<Output = T>` here because lower_body
@@ -1030,10 +1060,82 @@ impl<'hir> LoweringContext<'_, 'hir> {
10301060
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
10311061
) -> hir::BodyId {
10321062
self.lower_body(|this| {
1033-
(
1034-
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))),
1035-
body(this),
1036-
)
1063+
let params =
1064+
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));
1065+
let result = body(this);
1066+
1067+
let contract = this.contract.take();
1068+
1069+
// { body }
1070+
// ==>
1071+
// { rustc_contract_requires(PRECOND); { body } }
1072+
let result: hir::Expr<'hir> = if let Some(_contract) = contract {
1073+
let lit_unit = |this: &mut LoweringContext<'_, 'hir>| {
1074+
this.expr(_contract.span, hir::ExprKind::Tup(&[]))
1075+
};
1076+
1077+
let precond: hir::Stmt<'hir> = if let Some(req) = _contract.requires {
1078+
let lowered_req = this.lower_expr_mut(&req);
1079+
let precond = this.expr_call_lang_item_fn_mut(
1080+
req.span,
1081+
hir::LangItem::ContractCheckRequires,
1082+
&*arena_vec![this; lowered_req],
1083+
);
1084+
this.stmt_expr(req.span, precond)
1085+
} else {
1086+
let u = lit_unit(this);
1087+
this.stmt_expr(_contract.span, u)
1088+
};
1089+
1090+
let (postcond_checker, _opt_ident, result) = if let Some(ens) = _contract.ensures {
1091+
let crate::FnContractLoweringEnsures { expr: ens, fresh_ident } = ens;
1092+
let lowered_ens: hir::Expr<'hir> = this.lower_expr_mut(&ens);
1093+
let postcond_checker = this.expr_call_lang_item_fn(
1094+
ens.span,
1095+
hir::LangItem::ContractBuildCheckEnsures,
1096+
&*arena_vec![this; lowered_ens],
1097+
);
1098+
let checker_binding_pat = fresh_ident.1;
1099+
(
1100+
this.stmt_let_pat(
1101+
None,
1102+
ens.span,
1103+
Some(postcond_checker),
1104+
this.arena.alloc(checker_binding_pat),
1105+
hir::LocalSource::Contract,
1106+
),
1107+
Some((fresh_ident.0, fresh_ident.2)),
1108+
{
1109+
let checker_fn =
1110+
this.expr_ident(ens.span, fresh_ident.0, fresh_ident.2);
1111+
let span = this.mark_span_with_reason(
1112+
DesugaringKind::Contract,
1113+
ens.span,
1114+
None,
1115+
);
1116+
this.expr_call_mut(
1117+
span,
1118+
checker_fn,
1119+
std::slice::from_ref(this.arena.alloc(result)),
1120+
)
1121+
},
1122+
)
1123+
} else {
1124+
let u = lit_unit(this);
1125+
(this.stmt_expr(_contract.span, u), None, result)
1126+
};
1127+
1128+
let block = this.block_all(
1129+
_contract.span,
1130+
arena_vec![this; precond, postcond_checker],
1131+
Some(this.arena.alloc(result)),
1132+
);
1133+
this.expr_block(block)
1134+
} else {
1135+
result
1136+
};
1137+
1138+
(params, result)
10371139
})
10381140
}
10391141

0 commit comments

Comments
 (0)