Merge from rustc
This commit is contained in:
commit
7afb84525a
277 changed files with 4863 additions and 2198 deletions
|
|
@ -1098,9 +1098,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "elsa"
|
||||
version = "1.7.1"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "848fe615fbb0a74d9ae68dcaa510106d32e37d9416207bbea4bd008bd89c47ed"
|
||||
checksum = "2343daaeabe09879d4ea058bb4f1e63da3fc07dadc6634e01bda1b3d6a9d9d2b"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -3348,11 +3348,18 @@ pub struct Impl {
|
|||
pub items: ThinVec<P<AssocItem>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug, Default)]
|
||||
pub struct FnContract {
|
||||
pub requires: Option<P<Expr>>,
|
||||
pub ensures: Option<P<Expr>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub struct Fn {
|
||||
pub defaultness: Defaultness,
|
||||
pub generics: Generics,
|
||||
pub sig: FnSig,
|
||||
pub contract: Option<P<FnContract>>,
|
||||
pub body: Option<P<Block>>,
|
||||
}
|
||||
|
||||
|
|
@ -3650,7 +3657,7 @@ mod size_asserts {
|
|||
static_assert_size!(Block, 32);
|
||||
static_assert_size!(Expr, 72);
|
||||
static_assert_size!(ExprKind, 40);
|
||||
static_assert_size!(Fn, 160);
|
||||
static_assert_size!(Fn, 168);
|
||||
static_assert_size!(ForeignItem, 88);
|
||||
static_assert_size!(ForeignItemKind, 16);
|
||||
static_assert_size!(GenericArg, 24);
|
||||
|
|
|
|||
|
|
@ -143,6 +143,10 @@ pub trait MutVisitor: Sized {
|
|||
walk_flat_map_assoc_item(self, i, ctxt)
|
||||
}
|
||||
|
||||
fn visit_contract(&mut self, c: &mut P<FnContract>) {
|
||||
walk_contract(self, c);
|
||||
}
|
||||
|
||||
fn visit_fn_decl(&mut self, d: &mut P<FnDecl>) {
|
||||
walk_fn_decl(self, d);
|
||||
}
|
||||
|
|
@ -958,13 +962,16 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
|
|||
_ctxt,
|
||||
_ident,
|
||||
_vis,
|
||||
Fn { defaultness, generics, body, sig: FnSig { header, decl, span } },
|
||||
Fn { defaultness, generics, contract, body, sig: FnSig { header, decl, span } },
|
||||
) => {
|
||||
// Identifier and visibility are visited as a part of the item.
|
||||
visit_defaultness(vis, defaultness);
|
||||
vis.visit_fn_header(header);
|
||||
vis.visit_generics(generics);
|
||||
vis.visit_fn_decl(decl);
|
||||
if let Some(contract) = contract {
|
||||
vis.visit_contract(contract);
|
||||
}
|
||||
if let Some(body) = body {
|
||||
vis.visit_block(body);
|
||||
}
|
||||
|
|
@ -979,6 +986,16 @@ fn walk_fn<T: MutVisitor>(vis: &mut T, kind: FnKind<'_>) {
|
|||
}
|
||||
}
|
||||
|
||||
fn walk_contract<T: MutVisitor>(vis: &mut T, contract: &mut P<FnContract>) {
|
||||
let FnContract { requires, ensures } = contract.deref_mut();
|
||||
if let Some(pred) = requires {
|
||||
vis.visit_expr(pred);
|
||||
}
|
||||
if let Some(pred) = ensures {
|
||||
vis.visit_expr(pred);
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_fn_decl<T: MutVisitor>(vis: &mut T, decl: &mut P<FnDecl>) {
|
||||
let FnDecl { inputs, output } = decl.deref_mut();
|
||||
inputs.flat_map_in_place(|param| vis.flat_map_param(param));
|
||||
|
|
|
|||
|
|
@ -188,6 +188,9 @@ pub trait Visitor<'ast>: Sized {
|
|||
fn visit_closure_binder(&mut self, b: &'ast ClosureBinder) -> Self::Result {
|
||||
walk_closure_binder(self, b)
|
||||
}
|
||||
fn visit_contract(&mut self, c: &'ast FnContract) -> Self::Result {
|
||||
walk_contract(self, c)
|
||||
}
|
||||
fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result {
|
||||
walk_where_predicate(self, p)
|
||||
}
|
||||
|
|
@ -800,6 +803,17 @@ pub fn walk_closure_binder<'a, V: Visitor<'a>>(
|
|||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_contract<'a, V: Visitor<'a>>(visitor: &mut V, c: &'a FnContract) -> V::Result {
|
||||
let FnContract { requires, ensures } = c;
|
||||
if let Some(pred) = requires {
|
||||
visitor.visit_expr(pred);
|
||||
}
|
||||
if let Some(pred) = ensures {
|
||||
visitor.visit_expr(pred);
|
||||
}
|
||||
V::Result::output()
|
||||
}
|
||||
|
||||
pub fn walk_where_predicate<'a, V: Visitor<'a>>(
|
||||
visitor: &mut V,
|
||||
predicate: &'a WherePredicate,
|
||||
|
|
@ -862,12 +876,13 @@ pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Resu
|
|||
_ctxt,
|
||||
_ident,
|
||||
_vis,
|
||||
Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, body },
|
||||
Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, contract, body },
|
||||
) => {
|
||||
// Identifier and visibility are visited as a part of the item.
|
||||
try_visit!(visitor.visit_fn_header(header));
|
||||
try_visit!(visitor.visit_generics(generics));
|
||||
try_visit!(visitor.visit_fn_decl(decl));
|
||||
visit_opt!(visitor, visit_contract, contract);
|
||||
visit_opt!(visitor, visit_block, body);
|
||||
}
|
||||
FnKind::Closure(binder, coroutine_kind, decl, body) => {
|
||||
|
|
|
|||
|
|
@ -311,8 +311,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::ExprKind::Continue(self.lower_jump_destination(e.id, *opt_label))
|
||||
}
|
||||
ExprKind::Ret(e) => {
|
||||
let e = e.as_ref().map(|x| self.lower_expr(x));
|
||||
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) => {
|
||||
|
|
@ -379,6 +379,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);
|
||||
|
|
@ -1991,7 +2017,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);
|
||||
|
||||
|
|
@ -2040,7 +2067,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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -207,9 +207,40 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
sig: FnSig { decl, header, span: fn_sig_span },
|
||||
generics,
|
||||
body,
|
||||
contract,
|
||||
..
|
||||
}) => {
|
||||
self.with_new_scopes(*fn_sig_span, |this| {
|
||||
assert!(this.contract.is_none());
|
||||
if let Some(contract) = contract {
|
||||
let requires = contract.requires.clone();
|
||||
let ensures = contract.ensures.clone();
|
||||
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 =
|
||||
Ident::from_str_and_span("__ensures_checker", ens.span);
|
||||
let (checker_pat, checker_hir_id) = this.pat_ident_binding_mode_mut(
|
||||
ens.span,
|
||||
checker_ident,
|
||||
hir::BindingMode::NONE,
|
||||
);
|
||||
|
||||
crate::FnContractLoweringEnsures {
|
||||
expr: ens,
|
||||
fresh_ident: (checker_ident, checker_pat, checker_hir_id),
|
||||
}
|
||||
});
|
||||
|
||||
// Note: `with_new_scopes` will reinstall the outer
|
||||
// item's contract (if any) after its callback finishes.
|
||||
this.contract.replace(crate::FnContractLoweringInfo {
|
||||
span,
|
||||
requires,
|
||||
ensures,
|
||||
});
|
||||
}
|
||||
|
||||
// Note: we don't need to change the return type from `T` to
|
||||
// `impl Future<Output = T>` here because lower_body
|
||||
// only cares about the input argument patterns in the function
|
||||
|
|
@ -1054,10 +1085,64 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
|
||||
) -> hir::BodyId {
|
||||
self.lower_body(|this| {
|
||||
(
|
||||
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x))),
|
||||
body(this),
|
||||
)
|
||||
let params =
|
||||
this.arena.alloc_from_iter(decl.inputs.iter().map(|x| this.lower_param(x)));
|
||||
let result = body(this);
|
||||
|
||||
let opt_contract = this.contract.take();
|
||||
|
||||
// { body }
|
||||
// ==>
|
||||
// { 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(&[]))
|
||||
};
|
||||
|
||||
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))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,6 +86,19 @@ mod path;
|
|||
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct FnContractLoweringInfo<'hir> {
|
||||
pub span: Span,
|
||||
pub requires: Option<ast::ptr::P<ast::Expr>>,
|
||||
pub ensures: Option<FnContractLoweringEnsures<'hir>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct FnContractLoweringEnsures<'hir> {
|
||||
expr: ast::ptr::P<ast::Expr>,
|
||||
fresh_ident: (Ident, hir::Pat<'hir>, HirId),
|
||||
}
|
||||
|
||||
struct LoweringContext<'a, 'hir> {
|
||||
tcx: TyCtxt<'hir>,
|
||||
resolver: &'a mut ResolverAstLowering,
|
||||
|
|
@ -100,6 +113,8 @@ struct LoweringContext<'a, 'hir> {
|
|||
/// Collect items that were created by lowering the current owner.
|
||||
children: Vec<(LocalDefId, hir::MaybeOwner<'hir>)>,
|
||||
|
||||
contract: Option<FnContractLoweringInfo<'hir>>,
|
||||
|
||||
coroutine_kind: Option<hir::CoroutineKind>,
|
||||
|
||||
/// When inside an `async` context, this is the `HirId` of the
|
||||
|
|
@ -148,6 +163,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
bodies: Vec::new(),
|
||||
attrs: SortedMap::default(),
|
||||
children: Vec::default(),
|
||||
contract: None,
|
||||
current_hir_id_owner: hir::CRATE_OWNER_ID,
|
||||
item_local_id_counter: hir::ItemLocalId::ZERO,
|
||||
ident_and_label_to_local_id: Default::default(),
|
||||
|
|
@ -834,12 +850,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
let was_in_loop_condition = self.is_in_loop_condition;
|
||||
self.is_in_loop_condition = false;
|
||||
|
||||
let old_contract = self.contract.take();
|
||||
|
||||
let catch_scope = self.catch_scope.take();
|
||||
let loop_scope = self.loop_scope.take();
|
||||
let ret = f(self);
|
||||
self.catch_scope = catch_scope;
|
||||
self.loop_scope = loop_scope;
|
||||
|
||||
self.contract = old_contract;
|
||||
|
||||
self.is_in_loop_condition = was_in_loop_condition;
|
||||
|
||||
self.current_item = current_item;
|
||||
|
|
|
|||
|
|
@ -917,7 +917,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
walk_list!(self, visit_attribute, &item.attrs);
|
||||
return; // Avoid visiting again.
|
||||
}
|
||||
ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, body }) => {
|
||||
ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, contract: _, body }) => {
|
||||
self.check_defaultness(item.span, *defaultness);
|
||||
|
||||
let is_intrinsic =
|
||||
|
|
|
|||
|
|
@ -548,6 +548,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
|||
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
|
||||
gate_all!(unsafe_fields, "`unsafe` fields are experimental");
|
||||
gate_all!(unsafe_binders, "unsafe binder types are experimental");
|
||||
gate_all!(contracts, "contracts are incomplete");
|
||||
gate_all!(contracts_internals, "contract internal machinery is for internal use only");
|
||||
|
||||
if !visitor.features.never_patterns() {
|
||||
if let Some(spans) = spans.get(&sym::never_patterns) {
|
||||
|
|
|
|||
|
|
@ -650,13 +650,17 @@ impl<'a> State<'a> {
|
|||
attrs: &[ast::Attribute],
|
||||
func: &ast::Fn,
|
||||
) {
|
||||
let ast::Fn { defaultness, generics, sig, body } = func;
|
||||
let ast::Fn { defaultness, generics, sig, contract, body } = func;
|
||||
if body.is_some() {
|
||||
self.head("");
|
||||
}
|
||||
self.print_visibility(vis);
|
||||
self.print_defaultness(*defaultness);
|
||||
self.print_fn(&sig.decl, sig.header, Some(name), generics);
|
||||
if let Some(contract) = &contract {
|
||||
self.nbsp();
|
||||
self.print_contract(contract);
|
||||
}
|
||||
if let Some(body) = body {
|
||||
self.nbsp();
|
||||
self.print_block_with_attrs(body, attrs);
|
||||
|
|
@ -665,6 +669,21 @@ impl<'a> State<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn print_contract(&mut self, contract: &ast::FnContract) {
|
||||
if let Some(pred) = &contract.requires {
|
||||
self.word("rustc_requires");
|
||||
self.popen();
|
||||
self.print_expr(pred, FixupContext::default());
|
||||
self.pclose();
|
||||
}
|
||||
if let Some(pred) = &contract.ensures {
|
||||
self.word("rustc_ensures");
|
||||
self.popen();
|
||||
self.print_expr(pred, FixupContext::default());
|
||||
self.pclose();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn print_fn(
|
||||
&mut self,
|
||||
decl: &ast::FnDecl,
|
||||
|
|
|
|||
|
|
@ -1653,6 +1653,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
ConstraintCategory::SizedBound,
|
||||
);
|
||||
}
|
||||
&Rvalue::NullaryOp(NullOp::ContractChecks, _) => {}
|
||||
&Rvalue::NullaryOp(NullOp::UbChecks, _) => {}
|
||||
|
||||
Rvalue::ShallowInitBox(operand, ty) => {
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
|
|||
defaultness: ast::Defaultness::Final,
|
||||
sig,
|
||||
generics: Generics::default(),
|
||||
contract: None,
|
||||
body,
|
||||
}));
|
||||
|
||||
|
|
|
|||
176
compiler/rustc_builtin_macros/src/contracts.rs
Normal file
176
compiler/rustc_builtin_macros/src/contracts.rs
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
#![allow(unused_imports, unused_variables)]
|
||||
|
||||
use rustc_ast::token;
|
||||
use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_expand::base::{AttrProcMacro, ExtCtxt};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::{Ident, Symbol, kw, sym};
|
||||
|
||||
pub(crate) struct ExpandRequires;
|
||||
|
||||
pub(crate) struct ExpandEnsures;
|
||||
|
||||
impl AttrProcMacro for ExpandRequires {
|
||||
fn expand<'cx>(
|
||||
&self,
|
||||
ecx: &'cx mut ExtCtxt<'_>,
|
||||
span: Span,
|
||||
annotation: TokenStream,
|
||||
annotated: TokenStream,
|
||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||
expand_requires_tts(ecx, span, annotation, annotated)
|
||||
}
|
||||
}
|
||||
|
||||
impl AttrProcMacro for ExpandEnsures {
|
||||
fn expand<'cx>(
|
||||
&self,
|
||||
ecx: &'cx mut ExtCtxt<'_>,
|
||||
span: Span,
|
||||
annotation: TokenStream,
|
||||
annotated: TokenStream,
|
||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||
expand_ensures_tts(ecx, span, annotation, annotated)
|
||||
}
|
||||
}
|
||||
|
||||
/// Expand the function signature to include the contract clause.
|
||||
///
|
||||
/// The contracts clause will be injected before the function body and the optional where clause.
|
||||
/// For that, we search for the body / where token, and invoke the `inject` callback to generate the
|
||||
/// contract clause in the right place.
|
||||
///
|
||||
// FIXME: this kind of manual token tree munging does not have significant precedent among
|
||||
// rustc builtin macros, probably because most builtin macros use direct AST manipulation to
|
||||
// accomplish similar goals. But since our attributes need to take arbitrary expressions, and
|
||||
// our attribute infrastructure does not yet support mixing a token-tree annotation with an AST
|
||||
// annotated, we end up doing token tree manipulation.
|
||||
fn expand_contract_clause(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
attr_span: Span,
|
||||
annotated: TokenStream,
|
||||
inject: impl FnOnce(&mut TokenStream) -> Result<(), ErrorGuaranteed>,
|
||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||
let mut new_tts = TokenStream::default();
|
||||
let mut cursor = annotated.iter();
|
||||
|
||||
let is_kw = |tt: &TokenTree, sym: Symbol| {
|
||||
if let TokenTree::Token(token, _) = tt { token.is_ident_named(sym) } else { false }
|
||||
};
|
||||
|
||||
// Find the `fn` keyword to check if this is a function.
|
||||
if cursor
|
||||
.find(|tt| {
|
||||
new_tts.push_tree((*tt).clone());
|
||||
is_kw(tt, kw::Fn)
|
||||
})
|
||||
.is_none()
|
||||
{
|
||||
return Err(ecx
|
||||
.sess
|
||||
.dcx()
|
||||
.span_err(attr_span, "contract annotations can only be used on functions"));
|
||||
}
|
||||
|
||||
// Found the `fn` keyword, now find either the `where` token or the function body.
|
||||
let next_tt = loop {
|
||||
let Some(tt) = cursor.next() else {
|
||||
return Err(ecx.sess.dcx().span_err(
|
||||
attr_span,
|
||||
"contract annotations is only supported in functions with bodies",
|
||||
));
|
||||
};
|
||||
// If `tt` is the last element. Check if it is the function body.
|
||||
if cursor.peek().is_none() {
|
||||
if let TokenTree::Delimited(_, _, token::Delimiter::Brace, _) = tt {
|
||||
break tt;
|
||||
} else {
|
||||
return Err(ecx.sess.dcx().span_err(
|
||||
attr_span,
|
||||
"contract annotations is only supported in functions with bodies",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if is_kw(tt, kw::Where) {
|
||||
break tt;
|
||||
}
|
||||
new_tts.push_tree(tt.clone());
|
||||
};
|
||||
|
||||
// At this point, we've transcribed everything from the `fn` through the formal parameter list
|
||||
// and return type declaration, (if any), but `tt` itself has *not* been transcribed.
|
||||
//
|
||||
// Now inject the AST contract form.
|
||||
//
|
||||
inject(&mut new_tts)?;
|
||||
|
||||
// Above we injected the internal AST requires/ensures construct. Now copy over all the other
|
||||
// token trees.
|
||||
new_tts.push_tree(next_tt.clone());
|
||||
while let Some(tt) = cursor.next() {
|
||||
new_tts.push_tree(tt.clone());
|
||||
if cursor.peek().is_none()
|
||||
&& !matches!(tt, TokenTree::Delimited(_, _, token::Delimiter::Brace, _))
|
||||
{
|
||||
return Err(ecx.sess.dcx().span_err(
|
||||
attr_span,
|
||||
"contract annotations is only supported in functions with bodies",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Record the span as a contract attribute expansion.
|
||||
// This is used later to stop users from using the extended syntax directly
|
||||
// which is gated via `contracts_internals`.
|
||||
ecx.psess().contract_attribute_spans.push(attr_span);
|
||||
|
||||
Ok(new_tts)
|
||||
}
|
||||
|
||||
fn expand_requires_tts(
|
||||
_ecx: &mut ExtCtxt<'_>,
|
||||
attr_span: Span,
|
||||
annotation: TokenStream,
|
||||
annotated: TokenStream,
|
||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||
expand_contract_clause(_ecx, attr_span, annotated, |new_tts| {
|
||||
new_tts.push_tree(TokenTree::Token(
|
||||
token::Token::from_ast_ident(Ident::new(kw::ContractRequires, attr_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::Parenthesis,
|
||||
annotation,
|
||||
));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn expand_ensures_tts(
|
||||
_ecx: &mut ExtCtxt<'_>,
|
||||
attr_span: Span,
|
||||
annotation: TokenStream,
|
||||
annotated: TokenStream,
|
||||
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||
expand_contract_clause(_ecx, attr_span, annotated, |new_tts| {
|
||||
new_tts.push_tree(TokenTree::Token(
|
||||
token::Token::from_ast_ident(Ident::new(kw::ContractEnsures, attr_span)),
|
||||
Spacing::Joint,
|
||||
));
|
||||
new_tts.push_tree(TokenTree::Delimited(
|
||||
DelimSpan::from_single(attr_span),
|
||||
DelimSpacing::new(Spacing::JointHidden, Spacing::JointHidden),
|
||||
token::Delimiter::Parenthesis,
|
||||
annotation,
|
||||
));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
@ -1034,6 +1034,7 @@ impl<'a> MethodDef<'a> {
|
|||
defaultness,
|
||||
sig,
|
||||
generics: fn_generics,
|
||||
contract: None,
|
||||
body: Some(body_block),
|
||||
})),
|
||||
tokens: None,
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ impl AllocFnFactory<'_, '_> {
|
|||
defaultness: ast::Defaultness::Final,
|
||||
sig,
|
||||
generics: Generics::default(),
|
||||
contract: None,
|
||||
body,
|
||||
}));
|
||||
let item = self.cx.item(
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ mod trace_macros;
|
|||
|
||||
pub mod asm;
|
||||
pub mod cmdline_attrs;
|
||||
pub mod contracts;
|
||||
pub mod proc_macro_harness;
|
||||
pub mod standard_library_imports;
|
||||
pub mod test_harness;
|
||||
|
|
@ -137,4 +138,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
|
|||
|
||||
let client = proc_macro::bridge::client::Client::expand1(proc_macro::quote);
|
||||
register(sym::quote, SyntaxExtensionKind::Bang(Box::new(BangProcMacro { client })));
|
||||
let requires = SyntaxExtensionKind::Attr(Box::new(contracts::ExpandRequires));
|
||||
register(sym::contracts_requires, requires);
|
||||
let ensures = SyntaxExtensionKind::Attr(Box::new(contracts::ExpandEnsures));
|
||||
register(sym::contracts_ensures, ensures);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -344,6 +344,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
|
|||
defaultness,
|
||||
sig,
|
||||
generics: ast::Generics::default(),
|
||||
contract: None,
|
||||
body: Some(main_body),
|
||||
}));
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ index 7165c3e48af..968552ad435 100644
|
|||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
-compiler_builtins = { version = "=0.1.143", features = ['rustc-dep-of-std'] }
|
||||
+compiler_builtins = { version = "=0.1.143", features = ['rustc-dep-of-std', 'no-f16-f128'] }
|
||||
-compiler_builtins = { version = "=0.1.145", features = ['rustc-dep-of-std'] }
|
||||
+compiler_builtins = { version = "=0.1.145", features = ['rustc-dep-of-std', 'no-f16-f128'] }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
|
||||
|
|
|
|||
|
|
@ -868,7 +868,16 @@ fn codegen_stmt<'tcx>(
|
|||
NullOp::UbChecks => {
|
||||
let val = fx.tcx.sess.ub_checks();
|
||||
let val = CValue::by_val(
|
||||
fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()),
|
||||
fx.bcx.ins().iconst(types::I8, i64::from(val)),
|
||||
fx.layout_of(fx.tcx.types.bool),
|
||||
);
|
||||
lval.write_cvalue(fx, val);
|
||||
return;
|
||||
}
|
||||
NullOp::ContractChecks => {
|
||||
let val = fx.tcx.sess.contract_checks();
|
||||
let val = CValue::by_val(
|
||||
fx.bcx.ins().iconst(types::I8, i64::from(val)),
|
||||
fx.layout_of(fx.tcx.types.bool),
|
||||
);
|
||||
lval.write_cvalue(fx, val);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_middle::ty::Ty;
|
|||
use rustc_middle::ty::layout::LayoutOf;
|
||||
#[cfg(feature = "master")]
|
||||
use rustc_session::config;
|
||||
use rustc_target::abi::call::{ArgAttributes, CastTarget, FnAbi, PassMode, Reg, RegKind};
|
||||
use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode, Reg, RegKind};
|
||||
|
||||
use crate::builder::Builder;
|
||||
use crate::context::CodegenCx;
|
||||
|
|
@ -132,10 +132,10 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
|
|||
if cx.sess().opts.optimize == config::OptLevel::No {
|
||||
return ty;
|
||||
}
|
||||
if attrs.regular.contains(rustc_target::abi::call::ArgAttribute::NoAlias) {
|
||||
if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NoAlias) {
|
||||
ty = ty.make_restrict()
|
||||
}
|
||||
if attrs.regular.contains(rustc_target::abi::call::ArgAttribute::NonNull) {
|
||||
if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NonNull) {
|
||||
non_null_args.push(arg_index as i32 + 1);
|
||||
}
|
||||
ty
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use rustc_middle::ty::layout::{
|
|||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
use rustc_target::callconv::FnAbi;
|
||||
use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, WasmCAbi, X86Abi};
|
||||
|
||||
use crate::common::{SignType, TypeReflection, type_is_pointer};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#[cfg(feature = "master")]
|
||||
use gccjit::{FnAttribute, VarAttribute, Visibility};
|
||||
use gccjit::{Function, GlobalKind, LValue, RValue, ToRValue, Type};
|
||||
use rustc_abi::{self as abi, Align, HasDataLayout, Primitive, Size, WrappingRange};
|
||||
use rustc_codegen_ssa::traits::{
|
||||
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
|
||||
};
|
||||
|
|
@ -14,7 +15,6 @@ use rustc_middle::ty::layout::LayoutOf;
|
|||
use rustc_middle::ty::{self, Instance};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size, WrappingRange};
|
||||
|
||||
use crate::base;
|
||||
use crate::context::CodegenCx;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::cell::{Cell, RefCell};
|
|||
use gccjit::{
|
||||
Block, CType, Context, Function, FunctionPtrType, FunctionType, LValue, Location, RValue, Type,
|
||||
};
|
||||
use rustc_abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
|
||||
use rustc_codegen_ssa::base::wants_msvc_seh;
|
||||
use rustc_codegen_ssa::errors as ssa_errors;
|
||||
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, MiscCodegenMethods};
|
||||
|
|
@ -18,7 +19,6 @@ use rustc_middle::ty::{self, ExistentialTraitRef, Instance, Ty, TyCtxt};
|
|||
use rustc_session::Session;
|
||||
use rustc_span::source_map::respan;
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
use rustc_target::abi::{HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx};
|
||||
use rustc_target::spec::{
|
||||
HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, TlsModel, WasmCAbi, X86Abi,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use gccjit::{Location, RValue};
|
||||
use rustc_abi::Size;
|
||||
use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind};
|
||||
use rustc_codegen_ssa::traits::{DebugInfoBuilderMethods, DebugInfoCodegenMethods};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
|
@ -10,8 +11,7 @@ use rustc_middle::mir::{self, Body, SourceScope};
|
|||
use rustc_middle::ty::{ExistentialTraitRef, Instance, Ty};
|
||||
use rustc_session::config::DebugInfo;
|
||||
use rustc_span::{BytePos, Pos, SourceFile, SourceFileAndLine, Span, Symbol};
|
||||
use rustc_target::abi::Size;
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
use rustc_target::callconv::FnAbi;
|
||||
|
||||
use crate::builder::Builder;
|
||||
use crate::context::CodegenCx;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use gccjit::{Function, FunctionType, GlobalKind, LValue, RValue, Type};
|
|||
use rustc_codegen_ssa::traits::BaseTypeCodegenMethods;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::Symbol;
|
||||
use rustc_target::abi::call::FnAbi;
|
||||
use rustc_target::callconv::FnAbi;
|
||||
|
||||
use crate::abi::{FnAbiGcc, FnAbiGccExt};
|
||||
use crate::context::CodegenCx;
|
||||
|
|
|
|||
|
|
@ -3,12 +3,11 @@
|
|||
//! 128-bit integers on 32-bit platforms and thus require to be handled manually.
|
||||
|
||||
use gccjit::{BinaryOp, ComparisonOp, FunctionType, Location, RValue, ToRValue, Type, UnaryOp};
|
||||
use rustc_abi::{Endian, ExternAbi};
|
||||
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
|
||||
use rustc_codegen_ssa::traits::{BackendTypes, BaseTypeCodegenMethods, BuilderMethods, OverflowOp};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_target::abi::Endian;
|
||||
use rustc_target::abi::call::{ArgAbi, ArgAttributes, Conv, FnAbi, PassMode};
|
||||
use rustc_target::spec;
|
||||
use rustc_target::callconv::{ArgAbi, ArgAttributes, Conv, FnAbi, PassMode};
|
||||
|
||||
use crate::builder::{Builder, ToGccComp};
|
||||
use crate::common::{SignType, TypeReflection};
|
||||
|
|
@ -401,7 +400,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
|
|||
conv: Conv::C,
|
||||
can_unwind: false,
|
||||
};
|
||||
fn_abi.adjust_for_foreign_abi(self.cx, spec::abi::Abi::C { unwind: false }).unwrap();
|
||||
fn_abi.adjust_for_foreign_abi(self.cx, ExternAbi::C { unwind: false }).unwrap();
|
||||
|
||||
let ret_indirect = matches!(fn_abi.ret.mode, PassMode::Indirect { .. });
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ use std::iter;
|
|||
#[cfg(feature = "master")]
|
||||
use gccjit::FunctionType;
|
||||
use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp};
|
||||
#[cfg(feature = "master")]
|
||||
use rustc_abi::ExternAbi;
|
||||
use rustc_abi::HasDataLayout;
|
||||
use rustc_codegen_ssa::MemFlags;
|
||||
use rustc_codegen_ssa::base::wants_msvc_seh;
|
||||
use rustc_codegen_ssa::common::IntPredicate;
|
||||
|
|
@ -25,11 +28,8 @@ use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
|
|||
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
|
||||
use rustc_middle::ty::{self, Instance, Ty};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use rustc_target::abi::HasDataLayout;
|
||||
use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
|
||||
use rustc_target::callconv::{ArgAbi, FnAbi, PassMode};
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
#[cfg(feature = "master")]
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
#[cfg(feature = "master")]
|
||||
use crate::abi::FnAbiGccExt;
|
||||
|
|
@ -1238,7 +1238,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>(
|
|||
tcx.types.unit,
|
||||
false,
|
||||
rustc_hir::Safety::Unsafe,
|
||||
Abi::Rust,
|
||||
ExternAbi::Rust,
|
||||
)),
|
||||
);
|
||||
// `unsafe fn(*mut i8, *mut i8) -> ()`
|
||||
|
|
@ -1249,7 +1249,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>(
|
|||
tcx.types.unit,
|
||||
false,
|
||||
rustc_hir::Safety::Unsafe,
|
||||
Abi::Rust,
|
||||
ExternAbi::Rust,
|
||||
)),
|
||||
);
|
||||
// `unsafe fn(unsafe fn(*mut i8) -> (), *mut i8, unsafe fn(*mut i8, *mut i8) -> ()) -> i32`
|
||||
|
|
@ -1258,7 +1258,7 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>(
|
|||
tcx.types.i32,
|
||||
false,
|
||||
rustc_hir::Safety::Unsafe,
|
||||
Abi::Rust,
|
||||
ExternAbi::Rust,
|
||||
));
|
||||
let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen);
|
||||
cx.rust_try_fn.set(Some(rust_try));
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use std::iter::FromIterator;
|
|||
use gccjit::{BinaryOp, RValue, ToRValue, Type};
|
||||
#[cfg(feature = "master")]
|
||||
use gccjit::{ComparisonOp, UnaryOp};
|
||||
use rustc_abi::{Align, Size};
|
||||
use rustc_codegen_ssa::base::compare_simd_types;
|
||||
use rustc_codegen_ssa::common::{IntPredicate, TypeKind};
|
||||
#[cfg(feature = "master")]
|
||||
|
|
@ -17,7 +18,6 @@ use rustc_middle::mir::BinOp;
|
|||
use rustc_middle::ty::layout::HasTyCtxt;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use rustc_target::abi::{Align, Size};
|
||||
|
||||
use crate::builder::Builder;
|
||||
#[cfg(not(feature = "master"))]
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ use std::convert::TryInto;
|
|||
#[cfg(feature = "master")]
|
||||
use gccjit::CType;
|
||||
use gccjit::{RValue, Struct, Type};
|
||||
use rustc_abi::{AddressSpace, Align, Integer, Size};
|
||||
use rustc_codegen_ssa::common::TypeKind;
|
||||
use rustc_codegen_ssa::traits::{
|
||||
BaseTypeCodegenMethods, DerivedTypeCodegenMethods, TypeMembershipCodegenMethods,
|
||||
};
|
||||
use rustc_middle::ty::layout::TyAndLayout;
|
||||
use rustc_middle::{bug, ty};
|
||||
use rustc_target::abi::{AddressSpace, Align, Integer, Size};
|
||||
|
||||
use crate::common::TypeReflection;
|
||||
use crate::context::CodegenCx;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,9 @@ use std::fmt::Write;
|
|||
use gccjit::{Struct, Type};
|
||||
use rustc_abi as abi;
|
||||
use rustc_abi::Primitive::*;
|
||||
use rustc_abi::{BackendRepr, FieldsShape, Integer, PointeeInfo, Size, Variants};
|
||||
use rustc_abi::{
|
||||
BackendRepr, FieldsShape, Integer, PointeeInfo, Reg, Size, TyAbiInterface, Variants,
|
||||
};
|
||||
use rustc_codegen_ssa::traits::{
|
||||
BaseTypeCodegenMethods, DerivedTypeCodegenMethods, LayoutTypeCodegenMethods,
|
||||
};
|
||||
|
|
@ -11,8 +13,7 @@ use rustc_middle::bug;
|
|||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, CoroutineArgsExt, Ty, TypeVisitableExt};
|
||||
use rustc_target::abi::TyAbiInterface;
|
||||
use rustc_target::abi::call::{CastTarget, FnAbi, Reg};
|
||||
use rustc_target::callconv::{CastTarget, FnAbi};
|
||||
|
||||
use crate::abi::{FnAbiGcc, FnAbiGccExt, GccType};
|
||||
use crate::context::CodegenCx;
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ fn make_mir_scope<'ll, 'tcx>(
|
|||
})
|
||||
}
|
||||
None => unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateLexicalBlock(
|
||||
llvm::LLVMDIBuilderCreateLexicalBlock(
|
||||
DIB(cx),
|
||||
parent_scope.dbg_scope,
|
||||
file_metadata,
|
||||
|
|
|
|||
|
|
@ -931,7 +931,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
|
|||
|
||||
unsafe {
|
||||
let compile_unit_file = llvm::LLVMRustDIBuilderCreateFile(
|
||||
debug_context.builder,
|
||||
debug_context.builder.as_ref(),
|
||||
name_in_debuginfo.as_c_char_ptr(),
|
||||
name_in_debuginfo.len(),
|
||||
work_dir.as_c_char_ptr(),
|
||||
|
|
@ -944,7 +944,7 @@ pub(crate) fn build_compile_unit_di_node<'ll, 'tcx>(
|
|||
);
|
||||
|
||||
let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit(
|
||||
debug_context.builder,
|
||||
debug_context.builder.as_ref(),
|
||||
dwarf_const::DW_LANG_Rust,
|
||||
compile_unit_file,
|
||||
producer.as_c_char_ptr(),
|
||||
|
|
@ -1641,7 +1641,14 @@ pub(crate) fn extend_scope_to_file<'ll>(
|
|||
file: &SourceFile,
|
||||
) -> &'ll DILexicalBlock {
|
||||
let file_metadata = file_metadata(cx, file);
|
||||
unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlockFile(DIB(cx), scope_metadata, file_metadata) }
|
||||
unsafe {
|
||||
llvm::LLVMDIBuilderCreateLexicalBlockFile(
|
||||
DIB(cx),
|
||||
scope_metadata,
|
||||
file_metadata,
|
||||
/* Discriminator (default) */ 0u32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn tuple_field_name(field_index: usize) -> Cow<'static, str> {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ use crate::builder::Builder;
|
|||
use crate::common::{AsCCharPtr, CodegenCx};
|
||||
use crate::llvm;
|
||||
use crate::llvm::debuginfo::{
|
||||
DIArray, DIBuilder, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DIType,
|
||||
DIArray, DIBuilderBox, DIFile, DIFlags, DILexicalBlock, DILocation, DISPFlags, DIScope, DIType,
|
||||
DIVariable,
|
||||
};
|
||||
use crate::value::Value;
|
||||
|
|
@ -61,7 +61,7 @@ const DW_TAG_arg_variable: c_uint = 0x101;
|
|||
/// A context object for maintaining all state needed by the debuginfo module.
|
||||
pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> {
|
||||
llmod: &'ll llvm::Module,
|
||||
builder: &'ll mut DIBuilder<'ll>,
|
||||
builder: DIBuilderBox<'ll>,
|
||||
created_files: RefCell<UnordMap<Option<(StableSourceFileId, SourceFileHash)>, &'ll DIFile>>,
|
||||
|
||||
type_map: metadata::TypeMap<'ll, 'tcx>,
|
||||
|
|
@ -69,18 +69,10 @@ pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> {
|
|||
recursion_marker_type: OnceCell<&'ll DIType>,
|
||||
}
|
||||
|
||||
impl Drop for CodegenUnitDebugContext<'_, '_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
llvm::LLVMRustDIBuilderDispose(&mut *(self.builder as *mut _));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
|
||||
pub(crate) fn new(llmod: &'ll llvm::Module) -> Self {
|
||||
debug!("CodegenUnitDebugContext::new");
|
||||
let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) };
|
||||
let builder = DIBuilderBox::new(llmod);
|
||||
// DIBuilder inherits context from the module, so we'd better use the same one
|
||||
CodegenUnitDebugContext {
|
||||
llmod,
|
||||
|
|
@ -93,7 +85,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
|
|||
}
|
||||
|
||||
pub(crate) fn finalize(&self, sess: &Session) {
|
||||
unsafe { llvm::LLVMRustDIBuilderFinalize(self.builder) };
|
||||
unsafe { llvm::LLVMDIBuilderFinalize(self.builder.as_ref()) };
|
||||
|
||||
match sess.target.debuginfo_kind {
|
||||
DebuginfoKind::Dwarf | DebuginfoKind::DwarfDsym => {
|
||||
|
|
@ -105,7 +97,11 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
|
|||
// Android has the same issue (#22398)
|
||||
llvm::add_module_flag_u32(
|
||||
self.llmod,
|
||||
llvm::ModuleFlagMergeBehavior::Warning,
|
||||
// In the case where multiple CGUs with different dwarf version
|
||||
// values are being merged together, such as with cross-crate
|
||||
// LTO, then we want to use the highest version of dwarf
|
||||
// we can. This matches Clang's behavior as well.
|
||||
llvm::ModuleFlagMergeBehavior::Max,
|
||||
"Dwarf Version",
|
||||
sess.dwarf_version(),
|
||||
);
|
||||
|
|
@ -582,7 +578,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
|||
(line, col)
|
||||
};
|
||||
|
||||
unsafe { llvm::LLVMRustDIBuilderCreateDebugLocation(line, col, scope, inlined_at) }
|
||||
unsafe { llvm::LLVMDIBuilderCreateDebugLocation(self.llcx, line, col, scope, inlined_at) }
|
||||
}
|
||||
|
||||
fn create_vtable_debuginfo(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use rustc_hir::def_id::DefId;
|
|||
use rustc_middle::ty::{self, Instance};
|
||||
|
||||
use super::utils::{DIB, debug_context};
|
||||
use crate::common::{AsCCharPtr, CodegenCx};
|
||||
use crate::common::CodegenCx;
|
||||
use crate::llvm;
|
||||
use crate::llvm::debuginfo::DIScope;
|
||||
|
||||
|
|
@ -33,12 +33,12 @@ pub(crate) fn item_namespace<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'l
|
|||
};
|
||||
|
||||
let scope = unsafe {
|
||||
llvm::LLVMRustDIBuilderCreateNameSpace(
|
||||
llvm::LLVMDIBuilderCreateNameSpace(
|
||||
DIB(cx),
|
||||
parent_scope,
|
||||
namespace_name_string.as_c_char_ptr(),
|
||||
namespace_name_string.as_ptr(),
|
||||
namespace_name_string.len(),
|
||||
false, // ExportSymbols (only relevant for C++ anonymous namespaces)
|
||||
llvm::False, // ExportSymbols (only relevant for C++ anonymous namespaces)
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ pub(crate) fn debug_context<'a, 'll, 'tcx>(
|
|||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn DIB<'a, 'll>(cx: &'a CodegenCx<'ll, '_>) -> &'a DIBuilder<'ll> {
|
||||
cx.dbg_cx.as_ref().unwrap().builder
|
||||
cx.dbg_cx.as_ref().unwrap().builder.as_ref()
|
||||
}
|
||||
|
||||
pub(crate) fn get_namespace_for_item<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DIScope {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,15 @@
|
|||
//! Bindings to the LLVM-C API (`LLVM*`), and to our own `extern "C"` wrapper
|
||||
//! functions around the unstable LLVM C++ API (`LLVMRust*`).
|
||||
//!
|
||||
//! ## Passing pointer/length strings as `*const c_uchar`
|
||||
//!
|
||||
//! Normally it's a good idea for Rust-side bindings to match the corresponding
|
||||
//! C-side function declarations as closely as possible. But when passing `&str`
|
||||
//! or `&[u8]` data as a pointer/length pair, it's more convenient to declare
|
||||
//! the Rust-side pointer as `*const c_uchar` instead of `*const c_char`.
|
||||
//! Both pointer types have the same ABI, and using `*const c_uchar` avoids
|
||||
//! the need for an extra cast from `*const u8` on the Rust side.
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
|
|
@ -5,17 +17,18 @@ use std::fmt::Debug;
|
|||
use std::marker::PhantomData;
|
||||
use std::ptr;
|
||||
|
||||
use libc::{c_char, c_int, c_uint, c_ulonglong, c_void, size_t};
|
||||
use bitflags::bitflags;
|
||||
use libc::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_void, size_t};
|
||||
use rustc_macros::TryFromU32;
|
||||
use rustc_target::spec::SymbolVisibility;
|
||||
|
||||
use super::RustString;
|
||||
use super::debuginfo::{
|
||||
DIArray, DIBasicType, DIBuilder, DICompositeType, DIDerivedType, DIDescriptor, DIEnumerator,
|
||||
DIFile, DIFlags, DIGlobalVariableExpression, DILexicalBlock, DILocation, DINameSpace,
|
||||
DISPFlags, DIScope, DISubprogram, DISubrange, DITemplateTypeParameter, DIType, DIVariable,
|
||||
DebugEmissionKind, DebugNameTableKind,
|
||||
DIFile, DIFlags, DIGlobalVariableExpression, DILocation, DISPFlags, DIScope, DISubprogram,
|
||||
DISubrange, DITemplateTypeParameter, DIType, DIVariable, DebugEmissionKind, DebugNameTableKind,
|
||||
};
|
||||
use crate::llvm;
|
||||
|
||||
/// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`,
|
||||
/// which has a different ABI from Rust or C++ `bool`.
|
||||
|
|
@ -789,12 +802,50 @@ pub type DiagnosticHandlerTy = unsafe extern "C" fn(&DiagnosticInfo, *mut c_void
|
|||
pub type InlineAsmDiagHandlerTy = unsafe extern "C" fn(&SMDiagnostic, *const c_void, c_uint);
|
||||
|
||||
pub mod debuginfo {
|
||||
use std::ptr;
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
use super::{InvariantOpaque, Metadata};
|
||||
use crate::llvm::{self, Module};
|
||||
|
||||
/// Opaque target type for references to an LLVM debuginfo builder.
|
||||
///
|
||||
/// `&'_ DIBuilder<'ll>` corresponds to `LLVMDIBuilderRef`, which is the
|
||||
/// LLVM-C wrapper for `DIBuilder *`.
|
||||
///
|
||||
/// Debuginfo builders are created and destroyed during codegen, so the
|
||||
/// builder reference typically has a shorter lifetime than the LLVM
|
||||
/// session (`'ll`) that it participates in.
|
||||
#[repr(C)]
|
||||
pub struct DIBuilder<'a>(InvariantOpaque<'a>);
|
||||
pub struct DIBuilder<'ll>(InvariantOpaque<'ll>);
|
||||
|
||||
/// Owning pointer to a `DIBuilder<'ll>` that will dispose of the builder
|
||||
/// when dropped. Use `.as_ref()` to get the underlying `&DIBuilder`
|
||||
/// needed for debuginfo FFI calls.
|
||||
pub(crate) struct DIBuilderBox<'ll> {
|
||||
raw: ptr::NonNull<DIBuilder<'ll>>,
|
||||
}
|
||||
|
||||
impl<'ll> DIBuilderBox<'ll> {
|
||||
pub(crate) fn new(llmod: &'ll Module) -> Self {
|
||||
let raw = unsafe { llvm::LLVMCreateDIBuilder(llmod) };
|
||||
let raw = ptr::NonNull::new(raw).unwrap();
|
||||
Self { raw }
|
||||
}
|
||||
|
||||
pub(crate) fn as_ref(&self) -> &DIBuilder<'ll> {
|
||||
// SAFETY: This is an owning pointer, so `&DIBuilder` is valid
|
||||
// for as long as `&self` is.
|
||||
unsafe { self.raw.as_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ll> Drop for DIBuilderBox<'ll> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { llvm::LLVMDisposeDIBuilder(self.raw) };
|
||||
}
|
||||
}
|
||||
|
||||
pub type DIDescriptor = Metadata;
|
||||
pub type DILocation = Metadata;
|
||||
|
|
@ -914,7 +965,6 @@ pub mod debuginfo {
|
|||
}
|
||||
}
|
||||
|
||||
use bitflags::bitflags;
|
||||
// These values **must** match with LLVMRustAllocKindFlags
|
||||
bitflags! {
|
||||
#[repr(transparent)]
|
||||
|
|
@ -1675,6 +1725,50 @@ unsafe extern "C" {
|
|||
) -> &'a Value;
|
||||
}
|
||||
|
||||
// FFI bindings for `DIBuilder` functions in the LLVM-C API.
|
||||
// Try to keep these in the same order as in `llvm/include/llvm-c/DebugInfo.h`.
|
||||
//
|
||||
// FIXME(#134001): Audit all `Option` parameters, especially in lists, to check
|
||||
// that they really are nullable on the C/C++ side. LLVM doesn't appear to
|
||||
// actually document which ones are nullable.
|
||||
unsafe extern "C" {
|
||||
pub(crate) fn LLVMCreateDIBuilder<'ll>(M: &'ll Module) -> *mut DIBuilder<'ll>;
|
||||
pub(crate) fn LLVMDisposeDIBuilder<'ll>(Builder: ptr::NonNull<DIBuilder<'ll>>);
|
||||
|
||||
pub(crate) fn LLVMDIBuilderFinalize<'ll>(Builder: &DIBuilder<'ll>);
|
||||
|
||||
pub(crate) fn LLVMDIBuilderCreateNameSpace<'ll>(
|
||||
Builder: &DIBuilder<'ll>,
|
||||
ParentScope: Option<&'ll Metadata>,
|
||||
Name: *const c_uchar,
|
||||
NameLen: size_t,
|
||||
ExportSymbols: llvm::Bool,
|
||||
) -> &'ll Metadata;
|
||||
|
||||
pub(crate) fn LLVMDIBuilderCreateLexicalBlock<'ll>(
|
||||
Builder: &DIBuilder<'ll>,
|
||||
Scope: &'ll Metadata,
|
||||
File: &'ll Metadata,
|
||||
Line: c_uint,
|
||||
Column: c_uint,
|
||||
) -> &'ll Metadata;
|
||||
|
||||
pub(crate) fn LLVMDIBuilderCreateLexicalBlockFile<'ll>(
|
||||
Builder: &DIBuilder<'ll>,
|
||||
Scope: &'ll Metadata,
|
||||
File: &'ll Metadata,
|
||||
Discriminator: c_uint, // (optional "DWARF path discriminator"; default is 0)
|
||||
) -> &'ll Metadata;
|
||||
|
||||
pub(crate) fn LLVMDIBuilderCreateDebugLocation<'ll>(
|
||||
Ctx: &'ll Context,
|
||||
Line: c_uint,
|
||||
Column: c_uint,
|
||||
Scope: &'ll Metadata,
|
||||
InlinedAt: Option<&'ll Metadata>,
|
||||
) -> &'ll Metadata;
|
||||
}
|
||||
|
||||
#[link(name = "llvm-wrapper", kind = "static")]
|
||||
unsafe extern "C" {
|
||||
pub fn LLVMRustInstallErrorHandlers();
|
||||
|
|
@ -1942,12 +2036,6 @@ unsafe extern "C" {
|
|||
ValueLen: size_t,
|
||||
);
|
||||
|
||||
pub fn LLVMRustDIBuilderCreate(M: &Module) -> &mut DIBuilder<'_>;
|
||||
|
||||
pub fn LLVMRustDIBuilderDispose<'a>(Builder: &'a mut DIBuilder<'a>);
|
||||
|
||||
pub fn LLVMRustDIBuilderFinalize(Builder: &DIBuilder<'_>);
|
||||
|
||||
pub fn LLVMRustDIBuilderCreateCompileUnit<'a>(
|
||||
Builder: &DIBuilder<'a>,
|
||||
Lang: c_uint,
|
||||
|
|
@ -2110,20 +2198,6 @@ unsafe extern "C" {
|
|||
Type: &'a DIType,
|
||||
) -> &'a DIDerivedType;
|
||||
|
||||
pub fn LLVMRustDIBuilderCreateLexicalBlock<'a>(
|
||||
Builder: &DIBuilder<'a>,
|
||||
Scope: &'a DIScope,
|
||||
File: &'a DIFile,
|
||||
Line: c_uint,
|
||||
Col: c_uint,
|
||||
) -> &'a DILexicalBlock;
|
||||
|
||||
pub fn LLVMRustDIBuilderCreateLexicalBlockFile<'a>(
|
||||
Builder: &DIBuilder<'a>,
|
||||
Scope: &'a DIScope,
|
||||
File: &'a DIFile,
|
||||
) -> &'a DILexicalBlock;
|
||||
|
||||
pub fn LLVMRustDIBuilderCreateStaticVariable<'a>(
|
||||
Builder: &DIBuilder<'a>,
|
||||
Context: Option<&'a DIScope>,
|
||||
|
|
@ -2248,14 +2322,6 @@ unsafe extern "C" {
|
|||
Ty: &'a DIType,
|
||||
) -> &'a DITemplateTypeParameter;
|
||||
|
||||
pub fn LLVMRustDIBuilderCreateNameSpace<'a>(
|
||||
Builder: &DIBuilder<'a>,
|
||||
Scope: Option<&'a DIScope>,
|
||||
Name: *const c_char,
|
||||
NameLen: size_t,
|
||||
ExportSymbols: bool,
|
||||
) -> &'a DINameSpace;
|
||||
|
||||
pub fn LLVMRustDICompositeTypeReplaceArrays<'a>(
|
||||
Builder: &DIBuilder<'a>,
|
||||
CompositeType: &'a DIType,
|
||||
|
|
@ -2263,12 +2329,6 @@ unsafe extern "C" {
|
|||
Params: Option<&'a DIArray>,
|
||||
);
|
||||
|
||||
pub fn LLVMRustDIBuilderCreateDebugLocation<'a>(
|
||||
Line: c_uint,
|
||||
Column: c_uint,
|
||||
Scope: &'a DIScope,
|
||||
InlinedAt: Option<&'a DILocation>,
|
||||
) -> &'a DILocation;
|
||||
pub fn LLVMRustDILocationCloneWithBaseDiscriminator<'a>(
|
||||
Location: &'a DILocation,
|
||||
BD: c_uint,
|
||||
|
|
|
|||
|
|
@ -741,6 +741,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
let val = bx.tcx().sess.ub_checks();
|
||||
bx.cx().const_bool(val)
|
||||
}
|
||||
mir::NullOp::ContractChecks => {
|
||||
let val = bx.tcx().sess.contract_checks();
|
||||
bx.cx().const_bool(val)
|
||||
}
|
||||
};
|
||||
let tcx = self.cx.tcx();
|
||||
OperandRef {
|
||||
|
|
|
|||
|
|
@ -675,7 +675,11 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
|
|||
Rvalue::Cast(_, _, _) => {}
|
||||
|
||||
Rvalue::NullaryOp(
|
||||
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks,
|
||||
NullOp::SizeOf
|
||||
| NullOp::AlignOf
|
||||
| NullOp::OffsetOf(_)
|
||||
| NullOp::UbChecks
|
||||
| NullOp::ContractChecks,
|
||||
_,
|
||||
) => {}
|
||||
Rvalue::ShallowInitBox(_, _) => {}
|
||||
|
|
|
|||
|
|
@ -293,6 +293,9 @@ pub trait Machine<'tcx>: Sized {
|
|||
/// Determines the result of a `NullaryOp::UbChecks` invocation.
|
||||
fn ub_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>;
|
||||
|
||||
/// Determines the result of a `NullaryOp::ContractChecks` invocation.
|
||||
fn contract_checks(_ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool>;
|
||||
|
||||
/// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction.
|
||||
/// You can use this to detect long or endlessly running programs.
|
||||
#[inline]
|
||||
|
|
@ -679,6 +682,13 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
|
|||
interp_ok(true)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn contract_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> {
|
||||
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to
|
||||
// unsound differences in evaluating the same constant at different instantiation sites.
|
||||
interp_ok(true)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn adjust_global_allocation<'b>(
|
||||
_ecx: &InterpCx<$tcx, Self>,
|
||||
|
|
|
|||
|
|
@ -537,6 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
ImmTy::from_uint(val, usize_layout())
|
||||
}
|
||||
UbChecks => ImmTy::from_bool(M::ub_checks(self)?, *self.tcx),
|
||||
ContractChecks => ImmTy::from_bool(M::contract_checks(self)?, *self.tcx),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ edition = "2021"
|
|||
arrayvec = { version = "0.7", default-features = false }
|
||||
bitflags = "2.4.1"
|
||||
either = "1.0"
|
||||
elsa = "=1.7.1"
|
||||
elsa = "1.11.0"
|
||||
ena = "0.14.3"
|
||||
indexmap = "2.4.0"
|
||||
jobserver_crate = { version = "0.1.28", package = "jobserver" }
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ const GATED_CFGS: &[GatedCfg] = &[
|
|||
// (name in cfg, feature, function to check if the feature is enabled)
|
||||
(sym::overflow_checks, sym::cfg_overflow_checks, Features::cfg_overflow_checks),
|
||||
(sym::ub_checks, sym::cfg_ub_checks, Features::cfg_ub_checks),
|
||||
(sym::contract_checks, sym::cfg_contract_checks, Features::cfg_contract_checks),
|
||||
(sym::target_thread_local, sym::cfg_target_thread_local, Features::cfg_target_thread_local),
|
||||
(
|
||||
sym::target_has_atomic_equal_alignment,
|
||||
|
|
|
|||
|
|
@ -403,6 +403,8 @@ declare_features! (
|
|||
(unstable, c_variadic, "1.34.0", Some(44930)),
|
||||
/// Allows the use of `#[cfg(<true/false>)]`.
|
||||
(unstable, cfg_boolean_literals, "1.83.0", Some(131204)),
|
||||
/// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled.
|
||||
(unstable, cfg_contract_checks, "CURRENT_RUSTC_VERSION", Some(128044)),
|
||||
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
|
||||
(unstable, cfg_overflow_checks, "1.71.0", Some(111466)),
|
||||
/// Provides the relocation model information as cfg entry
|
||||
|
|
@ -445,6 +447,10 @@ declare_features! (
|
|||
(unstable, const_trait_impl, "1.42.0", Some(67792)),
|
||||
/// Allows the `?` operator in const contexts.
|
||||
(unstable, const_try, "1.56.0", Some(74935)),
|
||||
/// Allows use of contracts attributes.
|
||||
(incomplete, contracts, "CURRENT_RUSTC_VERSION", Some(128044)),
|
||||
/// Allows access to internal machinery used to implement contracts.
|
||||
(internal, contracts_internals, "CURRENT_RUSTC_VERSION", Some(128044)),
|
||||
/// Allows coroutines to be cloned.
|
||||
(unstable, coroutine_clone, "1.65.0", Some(95360)),
|
||||
/// Allows defining coroutines.
|
||||
|
|
|
|||
|
|
@ -2654,6 +2654,8 @@ pub enum LocalSource {
|
|||
/// A desugared `expr = expr`, where the LHS is a tuple, struct, array or underscore expression.
|
||||
/// The span is that of the `=` sign.
|
||||
AssignDesugar(Span),
|
||||
/// A contract `#[ensures(..)]` attribute injects a let binding for the check that runs at point of return.
|
||||
Contract,
|
||||
}
|
||||
|
||||
/// Hints at the original code for a `match _ { .. }`.
|
||||
|
|
|
|||
|
|
@ -423,6 +423,10 @@ language_item_table! {
|
|||
|
||||
String, sym::String, string, Target::Struct, GenericRequirement::None;
|
||||
CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
|
||||
|
||||
// Experimental lang items for implementing contract pre- and post-condition checking.
|
||||
ContractBuildCheckEnsures, sym::contract_build_check_ensures, contract_build_check_ensures_fn, Target::Fn, GenericRequirement::None;
|
||||
ContractCheckRequires, sym::contract_check_requires, contract_check_requires_fn, Target::Fn, GenericRequirement::None;
|
||||
}
|
||||
|
||||
pub enum GenericRequirement {
|
||||
|
|
|
|||
|
|
@ -132,6 +132,9 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
|
|||
| sym::aggregate_raw_ptr
|
||||
| sym::ptr_metadata
|
||||
| sym::ub_checks
|
||||
| sym::contract_checks
|
||||
| sym::contract_check_requires
|
||||
| sym::contract_check_ensures
|
||||
| sym::fadd_algebraic
|
||||
| sym::fsub_algebraic
|
||||
| sym::fmul_algebraic
|
||||
|
|
@ -219,6 +222,16 @@ pub fn check_intrinsic_type(
|
|||
}
|
||||
};
|
||||
(n_tps, 0, 0, inputs, output, hir::Safety::Unsafe)
|
||||
} else if intrinsic_name == sym::contract_check_ensures {
|
||||
// contract_check_ensures::<'a, Ret, C>(&'a Ret, C)
|
||||
// where C: impl Fn(&'a Ret) -> bool,
|
||||
//
|
||||
// so: two type params, one lifetime param, 0 const params, two inputs, no return
|
||||
|
||||
let p = generics.param_at(0, tcx);
|
||||
let r = ty::Region::new_early_param(tcx, p.to_early_bound_region_data());
|
||||
let ref_ret = Ty::new_imm_ref(tcx, r, param(1));
|
||||
(2, 1, 0, vec![ref_ret, param(2)], tcx.types.unit, hir::Safety::Safe)
|
||||
} else {
|
||||
let safety = intrinsic_operation_unsafety(tcx, intrinsic_id);
|
||||
let (n_tps, n_cts, inputs, output) = match intrinsic_name {
|
||||
|
|
@ -610,6 +623,11 @@ pub fn check_intrinsic_type(
|
|||
|
||||
sym::box_new => (1, 0, vec![param(0)], Ty::new_box(tcx, param(0))),
|
||||
|
||||
// contract_checks() -> bool
|
||||
sym::contract_checks => (0, 0, Vec::new(), tcx.types.bool),
|
||||
// contract_check_requires::<C>(C) -> bool, where C: impl Fn() -> bool
|
||||
sym::contract_check_requires => (1, 0, vec![param(0)], tcx.types.unit),
|
||||
|
||||
sym::simd_eq
|
||||
| sym::simd_ne
|
||||
| sym::simd_lt
|
||||
|
|
|
|||
|
|
@ -831,7 +831,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
|
||||
// prim -> prim
|
||||
(Int(CEnum), Int(_)) => {
|
||||
self.cenum_impl_drop_lint(fcx);
|
||||
self.err_if_cenum_impl_drop(fcx);
|
||||
Ok(CastKind::EnumCast)
|
||||
}
|
||||
(Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast),
|
||||
|
|
@ -1091,19 +1091,14 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) {
|
||||
fn err_if_cenum_impl_drop(&self, fcx: &FnCtxt<'a, 'tcx>) {
|
||||
if let ty::Adt(d, _) = self.expr_ty.kind()
|
||||
&& d.has_dtor(fcx.tcx)
|
||||
{
|
||||
let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
|
||||
let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty);
|
||||
|
||||
fcx.tcx.emit_node_span_lint(
|
||||
lint::builtin::CENUM_IMPL_DROP_CAST,
|
||||
self.expr.hir_id,
|
||||
self.span,
|
||||
errors::CastEnumDrop { expr_ty, cast_ty },
|
||||
);
|
||||
fcx.dcx().emit_err(errors::CastEnumDrop { span: self.span, expr_ty, cast_ty });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -677,9 +677,11 @@ pub(crate) struct CannotCastToBool<'tcx> {
|
|||
pub help: CannotCastToBoolHelp,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_typeck_cast_enum_drop)]
|
||||
pub(crate) struct CastEnumDrop<'tcx> {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub expr_ty: Ty<'tcx>,
|
||||
pub cast_ty: Ty<'tcx>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -269,6 +269,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// diverging expression (e.g. it arose from desugaring of `try { return }`),
|
||||
// we skip issuing a warning because it is autogenerated code.
|
||||
ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {}
|
||||
// Likewise, do not lint unreachable code injected via contracts desugaring.
|
||||
ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::Contract) => {}
|
||||
ExprKind::Call(callee, _) => self.warn_if_unreachable(expr.hir_id, callee.span, "call"),
|
||||
ExprKind::MethodCall(segment, ..) => {
|
||||
self.warn_if_unreachable(expr.hir_id, segment.ident.span, "call")
|
||||
|
|
|
|||
|
|
@ -570,8 +570,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> {
|
||||
let ty = match <.kind {
|
||||
rustc_hir::PatExprKind::Lit { lit, .. } => {
|
||||
self.check_expr_lit(lit, Expectation::NoExpectation)
|
||||
rustc_hir::PatExprKind::Lit { lit, negated } => {
|
||||
let ty = self.check_expr_lit(lit, Expectation::NoExpectation);
|
||||
if *negated {
|
||||
self.register_bound(
|
||||
ty,
|
||||
self.tcx.require_lang_item(LangItem::Neg, Some(lt.span)),
|
||||
ObligationCause::dummy_with_span(lt.span),
|
||||
);
|
||||
}
|
||||
ty
|
||||
}
|
||||
rustc_hir::PatExprKind::ConstBlock(c) => {
|
||||
self.check_expr_const_block(c, Expectation::NoExpectation)
|
||||
|
|
|
|||
|
|
@ -595,6 +595,11 @@ fn register_builtins(store: &mut LintStore) {
|
|||
<https://github.com/rust-lang/rust/pull/125380> for more information",
|
||||
);
|
||||
store.register_removed("unsupported_calling_conventions", "converted into hard error");
|
||||
store.register_removed(
|
||||
"cenum_impl_drop_cast",
|
||||
"converted into hard error, \
|
||||
see <https://github.com/rust-lang/rust/issues/73333> for more information",
|
||||
);
|
||||
}
|
||||
|
||||
fn register_internals(store: &mut LintStore) {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ declare_lint_pass! {
|
|||
BARE_TRAIT_OBJECTS,
|
||||
BINDINGS_WITH_VARIANT_NAME,
|
||||
BREAK_WITH_LABEL_AND_LOOP,
|
||||
CENUM_IMPL_DROP_CAST,
|
||||
COHERENCE_LEAK_CHECK,
|
||||
CONFLICTING_REPR_HINTS,
|
||||
CONST_EVALUATABLE_UNCHECKED,
|
||||
|
|
@ -2612,58 +2611,6 @@ declare_lint! {
|
|||
@edition Edition2024 => Warn;
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `cenum_impl_drop_cast` lint detects an `as` cast of a field-less
|
||||
/// `enum` that implements [`Drop`].
|
||||
///
|
||||
/// [`Drop`]: https://doc.rust-lang.org/std/ops/trait.Drop.html
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// # #![allow(unused)]
|
||||
/// enum E {
|
||||
/// A,
|
||||
/// }
|
||||
///
|
||||
/// impl Drop for E {
|
||||
/// fn drop(&mut self) {
|
||||
/// println!("Drop");
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let e = E::A;
|
||||
/// let i = e as u32;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Casting a field-less `enum` that does not implement [`Copy`] to an
|
||||
/// integer moves the value without calling `drop`. This can result in
|
||||
/// surprising behavior if it was expected that `drop` should be called.
|
||||
/// Calling `drop` automatically would be inconsistent with other move
|
||||
/// operations. Since neither behavior is clear or consistent, it was
|
||||
/// decided that a cast of this nature will no longer be allowed.
|
||||
///
|
||||
/// This is a [future-incompatible] lint to transition this to a hard error
|
||||
/// in the future. See [issue #73333] for more details.
|
||||
///
|
||||
/// [future-incompatible]: ../index.md#future-incompatible-lints
|
||||
/// [issue #73333]: https://github.com/rust-lang/rust/issues/73333
|
||||
/// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html
|
||||
pub CENUM_IMPL_DROP_CAST,
|
||||
Deny,
|
||||
"a C-like enum implementing Drop is cast",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
|
||||
reference: "issue #73333 <https://github.com/rust-lang/rust/issues/73333>",
|
||||
};
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer
|
||||
/// and a pointer.
|
||||
|
|
|
|||
|
|
@ -1003,10 +1003,6 @@ extern "C" void LLVMRustDIBuilderDispose(LLVMDIBuilderRef Builder) {
|
|||
delete unwrap(Builder);
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustDIBuilderFinalize(LLVMDIBuilderRef Builder) {
|
||||
unwrap(Builder)->finalize();
|
||||
}
|
||||
|
||||
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateCompileUnit(
|
||||
LLVMDIBuilderRef Builder, unsigned Lang, LLVMMetadataRef FileRef,
|
||||
const char *Producer, size_t ProducerLen, bool isOptimized,
|
||||
|
|
@ -1183,20 +1179,6 @@ LLVMRustDIBuilderCreateQualifiedType(LLVMDIBuilderRef Builder, unsigned Tag,
|
|||
unwrap(Builder)->createQualifiedType(Tag, unwrapDI<DIType>(Type)));
|
||||
}
|
||||
|
||||
extern "C" LLVMMetadataRef
|
||||
LLVMRustDIBuilderCreateLexicalBlock(LLVMDIBuilderRef Builder,
|
||||
LLVMMetadataRef Scope, LLVMMetadataRef File,
|
||||
unsigned Line, unsigned Col) {
|
||||
return wrap(unwrap(Builder)->createLexicalBlock(
|
||||
unwrapDI<DIDescriptor>(Scope), unwrapDI<DIFile>(File), Line, Col));
|
||||
}
|
||||
|
||||
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlockFile(
|
||||
LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, LLVMMetadataRef File) {
|
||||
return wrap(unwrap(Builder)->createLexicalBlockFile(
|
||||
unwrapDI<DIDescriptor>(Scope), unwrapDI<DIFile>(File)));
|
||||
}
|
||||
|
||||
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateStaticVariable(
|
||||
LLVMDIBuilderRef Builder, LLVMMetadataRef Context, const char *Name,
|
||||
size_t NameLen, const char *LinkageName, size_t LinkageNameLen,
|
||||
|
|
@ -1325,14 +1307,6 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateTemplateTypeParameter(
|
|||
unwrapDI<DIType>(Ty), IsDefault));
|
||||
}
|
||||
|
||||
extern "C" LLVMMetadataRef
|
||||
LLVMRustDIBuilderCreateNameSpace(LLVMDIBuilderRef Builder,
|
||||
LLVMMetadataRef Scope, const char *Name,
|
||||
size_t NameLen, bool ExportSymbols) {
|
||||
return wrap(unwrap(Builder)->createNameSpace(
|
||||
unwrapDI<DIDescriptor>(Scope), StringRef(Name, NameLen), ExportSymbols));
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustDICompositeTypeReplaceArrays(
|
||||
LLVMDIBuilderRef Builder, LLVMMetadataRef CompositeTy,
|
||||
LLVMMetadataRef Elements, LLVMMetadataRef Params) {
|
||||
|
|
@ -1341,16 +1315,6 @@ extern "C" void LLVMRustDICompositeTypeReplaceArrays(
|
|||
DINodeArray(unwrap<MDTuple>(Params)));
|
||||
}
|
||||
|
||||
extern "C" LLVMMetadataRef
|
||||
LLVMRustDIBuilderCreateDebugLocation(unsigned Line, unsigned Column,
|
||||
LLVMMetadataRef ScopeRef,
|
||||
LLVMMetadataRef InlinedAt) {
|
||||
MDNode *Scope = unwrapDIPtr<MDNode>(ScopeRef);
|
||||
DILocation *Loc = DILocation::get(Scope->getContext(), Line, Column, Scope,
|
||||
unwrapDIPtr<MDNode>(InlinedAt));
|
||||
return wrap(Loc);
|
||||
}
|
||||
|
||||
extern "C" LLVMMetadataRef
|
||||
LLVMRustDILocationCloneWithBaseDiscriminator(LLVMMetadataRef Location,
|
||||
unsigned BD) {
|
||||
|
|
|
|||
|
|
@ -1104,6 +1104,7 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
|||
NullOp::AlignOf => write!(fmt, "AlignOf({t})"),
|
||||
NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"),
|
||||
NullOp::UbChecks => write!(fmt, "UbChecks()"),
|
||||
NullOp::ContractChecks => write!(fmt, "ContractChecks()"),
|
||||
}
|
||||
}
|
||||
ThreadLocalRef(did) => ty::tls::with(|tcx| {
|
||||
|
|
|
|||
|
|
@ -1591,6 +1591,9 @@ pub enum NullOp<'tcx> {
|
|||
/// Returns whether we should perform some UB-checking at runtime.
|
||||
/// See the `ub_checks` intrinsic docs for details.
|
||||
UbChecks,
|
||||
/// Returns whether we should perform contract-checking at runtime.
|
||||
/// See the `contract_checks` intrinsic docs for details.
|
||||
ContractChecks,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
|
|
|
|||
|
|
@ -230,7 +230,8 @@ impl<'tcx> Rvalue<'tcx> {
|
|||
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
|
||||
tcx.types.usize
|
||||
}
|
||||
Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool,
|
||||
Rvalue::NullaryOp(NullOp::ContractChecks, _)
|
||||
| Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool,
|
||||
Rvalue::Aggregate(ref ak, ref ops) => match **ak {
|
||||
AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64),
|
||||
AggregateKind::Tuple => {
|
||||
|
|
|
|||
|
|
@ -105,13 +105,12 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>
|
|||
return Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar));
|
||||
}
|
||||
|
||||
let trunc = |n| {
|
||||
let width = match tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) {
|
||||
Ok(layout) => layout.size,
|
||||
Err(_) => {
|
||||
tcx.dcx().bug(format!("couldn't compute width of literal: {:?}", lit_input.lit))
|
||||
}
|
||||
};
|
||||
let trunc = |n, width: ty::UintTy| {
|
||||
let width = width
|
||||
.normalize(tcx.data_layout.pointer_size.bits().try_into().unwrap())
|
||||
.bit_width()
|
||||
.unwrap();
|
||||
let width = Size::from_bits(width);
|
||||
trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
|
||||
let result = width.truncate(n);
|
||||
trace!("trunc result: {}", result);
|
||||
|
|
@ -145,9 +144,11 @@ fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>
|
|||
(ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
|
||||
ConstValue::Scalar(Scalar::from_uint(*n, Size::from_bytes(1)))
|
||||
}
|
||||
(ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => {
|
||||
trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() })
|
||||
}
|
||||
(ast::LitKind::Int(n, _), ty::Uint(ui)) if !neg => trunc(n.get(), *ui),
|
||||
(ast::LitKind::Int(n, _), ty::Int(i)) => trunc(
|
||||
if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() },
|
||||
i.to_unsigned(),
|
||||
),
|
||||
(ast::LitKind::Float(n, _), ty::Float(fty)) => {
|
||||
parse_float_into_constval(*n, *fty, neg).unwrap()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use rustc_ast as ast;
|
||||
use rustc_abi::Size;
|
||||
use rustc_ast::{self as ast};
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::interpret::LitToConstInput;
|
||||
|
|
@ -17,13 +18,12 @@ pub(crate) fn lit_to_const<'tcx>(
|
|||
return ty::Const::new_error(tcx, guar);
|
||||
}
|
||||
|
||||
let trunc = |n| {
|
||||
let width = match tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) {
|
||||
Ok(layout) => layout.size,
|
||||
Err(_) => {
|
||||
tcx.dcx().bug(format!("couldn't compute width of literal: {:?}", lit_input.lit))
|
||||
}
|
||||
};
|
||||
let trunc = |n, width: ty::UintTy| {
|
||||
let width = width
|
||||
.normalize(tcx.data_layout.pointer_size.bits().try_into().unwrap())
|
||||
.bit_width()
|
||||
.unwrap();
|
||||
let width = Size::from_bits(width);
|
||||
trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
|
||||
let result = width.truncate(n);
|
||||
trace!("trunc result: {}", result);
|
||||
|
|
@ -55,9 +55,15 @@ pub(crate) fn lit_to_const<'tcx>(
|
|||
let bytes = data as &[u8];
|
||||
ty::ValTree::from_raw_bytes(tcx, bytes)
|
||||
}
|
||||
(ast::LitKind::Int(n, _), ty::Uint(_)) | (ast::LitKind::Int(n, _), ty::Int(_)) => {
|
||||
let scalar_int =
|
||||
trunc(if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() });
|
||||
(ast::LitKind::Int(n, _), ty::Uint(ui)) if !neg => {
|
||||
let scalar_int = trunc(n.get(), *ui);
|
||||
ty::ValTree::from_scalar_int(scalar_int)
|
||||
}
|
||||
(ast::LitKind::Int(n, _), ty::Int(i)) => {
|
||||
let scalar_int = trunc(
|
||||
if neg { (n.get() as i128).overflowing_neg().0 as u128 } else { n.get() },
|
||||
i.to_unsigned(),
|
||||
);
|
||||
ty::ValTree::from_scalar_int(scalar_int)
|
||||
}
|
||||
(ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()),
|
||||
|
|
|
|||
|
|
@ -417,7 +417,11 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
|
|||
| Rvalue::Discriminant(..)
|
||||
| Rvalue::Len(..)
|
||||
| Rvalue::NullaryOp(
|
||||
NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbChecks,
|
||||
NullOp::SizeOf
|
||||
| NullOp::AlignOf
|
||||
| NullOp::OffsetOf(..)
|
||||
| NullOp::UbChecks
|
||||
| NullOp::ContractChecks,
|
||||
_,
|
||||
) => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -545,6 +545,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
|||
.offset_of_subfield(self.typing_env(), layout, fields.iter())
|
||||
.bytes(),
|
||||
NullOp::UbChecks => return None,
|
||||
NullOp::ContractChecks => return None,
|
||||
};
|
||||
let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap();
|
||||
let imm = ImmTy::from_uint(val, usize_layout);
|
||||
|
|
|
|||
|
|
@ -629,6 +629,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
|||
.offset_of_subfield(self.typing_env, op_layout, fields.iter())
|
||||
.bytes(),
|
||||
NullOp::UbChecks => return None,
|
||||
NullOp::ContractChecks => return None,
|
||||
};
|
||||
ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,17 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics {
|
|||
});
|
||||
terminator.kind = TerminatorKind::Goto { target };
|
||||
}
|
||||
sym::contract_checks => {
|
||||
let target = target.unwrap();
|
||||
block.statements.push(Statement {
|
||||
source_info: terminator.source_info,
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
*destination,
|
||||
Rvalue::NullaryOp(NullOp::ContractChecks, tcx.types.bool),
|
||||
))),
|
||||
});
|
||||
terminator.kind = TerminatorKind::Goto { target };
|
||||
}
|
||||
sym::forget => {
|
||||
let target = target.unwrap();
|
||||
block.statements.push(Statement {
|
||||
|
|
|
|||
|
|
@ -457,6 +457,7 @@ impl<'tcx> Validator<'_, 'tcx> {
|
|||
NullOp::AlignOf => {}
|
||||
NullOp::OffsetOf(_) => {}
|
||||
NullOp::UbChecks => {}
|
||||
NullOp::ContractChecks => {}
|
||||
},
|
||||
|
||||
Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable),
|
||||
|
|
|
|||
|
|
@ -1379,7 +1379,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
|||
Rvalue::Repeat(_, _)
|
||||
| Rvalue::ThreadLocalRef(_)
|
||||
| Rvalue::RawPtr(_, _)
|
||||
| Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _)
|
||||
| Rvalue::NullaryOp(
|
||||
NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks | NullOp::ContractChecks,
|
||||
_,
|
||||
)
|
||||
| Rvalue::Discriminant(_) => {}
|
||||
|
||||
Rvalue::WrapUnsafeBinder(op, ty) => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use rustc_ast::{
|
|||
WhereClause, token,
|
||||
};
|
||||
use rustc_errors::{Applicability, PResult};
|
||||
use rustc_span::{Ident, Span, kw};
|
||||
use rustc_span::{Ident, Span, kw, sym};
|
||||
use thin_vec::ThinVec;
|
||||
|
||||
use super::{ForceCollect, Parser, Trailing, UsePreAttrPos};
|
||||
|
|
@ -297,6 +297,42 @@ impl<'a> Parser<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Parses an experimental fn contract
|
||||
/// (`contract_requires(WWW) contract_ensures(ZZZ)`)
|
||||
pub(super) fn parse_contract(
|
||||
&mut self,
|
||||
) -> PResult<'a, Option<rustc_ast::ptr::P<ast::FnContract>>> {
|
||||
let gate = |span| {
|
||||
if self.psess.contract_attribute_spans.contains(span) {
|
||||
// span was generated via a builtin contracts attribute, so gate as end-user visible
|
||||
self.psess.gated_spans.gate(sym::contracts, span);
|
||||
} else {
|
||||
// span was not generated via a builtin contracts attribute, so gate as internal machinery
|
||||
self.psess.gated_spans.gate(sym::contracts_internals, span);
|
||||
}
|
||||
};
|
||||
|
||||
let requires = if self.eat_keyword_noexpect(exp!(ContractRequires).kw) {
|
||||
let precond = self.parse_expr()?;
|
||||
gate(precond.span);
|
||||
Some(precond)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let ensures = if self.eat_keyword_noexpect(exp!(ContractEnsures).kw) {
|
||||
let postcond = self.parse_expr()?;
|
||||
gate(postcond.span);
|
||||
Some(postcond)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if requires.is_none() && ensures.is_none() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(rustc_ast::ptr::P(ast::FnContract { requires, ensures })))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses an optional where-clause.
|
||||
///
|
||||
/// ```ignore (only-for-syntax-highlight)
|
||||
|
|
|
|||
|
|
@ -213,9 +213,12 @@ impl<'a> Parser<'a> {
|
|||
self.parse_use_item()?
|
||||
} else if self.check_fn_front_matter(check_pub, case) {
|
||||
// FUNCTION ITEM
|
||||
let (ident, sig, generics, body) =
|
||||
let (ident, sig, generics, contract, body) =
|
||||
self.parse_fn(attrs, fn_parse_mode, lo, vis, case)?;
|
||||
(ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body })))
|
||||
(
|
||||
ident,
|
||||
ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, contract, body })),
|
||||
)
|
||||
} else if self.eat_keyword(exp!(Extern)) {
|
||||
if self.eat_keyword(exp!(Crate)) {
|
||||
// EXTERN CRATE
|
||||
|
|
@ -2372,7 +2375,7 @@ impl<'a> Parser<'a> {
|
|||
sig_lo: Span,
|
||||
vis: &Visibility,
|
||||
case: Case,
|
||||
) -> PResult<'a, (Ident, FnSig, Generics, Option<P<Block>>)> {
|
||||
) -> PResult<'a, (Ident, FnSig, Generics, Option<P<FnContract>>, Option<P<Block>>)> {
|
||||
let fn_span = self.token.span;
|
||||
let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn`
|
||||
let ident = self.parse_ident()?; // `foo`
|
||||
|
|
@ -2398,6 +2401,8 @@ impl<'a> Parser<'a> {
|
|||
// inside `parse_fn_body()`.
|
||||
let fn_params_end = self.prev_token.span.shrink_to_hi();
|
||||
|
||||
let contract = self.parse_contract()?;
|
||||
|
||||
generics.where_clause = self.parse_where_clause()?; // `where T: Ord`
|
||||
|
||||
// `fn_params_end` is needed only when it's followed by a where clause.
|
||||
|
|
@ -2409,7 +2414,7 @@ impl<'a> Parser<'a> {
|
|||
let body =
|
||||
self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?;
|
||||
let fn_sig_span = sig_lo.to(sig_hi);
|
||||
Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body))
|
||||
Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, contract, body))
|
||||
}
|
||||
|
||||
/// Provide diagnostics when function body is not found
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ pub enum TokenType {
|
|||
KwCatch,
|
||||
KwConst,
|
||||
KwContinue,
|
||||
KwContractEnsures,
|
||||
KwContractRequires,
|
||||
KwCrate,
|
||||
KwDefault,
|
||||
KwDyn,
|
||||
|
|
@ -217,6 +219,8 @@ impl TokenType {
|
|||
KwCatch,
|
||||
KwConst,
|
||||
KwContinue,
|
||||
KwContractEnsures,
|
||||
KwContractRequires,
|
||||
KwCrate,
|
||||
KwDefault,
|
||||
KwDyn,
|
||||
|
|
@ -289,6 +293,8 @@ impl TokenType {
|
|||
TokenType::KwCatch => Some(kw::Catch),
|
||||
TokenType::KwConst => Some(kw::Const),
|
||||
TokenType::KwContinue => Some(kw::Continue),
|
||||
TokenType::KwContractEnsures => Some(kw::ContractEnsures),
|
||||
TokenType::KwContractRequires => Some(kw::ContractRequires),
|
||||
TokenType::KwCrate => Some(kw::Crate),
|
||||
TokenType::KwDefault => Some(kw::Default),
|
||||
TokenType::KwDyn => Some(kw::Dyn),
|
||||
|
|
@ -519,6 +525,8 @@ macro_rules! exp {
|
|||
(Catch) => { exp!(@kw, Catch, KwCatch) };
|
||||
(Const) => { exp!(@kw, Const, KwConst) };
|
||||
(Continue) => { exp!(@kw, Continue, KwContinue) };
|
||||
(ContractEnsures) => { exp!(@kw, ContractEnsures, KwContractEnsures) };
|
||||
(ContractRequires) => { exp!(@kw, ContractRequires, KwContractRequires) };
|
||||
(Crate) => { exp!(@kw, Crate, KwCrate) };
|
||||
(Default) => { exp!(@kw, Default, KwDefault) };
|
||||
(Dyn) => { exp!(@kw, Dyn, KwDyn) };
|
||||
|
|
|
|||
|
|
@ -174,10 +174,13 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
|
|||
_ctxt,
|
||||
_ident,
|
||||
_vis,
|
||||
Fn { sig: FnSig { header, decl, span: _ }, generics, body, .. },
|
||||
Fn { sig: FnSig { header, decl, span: _ }, generics, contract, body, .. },
|
||||
) if let Some(coroutine_kind) = header.coroutine_kind => {
|
||||
self.visit_fn_header(header);
|
||||
self.visit_generics(generics);
|
||||
if let Some(contract) = contract {
|
||||
self.visit_contract(contract);
|
||||
}
|
||||
|
||||
// For async functions, we need to create their inner defs inside of a
|
||||
// closure to match their desugared representation. Besides that,
|
||||
|
|
|
|||
|
|
@ -1019,7 +1019,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
|
|||
// Create a label rib for the function.
|
||||
this.with_label_rib(RibKind::FnOrCoroutine, |this| {
|
||||
match fn_kind {
|
||||
FnKind::Fn(_, _, _, Fn { sig, generics, body, .. }) => {
|
||||
FnKind::Fn(_, _, _, Fn { sig, generics, contract, body, .. }) => {
|
||||
this.visit_generics(generics);
|
||||
|
||||
let declaration = &sig.decl;
|
||||
|
|
@ -1046,6 +1046,10 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
|
|||
},
|
||||
);
|
||||
|
||||
if let Some(contract) = contract {
|
||||
this.visit_contract(contract);
|
||||
}
|
||||
|
||||
if let Some(body) = body {
|
||||
// Ignore errors in function bodies if this is rustdoc
|
||||
// Be sure not to set this until the function signature has been resolved.
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) {
|
|||
(sym::overflow_checks, None) => disallow(cfg, "-C overflow-checks"),
|
||||
(sym::debug_assertions, None) => disallow(cfg, "-C debug-assertions"),
|
||||
(sym::ub_checks, None) => disallow(cfg, "-Z ub-checks"),
|
||||
(sym::contract_checks, None) => disallow(cfg, "-Z contract-checks"),
|
||||
(sym::sanitize, None | Some(_)) => disallow(cfg, "-Z sanitizer"),
|
||||
(
|
||||
sym::sanitizer_cfi_generalize_pointers | sym::sanitizer_cfi_normalize_integers,
|
||||
|
|
@ -300,6 +301,11 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg {
|
|||
if sess.is_nightly_build() && sess.opts.unstable_opts.emscripten_wasm_eh {
|
||||
ins_none!(sym::emscripten_wasm_eh);
|
||||
}
|
||||
|
||||
if sess.contract_checks() {
|
||||
ins_none!(sym::contract_checks);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
|
|
@ -464,6 +470,7 @@ impl CheckCfg {
|
|||
ins!(sym::target_thread_local, no_values);
|
||||
|
||||
ins!(sym::ub_checks, no_values);
|
||||
ins!(sym::contract_checks, no_values);
|
||||
|
||||
ins!(sym::unix, no_values);
|
||||
ins!(sym::windows, no_values);
|
||||
|
|
|
|||
|
|
@ -2114,6 +2114,8 @@ options! {
|
|||
"the backend to use"),
|
||||
combine_cgu: bool = (false, parse_bool, [TRACKED],
|
||||
"combine CGUs into a single one"),
|
||||
contract_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
||||
"emit runtime checks for contract pre- and post-conditions (default: no)"),
|
||||
coverage_options: CoverageOptions = (CoverageOptions::default(), parse_coverage_options, [TRACKED],
|
||||
"control details of coverage instrumentation"),
|
||||
crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
|
||||
|
|
|
|||
|
|
@ -207,6 +207,10 @@ pub struct ParseSess {
|
|||
pub config: Cfg,
|
||||
pub check_config: CheckCfg,
|
||||
pub edition: Edition,
|
||||
/// Places where contract attributes were expanded into unstable AST forms.
|
||||
/// This is used to allowlist those spans (so that we only check them against the feature
|
||||
/// gate for the externally visible interface, and not internal implmentation machinery).
|
||||
pub contract_attribute_spans: AppendOnlyVec<Span>,
|
||||
/// Places where raw identifiers were used. This is used to avoid complaining about idents
|
||||
/// clashing with keywords in new editions.
|
||||
pub raw_identifier_spans: AppendOnlyVec<Span>,
|
||||
|
|
@ -255,6 +259,7 @@ impl ParseSess {
|
|||
config: Cfg::default(),
|
||||
check_config: CheckCfg::default(),
|
||||
edition: ExpnId::root().expn_data().edition,
|
||||
contract_attribute_spans: Default::default(),
|
||||
raw_identifier_spans: Default::default(),
|
||||
bad_unicode_identifiers: Lock::new(Default::default()),
|
||||
source_map,
|
||||
|
|
|
|||
|
|
@ -709,6 +709,10 @@ impl Session {
|
|||
self.opts.unstable_opts.ub_checks.unwrap_or(self.opts.debug_assertions)
|
||||
}
|
||||
|
||||
pub fn contract_checks(&self) -> bool {
|
||||
self.opts.unstable_opts.contract_checks.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn relocation_model(&self) -> RelocModel {
|
||||
self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -291,6 +291,7 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> {
|
|||
indices.iter().map(|idx| idx.stable(tables)).collect(),
|
||||
),
|
||||
UbChecks => stable_mir::mir::NullOp::UbChecks,
|
||||
ContractChecks => stable_mir::mir::NullOp::ContractChecks,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1163,6 +1163,8 @@ pub enum DesugaringKind {
|
|||
WhileLoop,
|
||||
/// `async Fn()` bound modifier
|
||||
BoundModifier,
|
||||
/// Calls to contract checks (`#[requires]` to precond, `#[ensures]` to postcond)
|
||||
Contract,
|
||||
}
|
||||
|
||||
impl DesugaringKind {
|
||||
|
|
@ -1179,6 +1181,7 @@ impl DesugaringKind {
|
|||
DesugaringKind::ForLoop => "`for` loop",
|
||||
DesugaringKind::WhileLoop => "`while` loop",
|
||||
DesugaringKind::BoundModifier => "trait bound modifier",
|
||||
DesugaringKind::Contract => "contract check",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,8 @@ symbols! {
|
|||
MacroRules: "macro_rules",
|
||||
Raw: "raw",
|
||||
Reuse: "reuse",
|
||||
ContractEnsures: "contract_ensures",
|
||||
ContractRequires: "contract_requires",
|
||||
Safe: "safe",
|
||||
Union: "union",
|
||||
Yeet: "yeet",
|
||||
|
|
@ -569,6 +571,7 @@ symbols! {
|
|||
cfg_attr,
|
||||
cfg_attr_multi,
|
||||
cfg_boolean_literals,
|
||||
cfg_contract_checks,
|
||||
cfg_doctest,
|
||||
cfg_emscripten_wasm_eh,
|
||||
cfg_eval,
|
||||
|
|
@ -678,6 +681,14 @@ symbols! {
|
|||
const_ty_placeholder: "<const_ty>",
|
||||
constant,
|
||||
constructor,
|
||||
contract_build_check_ensures,
|
||||
contract_check_ensures,
|
||||
contract_check_requires,
|
||||
contract_checks,
|
||||
contracts,
|
||||
contracts_ensures,
|
||||
contracts_internals,
|
||||
contracts_requires,
|
||||
convert_identity,
|
||||
copy,
|
||||
copy_closures,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ pub(crate) fn target() -> Target {
|
|||
pointer_width: 32,
|
||||
data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(),
|
||||
arch: "powerpc".into(),
|
||||
options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base },
|
||||
options: TargetOptions {
|
||||
endian: Endian::Big,
|
||||
features: "+secure-plt".into(),
|
||||
mcount: "_mcount".into(),
|
||||
..base
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ pub(crate) fn target() -> Target {
|
|||
options: TargetOptions {
|
||||
abi: "spe".into(),
|
||||
endian: Endian::Big,
|
||||
features: "+secure-plt".into(),
|
||||
mcount: "_mcount".into(),
|
||||
..base
|
||||
},
|
||||
|
|
|
|||
|
|
@ -608,7 +608,8 @@ impl Rvalue {
|
|||
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => {
|
||||
Ok(Ty::usize_ty())
|
||||
}
|
||||
Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()),
|
||||
Rvalue::NullaryOp(NullOp::ContractChecks, _)
|
||||
| Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()),
|
||||
Rvalue::Aggregate(ak, ops) => match *ak {
|
||||
AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64),
|
||||
AggregateKind::Tuple => Ok(Ty::new_tuple(
|
||||
|
|
@ -1007,6 +1008,8 @@ pub enum NullOp {
|
|||
OffsetOf(Vec<(VariantIdx, FieldIdx)>),
|
||||
/// cfg!(ub_checks), but at codegen time
|
||||
UbChecks,
|
||||
/// cfg!(contract_checks), but at codegen time
|
||||
ContractChecks,
|
||||
}
|
||||
|
||||
impl Operand {
|
||||
|
|
|
|||
|
|
@ -61,9 +61,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.143"
|
||||
version = "0.1.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c85ba2077e3eab3dd81be4ece6b7fb2ad0887c1fb813e9a45400baf75c6c7c29"
|
||||
checksum = "da0705f5abaaab7168ccc14f8f340ded61be2bd3ebea86b9834b6acbc8495de8"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"rustc-std-workspace-core",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
compiler_builtins = { version = "=0.1.143", features = ['rustc-dep-of-std'] }
|
||||
compiler_builtins = { version = "=0.1.145", features = ['rustc-dep-of-std'] }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
|
||||
|
|
|
|||
21
library/core/src/contracts.rs
Normal file
21
library/core/src/contracts.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
//! Unstable module containing the unstable contracts lang items and attribute macros.
|
||||
#![cfg(not(bootstrap))]
|
||||
|
||||
pub use crate::macros::builtin::{contracts_ensures as ensures, contracts_requires as requires};
|
||||
|
||||
/// Emitted by rustc as a desugaring of `#[ensures(PRED)] fn foo() -> R { ... [return R;] ... }`
|
||||
/// into: `fn foo() { let _check = build_check_ensures(|ret| PRED) ... [return _check(R);] ... }`
|
||||
/// (including the implicit return of the tail expression, if any).
|
||||
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
|
||||
#[lang = "contract_build_check_ensures"]
|
||||
#[track_caller]
|
||||
pub fn build_check_ensures<Ret, C>(cond: C) -> impl (Fn(Ret) -> Ret) + Copy
|
||||
where
|
||||
C: for<'a> Fn(&'a Ret) -> bool + Copy + 'static,
|
||||
{
|
||||
#[track_caller]
|
||||
move |ret| {
|
||||
crate::intrinsics::contract_check_ensures(&ret, cond);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
|
@ -1228,6 +1228,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
|||
/// assert_eq!(format!("{:?}", wrapped), "'a'");
|
||||
/// ```
|
||||
#[unstable(feature = "debug_closure_helpers", issue = "117729")]
|
||||
#[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"]
|
||||
pub fn from_fn<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result>(f: F) -> FromFn<F> {
|
||||
FromFn(f)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4064,6 +4064,52 @@ pub const unsafe fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize)
|
|||
// Runtime NOP
|
||||
}
|
||||
|
||||
/// Returns whether we should perform contract-checking at runtime.
|
||||
///
|
||||
/// This is meant to be similar to the ub_checks intrinsic, in terms
|
||||
/// of not prematurely commiting at compile-time to whether contract
|
||||
/// checking is turned on, so that we can specify contracts in libstd
|
||||
/// and let an end user opt into turning them on.
|
||||
#[cfg(not(bootstrap))]
|
||||
#[rustc_const_unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
|
||||
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
|
||||
#[inline(always)]
|
||||
#[rustc_intrinsic]
|
||||
pub const fn contract_checks() -> bool {
|
||||
// FIXME: should this be `false` or `cfg!(contract_checks)`?
|
||||
|
||||
// cfg!(contract_checks)
|
||||
false
|
||||
}
|
||||
|
||||
/// Check if the pre-condition `cond` has been met.
|
||||
///
|
||||
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
|
||||
/// returns false.
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
|
||||
#[lang = "contract_check_requires"]
|
||||
#[rustc_intrinsic]
|
||||
pub fn contract_check_requires<C: Fn() -> bool>(cond: C) {
|
||||
if contract_checks() && !cond() {
|
||||
// Emit no unwind panic in case this was a safety requirement.
|
||||
crate::panicking::panic_nounwind("failed requires check");
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the post-condition `cond` has been met.
|
||||
///
|
||||
/// By default, if `contract_checks` is enabled, this will panic with no unwind if the condition
|
||||
/// returns false.
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "contracts_internals", issue = "128044" /* compiler-team#759 */)]
|
||||
#[rustc_intrinsic]
|
||||
pub fn contract_check_ensures<'a, Ret, C: Fn(&'a Ret) -> bool>(ret: &'a Ret, cond: C) {
|
||||
if contract_checks() && !cond(ret) {
|
||||
crate::panicking::panic_nounwind("failed ensures check");
|
||||
}
|
||||
}
|
||||
|
||||
/// The intrinsic will return the size stored in that vtable.
|
||||
///
|
||||
/// # Safety
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@
|
|||
#![feature(bigint_helper_methods)]
|
||||
#![feature(bstr)]
|
||||
#![feature(bstr_internals)]
|
||||
#![feature(closure_track_caller)]
|
||||
#![feature(const_carrying_mul_add)]
|
||||
#![feature(const_eval_select)]
|
||||
#![feature(core_intrinsics)]
|
||||
|
|
@ -247,6 +248,10 @@ pub mod autodiff {
|
|||
pub use crate::macros::builtin::autodiff;
|
||||
}
|
||||
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "contracts", issue = "128044")]
|
||||
pub mod contracts;
|
||||
|
||||
#[unstable(feature = "cfg_match", issue = "115585")]
|
||||
pub use crate::macros::cfg_match;
|
||||
|
||||
|
|
|
|||
|
|
@ -1777,6 +1777,32 @@ pub(crate) mod builtin {
|
|||
/* compiler built-in */
|
||||
}
|
||||
|
||||
/// Attribute macro applied to a function to give it a post-condition.
|
||||
///
|
||||
/// The attribute carries an argument token-tree which is
|
||||
/// eventually parsed as a unary closure expression that is
|
||||
/// invoked on a reference to the return value.
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "contracts", issue = "128044")]
|
||||
#[allow_internal_unstable(contracts_internals)]
|
||||
#[rustc_builtin_macro]
|
||||
pub macro contracts_ensures($item:item) {
|
||||
/* compiler built-in */
|
||||
}
|
||||
|
||||
/// Attribute macro applied to a function to give it a precondition.
|
||||
///
|
||||
/// The attribute carries an argument token-tree which is
|
||||
/// eventually parsed as an boolean expression with access to the
|
||||
/// function's formal parameters
|
||||
#[cfg(not(bootstrap))]
|
||||
#[unstable(feature = "contracts", issue = "128044")]
|
||||
#[allow_internal_unstable(contracts_internals)]
|
||||
#[rustc_builtin_macro]
|
||||
pub macro contracts_requires($item:item) {
|
||||
/* compiler built-in */
|
||||
}
|
||||
|
||||
/// Attribute macro applied to a function to register it as a handler for allocation failure.
|
||||
///
|
||||
/// See also [`std::alloc::handle_alloc_error`](../../../std/alloc/fn.handle_alloc_error.html).
|
||||
|
|
|
|||
|
|
@ -182,10 +182,10 @@ pub use self::function::{Fn, FnMut, FnOnce};
|
|||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::index::{Index, IndexMut};
|
||||
pub(crate) use self::index_range::IndexRange;
|
||||
#[unstable(feature = "one_sided_range", issue = "69780")]
|
||||
pub use self::range::OneSidedRange;
|
||||
#[stable(feature = "inclusive_range", since = "1.26.0")]
|
||||
pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive};
|
||||
#[unstable(feature = "one_sided_range", issue = "69780")]
|
||||
pub use self::range::{OneSidedRange, OneSidedRangeBound};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::range::{Range, RangeFrom, RangeFull, RangeTo};
|
||||
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
|
||||
|
|
|
|||
|
|
@ -979,6 +979,19 @@ impl<T> RangeBounds<T> for RangeToInclusive<&T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// An internal helper for `split_off` functions indicating
|
||||
/// which end a `OneSidedRange` is bounded on.
|
||||
#[unstable(feature = "one_sided_range", issue = "69780")]
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub enum OneSidedRangeBound {
|
||||
/// The range is bounded inclusively from below and is unbounded above.
|
||||
StartInclusive,
|
||||
/// The range is bounded exclusively from above and is unbounded below.
|
||||
End,
|
||||
/// The range is bounded inclusively from above and is unbounded below.
|
||||
EndInclusive,
|
||||
}
|
||||
|
||||
/// `OneSidedRange` is implemented for built-in range types that are unbounded
|
||||
/// on one side. For example, `a..`, `..b` and `..=c` implement `OneSidedRange`,
|
||||
/// but `..`, `d..e`, and `f..=g` do not.
|
||||
|
|
@ -986,13 +999,38 @@ impl<T> RangeBounds<T> for RangeToInclusive<&T> {
|
|||
/// Types that implement `OneSidedRange<T>` must return `Bound::Unbounded`
|
||||
/// from one of `RangeBounds::start_bound` or `RangeBounds::end_bound`.
|
||||
#[unstable(feature = "one_sided_range", issue = "69780")]
|
||||
pub trait OneSidedRange<T: ?Sized>: RangeBounds<T> {}
|
||||
pub trait OneSidedRange<T: ?Sized>: RangeBounds<T> {
|
||||
/// An internal-only helper function for `split_off` and
|
||||
/// `split_off_mut` that returns the bound of the one-sided range.
|
||||
fn bound(self) -> (OneSidedRangeBound, T);
|
||||
}
|
||||
|
||||
#[unstable(feature = "one_sided_range", issue = "69780")]
|
||||
impl<T> OneSidedRange<T> for RangeTo<T> where Self: RangeBounds<T> {}
|
||||
impl<T> OneSidedRange<T> for RangeTo<T>
|
||||
where
|
||||
Self: RangeBounds<T>,
|
||||
{
|
||||
fn bound(self) -> (OneSidedRangeBound, T) {
|
||||
(OneSidedRangeBound::End, self.end)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "one_sided_range", issue = "69780")]
|
||||
impl<T> OneSidedRange<T> for RangeFrom<T> where Self: RangeBounds<T> {}
|
||||
impl<T> OneSidedRange<T> for RangeFrom<T>
|
||||
where
|
||||
Self: RangeBounds<T>,
|
||||
{
|
||||
fn bound(self) -> (OneSidedRangeBound, T) {
|
||||
(OneSidedRangeBound::StartInclusive, self.start)
|
||||
}
|
||||
}
|
||||
|
||||
#[unstable(feature = "one_sided_range", issue = "69780")]
|
||||
impl<T> OneSidedRange<T> for RangeToInclusive<T> where Self: RangeBounds<T> {}
|
||||
impl<T> OneSidedRange<T> for RangeToInclusive<T>
|
||||
where
|
||||
Self: RangeBounds<T>,
|
||||
{
|
||||
fn bound(self) -> (OneSidedRangeBound, T) {
|
||||
(OneSidedRangeBound::EndInclusive, self.end)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less};
|
|||
use crate::intrinsics::{exact_div, unchecked_sub};
|
||||
use crate::mem::{self, SizedTypeProperties};
|
||||
use crate::num::NonZero;
|
||||
use crate::ops::{Bound, OneSidedRange, Range, RangeBounds, RangeInclusive};
|
||||
use crate::ops::{OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeInclusive};
|
||||
use crate::panic::const_panic;
|
||||
use crate::simd::{self, Simd};
|
||||
use crate::ub_checks::assert_unsafe_precondition;
|
||||
|
|
@ -83,14 +83,12 @@ pub use raw::{from_raw_parts, from_raw_parts_mut};
|
|||
/// which to split. Returns `None` if the split index would overflow.
|
||||
#[inline]
|
||||
fn split_point_of(range: impl OneSidedRange<usize>) -> Option<(Direction, usize)> {
|
||||
use Bound::*;
|
||||
use OneSidedRangeBound::{End, EndInclusive, StartInclusive};
|
||||
|
||||
Some(match (range.start_bound(), range.end_bound()) {
|
||||
(Unbounded, Excluded(i)) => (Direction::Front, *i),
|
||||
(Unbounded, Included(i)) => (Direction::Front, i.checked_add(1)?),
|
||||
(Excluded(i), Unbounded) => (Direction::Back, i.checked_add(1)?),
|
||||
(Included(i), Unbounded) => (Direction::Back, *i),
|
||||
_ => unreachable!(),
|
||||
Some(match range.bound() {
|
||||
(StartInclusive, i) => (Direction::Back, i),
|
||||
(End, i) => (Direction::Front, i),
|
||||
(EndInclusive, i) => (Direction::Front, i.checked_add(1)?),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -4294,25 +4292,25 @@ impl<T> [T] {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Taking the first three elements of a slice:
|
||||
/// Splitting off the first three elements of a slice:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(slice_take)]
|
||||
///
|
||||
/// let mut slice: &[_] = &['a', 'b', 'c', 'd'];
|
||||
/// let mut first_three = slice.take(..3).unwrap();
|
||||
/// let mut first_three = slice.split_off(..3).unwrap();
|
||||
///
|
||||
/// assert_eq!(slice, &['d']);
|
||||
/// assert_eq!(first_three, &['a', 'b', 'c']);
|
||||
/// ```
|
||||
///
|
||||
/// Taking the last two elements of a slice:
|
||||
/// Splitting off the last two elements of a slice:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(slice_take)]
|
||||
///
|
||||
/// let mut slice: &[_] = &['a', 'b', 'c', 'd'];
|
||||
/// let mut tail = slice.take(2..).unwrap();
|
||||
/// let mut tail = slice.split_off(2..).unwrap();
|
||||
///
|
||||
/// assert_eq!(slice, &['a', 'b']);
|
||||
/// assert_eq!(tail, &['c', 'd']);
|
||||
|
|
@ -4325,16 +4323,19 @@ impl<T> [T] {
|
|||
///
|
||||
/// let mut slice: &[_] = &['a', 'b', 'c', 'd'];
|
||||
///
|
||||
/// assert_eq!(None, slice.take(5..));
|
||||
/// assert_eq!(None, slice.take(..5));
|
||||
/// assert_eq!(None, slice.take(..=4));
|
||||
/// assert_eq!(None, slice.split_off(5..));
|
||||
/// assert_eq!(None, slice.split_off(..5));
|
||||
/// assert_eq!(None, slice.split_off(..=4));
|
||||
/// let expected: &[char] = &['a', 'b', 'c', 'd'];
|
||||
/// assert_eq!(Some(expected), slice.take(..4));
|
||||
/// assert_eq!(Some(expected), slice.split_off(..4));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use = "method does not modify the slice if the range is out of bounds"]
|
||||
#[unstable(feature = "slice_take", issue = "62280")]
|
||||
pub fn take<'a, R: OneSidedRange<usize>>(self: &mut &'a Self, range: R) -> Option<&'a Self> {
|
||||
pub fn split_off<'a, R: OneSidedRange<usize>>(
|
||||
self: &mut &'a Self,
|
||||
range: R,
|
||||
) -> Option<&'a Self> {
|
||||
let (direction, split_index) = split_point_of(range)?;
|
||||
if split_index > self.len() {
|
||||
return None;
|
||||
|
|
@ -4363,13 +4364,13 @@ impl<T> [T] {
|
|||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Taking the first three elements of a slice:
|
||||
/// Splitting off the first three elements of a slice:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(slice_take)]
|
||||
///
|
||||
/// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
|
||||
/// let mut first_three = slice.take_mut(..3).unwrap();
|
||||
/// let mut first_three = slice.split_off_mut(..3).unwrap();
|
||||
///
|
||||
/// assert_eq!(slice, &mut ['d']);
|
||||
/// assert_eq!(first_three, &mut ['a', 'b', 'c']);
|
||||
|
|
@ -4381,7 +4382,7 @@ impl<T> [T] {
|
|||
/// #![feature(slice_take)]
|
||||
///
|
||||
/// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
|
||||
/// let mut tail = slice.take_mut(2..).unwrap();
|
||||
/// let mut tail = slice.split_off_mut(2..).unwrap();
|
||||
///
|
||||
/// assert_eq!(slice, &mut ['a', 'b']);
|
||||
/// assert_eq!(tail, &mut ['c', 'd']);
|
||||
|
|
@ -4394,16 +4395,16 @@ impl<T> [T] {
|
|||
///
|
||||
/// let mut slice: &mut [_] = &mut ['a', 'b', 'c', 'd'];
|
||||
///
|
||||
/// assert_eq!(None, slice.take_mut(5..));
|
||||
/// assert_eq!(None, slice.take_mut(..5));
|
||||
/// assert_eq!(None, slice.take_mut(..=4));
|
||||
/// assert_eq!(None, slice.split_off_mut(5..));
|
||||
/// assert_eq!(None, slice.split_off_mut(..5));
|
||||
/// assert_eq!(None, slice.split_off_mut(..=4));
|
||||
/// let expected: &mut [_] = &mut ['a', 'b', 'c', 'd'];
|
||||
/// assert_eq!(Some(expected), slice.take_mut(..4));
|
||||
/// assert_eq!(Some(expected), slice.split_off_mut(..4));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use = "method does not modify the slice if the range is out of bounds"]
|
||||
#[unstable(feature = "slice_take", issue = "62280")]
|
||||
pub fn take_mut<'a, R: OneSidedRange<usize>>(
|
||||
pub fn split_off_mut<'a, R: OneSidedRange<usize>>(
|
||||
self: &mut &'a mut Self,
|
||||
range: R,
|
||||
) -> Option<&'a mut Self> {
|
||||
|
|
@ -4435,14 +4436,14 @@ impl<T> [T] {
|
|||
/// #![feature(slice_take)]
|
||||
///
|
||||
/// let mut slice: &[_] = &['a', 'b', 'c'];
|
||||
/// let first = slice.take_first().unwrap();
|
||||
/// let first = slice.split_off_first().unwrap();
|
||||
///
|
||||
/// assert_eq!(slice, &['b', 'c']);
|
||||
/// assert_eq!(first, &'a');
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "slice_take", issue = "62280")]
|
||||
pub fn take_first<'a>(self: &mut &'a Self) -> Option<&'a T> {
|
||||
pub fn split_off_first<'a>(self: &mut &'a Self) -> Option<&'a T> {
|
||||
let (first, rem) = self.split_first()?;
|
||||
*self = rem;
|
||||
Some(first)
|
||||
|
|
@ -4459,7 +4460,7 @@ impl<T> [T] {
|
|||
/// #![feature(slice_take)]
|
||||
///
|
||||
/// let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
|
||||
/// let first = slice.take_first_mut().unwrap();
|
||||
/// let first = slice.split_off_first_mut().unwrap();
|
||||
/// *first = 'd';
|
||||
///
|
||||
/// assert_eq!(slice, &['b', 'c']);
|
||||
|
|
@ -4467,7 +4468,7 @@ impl<T> [T] {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "slice_take", issue = "62280")]
|
||||
pub fn take_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> {
|
||||
pub fn split_off_first_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> {
|
||||
let (first, rem) = mem::take(self).split_first_mut()?;
|
||||
*self = rem;
|
||||
Some(first)
|
||||
|
|
@ -4484,14 +4485,14 @@ impl<T> [T] {
|
|||
/// #![feature(slice_take)]
|
||||
///
|
||||
/// let mut slice: &[_] = &['a', 'b', 'c'];
|
||||
/// let last = slice.take_last().unwrap();
|
||||
/// let last = slice.split_off_last().unwrap();
|
||||
///
|
||||
/// assert_eq!(slice, &['a', 'b']);
|
||||
/// assert_eq!(last, &'c');
|
||||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "slice_take", issue = "62280")]
|
||||
pub fn take_last<'a>(self: &mut &'a Self) -> Option<&'a T> {
|
||||
pub fn split_off_last<'a>(self: &mut &'a Self) -> Option<&'a T> {
|
||||
let (last, rem) = self.split_last()?;
|
||||
*self = rem;
|
||||
Some(last)
|
||||
|
|
@ -4508,7 +4509,7 @@ impl<T> [T] {
|
|||
/// #![feature(slice_take)]
|
||||
///
|
||||
/// let mut slice: &mut [_] = &mut ['a', 'b', 'c'];
|
||||
/// let last = slice.take_last_mut().unwrap();
|
||||
/// let last = slice.split_off_last_mut().unwrap();
|
||||
/// *last = 'd';
|
||||
///
|
||||
/// assert_eq!(slice, &['a', 'b']);
|
||||
|
|
@ -4516,7 +4517,7 @@ impl<T> [T] {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[unstable(feature = "slice_take", issue = "62280")]
|
||||
pub fn take_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> {
|
||||
pub fn split_off_last_mut<'a>(self: &mut &'a mut Self) -> Option<&'a mut T> {
|
||||
let (last, rem) = mem::take(self).split_last_mut()?;
|
||||
*self = rem;
|
||||
Some(last)
|
||||
|
|
|
|||
|
|
@ -160,6 +160,182 @@ impl str {
|
|||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Converts a slice of bytes to a string slice.
|
||||
///
|
||||
/// A string slice ([`&str`]) is made of bytes ([`u8`]), and a byte slice
|
||||
/// ([`&[u8]`][byteslice]) is made of bytes, so this function converts between
|
||||
/// the two. Not all byte slices are valid string slices, however: [`&str`] requires
|
||||
/// that it is valid UTF-8. `from_utf8()` checks to ensure that the bytes are valid
|
||||
/// UTF-8, and then does the conversion.
|
||||
///
|
||||
/// [`&str`]: str
|
||||
/// [byteslice]: prim@slice
|
||||
///
|
||||
/// If you are sure that the byte slice is valid UTF-8, and you don't want to
|
||||
/// incur the overhead of the validity check, there is an unsafe version of
|
||||
/// this function, [`from_utf8_unchecked`], which has the same
|
||||
/// behavior but skips the check.
|
||||
///
|
||||
/// If you need a `String` instead of a `&str`, consider
|
||||
/// [`String::from_utf8`][string].
|
||||
///
|
||||
/// [string]: ../std/string/struct.String.html#method.from_utf8
|
||||
///
|
||||
/// Because you can stack-allocate a `[u8; N]`, and you can take a
|
||||
/// [`&[u8]`][byteslice] of it, this function is one way to have a
|
||||
/// stack-allocated string. There is an example of this in the
|
||||
/// examples section below.
|
||||
///
|
||||
/// [byteslice]: slice
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `Err` if the slice is not UTF-8 with a description as to why the
|
||||
/// provided slice is not UTF-8.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// use std::str;
|
||||
///
|
||||
/// // some bytes, in a vector
|
||||
/// let sparkle_heart = vec![240, 159, 146, 150];
|
||||
///
|
||||
/// // We can use the ? (try) operator to check if the bytes are valid
|
||||
/// let sparkle_heart = str::from_utf8(&sparkle_heart)?;
|
||||
///
|
||||
/// assert_eq!("💖", sparkle_heart);
|
||||
/// # Ok::<_, str::Utf8Error>(())
|
||||
/// ```
|
||||
///
|
||||
/// Incorrect bytes:
|
||||
///
|
||||
/// ```
|
||||
/// use std::str;
|
||||
///
|
||||
/// // some invalid bytes, in a vector
|
||||
/// let sparkle_heart = vec![0, 159, 146, 150];
|
||||
///
|
||||
/// assert!(str::from_utf8(&sparkle_heart).is_err());
|
||||
/// ```
|
||||
///
|
||||
/// See the docs for [`Utf8Error`] for more details on the kinds of
|
||||
/// errors that can be returned.
|
||||
///
|
||||
/// A "stack allocated string":
|
||||
///
|
||||
/// ```
|
||||
/// use std::str;
|
||||
///
|
||||
/// // some bytes, in a stack-allocated array
|
||||
/// let sparkle_heart = [240, 159, 146, 150];
|
||||
///
|
||||
/// // We know these bytes are valid, so just use `unwrap()`.
|
||||
/// let sparkle_heart: &str = str::from_utf8(&sparkle_heart).unwrap();
|
||||
///
|
||||
/// assert_eq!("💖", sparkle_heart);
|
||||
/// ```
|
||||
#[unstable(feature = "inherent_str_constructors", issue = "131114")]
|
||||
pub const fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error> {
|
||||
converts::from_utf8(v)
|
||||
}
|
||||
|
||||
/// Converts a mutable slice of bytes to a mutable string slice.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// use std::str;
|
||||
///
|
||||
/// // "Hello, Rust!" as a mutable vector
|
||||
/// let mut hellorust = vec![72, 101, 108, 108, 111, 44, 32, 82, 117, 115, 116, 33];
|
||||
///
|
||||
/// // As we know these bytes are valid, we can use `unwrap()`
|
||||
/// let outstr = str::from_utf8_mut(&mut hellorust).unwrap();
|
||||
///
|
||||
/// assert_eq!("Hello, Rust!", outstr);
|
||||
/// ```
|
||||
///
|
||||
/// Incorrect bytes:
|
||||
///
|
||||
/// ```
|
||||
/// use std::str;
|
||||
///
|
||||
/// // Some invalid bytes in a mutable vector
|
||||
/// let mut invalid = vec![128, 223];
|
||||
///
|
||||
/// assert!(str::from_utf8_mut(&mut invalid).is_err());
|
||||
/// ```
|
||||
/// See the docs for [`Utf8Error`] for more details on the kinds of
|
||||
/// errors that can be returned.
|
||||
#[unstable(feature = "inherent_str_constructors", issue = "131114")]
|
||||
#[rustc_const_unstable(feature = "const_str_from_utf8", issue = "91006")]
|
||||
pub const fn from_utf8_mut(v: &mut [u8]) -> Result<&mut str, Utf8Error> {
|
||||
converts::from_utf8_mut(v)
|
||||
}
|
||||
|
||||
/// Converts a slice of bytes to a string slice without checking
|
||||
/// that the string contains valid UTF-8.
|
||||
///
|
||||
/// See the safe version, [`from_utf8`], for more information.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The bytes passed in must be valid UTF-8.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// use std::str;
|
||||
///
|
||||
/// // some bytes, in a vector
|
||||
/// let sparkle_heart = vec![240, 159, 146, 150];
|
||||
///
|
||||
/// let sparkle_heart = unsafe {
|
||||
/// str::from_utf8_unchecked(&sparkle_heart)
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!("💖", sparkle_heart);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[unstable(feature = "inherent_str_constructors", issue = "131114")]
|
||||
pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str {
|
||||
// SAFETY: converts::from_utf8_unchecked has the same safety requirements as this function.
|
||||
unsafe { converts::from_utf8_unchecked(v) }
|
||||
}
|
||||
|
||||
/// Converts a slice of bytes to a string slice without checking
|
||||
/// that the string contains valid UTF-8; mutable version.
|
||||
///
|
||||
/// See the immutable version, [`from_utf8_unchecked()`] for more information.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// ```
|
||||
/// use std::str;
|
||||
///
|
||||
/// let mut heart = vec![240, 159, 146, 150];
|
||||
/// let heart = unsafe { str::from_utf8_unchecked_mut(&mut heart) };
|
||||
///
|
||||
/// assert_eq!("💖", heart);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
#[unstable(feature = "inherent_str_constructors", issue = "131114")]
|
||||
pub const unsafe fn from_utf8_unchecked_mut(v: &mut [u8]) -> &mut str {
|
||||
// SAFETY: converts::from_utf8_unchecked_mut has the same safety requirements as this function.
|
||||
unsafe { converts::from_utf8_unchecked_mut(v) }
|
||||
}
|
||||
|
||||
/// Checks that `index`-th byte is the first byte in a UTF-8 code point
|
||||
/// sequence or the end of the string.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -2399,18 +2399,18 @@ fn slice_rsplit_once() {
|
|||
assert_eq!(v.rsplit_once(|&x| x == 0), None);
|
||||
}
|
||||
|
||||
macro_rules! take_tests {
|
||||
macro_rules! split_off_tests {
|
||||
(slice: &[], $($tts:tt)*) => {
|
||||
take_tests!(ty: &[()], slice: &[], $($tts)*);
|
||||
split_off_tests!(ty: &[()], slice: &[], $($tts)*);
|
||||
};
|
||||
(slice: &mut [], $($tts:tt)*) => {
|
||||
take_tests!(ty: &mut [()], slice: &mut [], $($tts)*);
|
||||
split_off_tests!(ty: &mut [()], slice: &mut [], $($tts)*);
|
||||
};
|
||||
(slice: &$slice:expr, $($tts:tt)*) => {
|
||||
take_tests!(ty: &[_], slice: &$slice, $($tts)*);
|
||||
split_off_tests!(ty: &[_], slice: &$slice, $($tts)*);
|
||||
};
|
||||
(slice: &mut $slice:expr, $($tts:tt)*) => {
|
||||
take_tests!(ty: &mut [_], slice: &mut $slice, $($tts)*);
|
||||
split_off_tests!(ty: &mut [_], slice: &mut $slice, $($tts)*);
|
||||
};
|
||||
(ty: $ty:ty, slice: $slice:expr, method: $method:ident, $(($test_name:ident, ($($args:expr),*), $output:expr, $remaining:expr),)*) => {
|
||||
$(
|
||||
|
|
@ -2425,64 +2425,64 @@ macro_rules! take_tests {
|
|||
};
|
||||
}
|
||||
|
||||
take_tests! {
|
||||
slice: &[0, 1, 2, 3], method: take,
|
||||
(take_in_bounds_range_to, (..1), Some(&[0] as _), &[1, 2, 3]),
|
||||
(take_in_bounds_range_to_inclusive, (..=0), Some(&[0] as _), &[1, 2, 3]),
|
||||
(take_in_bounds_range_from, (2..), Some(&[2, 3] as _), &[0, 1]),
|
||||
(take_oob_range_to, (..5), None, &[0, 1, 2, 3]),
|
||||
(take_oob_range_to_inclusive, (..=4), None, &[0, 1, 2, 3]),
|
||||
(take_oob_range_from, (5..), None, &[0, 1, 2, 3]),
|
||||
split_off_tests! {
|
||||
slice: &[0, 1, 2, 3], method: split_off,
|
||||
(split_off_in_bounds_range_to, (..1), Some(&[0] as _), &[1, 2, 3]),
|
||||
(split_off_in_bounds_range_to_inclusive, (..=0), Some(&[0] as _), &[1, 2, 3]),
|
||||
(split_off_in_bounds_range_from, (2..), Some(&[2, 3] as _), &[0, 1]),
|
||||
(split_off_oob_range_to, (..5), None, &[0, 1, 2, 3]),
|
||||
(split_off_oob_range_to_inclusive, (..=4), None, &[0, 1, 2, 3]),
|
||||
(split_off_oob_range_from, (5..), None, &[0, 1, 2, 3]),
|
||||
}
|
||||
|
||||
take_tests! {
|
||||
slice: &mut [0, 1, 2, 3], method: take_mut,
|
||||
(take_mut_in_bounds_range_to, (..1), Some(&mut [0] as _), &mut [1, 2, 3]),
|
||||
(take_mut_in_bounds_range_to_inclusive, (..=0), Some(&mut [0] as _), &mut [1, 2, 3]),
|
||||
(take_mut_in_bounds_range_from, (2..), Some(&mut [2, 3] as _), &mut [0, 1]),
|
||||
(take_mut_oob_range_to, (..5), None, &mut [0, 1, 2, 3]),
|
||||
(take_mut_oob_range_to_inclusive, (..=4), None, &mut [0, 1, 2, 3]),
|
||||
(take_mut_oob_range_from, (5..), None, &mut [0, 1, 2, 3]),
|
||||
split_off_tests! {
|
||||
slice: &mut [0, 1, 2, 3], method: split_off_mut,
|
||||
(split_off_mut_in_bounds_range_to, (..1), Some(&mut [0] as _), &mut [1, 2, 3]),
|
||||
(split_off_mut_in_bounds_range_to_inclusive, (..=0), Some(&mut [0] as _), &mut [1, 2, 3]),
|
||||
(split_off_mut_in_bounds_range_from, (2..), Some(&mut [2, 3] as _), &mut [0, 1]),
|
||||
(split_off_mut_oob_range_to, (..5), None, &mut [0, 1, 2, 3]),
|
||||
(split_off_mut_oob_range_to_inclusive, (..=4), None, &mut [0, 1, 2, 3]),
|
||||
(split_off_mut_oob_range_from, (5..), None, &mut [0, 1, 2, 3]),
|
||||
}
|
||||
|
||||
take_tests! {
|
||||
slice: &[1, 2], method: take_first,
|
||||
(take_first_nonempty, (), Some(&1), &[2]),
|
||||
split_off_tests! {
|
||||
slice: &[1, 2], method: split_off_first,
|
||||
(split_off_first_nonempty, (), Some(&1), &[2]),
|
||||
}
|
||||
|
||||
take_tests! {
|
||||
slice: &mut [1, 2], method: take_first_mut,
|
||||
(take_first_mut_nonempty, (), Some(&mut 1), &mut [2]),
|
||||
split_off_tests! {
|
||||
slice: &mut [1, 2], method: split_off_first_mut,
|
||||
(split_off_first_mut_nonempty, (), Some(&mut 1), &mut [2]),
|
||||
}
|
||||
|
||||
take_tests! {
|
||||
slice: &[1, 2], method: take_last,
|
||||
(take_last_nonempty, (), Some(&2), &[1]),
|
||||
split_off_tests! {
|
||||
slice: &[1, 2], method: split_off_last,
|
||||
(split_off_last_nonempty, (), Some(&2), &[1]),
|
||||
}
|
||||
|
||||
take_tests! {
|
||||
slice: &mut [1, 2], method: take_last_mut,
|
||||
(take_last_mut_nonempty, (), Some(&mut 2), &mut [1]),
|
||||
split_off_tests! {
|
||||
slice: &mut [1, 2], method: split_off_last_mut,
|
||||
(split_off_last_mut_nonempty, (), Some(&mut 2), &mut [1]),
|
||||
}
|
||||
|
||||
take_tests! {
|
||||
slice: &[], method: take_first,
|
||||
(take_first_empty, (), None, &[]),
|
||||
split_off_tests! {
|
||||
slice: &[], method: split_off_first,
|
||||
(split_off_first_empty, (), None, &[]),
|
||||
}
|
||||
|
||||
take_tests! {
|
||||
slice: &mut [], method: take_first_mut,
|
||||
(take_first_mut_empty, (), None, &mut []),
|
||||
split_off_tests! {
|
||||
slice: &mut [], method: split_off_first_mut,
|
||||
(split_off_first_mut_empty, (), None, &mut []),
|
||||
}
|
||||
|
||||
take_tests! {
|
||||
slice: &[], method: take_last,
|
||||
(take_last_empty, (), None, &[]),
|
||||
split_off_tests! {
|
||||
slice: &[], method: split_off_last,
|
||||
(split_off_last_empty, (), None, &[]),
|
||||
}
|
||||
|
||||
take_tests! {
|
||||
slice: &mut [], method: take_last_mut,
|
||||
(take_last_mut_empty, (), None, &mut []),
|
||||
split_off_tests! {
|
||||
slice: &mut [], method: split_off_last_mut,
|
||||
(split_off_last_mut_empty, (), None, &mut []),
|
||||
}
|
||||
|
||||
#[cfg(not(miri))] // unused in Miri
|
||||
|
|
@ -2497,19 +2497,19 @@ macro_rules! empty_max_mut {
|
|||
}
|
||||
|
||||
#[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations)
|
||||
take_tests! {
|
||||
slice: &[(); usize::MAX], method: take,
|
||||
(take_in_bounds_max_range_to, (..usize::MAX), Some(EMPTY_MAX), &[(); 0]),
|
||||
(take_oob_max_range_to_inclusive, (..=usize::MAX), None, EMPTY_MAX),
|
||||
(take_in_bounds_max_range_from, (usize::MAX..), Some(&[] as _), EMPTY_MAX),
|
||||
split_off_tests! {
|
||||
slice: &[(); usize::MAX], method: split_off,
|
||||
(split_off_in_bounds_max_range_to, (..usize::MAX), Some(EMPTY_MAX), &[(); 0]),
|
||||
(split_off_oob_max_range_to_inclusive, (..=usize::MAX), None, EMPTY_MAX),
|
||||
(split_off_in_bounds_max_range_from, (usize::MAX..), Some(&[] as _), EMPTY_MAX),
|
||||
}
|
||||
|
||||
#[cfg(not(miri))] // Comparing usize::MAX many elements takes forever in Miri (and in rustc without optimizations)
|
||||
take_tests! {
|
||||
slice: &mut [(); usize::MAX], method: take_mut,
|
||||
(take_mut_in_bounds_max_range_to, (..usize::MAX), Some(empty_max_mut!()), &mut [(); 0]),
|
||||
(take_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()),
|
||||
(take_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()),
|
||||
split_off_tests! {
|
||||
slice: &mut [(); usize::MAX], method: split_off_mut,
|
||||
(split_off_mut_in_bounds_max_range_to, (..usize::MAX), Some(empty_max_mut!()), &mut [(); 0]),
|
||||
(split_off_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()),
|
||||
(split_off_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()),
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
|
|||
panic_unwind = { path = "../panic_unwind", optional = true }
|
||||
panic_abort = { path = "../panic_abort" }
|
||||
core = { path = "../core", public = true }
|
||||
compiler_builtins = { version = "=0.1.143" }
|
||||
compiler_builtins = { version = "=0.1.145" }
|
||||
unwind = { path = "../unwind" }
|
||||
hashbrown = { version = "0.15", default-features = false, features = [
|
||||
'rustc-dep-of-std',
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ mod tests;
|
|||
pub use core::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
|
||||
use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use crate::sys::net::netc as c;
|
||||
use crate::sys_common::net::LookupHost;
|
||||
use crate::sys::net::{LookupHost, netc as c};
|
||||
use crate::sys_common::{FromInner, IntoInner};
|
||||
use crate::{io, iter, mem, option, slice, vec};
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ use crate::io::prelude::*;
|
|||
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut};
|
||||
use crate::iter::FusedIterator;
|
||||
use crate::net::{Shutdown, SocketAddr, ToSocketAddrs};
|
||||
use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp};
|
||||
use crate::sys::net as net_imp;
|
||||
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
||||
use crate::time::Duration;
|
||||
|
||||
/// A TCP stream between a local and a remote socket.
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ mod tests;
|
|||
use crate::fmt;
|
||||
use crate::io::{self, ErrorKind};
|
||||
use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs};
|
||||
use crate::sys_common::{AsInner, FromInner, IntoInner, net as net_imp};
|
||||
use crate::sys::net as net_imp;
|
||||
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
||||
use crate::time::Duration;
|
||||
|
||||
/// A UDP socket.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::os::fd::owned::OwnedFd;
|
||||
use crate::os::fd::raw::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use crate::sys_common::{self, AsInner, FromInner, IntoInner};
|
||||
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
||||
use crate::{net, sys};
|
||||
|
||||
macro_rules! impl_as_raw_fd {
|
||||
|
|
@ -24,7 +24,7 @@ macro_rules! impl_from_raw_fd {
|
|||
unsafe fn from_raw_fd(fd: RawFd) -> net::$t {
|
||||
unsafe {
|
||||
let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd)));
|
||||
net::$t::from_inner(sys_common::net::$t::from_inner(socket))
|
||||
net::$t::from_inner(sys::net::$t::from_inner(socket))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ macro_rules! impl_from_raw_fd {
|
|||
unsafe fn from_raw_fd(fd: RawFd) -> net::$t {
|
||||
unsafe {
|
||||
let socket = sys::net::Socket::from_inner(FromInner::from_inner(OwnedFd::from_raw_fd(fd)));
|
||||
net::$t::from_inner(sys_common::net::$t::from_inner(socket))
|
||||
net::$t::from_inner(sys::net::$t::from_inner(socket))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::ManuallyDrop;
|
||||
use crate::sys_common::{self, AsInner, FromInner, IntoInner};
|
||||
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
||||
use crate::{fmt, net, sys};
|
||||
|
||||
/// Raw file descriptors.
|
||||
|
|
@ -387,7 +387,7 @@ macro_rules! impl_from_raw_fd {
|
|||
#[inline]
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> net::$t {
|
||||
let socket = unsafe { sys::net::Socket::from_raw_fd(fd) };
|
||||
net::$t::from_inner(sys_common::net::$t::from_inner(socket))
|
||||
net::$t::from_inner(sys::net::$t::from_inner(socket))
|
||||
}
|
||||
}
|
||||
)*};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
use crate::os::windows::io::{AsHandle, AsSocket};
|
||||
use crate::os::windows::io::{OwnedHandle, OwnedSocket};
|
||||
use crate::os::windows::raw;
|
||||
use crate::sys_common::{self, AsInner, FromInner, IntoInner};
|
||||
use crate::sys_common::{AsInner, FromInner, IntoInner};
|
||||
use crate::{fs, io, net, ptr, sys};
|
||||
|
||||
/// Raw HANDLEs.
|
||||
|
|
@ -262,7 +262,7 @@ impl FromRawSocket for net::TcpStream {
|
|||
unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpStream {
|
||||
unsafe {
|
||||
let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock));
|
||||
net::TcpStream::from_inner(sys_common::net::TcpStream::from_inner(sock))
|
||||
net::TcpStream::from_inner(sys::net::TcpStream::from_inner(sock))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -272,7 +272,7 @@ impl FromRawSocket for net::TcpListener {
|
|||
unsafe fn from_raw_socket(sock: RawSocket) -> net::TcpListener {
|
||||
unsafe {
|
||||
let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock));
|
||||
net::TcpListener::from_inner(sys_common::net::TcpListener::from_inner(sock))
|
||||
net::TcpListener::from_inner(sys::net::TcpListener::from_inner(sock))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -282,7 +282,7 @@ impl FromRawSocket for net::UdpSocket {
|
|||
unsafe fn from_raw_socket(sock: RawSocket) -> net::UdpSocket {
|
||||
unsafe {
|
||||
let sock = sys::net::Socket::from_inner(OwnedSocket::from_raw_socket(sock));
|
||||
net::UdpSocket::from_inner(sys_common::net::UdpSocket::from_inner(sock))
|
||||
net::UdpSocket::from_inner(sys::net::UdpSocket::from_inner(sock))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ pub mod anonymous_pipe;
|
|||
pub mod backtrace;
|
||||
pub mod cmath;
|
||||
pub mod exit_guard;
|
||||
pub mod net;
|
||||
pub mod os_str;
|
||||
pub mod path;
|
||||
pub mod random;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue