Add contract variable declarations
Contract variables can be declared in the `requires` clause and can be referenced both in `requires` and `ensures`, subject to usual borrow checking rules. This allows any setup common to both the `requires` and `ensures` clauses to only be done once.
This commit is contained in:
@@ -3700,6 +3700,9 @@ pub struct TraitImplHeader {
|
|||||||
|
|
||||||
#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
|
#[derive(Clone, Encodable, Decodable, Debug, Default, Walkable)]
|
||||||
pub struct FnContract {
|
pub struct FnContract {
|
||||||
|
/// Declarations of variables accessible both in the `requires` and
|
||||||
|
/// `ensures` clauses.
|
||||||
|
pub declarations: ThinVec<Stmt>,
|
||||||
pub requires: Option<Box<Expr>>,
|
pub requires: Option<Box<Expr>>,
|
||||||
pub ensures: Option<Box<Expr>>,
|
pub ensures: Option<Box<Expr>>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break }
|
hir::Block { hir_id, stmts, expr, rules, span: self.lower_span(b.span), targeted_by_break }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_stmts(
|
pub(super) fn lower_stmts(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut ast_stmts: &[Stmt],
|
mut ast_stmts: &[Stmt],
|
||||||
) -> (&'hir [hir::Stmt<'hir>], Option<&'hir hir::Expr<'hir>>) {
|
) -> (&'hir [hir::Stmt<'hir>], Option<&'hir hir::Expr<'hir>>) {
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>,
|
body: impl FnOnce(&mut Self) -> rustc_hir::Expr<'hir>,
|
||||||
contract: &rustc_ast::FnContract,
|
contract: &rustc_ast::FnContract,
|
||||||
) -> rustc_hir::Expr<'hir> {
|
) -> rustc_hir::Expr<'hir> {
|
||||||
|
// The order in which things are lowered is important! I.e to
|
||||||
|
// refer to variables in contract_decls from postcond/precond,
|
||||||
|
// we must lower it first!
|
||||||
|
let contract_decls = self.lower_stmts(&contract.declarations).0;
|
||||||
|
|
||||||
match (&contract.requires, &contract.ensures) {
|
match (&contract.requires, &contract.ensures) {
|
||||||
(Some(req), Some(ens)) => {
|
(Some(req), Some(ens)) => {
|
||||||
// Lower the fn contract, which turns:
|
// Lower the fn contract, which turns:
|
||||||
@@ -27,6 +32,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
// into:
|
// into:
|
||||||
//
|
//
|
||||||
// let __postcond = if contract_checks {
|
// let __postcond = if contract_checks {
|
||||||
|
// CONTRACT_DECLARATIONS;
|
||||||
// contract_check_requires(PRECOND);
|
// contract_check_requires(PRECOND);
|
||||||
// Some(|ret_val| POSTCOND)
|
// Some(|ret_val| POSTCOND)
|
||||||
// } else {
|
// } else {
|
||||||
@@ -45,8 +51,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
let precond = self.lower_precond(req);
|
let precond = self.lower_precond(req);
|
||||||
let postcond_checker = self.lower_postcond_checker(ens);
|
let postcond_checker = self.lower_postcond_checker(ens);
|
||||||
|
|
||||||
let contract_check =
|
let contract_check = self.lower_contract_check_with_postcond(
|
||||||
self.lower_contract_check_with_postcond(Some(precond), postcond_checker);
|
contract_decls,
|
||||||
|
Some(precond),
|
||||||
|
postcond_checker,
|
||||||
|
);
|
||||||
|
|
||||||
let wrapped_body =
|
let wrapped_body =
|
||||||
self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
|
self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
|
||||||
@@ -68,15 +77,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
// let ret = { body };
|
// let ret = { body };
|
||||||
//
|
//
|
||||||
// if contract_checks {
|
// if contract_checks {
|
||||||
|
// CONTRACT_DECLARATIONS;
|
||||||
// contract_check_ensures(__postcond, ret)
|
// contract_check_ensures(__postcond, ret)
|
||||||
// } else {
|
// } else {
|
||||||
// ret
|
// ret
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
let postcond_checker = self.lower_postcond_checker(ens);
|
let postcond_checker = self.lower_postcond_checker(ens);
|
||||||
let contract_check =
|
let contract_check =
|
||||||
self.lower_contract_check_with_postcond(None, postcond_checker);
|
self.lower_contract_check_with_postcond(contract_decls, None, postcond_checker);
|
||||||
|
|
||||||
let wrapped_body =
|
let wrapped_body =
|
||||||
self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
|
self.wrap_body_with_contract_check(body, contract_check, postcond_checker.span);
|
||||||
@@ -91,12 +100,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
//
|
//
|
||||||
// {
|
// {
|
||||||
// if contracts_checks {
|
// if contracts_checks {
|
||||||
|
// CONTRACT_DECLARATIONS;
|
||||||
// contract_requires(PRECOND);
|
// contract_requires(PRECOND);
|
||||||
// }
|
// }
|
||||||
// body
|
// body
|
||||||
// }
|
// }
|
||||||
let precond = self.lower_precond(req);
|
let precond = self.lower_precond(req);
|
||||||
let precond_check = self.lower_contract_check_just_precond(precond);
|
let precond_check = self.lower_contract_check_just_precond(contract_decls, precond);
|
||||||
|
|
||||||
let body = self.arena.alloc(body(self));
|
let body = self.arena.alloc(body(self));
|
||||||
|
|
||||||
@@ -145,9 +155,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
|
|
||||||
fn lower_contract_check_just_precond(
|
fn lower_contract_check_just_precond(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
contract_decls: &'hir [rustc_hir::Stmt<'hir>],
|
||||||
precond: rustc_hir::Stmt<'hir>,
|
precond: rustc_hir::Stmt<'hir>,
|
||||||
) -> rustc_hir::Stmt<'hir> {
|
) -> rustc_hir::Stmt<'hir> {
|
||||||
let stmts = self.arena.alloc_from_iter([precond].into_iter());
|
let stmts = self
|
||||||
|
.arena
|
||||||
|
.alloc_from_iter(contract_decls.into_iter().map(|d| *d).chain([precond].into_iter()));
|
||||||
|
|
||||||
let then_block_stmts = self.block_all(precond.span, stmts, None);
|
let then_block_stmts = self.block_all(precond.span, stmts, None);
|
||||||
let then_block = self.arena.alloc(self.expr_block(&then_block_stmts));
|
let then_block = self.arena.alloc(self.expr_block(&then_block_stmts));
|
||||||
@@ -164,10 +177,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||||||
|
|
||||||
fn lower_contract_check_with_postcond(
|
fn lower_contract_check_with_postcond(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
contract_decls: &'hir [rustc_hir::Stmt<'hir>],
|
||||||
precond: Option<rustc_hir::Stmt<'hir>>,
|
precond: Option<rustc_hir::Stmt<'hir>>,
|
||||||
postcond_checker: &'hir rustc_hir::Expr<'hir>,
|
postcond_checker: &'hir rustc_hir::Expr<'hir>,
|
||||||
) -> &'hir rustc_hir::Expr<'hir> {
|
) -> &'hir rustc_hir::Expr<'hir> {
|
||||||
let stmts = self.arena.alloc_from_iter(precond.into_iter());
|
let stmts = self
|
||||||
|
.arena
|
||||||
|
.alloc_from_iter(contract_decls.into_iter().map(|d| *d).chain(precond.into_iter()));
|
||||||
let span = match precond {
|
let span = match precond {
|
||||||
Some(precond) => precond.span,
|
Some(precond) => precond.span,
|
||||||
None => postcond_checker.span,
|
None => postcond_checker.span,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ impl AttrProcMacro for ExpandRequires {
|
|||||||
annotation: TokenStream,
|
annotation: TokenStream,
|
||||||
annotated: TokenStream,
|
annotated: TokenStream,
|
||||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||||
expand_requires_tts(ecx, span, annotation, annotated)
|
expand_contract_clause_tts(ecx, span, annotation, annotated, kw::ContractRequires)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ impl AttrProcMacro for ExpandEnsures {
|
|||||||
annotation: TokenStream,
|
annotation: TokenStream,
|
||||||
annotated: TokenStream,
|
annotated: TokenStream,
|
||||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||||
expand_ensures_tts(ecx, span, annotation, annotated)
|
expand_contract_clause_tts(ecx, span, annotation, annotated, kw::ContractEnsures)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,42 +130,17 @@ fn expand_contract_clause(
|
|||||||
Ok(new_tts)
|
Ok(new_tts)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_requires_tts(
|
fn expand_contract_clause_tts(
|
||||||
ecx: &mut ExtCtxt<'_>,
|
ecx: &mut ExtCtxt<'_>,
|
||||||
attr_span: Span,
|
attr_span: Span,
|
||||||
annotation: TokenStream,
|
annotation: TokenStream,
|
||||||
annotated: TokenStream,
|
annotated: TokenStream,
|
||||||
|
clause_keyword: rustc_span::Symbol,
|
||||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||||
let feature_span = ecx.with_def_site_ctxt(attr_span);
|
let feature_span = ecx.with_def_site_ctxt(attr_span);
|
||||||
expand_contract_clause(ecx, attr_span, annotated, |new_tts| {
|
expand_contract_clause(ecx, attr_span, annotated, |new_tts| {
|
||||||
new_tts.push_tree(TokenTree::Token(
|
new_tts.push_tree(TokenTree::Token(
|
||||||
token::Token::from_ast_ident(Ident::new(kw::ContractRequires, feature_span)),
|
token::Token::from_ast_ident(Ident::new(clause_keyword, feature_span)),
|
||||||
Spacing::Joint,
|
|
||||||
));
|
|
||||||
new_tts.push_tree(TokenTree::Token(
|
|
||||||
token::Token::new(token::TokenKind::OrOr, attr_span),
|
|
||||||
Spacing::Alone,
|
|
||||||
));
|
|
||||||
new_tts.push_tree(TokenTree::Delimited(
|
|
||||||
DelimSpan::from_single(attr_span),
|
|
||||||
DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden),
|
|
||||||
token::Delimiter::Brace,
|
|
||||||
annotation,
|
|
||||||
));
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expand_ensures_tts(
|
|
||||||
ecx: &mut ExtCtxt<'_>,
|
|
||||||
attr_span: Span,
|
|
||||||
annotation: TokenStream,
|
|
||||||
annotated: TokenStream,
|
|
||||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
|
||||||
let feature_span = ecx.with_def_site_ctxt(attr_span);
|
|
||||||
expand_contract_clause(ecx, attr_span, annotated, |new_tts| {
|
|
||||||
new_tts.push_tree(TokenTree::Token(
|
|
||||||
token::Token::from_ast_ident(Ident::new(kw::ContractEnsures, feature_span)),
|
|
||||||
Spacing::Joint,
|
Spacing::Joint,
|
||||||
));
|
));
|
||||||
new_tts.push_tree(TokenTree::Delimited(
|
new_tts.push_tree(TokenTree::Delimited(
|
||||||
|
|||||||
@@ -4036,6 +4036,30 @@ impl<'a> Parser<'a> {
|
|||||||
self.mk_expr(span, ExprKind::Err(guar))
|
self.mk_expr(span, ExprKind::Err(guar))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn mk_unit_expr(&self, span: Span) -> Box<Expr> {
|
||||||
|
self.mk_expr(span, ExprKind::Tup(Default::default()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn mk_closure_expr(&self, span: Span, body: Box<Expr>) -> Box<Expr> {
|
||||||
|
self.mk_expr(
|
||||||
|
span,
|
||||||
|
ast::ExprKind::Closure(Box::new(ast::Closure {
|
||||||
|
binder: rustc_ast::ClosureBinder::NotPresent,
|
||||||
|
constness: rustc_ast::Const::No,
|
||||||
|
movability: rustc_ast::Movability::Movable,
|
||||||
|
capture_clause: rustc_ast::CaptureBy::Ref,
|
||||||
|
coroutine_kind: None,
|
||||||
|
fn_decl: Box::new(rustc_ast::FnDecl {
|
||||||
|
inputs: Default::default(),
|
||||||
|
output: rustc_ast::FnRetTy::Default(span),
|
||||||
|
}),
|
||||||
|
fn_arg_span: span,
|
||||||
|
fn_decl_span: span,
|
||||||
|
body,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create expression span ensuring the span of the parent node
|
/// Create expression span ensuring the span of the parent node
|
||||||
/// is larger than the span of lhs and rhs, including the attributes.
|
/// is larger than the span of lhs and rhs, including the attributes.
|
||||||
fn mk_expr_sp(&self, lhs: &Box<Expr>, lhs_span: Span, op_span: Span, rhs_span: Span) -> Span {
|
fn mk_expr_sp(&self, lhs: &Box<Expr>, lhs_span: Span, op_span: Span, rhs_span: Span) -> Span {
|
||||||
|
|||||||
@@ -312,25 +312,48 @@ impl<'a> Parser<'a> {
|
|||||||
/// Parses an experimental fn contract
|
/// Parses an experimental fn contract
|
||||||
/// (`contract_requires(WWW) contract_ensures(ZZZ)`)
|
/// (`contract_requires(WWW) contract_ensures(ZZZ)`)
|
||||||
pub(super) fn parse_contract(&mut self) -> PResult<'a, Option<Box<ast::FnContract>>> {
|
pub(super) fn parse_contract(&mut self) -> PResult<'a, Option<Box<ast::FnContract>>> {
|
||||||
let requires = if self.eat_keyword_noexpect(exp!(ContractRequires).kw) {
|
let (declarations, requires) = self.parse_contract_requires()?;
|
||||||
self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span);
|
let ensures = self.parse_contract_ensures()?;
|
||||||
let precond = self.parse_expr()?;
|
|
||||||
Some(precond)
|
if requires.is_none() && ensures.is_none() {
|
||||||
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
None
|
Ok(Some(Box::new(ast::FnContract { declarations, requires, ensures })))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_contract_requires(
|
||||||
|
&mut self,
|
||||||
|
) -> PResult<'a, (ThinVec<rustc_ast::Stmt>, Option<Box<rustc_ast::Expr>>)> {
|
||||||
|
Ok(if self.eat_keyword_noexpect(exp!(ContractRequires).kw) {
|
||||||
|
self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span);
|
||||||
|
let mut decls_and_precond = self.parse_block()?;
|
||||||
|
|
||||||
|
let precond = match decls_and_precond.stmts.pop() {
|
||||||
|
Some(precond) => match precond.kind {
|
||||||
|
rustc_ast::StmtKind::Expr(expr) => expr,
|
||||||
|
// Insert dummy node that will be rejected by typechecker to
|
||||||
|
// avoid reinventing an error
|
||||||
|
_ => self.mk_unit_expr(decls_and_precond.span),
|
||||||
|
},
|
||||||
|
None => self.mk_unit_expr(decls_and_precond.span),
|
||||||
};
|
};
|
||||||
let ensures = if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) {
|
let precond = self.mk_closure_expr(precond.span, precond);
|
||||||
|
let decls = decls_and_precond.stmts;
|
||||||
|
(decls, Some(precond))
|
||||||
|
} else {
|
||||||
|
(Default::default(), None)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_contract_ensures(&mut self) -> PResult<'a, Option<Box<rustc_ast::Expr>>> {
|
||||||
|
Ok(if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) {
|
||||||
self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span);
|
self.psess.gated_spans.gate(sym::contracts_internals, self.prev_token.span);
|
||||||
let postcond = self.parse_expr()?;
|
let postcond = self.parse_expr()?;
|
||||||
Some(postcond)
|
Some(postcond)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
})
|
||||||
if requires.is_none() && ensures.is_none() {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
Ok(Some(Box::new(ast::FnContract { requires, ensures })))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses an optional where-clause.
|
/// Parses an optional where-clause.
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
//@ run-pass
|
||||||
|
#![feature(contracts)]
|
||||||
|
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
|
use core::contracts::requires;
|
||||||
|
|
||||||
|
#[requires(*x = 0; true)]
|
||||||
|
fn buggy_add(x: &mut u32, y: u32) {
|
||||||
|
*x = *x + y;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut x = 10;
|
||||||
|
buggy_add(&mut x, 100);
|
||||||
|
assert_eq!(x, 110);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/contracts-disabled-side-effect-declarations.rs:2:12
|
||||||
|
|
|
||||||
|
LL | #![feature(contracts)]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
extern crate core;
|
extern crate core;
|
||||||
use core::contracts::ensures;
|
use core::contracts::ensures;
|
||||||
|
|
||||||
#[ensures({*x = 0; |_ret| true})]
|
#[ensures(*x = 0; |_ret| true)]
|
||||||
fn buggy_add(x: &mut u32, y: u32) {
|
fn buggy_add(x: &mut u32, y: u32) {
|
||||||
*x = *x + y;
|
*x = *x + y;
|
||||||
}
|
}
|
||||||
|
|||||||
19
tests/ui/contracts/declared-vars-referring-to-params.rs
Normal file
19
tests/ui/contracts/declared-vars-referring-to-params.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//@ run-pass
|
||||||
|
//@ compile-flags: -Zcontract-checks=yes
|
||||||
|
#![feature(contracts)]
|
||||||
|
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
|
use core::contracts::{ensures, requires};
|
||||||
|
|
||||||
|
// checks that variable declarations are lowered properly, with the ability to
|
||||||
|
// access function parameters
|
||||||
|
#[requires(let y = 2 * x; true)]
|
||||||
|
#[ensures(move |ret| { *ret == y })]
|
||||||
|
fn foo(x: u32) -> u32 {
|
||||||
|
x * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo(1);
|
||||||
|
}
|
||||||
11
tests/ui/contracts/declared-vars-referring-to-params.stderr
Normal file
11
tests/ui/contracts/declared-vars-referring-to-params.stderr
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/declared-vars-referring-to-params.rs:3:12
|
||||||
|
|
|
||||||
|
LL | #![feature(contracts)]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
||||||
17
tests/ui/contracts/declared-vars-used-in-ensures.rs
Normal file
17
tests/ui/contracts/declared-vars-used-in-ensures.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
//@ run-pass
|
||||||
|
//@ compile-flags: -Zcontract-checks=yes
|
||||||
|
#![feature(contracts)]
|
||||||
|
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
|
use core::contracts::{ensures, requires};
|
||||||
|
|
||||||
|
#[requires(let y = 1; true)]
|
||||||
|
#[ensures(move |_ret| { y == 1 })]
|
||||||
|
fn foo(x: u32) -> u32 {
|
||||||
|
x * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo(1);
|
||||||
|
}
|
||||||
11
tests/ui/contracts/declared-vars-used-in-ensures.stderr
Normal file
11
tests/ui/contracts/declared-vars-used-in-ensures.stderr
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/declared-vars-used-in-ensures.rs:3:12
|
||||||
|
|
|
||||||
|
LL | #![feature(contracts)]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
//@ run-pass
|
||||||
|
//@ compile-flags: -Zcontract-checks=yes
|
||||||
|
#![feature(contracts)]
|
||||||
|
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
|
use core::contracts::{ensures, requires};
|
||||||
|
|
||||||
|
// checks that variable declarations are lowered properly, with the ability to
|
||||||
|
// refer to them *both* in requires and ensures
|
||||||
|
#[requires(let y = 2 * x; y > 0)]
|
||||||
|
#[ensures(move |ret| { *ret == y })]
|
||||||
|
fn foo(x: u32) -> u32 {
|
||||||
|
x * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/declared-vars-used-in-requires-and-ensures.rs:3:12
|
||||||
|
|
|
||||||
|
LL | #![feature(contracts)]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@
|
|||||||
#![feature(contracts_internals)]
|
#![feature(contracts_internals)]
|
||||||
|
|
||||||
fn nest(x: Baz) -> i32
|
fn nest(x: Baz) -> i32
|
||||||
contract_requires(|| x.baz > 0)
|
contract_requires { x.baz > 0 }
|
||||||
contract_ensures(|ret| *ret > 100)
|
contract_ensures { |ret| *ret > 100 }
|
||||||
{
|
{
|
||||||
loop {
|
loop {
|
||||||
return x.baz + 50;
|
return x.baz + 50;
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
#![feature(contracts_internals)]
|
#![feature(contracts_internals)]
|
||||||
|
|
||||||
fn tail(x: Baz) -> i32
|
fn tail(x: Baz) -> i32
|
||||||
contract_requires(|| x.baz > 0)
|
contract_requires { x.baz > 0 }
|
||||||
contract_ensures(|ret| *ret > 100)
|
contract_ensures { |ret| *ret > 100 }
|
||||||
{
|
{
|
||||||
x.baz + 50
|
x.baz + 50
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#![feature(contracts_internals)]
|
#![feature(contracts_internals)]
|
||||||
|
|
||||||
fn outer() -> i32
|
fn outer() -> i32
|
||||||
contract_ensures(|ret| *ret > 0)
|
contract_ensures { |ret| *ret > 0 }
|
||||||
{
|
{
|
||||||
let inner_closure = || -> i32 { 0 };
|
let inner_closure = || -> i32 { 0 };
|
||||||
inner_closure();
|
inner_closure();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
struct Outer { outer: std::cell::Cell<i32> }
|
struct Outer { outer: std::cell::Cell<i32> }
|
||||||
|
|
||||||
fn outer(x: Outer)
|
fn outer(x: Outer)
|
||||||
contract_requires(|| x.outer.get() > 0)
|
contract_requires { x.outer.get() > 0 }
|
||||||
{
|
{
|
||||||
let inner_closure = || { };
|
let inner_closure = || { };
|
||||||
x.outer.set(0);
|
x.outer.set(0);
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ fn main() {
|
|||||||
//~^ ERROR use of unstable library feature `contracts_internals`
|
//~^ ERROR use of unstable library feature `contracts_internals`
|
||||||
|
|
||||||
// ast extensions are guarded by contracts_internals feature gate
|
// ast extensions are guarded by contracts_internals feature gate
|
||||||
fn identity_1() -> i32 contract_requires(|| true) { 10 }
|
fn identity_1() -> i32 contract_requires { true } { 10 }
|
||||||
//~^ ERROR contract internal machinery is for internal use only
|
//~^ ERROR contract internal machinery is for internal use only
|
||||||
fn identity_2() -> i32 contract_ensures(|_| true) { 10 }
|
fn identity_2() -> i32 contract_ensures { |_| true } { 10 }
|
||||||
//~^ ERROR contract internal machinery is for internal use only
|
//~^ ERROR contract internal machinery is for internal use only
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
error[E0658]: contract internal machinery is for internal use only
|
error[E0658]: contract internal machinery is for internal use only
|
||||||
--> $DIR/internal-feature-gating.rs:14:28
|
--> $DIR/internal-feature-gating.rs:14:28
|
||||||
|
|
|
|
||||||
LL | fn identity_1() -> i32 contract_requires(|| true) { 10 }
|
LL | fn identity_1() -> i32 contract_requires { true } { 10 }
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
|
||||||
@@ -11,7 +11,7 @@ LL | fn identity_1() -> i32 contract_requires(|| true) { 10 }
|
|||||||
error[E0658]: contract internal machinery is for internal use only
|
error[E0658]: contract internal machinery is for internal use only
|
||||||
--> $DIR/internal-feature-gating.rs:16:28
|
--> $DIR/internal-feature-gating.rs:16:28
|
||||||
|
|
|
|
||||||
LL | fn identity_2() -> i32 contract_ensures(|_| true) { 10 }
|
LL | fn identity_2() -> i32 contract_ensures { |_| true } { 10 }
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
|
||||||
|
|||||||
24
tests/ui/contracts/internal_machinery/lowering/basics.rs
Normal file
24
tests/ui/contracts/internal_machinery/lowering/basics.rs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
//@ run-pass
|
||||||
|
#![feature(contracts, cfg_contract_checks, contracts_internals, core_intrinsics)]
|
||||||
|
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
|
|
||||||
|
// we check here if the "lowered" program behaves as expected before
|
||||||
|
// implementing the actual lowering in the compiler
|
||||||
|
|
||||||
|
fn foo(x: u32) -> u32 {
|
||||||
|
let post = {
|
||||||
|
let y = 2 * x;
|
||||||
|
// call contract_check_requires here to avoid borrow checker issues
|
||||||
|
// with variables declared in contract requires
|
||||||
|
core::intrinsics::contract_check_requires(|| y > 0);
|
||||||
|
Some(core::contracts::build_check_ensures(move |ret| *ret == y))
|
||||||
|
};
|
||||||
|
|
||||||
|
core::intrinsics::contract_check_ensures(post, { 2 * x })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo(1);
|
||||||
|
}
|
||||||
11
tests/ui/contracts/internal_machinery/lowering/basics.stderr
Normal file
11
tests/ui/contracts/internal_machinery/lowering/basics.stderr
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/basics.rs:2:12
|
||||||
|
|
|
||||||
|
LL | #![feature(contracts, cfg_contract_checks, contracts_internals, core_intrinsics)]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
||||||
18
tests/ui/contracts/requires-bool-expr-with-semicolon.rs
Normal file
18
tests/ui/contracts/requires-bool-expr-with-semicolon.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//@ dont-require-annotations: NOTE
|
||||||
|
//@ compile-flags: -Zcontract-checks=yes
|
||||||
|
#![feature(contracts)]
|
||||||
|
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
|
use core::contracts::requires;
|
||||||
|
|
||||||
|
#[requires(true;)]
|
||||||
|
//~^ ERROR mismatched types [E0308]
|
||||||
|
//~| NOTE expected `bool`, found `()`
|
||||||
|
fn foo(x: u32) -> u32 {
|
||||||
|
x * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo(1);
|
||||||
|
}
|
||||||
18
tests/ui/contracts/requires-bool-expr-with-semicolon.stderr
Normal file
18
tests/ui/contracts/requires-bool-expr-with-semicolon.stderr
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/requires-bool-expr-with-semicolon.rs:3:12
|
||||||
|
|
|
||||||
|
LL | #![feature(contracts)]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/requires-bool-expr-with-semicolon.rs:9:1
|
||||||
|
|
|
||||||
|
LL | #[requires(true;)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error; 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
||||||
18
tests/ui/contracts/requires-no-final-expression.rs
Normal file
18
tests/ui/contracts/requires-no-final-expression.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//@ dont-require-annotations: NOTE
|
||||||
|
//@ compile-flags: -Zcontract-checks=yes
|
||||||
|
#![feature(contracts)]
|
||||||
|
//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features]
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
|
use core::contracts::requires;
|
||||||
|
|
||||||
|
#[requires(let y = 1;)]
|
||||||
|
//~^ ERROR mismatched types [E0308]
|
||||||
|
//~| NOTE expected `bool`, found `()`
|
||||||
|
fn foo(x: u32) -> u32 {
|
||||||
|
x * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo(1);
|
||||||
|
}
|
||||||
18
tests/ui/contracts/requires-no-final-expression.stderr
Normal file
18
tests/ui/contracts/requires-no-final-expression.stderr
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/requires-no-final-expression.rs:3:12
|
||||||
|
|
|
||||||
|
LL | #![feature(contracts)]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #128044 <https://github.com/rust-lang/rust/issues/128044> for more information
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/requires-no-final-expression.rs:9:1
|
||||||
|
|
|
||||||
|
LL | #[requires(let y = 1;)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error; 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
||||||
Reference in New Issue
Block a user