Desugars contract into the internal AST extensions
Check ensures on early return due to Try / Yeet Expand these two expressions to include a call to contract checking
This commit is contained in:
parent
38eff16d0a
commit
ae7eff0be5
12 changed files with 457 additions and 88 deletions
|
@ -314,21 +314,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label))
|
||||
}
|
||||
ExprKind::Ret(e) => {
|
||||
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)
|
||||
let expr = e.as_ref().map(|x| self.lower_expr(x));
|
||||
self.checked_return(expr)
|
||||
}
|
||||
ExprKind::Yeet(sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
|
||||
ExprKind::Become(sub_expr) => {
|
||||
|
@ -395,6 +382,32 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Create an `ExprKind::Ret` that is preceded by a call to check contract ensures clause.
|
||||
fn checked_return(&mut self, opt_expr: Option<&'hir hir::Expr<'hir>>) -> hir::ExprKind<'hir> {
|
||||
let checked_ret = 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 expr = opt_expr.unwrap_or_else(|| self.expr_unit(span));
|
||||
Some(self.inject_ensures_check(expr, span, fresh_ident.0, fresh_ident.2))
|
||||
} else {
|
||||
opt_expr
|
||||
};
|
||||
hir::ExprKind::Ret(checked_ret)
|
||||
}
|
||||
|
||||
/// Wraps an expression with a call to the ensures check before it gets returned.
|
||||
pub(crate) fn inject_ensures_check(
|
||||
&mut self,
|
||||
expr: &'hir hir::Expr<'hir>,
|
||||
span: Span,
|
||||
check_ident: Ident,
|
||||
check_hir_id: HirId,
|
||||
) -> &'hir hir::Expr<'hir> {
|
||||
let checker_fn = self.expr_ident(span, check_ident, check_hir_id);
|
||||
let span = self.mark_span_with_reason(DesugaringKind::Contract, span, None);
|
||||
self.expr_call(span, checker_fn, std::slice::from_ref(expr))
|
||||
}
|
||||
|
||||
pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock {
|
||||
self.with_new_scopes(c.value.span, |this| {
|
||||
let def_id = this.local_def_id(c.id);
|
||||
|
@ -1983,7 +1996,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
),
|
||||
))
|
||||
} else {
|
||||
self.arena.alloc(self.expr(try_span, hir::ExprKind::Ret(Some(from_residual_expr))))
|
||||
let ret_expr = self.checked_return(Some(from_residual_expr));
|
||||
self.arena.alloc(self.expr(try_span, ret_expr))
|
||||
};
|
||||
self.lower_attrs(ret_expr.hir_id, &attrs);
|
||||
|
||||
|
@ -2032,7 +2046,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let target_id = Ok(catch_id);
|
||||
hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr))
|
||||
} else {
|
||||
hir::ExprKind::Ret(Some(from_yeet_expr))
|
||||
self.checked_return(Some(from_yeet_expr))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -215,7 +215,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
if let Some(contract) = contract {
|
||||
let requires = contract.requires.clone();
|
||||
let ensures = contract.ensures.clone();
|
||||
let ensures = if let Some(ens) = ensures {
|
||||
let ensures = ensures.map(|ens| {
|
||||
// FIXME: this needs to be a fresh (or illegal) identifier to prevent
|
||||
// accidental capture of a parameter or global variable.
|
||||
let checker_ident: Ident =
|
||||
|
@ -226,13 +226,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::BindingMode::NONE,
|
||||
);
|
||||
|
||||
Some(crate::FnContractLoweringEnsures {
|
||||
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.
|
||||
|
@ -1095,73 +1093,56 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
|
||||
// { 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
|
||||
// { contract_requires(PRECOND); { body } }
|
||||
let Some(contract) = opt_contract else { return (params, result) };
|
||||
let result_ref = this.arena.alloc(result);
|
||||
let lit_unit = |this: &mut LoweringContext<'_, 'hir>| {
|
||||
this.expr(contract.span, hir::ExprKind::Tup(&[]))
|
||||
};
|
||||
|
||||
(params, result)
|
||||
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,
|
||||
),
|
||||
this.inject_ensures_check(result_ref, ens.span, fresh_ident.0, fresh_ident.2),
|
||||
)
|
||||
} else {
|
||||
let u = lit_unit(this);
|
||||
(this.stmt_expr(contract.span, u), &*result_ref)
|
||||
};
|
||||
|
||||
let block = this.block_all(
|
||||
contract.span,
|
||||
arena_vec![this; precond, postcond_checker],
|
||||
Some(result),
|
||||
);
|
||||
(params, this.expr_block(block))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue