diff --git a/Cargo.lock b/Cargo.lock index 18d5b66eb73e..7c8f06a02396 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index b6f331d316cc..08621c1c56a1 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3348,11 +3348,18 @@ pub struct Impl { pub items: ThinVec>, } +#[derive(Clone, Encodable, Decodable, Debug, Default)] +pub struct FnContract { + pub requires: Option>, + pub ensures: Option>, +} + #[derive(Clone, Encodable, Decodable, Debug)] pub struct Fn { pub defaultness: Defaultness, pub generics: Generics, pub sig: FnSig, + pub contract: Option>, pub body: Option>, } @@ -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); diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 7caf7c4c3568..70616fe87691 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -143,6 +143,10 @@ pub trait MutVisitor: Sized { walk_flat_map_assoc_item(self, i, ctxt) } + fn visit_contract(&mut self, c: &mut P) { + walk_contract(self, c); + } + fn visit_fn_decl(&mut self, d: &mut P) { walk_fn_decl(self, d); } @@ -958,13 +962,16 @@ fn walk_fn(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(vis: &mut T, kind: FnKind<'_>) { } } +fn walk_contract(vis: &mut T, contract: &mut P) { + 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(vis: &mut T, decl: &mut P) { let FnDecl { inputs, output } = decl.deref_mut(); inputs.flat_map_in_place(|param| vis.flat_map_param(param)); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 232fd546de9a..714b074f930c 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -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) => { diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 98d6372ca896..89d5b5a75a24 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -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)) } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 74870d741504..c96110fee619 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -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` 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)) }) } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 8728af9997a1..bb7c66f60940 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -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>, + pub ensures: Option>, +} + +#[derive(Debug, Clone)] +struct FnContractLoweringEnsures<'hir> { + expr: ast::ptr::P, + 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>, + coroutine_kind: Option, /// 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; diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index ea1f4a6559ac..0049c5b4823c 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -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 = diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 80b99f944856..62e451fa8764 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -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) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 4cfcaa95233d..c10b5ad34e10 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -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, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 78aa38e9e48f..002f6e36cd8b 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1653,6 +1653,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::SizedBound, ); } + &Rvalue::NullaryOp(NullOp::ContractChecks, _) => {} &Rvalue::NullaryOp(NullOp::UbChecks, _) => {} Rvalue::ShallowInitBox(operand, ty) => { diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index d2b4e1ca824f..cffc49786013 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -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, })); diff --git a/compiler/rustc_builtin_macros/src/contracts.rs b/compiler/rustc_builtin_macros/src/contracts.rs new file mode 100644 index 000000000000..85a30f7bdc9b --- /dev/null +++ b/compiler/rustc_builtin_macros/src/contracts.rs @@ -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 { + 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 { + 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 { + 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 { + 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 { + 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(()) + }) +} diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 755a733286ce..0631c5a80fc5 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -1034,6 +1034,7 @@ impl<'a> MethodDef<'a> { defaultness, sig, generics: fn_generics, + contract: None, body: Some(body_block), })), tokens: None, diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 8388e9dcafb8..8fdbbf8e704a 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -81,6 +81,7 @@ impl AllocFnFactory<'_, '_> { defaultness: ast::Defaultness::Final, sig, generics: Generics::default(), + contract: None, body, })); let item = self.cx.item( diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 6987ae95980a..ca16583a45de 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -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); } diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 31b068bd33da..472e16e62d5b 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -344,6 +344,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P { defaultness, sig, generics: ast::Generics::default(), + contract: None, body: Some(main_body), })); diff --git a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch index bf58e4851585..bac411d1eb03 100644 --- a/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch +++ b/compiler/rustc_codegen_cranelift/patches/0029-stdlib-Disable-f16-and-f128-in-compiler-builtins.patch @@ -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"] } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 7a40d236b928..a2b9e5712e50 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -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); diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 14fc23593f0e..d94fc5525288 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -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 diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 89e5cf1b8c6b..bba19d062583 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -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}; diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 1631ecfeecf3..fb0ca31c5433 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -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; diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 570ef938dc44..1e1f577bb3a1 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -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, }; diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index 86d3de225f71..3f6fdea9fb8c 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -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; diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs index 442488b7fd65..7cdbe3c0c629 100644 --- a/compiler/rustc_codegen_gcc/src/declare.rs +++ b/compiler/rustc_codegen_gcc/src/declare.rs @@ -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; diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index fe6a65bed03b..4a1db8d662a9 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -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 { .. }); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 48606f5f91c0..a1123fafe2f3 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -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)); diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 1be452e5d05d..12a7dab155b4 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -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"))] diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs index 4ea5544721de..cb08723431a9 100644 --- a/compiler/rustc_codegen_gcc/src/type_.rs +++ b/compiler/rustc_codegen_gcc/src/type_.rs @@ -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; diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 0efdf36da485..8b8b54753e7f 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -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; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs index 11eb9651af6c..f52991b36979 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -127,7 +127,7 @@ fn make_mir_scope<'ll, 'tcx>( }) } None => unsafe { - llvm::LLVMRustDIBuilderCreateLexicalBlock( + llvm::LLVMDIBuilderCreateLexicalBlock( DIB(cx), parent_scope.dbg_scope, file_metadata, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 3a0c7f007bdd..f497ba95661d 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -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> { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index b1ce52667bd6..471cdc17148c 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -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, &'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( diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs index 33d9bc238903..b4d639368b00 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/namespace.rs @@ -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) ) }; diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs index 6e8412934778..cc1d504b4301 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs @@ -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 { diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 8d494b0fe621..0d04f770bc69 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -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>, + } + + 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>); + + 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, diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 85de3238b3e7..27cb7883b9a6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -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 { diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index e8052a3c83a1..d0ce027ec2b7 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -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(_, _) => {} diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 8f6b15b8df01..1a799f5dea5b 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -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>, diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs index 5fa632fc57aa..899670aeb62d 100644 --- a/compiler/rustc_const_eval/src/interpret/operator.rs +++ b/compiler/rustc_const_eval/src/interpret/operator.rs @@ -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), }) } } diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 8e5af33d8b67..a8f83ca13e26 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -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" } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index e0543977e98d..67eb96e4d56a 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -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, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8aaa2d443925..af3570ae77ab 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -403,6 +403,8 @@ declare_features! ( (unstable, c_variadic, "1.34.0", Some(44930)), /// Allows the use of `#[cfg()]`. (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. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 4631432038b3..1c87a0eb1a78 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -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 _ { .. }`. diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 0f988568341b..b6689c95c4bb 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -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 { diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index c3404fa1624b..40769ef7536d 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -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) -> 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 diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 5d00ecbe9187..65021a0cd110 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -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 }); } } diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 052adaa69b2b..143736072aa5 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -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>, } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 838cd1e03f98..49f712090f03 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -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") diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index c947ecde6560..2e7415cec5d3 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -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) diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 83a168c3f446..fd6c17735c5a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -595,6 +595,11 @@ fn register_builtins(store: &mut LintStore) { 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 for more information", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 5b4a1f174cbb..9561b3de5f8b 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -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 ", - }; -} - declare_lint! { /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer /// and a pointer. diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 7ff316ba83a6..76ad7fc89091 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -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(Type))); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateLexicalBlock(LLVMDIBuilderRef Builder, - LLVMMetadataRef Scope, LLVMMetadataRef File, - unsigned Line, unsigned Col) { - return wrap(unwrap(Builder)->createLexicalBlock( - unwrapDI(Scope), unwrapDI(File), Line, Col)); -} - -extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateLexicalBlockFile( - LLVMDIBuilderRef Builder, LLVMMetadataRef Scope, LLVMMetadataRef File) { - return wrap(unwrap(Builder)->createLexicalBlockFile( - unwrapDI(Scope), unwrapDI(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(Ty), IsDefault)); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateNameSpace(LLVMDIBuilderRef Builder, - LLVMMetadataRef Scope, const char *Name, - size_t NameLen, bool ExportSymbols) { - return wrap(unwrap(Builder)->createNameSpace( - unwrapDI(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(Params))); } -extern "C" LLVMMetadataRef -LLVMRustDIBuilderCreateDebugLocation(unsigned Line, unsigned Column, - LLVMMetadataRef ScopeRef, - LLVMMetadataRef InlinedAt) { - MDNode *Scope = unwrapDIPtr(ScopeRef); - DILocation *Loc = DILocation::get(Scope->getContext(), Line, Column, Scope, - unwrapDIPtr(InlinedAt)); - return wrap(Loc); -} - extern "C" LLVMMetadataRef LLVMRustDILocationCloneWithBaseDiscriminator(LLVMMetadataRef Location, unsigned BD) { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index aa8ec3aff040..3007b7874968 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -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| { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 2da25f480c65..9cec8d832dd1 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -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)] diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 49449426fa40..af23c8b2ea76 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -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 => { diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index e4e452aff755..84c9297e6589 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -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() } diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index 49db522cf0ee..f303053390c4 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -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()), diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 6e00e427a46c..b6c259aa4e0a 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -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, _, ) => {} } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index c261e25100d3..d2ffd26f0a06 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -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); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 2864cc0b9fe0..e43254ba089e 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -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() } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 9a9f66ed4fd7..9c21bcfc0d26 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -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 { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 9101c9bfc9ae..4dbbcae1756b 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -457,6 +457,7 @@ impl<'tcx> Validator<'_, 'tcx> { NullOp::AlignOf => {} NullOp::OffsetOf(_) => {} NullOp::UbChecks => {} + NullOp::ContractChecks => {} }, Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index e282eaf761c1..b7a3770fc6b1 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -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) => { diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index b1b84b0b7011..86816819be27 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -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>> { + 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) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index f3e56be9f6e8..dbdc31f06a29 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -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>)> { + ) -> PResult<'a, (Ident, FnSig, Generics, Option>, Option>)> { 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 diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index 73f3ac001c8a..40631d9154d3 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -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) }; diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 16c0a345f879..db607dbb4196 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -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, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 4842cbd556c3..e37e7e98ee7a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -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. diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs index d586f913335e..52920e0372e2 100644 --- a/compiler/rustc_session/src/config/cfg.rs +++ b/compiler/rustc_session/src/config/cfg.rs @@ -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); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 4ed5499d32c6..35819f896c5b 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -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 = (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 = (Vec::new(), parse_string_push, [TRACKED], diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 81ae06602cdb..abfd3efc6117 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -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, /// 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, @@ -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, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 2b79081a26e7..c9bb42bdfa1e 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -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) } diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 4a0420cc6031..bdd6e16a7c17 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -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, } } } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index a5826137181d..dbbbb5077cb5 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -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", } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 621d150281ca..6eb2cdc4e11f 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -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: "", 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, diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnu.rs index 1ae879d01bd4..1c537491a6ca 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnu.rs @@ -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 + }, } } diff --git a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs index 4a5ab375c730..4f5d9c661b08 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_unknown_linux_gnuspe.rs @@ -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 }, diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index eec6cd8d49ba..a6406e9db8e3 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -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 { diff --git a/library/Cargo.lock b/library/Cargo.lock index 30875482dc06..8b78908e6d73 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -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", diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 96caac890a35..db7eaf52fb22 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -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"] } diff --git a/library/core/src/contracts.rs b/library/core/src/contracts.rs new file mode 100644 index 000000000000..c769e219e4d4 --- /dev/null +++ b/library/core/src/contracts.rs @@ -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(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 + } +} diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 1862be0e86c5..665b05b12ec0 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -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) -> fmt::Result>(f: F) -> FromFn { FromFn(f) } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 1362b9897dce..3b249c835f23 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -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 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 diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index b9471f169797..49ce1bbcf398 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -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; diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 3669051ce82d..4c6fd196bd31 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -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). diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 40526f9583e6..7b2ced2cc4bd 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -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")] diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 727a22e454d3..42e07a0e51da 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -979,6 +979,19 @@ impl RangeBounds 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 RangeBounds for RangeToInclusive<&T> { /// Types that implement `OneSidedRange` must return `Bound::Unbounded` /// from one of `RangeBounds::start_bound` or `RangeBounds::end_bound`. #[unstable(feature = "one_sided_range", issue = "69780")] -pub trait OneSidedRange: RangeBounds {} +pub trait OneSidedRange: RangeBounds { + /// 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 OneSidedRange for RangeTo where Self: RangeBounds {} +impl OneSidedRange for RangeTo +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::End, self.end) + } +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeFrom where Self: RangeBounds {} +impl OneSidedRange for RangeFrom +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::StartInclusive, self.start) + } +} #[unstable(feature = "one_sided_range", issue = "69780")] -impl OneSidedRange for RangeToInclusive where Self: RangeBounds {} +impl OneSidedRange for RangeToInclusive +where + Self: RangeBounds, +{ + fn bound(self) -> (OneSidedRangeBound, T) { + (OneSidedRangeBound::EndInclusive, self.end) + } +} diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 1993a7491e10..fe9d7c10db28 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -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) -> 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] { /// /// # 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] { /// /// 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>(self: &mut &'a Self, range: R) -> Option<&'a Self> { + pub fn split_off<'a, R: OneSidedRange>( + 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] { /// /// # 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] { /// #![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] { /// /// 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>( + pub fn split_off_mut<'a, R: OneSidedRange>( self: &mut &'a mut Self, range: R, ) -> Option<&'a mut Self> { @@ -4435,14 +4436,14 @@ impl [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] { /// #![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] { /// ``` #[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] { /// #![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] { /// #![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] { /// ``` #[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) diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 39fa6c1a25fe..5b258a7c844f 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -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. /// diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index 510dd4967c96..ea5322da3812 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -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] diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 944c34385572..aa391a4b317a 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -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', diff --git a/library/std/src/net/socket_addr.rs b/library/std/src/net/socket_addr.rs index ba9c948a2e96..e8355cc31d7a 100644 --- a/library/std/src/net/socket_addr.rs +++ b/library/std/src/net/socket_addr.rs @@ -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}; diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 67a0f7e439d5..9b68f872955c 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -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. diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index 674c5fb7d6ea..3eb798ad34aa 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -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. diff --git a/library/std/src/os/fd/net.rs b/library/std/src/os/fd/net.rs index 843f45f7f5f9..34479ca0e190 100644 --- a/library/std/src/os/fd/net.rs +++ b/library/std/src/os/fd/net.rs @@ -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)) } } } diff --git a/library/std/src/os/hermit/io/net.rs b/library/std/src/os/hermit/io/net.rs index 7a774345b231..233bc885fc73 100644 --- a/library/std/src/os/hermit/io/net.rs +++ b/library/std/src/os/hermit/io/net.rs @@ -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)) } } } diff --git a/library/std/src/os/solid/io.rs b/library/std/src/os/solid/io.rs index b8601b533fe0..b8c3440542d0 100644 --- a/library/std/src/os/solid/io.rs +++ b/library/std/src/os/solid/io.rs @@ -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)) } } )*}; diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 6658248d574c..c0517fab9506 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -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)) } } } diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index f17dd47deced..39a0bc6e337c 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -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; diff --git a/library/std/src/sys/pal/sgx/net.rs b/library/std/src/sys/net/connection/sgx.rs similarity index 99% rename from library/std/src/sys/pal/sgx/net.rs rename to library/std/src/sys/net/connection/sgx.rs index c966886d1634..b390a5eac5f7 100644 --- a/library/std/src/sys/pal/sgx/net.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -1,7 +1,7 @@ -use super::abi::usercalls; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::Arc; +use crate::sys::abi::usercalls; use crate::sys::fd::FileDesc; use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported}; use crate::time::Duration; diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys/net/connection/socket.rs similarity index 97% rename from library/std/src/sys_common/net.rs rename to library/std/src/sys/net/connection/socket.rs index 74306978d228..6fe3430b53f7 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys/net/connection/socket.rs @@ -5,11 +5,31 @@ use crate::ffi::{c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::sys::common::small_c_string::run_with_cstr; -use crate::sys::net::{Socket, cvt, cvt_gai, cvt_r, init, netc as c, wrlen_t}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, fmt, mem, ptr}; +cfg_if::cfg_if! { + if #[cfg(target_os = "hermit")] { + mod hermit; + pub use hermit::*; + } else if #[cfg(target_os = "solid_asp3")] { + mod solid; + pub use solid::*; + } else if #[cfg(target_family = "unix")] { + mod unix; + pub use unix::*; + } else if #[cfg(all(target_os = "wasi", target_env = "p2"))] { + mod wasip2; + pub use wasip2::*; + } else if #[cfg(target_os = "windows")] { + mod windows; + pub use windows::*; + } +} + +use netc as c; + cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", @@ -24,11 +44,11 @@ cfg_if::cfg_if! { target_os = "nuttx", target_vendor = "apple", ))] { - use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; + use c::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; + use c::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; } else { - use crate::sys::net::netc::IPV6_ADD_MEMBERSHIP; - use crate::sys::net::netc::IPV6_DROP_MEMBERSHIP; + use c::IPV6_ADD_MEMBERSHIP; + use c::IPV6_DROP_MEMBERSHIP; } } diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/net/connection/socket/hermit.rs similarity index 98% rename from library/std/src/sys/pal/hermit/net.rs rename to library/std/src/sys/net/connection/socket/hermit.rs index 4e12374203e3..42179dcc9156 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -2,21 +2,20 @@ use core::ffi::c_int; -use super::fd::FileDesc; +pub(crate) use hermit_abi as netc; + use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, RawFd}; +use crate::sys::fd::FileDesc; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys::time::Instant; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +pub use crate::sys::{cvt, cvt_r}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem}; -#[allow(unused_extern_crates)] -pub extern crate hermit_abi as netc; - -pub use crate::sys::{cvt, cvt_r}; - +#[expect(non_camel_case_types)] pub type wrlen_t = usize; pub fn cvt_gai(err: i32) -> io::Result<()> { diff --git a/library/std/src/sys/pal/solid/net.rs b/library/std/src/sys/net/connection/socket/solid.rs similarity index 95% rename from library/std/src/sys/pal/solid/net.rs rename to library/std/src/sys/net/connection/socket/solid.rs index 5f6436807e27..f85ecbb883ee 100644 --- a/library/std/src/sys/pal/solid/net.rs +++ b/library/std/src/sys/net/connection/socket/solid.rs @@ -1,24 +1,23 @@ use libc::{c_int, c_void, size_t}; use self::netc::{MSG_PEEK, sockaddr, socklen_t}; -use super::abi; use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::solid::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd}; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::abi; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem, ptr, str}; pub mod netc { - pub use super::super::abi::sockets::*; + pub use crate::sys::abi::sockets::*; } +#[expect(non_camel_case_types)] pub type wrlen_t = size_t; -const READ_LIMIT: usize = libc::ssize_t::MAX as usize; - const fn max_iov() -> usize { // Judging by the source code, it's unlimited, but specify a lower // value just in case. @@ -78,7 +77,7 @@ fn last_error() -> io::Error { io::Error::from_raw_os_error(unsafe { netc::SOLID_NET_GetLastError() }) } -pub(super) fn error_name(er: abi::ER) -> Option<&'static str> { +pub fn error_name(er: abi::ER) -> Option<&'static str> { unsafe { CStr::from_ptr(netc::strerror(er)) }.to_str().ok() } @@ -87,7 +86,7 @@ pub fn is_interrupted(er: abi::ER) -> bool { er == netc::SOLID_NET_ERR_BASE - libc::EINTR } -pub(super) fn decode_error_kind(er: abi::ER) -> ErrorKind { +pub fn decode_error_kind(er: abi::ER) -> ErrorKind { let errno = netc::SOLID_NET_ERR_BASE - er; match errno as libc::c_int { libc::ECONNREFUSED => ErrorKind::ConnectionRefused, @@ -268,17 +267,6 @@ impl Socket { self.recv_from_with_flags(buf, MSG_PEEK) } - pub fn write(&self, buf: &[u8]) -> io::Result { - let ret = cvt(unsafe { - netc::write( - self.as_raw_fd(), - buf.as_ptr() as *const c_void, - cmp::min(buf.len(), READ_LIMIT), - ) - })?; - Ok(ret as usize) - } - pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { let ret = cvt(unsafe { netc::writev( diff --git a/library/std/src/sys_common/net/tests.rs b/library/std/src/sys/net/connection/socket/tests.rs similarity index 100% rename from library/std/src/sys_common/net/tests.rs rename to library/std/src/sys/net/connection/socket/tests.rs diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/net/connection/socket/unix.rs similarity index 99% rename from library/std/src/sys/pal/unix/net.rs rename to library/std/src/sys/net/connection/socket/unix.rs index d73b9fd5eb88..da6316055273 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/net/connection/socket/unix.rs @@ -5,8 +5,8 @@ use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; -use crate::sys::pal::unix::IsMinusOne; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys::pal::IsMinusOne; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem}; @@ -19,11 +19,11 @@ cfg_if::cfg_if! { } } +pub(crate) use libc as netc; + pub use crate::sys::{cvt, cvt_r}; -#[allow(unused_extern_crates)] -pub extern crate libc as netc; - +#[expect(non_camel_case_types)] pub type wrlen_t = size_t; pub struct Socket(FileDesc); diff --git a/library/std/src/sys/pal/wasip2/net.rs b/library/std/src/sys/net/connection/socket/wasip2.rs similarity index 99% rename from library/std/src/sys/pal/wasip2/net.rs rename to library/std/src/sys/net/connection/socket/wasip2.rs index f009a51821f3..9d1c05a473e4 100644 --- a/library/std/src/sys/pal/wasip2/net.rs +++ b/library/std/src/sys/net/connection/socket/wasip2.rs @@ -6,8 +6,8 @@ use crate::ffi::CStr; use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Shutdown, SocketAddr}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; +use crate::sys::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys::unsupported; -use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::{Duration, Instant}; use crate::{cmp, mem, str}; diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/net/connection/socket/windows.rs similarity index 95% rename from library/std/src/sys/pal/windows/net.rs rename to library/std/src/sys/net/connection/socket/windows.rs index a92853c642c0..80cf37eaf058 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/net/connection/socket/windows.rs @@ -9,7 +9,7 @@ use crate::os::windows::io::{ }; use crate::sync::OnceLock; use crate::sys::c; -use crate::sys_common::{AsInner, FromInner, IntoInner, net}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; use crate::{cmp, mem, ptr, sys}; @@ -110,6 +110,7 @@ pub mod netc { } } +#[expect(missing_debug_implementations)] pub struct Socket(OwnedSocket); static WSA_CLEANUP: OnceLock i32> = OnceLock::new(); @@ -400,12 +401,12 @@ impl Socket { let error = unsafe { c::WSAGetLastError() }; if error == c::WSAESHUTDOWN { - Ok((0, net::sockaddr_to_addr(&storage, addrlen as usize)?)) + Ok((0, super::sockaddr_to_addr(&storage, addrlen as usize)?)) } else { Err(io::Error::from_raw_os_error(error)) } } - _ => Ok((result as usize, net::sockaddr_to_addr(&storage, addrlen as usize)?)), + _ => Ok((result as usize, super::sockaddr_to_addr(&storage, addrlen as usize)?)), } } @@ -450,11 +451,11 @@ impl Socket { } None => 0, }; - net::setsockopt(self, c::SOL_SOCKET, kind, timeout) + super::setsockopt(self, c::SOL_SOCKET, kind, timeout) } pub fn timeout(&self, kind: c_int) -> io::Result> { - let raw: u32 = net::getsockopt(self, c::SOL_SOCKET, kind)?; + let raw: u32 = super::getsockopt(self, c::SOL_SOCKET, kind)?; if raw == 0 { Ok(None) } else { @@ -487,26 +488,26 @@ impl Socket { l_linger: linger.unwrap_or_default().as_secs() as c_ushort, }; - net::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) + super::setsockopt(self, c::SOL_SOCKET, c::SO_LINGER, linger) } pub fn linger(&self) -> io::Result> { - let val: c::LINGER = net::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; + let val: c::LINGER = super::getsockopt(self, c::SOL_SOCKET, c::SO_LINGER)?; Ok((val.l_onoff != 0).then(|| Duration::from_secs(val.l_linger as u64))) } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) + super::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) } pub fn nodelay(&self) -> io::Result { - let raw: c::BOOL = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + let raw: c::BOOL = super::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; Ok(raw != 0) } pub fn take_error(&self) -> io::Result> { - let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; + let raw: c_int = super::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?; if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } diff --git a/library/std/src/sys/pal/unsupported/net.rs b/library/std/src/sys/net/connection/unsupported.rs similarity index 100% rename from library/std/src/sys/pal/unsupported/net.rs rename to library/std/src/sys/net/connection/unsupported.rs diff --git a/library/std/src/sys/pal/wasi/net.rs b/library/std/src/sys/net/connection/wasip1.rs similarity index 99% rename from library/std/src/sys/pal/wasi/net.rs rename to library/std/src/sys/net/connection/wasip1.rs index a64867998281..27e3a528af49 100644 --- a/library/std/src/sys/pal/wasi/net.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -1,12 +1,11 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use super::err2io; -use super::fd::WasiFd; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; -use crate::sys::unsupported; +use crate::sys::fd::WasiFd; +use crate::sys::{err2io, unsupported}; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; diff --git a/library/std/src/sys/pal/xous/net/dns.rs b/library/std/src/sys/net/connection/xous/dns.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/dns.rs rename to library/std/src/sys/net/connection/xous/dns.rs diff --git a/library/std/src/sys/pal/xous/net/mod.rs b/library/std/src/sys/net/connection/xous/mod.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/mod.rs rename to library/std/src/sys/net/connection/xous/mod.rs diff --git a/library/std/src/sys/pal/xous/net/tcplistener.rs b/library/std/src/sys/net/connection/xous/tcplistener.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/tcplistener.rs rename to library/std/src/sys/net/connection/xous/tcplistener.rs diff --git a/library/std/src/sys/pal/xous/net/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/tcpstream.rs rename to library/std/src/sys/net/connection/xous/tcpstream.rs diff --git a/library/std/src/sys/pal/xous/net/udp.rs b/library/std/src/sys/net/connection/xous/udp.rs similarity index 100% rename from library/std/src/sys/pal/xous/net/udp.rs rename to library/std/src/sys/net/connection/xous/udp.rs diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs new file mode 100644 index 000000000000..5aa197fbc0df --- /dev/null +++ b/library/std/src/sys/net/mod.rs @@ -0,0 +1,36 @@ +cfg_if::cfg_if! { + if #[cfg(any( + all(target_family = "unix", not(target_os = "l4re")), + target_os = "windows", + target_os = "hermit", + all(target_os = "wasi", target_env = "p2"), + target_os = "solid_asp3", + ))] { + mod connection { + mod socket; + pub use socket::*; + } + } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] { + mod connection { + mod sgx; + pub use sgx::*; + } + } else if #[cfg(all(target_os = "wasi", target_env = "p1"))] { + mod connection { + mod wasip1; + pub use wasip1::*; + } + } else if #[cfg(target_os = "xous")] { + mod connection { + mod xous; + pub use xous::*; + } + } else { + mod connection { + mod unsupported; + pub use unsupported::*; + } + } +} + +pub use connection::*; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index d833c9d632c6..032007aa4dca 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -24,7 +24,6 @@ pub mod fd; pub mod fs; pub mod futex; pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index ce8a2fed4bc6..0f5935d0c718 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -17,7 +17,6 @@ pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; mod libunwind_integration; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/solid/error.rs b/library/std/src/sys/pal/solid/error.rs index e092497856d4..b399463c0c28 100644 --- a/library/std/src/sys/pal/solid/error.rs +++ b/library/std/src/sys/pal/solid/error.rs @@ -1,6 +1,7 @@ pub use self::itron::error::{ItronError as SolidError, expect_success}; -use super::{abi, itron, net}; +use super::{abi, itron}; use crate::io::ErrorKind; +use crate::sys::net; /// Describe the specified SOLID error code. Returns `None` if it's an /// undefined error code. diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index d41042be5184..caf848a4e9b0 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -24,7 +24,6 @@ pub mod env; pub(crate) mod error; pub mod fs; pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -51,7 +50,7 @@ pub fn unsupported_err() -> crate::io::Error { #[inline] pub fn is_interrupted(code: i32) -> bool { - net::is_interrupted(code) + crate::sys::net::is_interrupted(code) } pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index a9900f55b192..a9904e66664e 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -15,7 +15,6 @@ pub mod env; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/teeos/net.rs b/library/std/src/sys/pal/teeos/net.rs deleted file mode 100644 index fed95205027a..000000000000 --- a/library/std/src/sys/pal/teeos/net.rs +++ /dev/null @@ -1,369 +0,0 @@ -use crate::fmt; -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; -use crate::sys::unsupported; -use crate::time::Duration; - -pub struct TcpStream(!); - -impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unsupported() - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn read_buf(&self, _buf: BorrowedCursor<'_>) -> io::Result<()> { - self.0 - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - self.0 - } - - pub fn is_read_vectored(&self) -> bool { - self.0 - } - - pub fn write(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - self.0 - } - - pub fn is_write_vectored(&self) -> bool { - self.0 - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn linger(&self) -> io::Result> { - self.0 - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn nodelay(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for TcpStream { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct TcpListener(!); - -impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn only_v6(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for TcpListener { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct UdpSocket(!); - -impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unsupported() - } - - pub fn peer_addr(&self) -> io::Result { - self.0 - } - - pub fn socket_addr(&self) -> io::Result { - self.0 - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - self.0 - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - self.0 - } - - pub fn duplicate(&self) -> io::Result { - self.0 - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - self.0 - } - - pub fn read_timeout(&self) -> io::Result> { - self.0 - } - - pub fn write_timeout(&self) -> io::Result> { - self.0 - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn broadcast(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - self.0 - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn multicast_loop_v6(&self) -> io::Result { - self.0 - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - self.0 - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - self.0 - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - self.0 - } - - pub fn ttl(&self) -> io::Result { - self.0 - } - - pub fn take_error(&self) -> io::Result> { - self.0 - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - self.0 - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - self.0 - } - - pub fn send(&self, _: &[u8]) -> io::Result { - self.0 - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - self.0 - } -} - -impl fmt::Debug for UdpSocket { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} - -pub struct LookupHost(!); - -impl LookupHost { - pub fn port(&self) -> u16 { - self.0 - } -} - -impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - self.0 - } -} - -impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unsupported() - } -} - -impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unsupported() - } -} - -#[allow(nonstandard_style)] -pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; - pub type sa_family_t = u8; - - #[derive(Copy, Clone)] - pub struct in_addr { - pub s_addr: u32, - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in { - pub sin_family: sa_family_t, - pub sin_port: u16, - pub sin_addr: in_addr, - } - - #[derive(Copy, Clone)] - pub struct in6_addr { - pub s6_addr: [u8; 16], - } - - #[derive(Copy, Clone)] - pub struct sockaddr_in6 { - pub sin6_family: sa_family_t, - pub sin6_port: u16, - pub sin6_addr: in6_addr, - pub sin6_flowinfo: u32, - pub sin6_scope_id: u32, - } -} - -pub type Socket = UdpSocket; diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 111bed7a7eb6..07025a304bfd 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -19,8 +19,6 @@ pub mod fs; pub mod helpers; #[path = "../unsupported/io.rs"] pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index 3077a72eac66..0757f1cb490d 100644 --- a/library/std/src/sys/pal/uefi/process.rs +++ b/library/std/src/sys/pal/uefi/process.rs @@ -1,6 +1,7 @@ use r_efi::protocols::simple_text_output; use super::helpers; +use crate::collections::BTreeMap; pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; use crate::num::{NonZero, NonZeroI32}; @@ -21,6 +22,7 @@ pub struct Command { args: Vec, stdout: Option, stderr: Option, + env: CommandEnv, } // passed back to std::process with the pipes connected to the child, if any @@ -40,7 +42,13 @@ pub enum Stdio { impl Command { pub fn new(program: &OsStr) -> Command { - Command { prog: program.to_os_string(), args: Vec::new(), stdout: None, stderr: None } + Command { + prog: program.to_os_string(), + args: Vec::new(), + stdout: None, + stderr: None, + env: Default::default(), + } } pub fn arg(&mut self, arg: &OsStr) { @@ -48,7 +56,7 @@ impl Command { } pub fn env_mut(&mut self) -> &mut CommandEnv { - panic!("unsupported") + &mut self.env } pub fn cwd(&mut self, _dir: &OsStr) { @@ -76,7 +84,7 @@ impl Command { } pub fn get_envs(&self) -> CommandEnvs<'_> { - panic!("unsupported") + self.env.iter() } pub fn get_current_dir(&self) -> Option<&Path> { @@ -140,8 +148,30 @@ impl Command { cmd.stderr_inherit() }; + let env = env_changes(&self.env); + + // Set any new vars + if let Some(e) = &env { + for (k, (_, v)) in e { + match v { + Some(v) => crate::env::set_var(k, v), + None => crate::env::remove_var(k), + } + } + } + let stat = cmd.start_image()?; + // Rollback any env changes + if let Some(e) = env { + for (k, (v, _)) in e { + match v { + Some(v) => crate::env::set_var(k, v), + None => crate::env::remove_var(k), + } + } + } + let stdout = cmd.stdout()?; let stderr = cmd.stderr()?; @@ -725,3 +755,32 @@ mod uefi_command_internal { res.into_boxed_slice() } } + +/// Create a map of environment variable changes. Allows efficient setting and rolling back of +/// enviroment variable changes. +/// +/// Entry: (Old Value, New Value) +fn env_changes(env: &CommandEnv) -> Option, Option)>> { + if env.is_unchanged() { + return None; + } + + let mut result = BTreeMap::, Option)>::new(); + + // Check if we want to clear all prior variables + if env.does_clear() { + for (k, v) in crate::env::vars_os() { + result.insert(k.into(), (Some(v), None)); + } + } + + for (k, v) in env.iter() { + let v: Option = v.map(Into::into); + result + .entry(k.into()) + .and_modify(|cur| *cur = (cur.0.clone(), v.clone())) + .or_insert((crate::env::var_os(k), v)); + } + + Some(result) +} diff --git a/library/std/src/sys/pal/unix/l4re.rs b/library/std/src/sys/pal/unix/l4re.rs deleted file mode 100644 index 37dd370c5146..000000000000 --- a/library/std/src/sys/pal/unix/l4re.rs +++ /dev/null @@ -1,564 +0,0 @@ -macro_rules! unimpl { - () => { - return Err(io::const_error!( - io::ErrorKind::Unsupported, - "No networking available on L4Re.", - )); - }; -} - -pub mod net { - #![allow(warnings)] - use crate::fmt; - use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; - use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; - use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; - use crate::sys::fd::FileDesc; - use crate::sys_common::{AsInner, FromInner, IntoInner}; - use crate::time::Duration; - - #[allow(unused_extern_crates)] - pub extern crate libc as netc; - - pub struct Socket(FileDesc); - impl Socket { - pub fn new(_: &SocketAddr, _: libc::c_int) -> io::Result { - unimpl!(); - } - - pub fn new_raw(_: libc::c_int, _: libc::c_int) -> io::Result { - unimpl!(); - } - - pub fn new_pair(_: libc::c_int, _: libc::c_int) -> io::Result<(Socket, Socket)> { - unimpl!(); - } - - pub fn connect_timeout(&self, _: &SocketAddr, _: Duration) -> io::Result<()> { - unimpl!(); - } - - pub fn accept( - &self, - _: *mut libc::sockaddr, - _: *mut libc::socklen_t, - ) -> io::Result { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn write(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn set_timeout(&self, _: Option, _: libc::c_int) -> io::Result<()> { - unimpl!(); - } - - pub fn timeout(&self, _: libc::c_int) -> io::Result> { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - // This is used by sys_common code to abstract over Windows and Unix. - pub fn as_raw(&self) -> RawFd { - self.as_raw_fd() - } - } - - impl AsInner for Socket { - #[inline] - fn as_inner(&self) -> &FileDesc { - &self.0 - } - } - - impl FromInner for Socket { - fn from_inner(file_desc: FileDesc) -> Socket { - Socket(file_desc) - } - } - - impl IntoInner for Socket { - fn into_inner(self) -> FileDesc { - self.0 - } - } - - impl AsFd for Socket { - fn as_fd(&self) -> BorrowedFd<'_> { - self.0.as_fd() - } - } - - impl AsRawFd for Socket { - #[inline] - fn as_raw_fd(&self) -> RawFd { - self.0.as_raw_fd() - } - } - - impl IntoRawFd for Socket { - fn into_raw_fd(self) -> RawFd { - self.0.into_raw_fd() - } - } - - impl FromRawFd for Socket { - unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { - Self(FromRawFd::from_raw_fd(raw_fd)) - } - } - - pub struct TcpStream { - inner: Socket, - } - - impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn read_buf(&self, _: BorrowedCursor<'_>) -> io::Result<()> { - unimpl!(); - } - - pub fn read_vectored(&self, _: &mut [IoSliceMut<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_read_vectored(&self) -> bool { - false - } - - pub fn write(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn write_vectored(&self, _: &[IoSlice<'_>]) -> io::Result { - unimpl!(); - } - - pub fn is_write_vectored(&self) -> bool { - false - } - - pub fn peer_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_linger(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn linger(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn nodelay(&self) -> io::Result { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for TcpStream { - fn from_inner(socket: Socket) -> TcpStream { - TcpStream { inner: socket } - } - } - - impl fmt::Debug for TcpStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re") - } - } - - pub struct TcpListener { - inner: Socket, - } - - impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn set_only_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn only_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for TcpListener { - fn from_inner(socket: Socket) -> TcpListener { - TcpListener { inner: socket } - } - } - - impl fmt::Debug for TcpListener { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support available on L4Re.") - } - } - - pub struct UdpSocket { - inner: Socket, - } - - impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result { - unimpl!(); - } - - #[inline] - pub fn socket(&self) -> &Socket { - &self.inner - } - - pub fn into_socket(self) -> Socket { - self.inner - } - - pub fn peer_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn socket_addr(&self) -> io::Result { - unimpl!(); - } - - pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { - unimpl!(); - } - - pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { - unimpl!(); - } - - pub fn duplicate(&self) -> io::Result { - unimpl!(); - } - - pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - unimpl!(); - } - - pub fn read_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn write_timeout(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_broadcast(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn broadcast(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_ttl_v4(&self) -> io::Result { - unimpl!(); - } - - pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn multicast_loop_v6(&self) -> io::Result { - unimpl!(); - } - - pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> { - unimpl!(); - } - - pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn set_ttl(&self, _: u32) -> io::Result<()> { - unimpl!(); - } - - pub fn ttl(&self) -> io::Result { - unimpl!(); - } - - pub fn take_error(&self) -> io::Result> { - unimpl!(); - } - - pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - unimpl!(); - } - - pub fn recv(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn peek(&self, _: &mut [u8]) -> io::Result { - unimpl!(); - } - - pub fn send(&self, _: &[u8]) -> io::Result { - unimpl!(); - } - - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { - unimpl!(); - } - } - - impl FromInner for UdpSocket { - fn from_inner(socket: Socket) -> UdpSocket { - UdpSocket { inner: socket } - } - } - - impl fmt::Debug for UdpSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "No networking support on L4Re available.") - } - } - - pub struct LookupHost { - original: *mut libc::addrinfo, - cur: *mut libc::addrinfo, - } - - impl Iterator for LookupHost { - type Item = SocketAddr; - fn next(&mut self) -> Option { - None - } - } - - impl LookupHost { - pub fn port(&self) -> u16 { - 0 // unimplemented - } - } - - unsafe impl Sync for LookupHost {} - unsafe impl Send for LookupHost {} - - impl TryFrom<&str> for LookupHost { - type Error = io::Error; - - fn try_from(_v: &str) -> io::Result { - unimpl!(); - } - } - - impl<'a> TryFrom<(&'a str, u16)> for LookupHost { - type Error = io::Error; - - fn try_from(_v: (&'a str, u16)) -> io::Result { - unimpl!(); - } - } -} diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 3cc1cae8d000..6862399b9425 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -14,14 +14,8 @@ pub mod futex; pub mod io; #[cfg(any(target_os = "linux", target_os = "android"))] pub mod kernel_copy; -#[cfg(target_os = "l4re")] -mod l4re; #[cfg(target_os = "linux")] pub mod linux; -#[cfg(not(target_os = "l4re"))] -pub mod net; -#[cfg(target_os = "l4re")] -pub use self::l4re::net; pub mod os; pub mod pipe; pub mod process; diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs index 01d516f7568b..4941dd4918c8 100644 --- a/library/std/src/sys/pal/unsupported/mod.rs +++ b/library/std/src/sys/pal/unsupported/mod.rs @@ -4,7 +4,6 @@ pub mod args; pub mod env; pub mod fs; pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs index 361802d101df..312ad28ab51c 100644 --- a/library/std/src/sys/pal/wasi/mod.rs +++ b/library/std/src/sys/pal/wasi/mod.rs @@ -22,7 +22,6 @@ pub mod fs; pub mod futex; pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; @@ -45,5 +44,4 @@ mod helpers; // import conflict rules. If we glob export `helpers` and `common` together, // then the compiler complains about conflicts. -use helpers::err2io; -pub use helpers::{abort_internal, decode_error_kind, is_interrupted}; +pub(crate) use helpers::{abort_internal, decode_error_kind, err2io, is_interrupted}; diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 320712fdcc9f..234e946d3b8c 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -20,7 +20,6 @@ pub mod futex; #[path = "../wasi/io.rs"] pub mod io; -pub mod net; #[path = "../wasi/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 41fe019f1102..1280f3532001 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -23,8 +23,6 @@ pub mod env; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; #[path = "../unsupported/os.rs"] pub mod os; #[path = "../unsupported/pipe.rs"] diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 4282dbb54934..f9aa049ca9ae 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -22,7 +22,6 @@ pub mod fs; pub mod futex; pub mod handle; pub mod io; -pub mod net; pub mod os; pub mod pipe; pub mod process; @@ -63,7 +62,7 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { // SAFETY: must be called only once during runtime cleanup. // NOTE: this is not guaranteed to run, for example when the program aborts. pub unsafe fn cleanup() { - net::cleanup(); + crate::sys::net::cleanup(); } #[inline] diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index a64cd0685600..8ba2b6e2f20f 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -7,7 +7,6 @@ pub mod env; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs index 6ea057720296..9e9ae8610703 100644 --- a/library/std/src/sys/pal/zkvm/mod.rs +++ b/library/std/src/sys/pal/zkvm/mod.rs @@ -18,8 +18,6 @@ pub mod env; pub mod fs; #[path = "../unsupported/io.rs"] pub mod io; -#[path = "../unsupported/net.rs"] -pub mod net; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 4f7a131f6bb9..959fbd4ca0a1 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -26,20 +26,6 @@ pub mod process; pub mod wstr; pub mod wtf8; -cfg_if::cfg_if! { - if #[cfg(any( - all(unix, not(target_os = "l4re")), - windows, - target_os = "hermit", - target_os = "solid_asp3", - all(target_os = "wasi", target_env = "p2") - ))] { - pub mod net; - } else { - pub use crate::sys::net; - } -} - // common error constructors /// A trait for viewing representations from std types diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 5fcf7eda8df7..441674936c66 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -11,12 +11,12 @@ use std::str::FromStr; use std::{env, process}; use bootstrap::{ - Build, CONFIG_CHANGE_HISTORY, Config, Flags, Subcommand, find_recent_config_change_ids, + Build, CONFIG_CHANGE_HISTORY, Config, Flags, Subcommand, debug, find_recent_config_change_ids, human_readable_changes, t, }; use build_helper::ci::CiEnv; #[cfg(feature = "tracing")] -use tracing::{debug, instrument}; +use tracing::instrument; #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "main"))] fn main() { @@ -29,10 +29,8 @@ fn main() { return; } - #[cfg(feature = "tracing")] debug!("parsing flags"); let flags = Flags::parse(&args); - #[cfg(feature = "tracing")] debug!("parsing config based on flags"); let config = Config::parse(flags); @@ -95,7 +93,6 @@ fn main() { let dump_bootstrap_shims = config.dump_bootstrap_shims; let out_dir = config.out.clone(); - #[cfg(feature = "tracing")] debug!("creating new build based on config"); Build::new(config).build(); @@ -207,8 +204,9 @@ fn check_version(config: &Config) -> Option { // Due to the conditional compilation via the `tracing` cargo feature, this means that `tracing` // usages in bootstrap need to be also gated behind the `tracing` feature: // -// - `tracing` macros (like `trace!`) and anything from `tracing`, `tracing_subscriber` and -// `tracing-tree` will need to be gated by `#[cfg(feature = "tracing")]`. +// - `tracing` macros with log levels (`trace!`, `debug!`, `warn!`, `info`, `error`) should not be +// used *directly*. You should use the wrapped `tracing` macros which gate the actual invocations +// behind `feature = "tracing"`. // - `tracing`'s `#[instrument(..)]` macro will need to be gated like `#![cfg_attr(feature = // "tracing", instrument(..))]`. #[cfg(feature = "tracing")] diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index d56f35f866cb..2dd83d5938e9 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -28,8 +28,6 @@ use std::{env, fs, io, str}; use build_helper::ci::gha; use build_helper::exit; use termcolor::{ColorChoice, StandardStream, WriteColor}; -#[cfg(feature = "tracing")] -use tracing::{debug, instrument, span, trace}; use utils::build_stamp::BuildStamp; use utils::channel::GitInfo; @@ -46,6 +44,8 @@ pub use core::builder::PathSet; pub use core::config::Config; pub use core::config::flags::{Flags, Subcommand}; +#[cfg(feature = "tracing")] +use tracing::{instrument, span}; pub use utils::change_tracker::{ CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes, }; @@ -541,72 +541,71 @@ impl Build { /// Executes the entire build, as configured by the flags and configuration. #[cfg_attr(feature = "tracing", instrument(level = "debug", name = "Build::build", skip_all))] pub fn build(&mut self) { - #[cfg(feature = "tracing")] trace!("setting up job management"); unsafe { crate::utils::job::setup(self); } - #[cfg(feature = "tracing")] - trace!("downloading rustfmt early"); - // Download rustfmt early so that it can be used in rust-analyzer configs. + trace!("downloading rustfmt early"); let _ = &builder::Builder::new(self).initial_rustfmt(); - #[cfg(feature = "tracing")] - let hardcoded_span = - span!(tracing::Level::DEBUG, "handling hardcoded subcommands (Format, Suggest, Perf)") - .entered(); + // Handle hard-coded subcommands. + { + #[cfg(feature = "tracing")] + let _hardcoded_span = span!( + tracing::Level::DEBUG, + "handling hardcoded subcommands (Format, Suggest, Perf)" + ) + .entered(); - // hardcoded subcommands - match &self.config.cmd { - Subcommand::Format { check, all } => { - return core::build_steps::format::format( - &builder::Builder::new(self), - *check, - *all, - &self.config.paths, - ); - } - Subcommand::Suggest { run } => { - return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run); - } - Subcommand::Perf { .. } => { - return core::build_steps::perf::perf(&builder::Builder::new(self)); - } - _cmd => { - #[cfg(feature = "tracing")] - debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling"); + match &self.config.cmd { + Subcommand::Format { check, all } => { + return core::build_steps::format::format( + &builder::Builder::new(self), + *check, + *all, + &self.config.paths, + ); + } + Subcommand::Suggest { run } => { + return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run); + } + Subcommand::Perf { .. } => { + return core::build_steps::perf::perf(&builder::Builder::new(self)); + } + _cmd => { + debug!(cmd = ?_cmd, "not a hardcoded subcommand; returning to normal handling"); + } } + + debug!("handling subcommand normally"); } - #[cfg(feature = "tracing")] - drop(hardcoded_span); - #[cfg(feature = "tracing")] - debug!("handling subcommand normally"); - if !self.config.dry_run() { #[cfg(feature = "tracing")] let _real_run_span = span!(tracing::Level::DEBUG, "executing real run").entered(); + // We first do a dry-run. This is a sanity-check to ensure that + // steps don't do anything expensive in the dry-run. { #[cfg(feature = "tracing")] let _sanity_check_span = span!(tracing::Level::DEBUG, "(1) executing dry-run sanity-check").entered(); - - // We first do a dry-run. This is a sanity-check to ensure that - // steps don't do anything expensive in the dry-run. self.config.dry_run = DryRun::SelfCheck; let builder = builder::Builder::new(self); builder.execute_cli(); } - #[cfg(feature = "tracing")] - let _actual_run_span = - span!(tracing::Level::DEBUG, "(2) executing actual run").entered(); - self.config.dry_run = DryRun::Disabled; - let builder = builder::Builder::new(self); - builder.execute_cli(); + // Actual run. + { + #[cfg(feature = "tracing")] + let _actual_run_span = + span!(tracing::Level::DEBUG, "(2) executing actual run").entered(); + self.config.dry_run = DryRun::Disabled; + let builder = builder::Builder::new(self); + builder.execute_cli(); + } } else { #[cfg(feature = "tracing")] let _dry_run_span = span!(tracing::Level::DEBUG, "executing dry run").entered(); diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs index ea56932b4043..caef8ce3088a 100644 --- a/src/bootstrap/src/utils/mod.rs +++ b/src/bootstrap/src/utils/mod.rs @@ -14,6 +14,8 @@ pub(crate) mod render_tests; pub(crate) mod shared_helpers; pub(crate) mod tarball; +pub(crate) mod tracing; + #[cfg(feature = "build-metrics")] pub(crate) mod metrics; diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs new file mode 100644 index 000000000000..e89decf9e558 --- /dev/null +++ b/src/bootstrap/src/utils/tracing.rs @@ -0,0 +1,49 @@ +//! Wrapper macros for `tracing` macros to avoid having to write `cfg(feature = "tracing")`-gated +//! `debug!`/`trace!` everytime, e.g. +//! +//! ```rust,ignore (example) +//! #[cfg(feature = "tracing")] +//! trace!("..."); +//! ``` +//! +//! When `feature = "tracing"` is inactive, these macros expand to nothing. + +#[macro_export] +macro_rules! trace { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::trace!($($tokens)*) + } +} + +#[macro_export] +macro_rules! debug { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::debug!($($tokens)*) + } +} + +#[macro_export] +macro_rules! warn { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::warn!($($tokens)*) + } +} + +#[macro_export] +macro_rules! info { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::info!($($tokens)*) + } +} + +#[macro_export] +macro_rules! error { + ($($tokens:tt)*) => { + #[cfg(feature = "tracing")] + ::tracing::error!($($tokens)*) + } +} diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 7eb1b8df2333..3abd538d372c 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -330,7 +330,7 @@ the source. ## `--show-type-layout`: add a section to each type's docs describing its memory layout -* Tracking issue: [#113248](https://github.com/rust-lang/rust/issues/113248) + * Tracking issue: [#113248](https://github.com/rust-lang/rust/issues/113248) Using this flag looks like this: @@ -526,9 +526,10 @@ use `-o -`. ### json + * Tracking Issue: [#76578](https://github.com/rust-lang/rust/issues/76578) + `--output-format json` emits documentation in the experimental -[JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). `--output-format html` has no effect, -and is also accepted on stable toolchains. +[JSON format](https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc_json_types/). JSON Output for toolchain crates (`std`, `alloc`, `core`, `test`, and `proc_macro`) is available via the `rust-docs-json` rustup component. @@ -546,11 +547,11 @@ information. ### doctest + * Tracking issue: [#134529](https://github.com/rust-lang/rust/issues/134529) + `--output-format doctest` emits JSON on stdout which gives you information about doctests in the provided crate. -Tracking issue: [#134529](https://github.com/rust-lang/rust/issues/134529) - You can use this option like this: ```bash @@ -606,6 +607,11 @@ The generated output (formatted) will look like this: * `doctest_code` is the code modified by rustdoc that will be run. If there is a fatal syntax error, this field will not be present. * `name` is the name generated by rustdoc which represents this doctest. +### html + +`--output-format html` has no effect, as the default output is HTML. This is +accepted on stable, even though the other options for this flag aren't. + ## `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests * Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245) diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 70749f7cb17a..d9bd11267dad 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -33,6 +33,7 @@ features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] [build-dependencies] sha2 = "0.10.8" +minifier = { version = "0.3.2", default-features = false } [dev-dependencies] expect-test = "1.4.0" diff --git a/src/librustdoc/build.rs b/src/librustdoc/build.rs index b4b0a8d0615d..07269d5bdc24 100644 --- a/src/librustdoc/build.rs +++ b/src/librustdoc/build.rs @@ -1,3 +1,6 @@ +use std::str; + +use sha2::Digest; fn main() { // generate sha256 files // this avoids having to perform hashing at runtime @@ -40,14 +43,27 @@ fn main() { for path in files { let inpath = format!("html/{path}"); println!("cargo::rerun-if-changed={inpath}"); - let bytes = std::fs::read(inpath).expect("static path exists"); - use sha2::Digest; - let bytes = sha2::Sha256::digest(bytes); - let mut digest = format!("-{bytes:x}"); + let data_bytes = std::fs::read(&inpath).expect("static path exists"); + let hash_bytes = sha2::Sha256::digest(&data_bytes); + let mut digest = format!("-{hash_bytes:x}"); digest.truncate(9); let outpath = std::path::PathBuf::from(format!("{out_dir}/{path}.sha256")); std::fs::create_dir_all(outpath.parent().expect("all file paths are in a directory")) .expect("should be able to write to out_dir"); std::fs::write(&outpath, digest.as_bytes()).expect("write to out_dir"); + let minified_path = std::path::PathBuf::from(format!("{out_dir}/{path}.min")); + if path.ends_with(".js") || path.ends_with(".css") { + let minified: String = if path.ends_with(".css") { + minifier::css::minify(str::from_utf8(&data_bytes).unwrap()) + .unwrap() + .to_string() + .into() + } else { + minifier::js::minify(str::from_utf8(&data_bytes).unwrap()).to_string().into() + }; + std::fs::write(&minified_path, minified.as_bytes()).expect("write to out_dir"); + } else { + std::fs::copy(&inpath, &minified_path).unwrap(); + } } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 55bd7da88163..b14381c0c404 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -516,8 +516,7 @@ fn projection_to_path_segment<'tcx>( ty.map_bound(|ty| &ty.args[generics.parent_count..]), false, def_id, - ) - .into(), + ), constraints: Default::default(), }, } @@ -2214,8 +2213,7 @@ pub(crate) fn clean_middle_ty<'tcx>( alias_ty.map_bound(|ty| ty.args.as_slice()), true, def_id, - ) - .into(), + ), constraints: Default::default(), }, }, @@ -2533,7 +2531,7 @@ fn clean_generic_args<'tcx>( ) -> GenericArgs { // FIXME(return_type_notation): Fix RTN parens rendering if let Some((inputs, output)) = generic_args.paren_sugar_inputs_output() { - let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect::>().into(); + let inputs = inputs.iter().map(|x| clean_ty(x, cx)).collect::>().into(); let output = match output.kind { hir::TyKind::Tup(&[]) => None, _ => Some(Box::new(clean_ty(output, cx))), @@ -2554,7 +2552,7 @@ fn clean_generic_args<'tcx>( } hir::GenericArg::Infer(_inf) => GenericArg::Infer, }) - .collect::>() + .collect::>() .into(); let constraints = generic_args .constraints diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index a44d74e6df67..583cd404dca9 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2246,8 +2246,8 @@ impl GenericArg { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) enum GenericArgs { - AngleBracketed { args: Box<[GenericArg]>, constraints: ThinVec }, - Parenthesized { inputs: Box<[Type]>, output: Option> }, + AngleBracketed { args: ThinVec, constraints: ThinVec }, + Parenthesized { inputs: ThinVec, output: Option> }, } impl GenericArgs { @@ -2271,7 +2271,7 @@ impl GenericArgs { assoc: PathSegment { name: sym::Output, args: GenericArgs::AngleBracketed { - args: Vec::new().into_boxed_slice(), + args: ThinVec::new(), constraints: ThinVec::new(), }, }, @@ -2588,12 +2588,12 @@ mod size_asserts { static_assert_size!(Crate, 56); // frequently moved by-value static_assert_size!(DocFragment, 32); static_assert_size!(GenericArg, 32); - static_assert_size!(GenericArgs, 32); + static_assert_size!(GenericArgs, 24); static_assert_size!(GenericParamDef, 40); static_assert_size!(Generics, 16); static_assert_size!(Item, 48); static_assert_size!(ItemKind, 48); - static_assert_size!(PathSegment, 40); + static_assert_size!(PathSegment, 32); static_assert_size!(Type, 32); // tidy-alphabetical-end } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index f1921b90cc68..8a7d140bb1a6 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -81,11 +81,11 @@ pub(crate) fn clean_middle_generic_args<'tcx>( args: ty::Binder<'tcx, &'tcx [ty::GenericArg<'tcx>]>, mut has_self: bool, owner: DefId, -) -> Vec { +) -> ThinVec { let (args, bound_vars) = (args.skip_binder(), args.bound_vars()); if args.is_empty() { // Fast path which avoids executing the query `generics_of`. - return Vec::new(); + return ThinVec::new(); } // If the container is a trait object type, the arguments won't contain the self type but the @@ -144,7 +144,7 @@ pub(crate) fn clean_middle_generic_args<'tcx>( }; let offset = if has_self { 1 } else { 0 }; - let mut clean_args = Vec::with_capacity(args.len().saturating_sub(offset)); + let mut clean_args = ThinVec::with_capacity(args.len().saturating_sub(offset)); clean_args.extend(args.iter().enumerate().rev().filter_map(clean_arg)); clean_args.reverse(); clean_args diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index c6fb70eee085..fd4d9845c892 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -489,7 +489,7 @@ impl Options { let to_check = matches.opt_strs("check-theme"); if !to_check.is_empty() { let mut content = - std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.bytes).unwrap(); + std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.src_bytes).unwrap(); if let Some((_, inside)) = content.split_once("/* Begin theme: light */") { content = inside; } @@ -638,7 +638,7 @@ impl Options { let mut themes = Vec::new(); if matches.opt_present("theme") { let mut content = - std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.bytes).unwrap(); + std::str::from_utf8(static_files::STATIC_FILES.rustdoc_css.src_bytes).unwrap(); if let Some((_, inside)) = content.split_once("/* Begin theme: light */") { content = inside; } diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index e4a9a2b512e5..8ac8b3a1f843 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -821,7 +821,7 @@ pub(crate) fn get_function_type_for_search( .map(|name| clean::PathSegment { name: *name, args: clean::GenericArgs::AngleBracketed { - args: Vec::new().into_boxed_slice(), + args: ThinVec::new(), constraints: ThinVec::new(), }, }) diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index fb6f3bc2c76e..57d07c05c118 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -207,14 +207,8 @@ fn write_static_files( if opt.emit.is_empty() || opt.emit.contains(&EmitType::Toolchain) { static_files::for_each(|f: &static_files::StaticFile| { let filename = static_dir.join(f.output_filename()); - let contents: &[u8]; - let contents_vec: Vec; - if opt.disable_minification { - contents = f.bytes; - } else { - contents_vec = f.minified(); - contents = &contents_vec; - }; + let contents: &[u8] = + if opt.disable_minification { f.src_bytes } else { f.minified_bytes }; fs::write(&filename, contents).map_err(|e| PathError::new(e, &filename)) })?; } diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index bfd5cb7764fd..a348c6c5678b 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -196,15 +196,13 @@ function switchDisplayedElement(elemToDisplay) { removeClass(el, "hidden"); const mainHeading = elemToDisplay.querySelector(".main-heading"); - // @ts-expect-error - if (mainHeading && searchState.rustdocToolbar) { - // @ts-expect-error - if (searchState.rustdocToolbar.parentElement) { - // @ts-expect-error - searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); + if (mainHeading && window.searchState.rustdocToolbar) { + if (window.searchState.rustdocToolbar.parentElement) { + window.searchState.rustdocToolbar.parentElement.removeChild( + window.searchState.rustdocToolbar, + ); } - // @ts-expect-error - mainHeading.appendChild(searchState.rustdocToolbar); + mainHeading.appendChild(window.searchState.rustdocToolbar); } } @@ -212,7 +210,12 @@ function browserSupportsHistoryApi() { return window.history && typeof window.history.pushState === "function"; } -// @ts-expect-error +/** + * Download CSS from the web without making it the active stylesheet. + * We use this in the settings popover so that you don't get FOUC when switching. + * + * @param {string} cssUrl + */ function preLoadCss(cssUrl) { // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload const link = document.createElement("link"); @@ -225,7 +228,11 @@ function preLoadCss(cssUrl) { (function() { const isHelpPage = window.location.pathname.endsWith("/help.html"); - // @ts-expect-error + /** + * Run a JavaScript file asynchronously. + * @param {string} url + * @param {function(): any} errorCallback + */ function loadScript(url, errorCallback) { const script = document.createElement("script"); script.src = url; @@ -235,13 +242,12 @@ function preLoadCss(cssUrl) { document.head.append(script); } - if (getSettingsButton()) { - // @ts-expect-error - getSettingsButton().onclick = event => { + const settingsButton = getSettingsButton(); + if (settingsButton) { + settingsButton.onclick = event => { if (event.ctrlKey || event.altKey || event.metaKey) { return; } - // @ts-expect-error window.hideAllModals(false); addClass(getSettingsButton(), "rotate"); event.preventDefault(); @@ -470,7 +476,6 @@ function preLoadCss(cssUrl) { } return onEachLazy(implElem.parentElement.parentElement.querySelectorAll( `[id^="${assocId}"]`), - // @ts-expect-error item => { const numbered = /^(.+?)-([0-9]+)$/.exec(item.id); if (item.id === assocId || (numbered && numbered[1] === assocId)) { @@ -522,7 +527,6 @@ function preLoadCss(cssUrl) { ev.preventDefault(); // @ts-expect-error searchState.defocus(); - // @ts-expect-error window.hideAllModals(true); // true = reset focus for tooltips } @@ -687,7 +691,6 @@ function preLoadCss(cssUrl) { // // By the way, this is only used by and useful for traits implemented automatically // (like "Send" and "Sync"). - // @ts-expect-error onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => { const aliases = el.getAttribute("data-aliases"); if (!aliases) { @@ -740,7 +743,6 @@ function preLoadCss(cssUrl) { code.innerHTML = struct[TEXT_IDX]; addClass(code, "code-header"); - // @ts-expect-error onEachLazy(code.getElementsByTagName("a"), elem => { const href = elem.getAttribute("href"); @@ -886,7 +888,6 @@ function preLoadCss(cssUrl) { const template = document.createElement("template"); template.innerHTML = text; - // @ts-expect-error onEachLazy(template.content.querySelectorAll("a"), elem => { const href = elem.getAttribute("href"); @@ -894,7 +895,6 @@ function preLoadCss(cssUrl) { elem.setAttribute("href", window.rootPath + href); } }); - // @ts-expect-error onEachLazy(template.content.querySelectorAll("[id]"), el => { let i = 0; if (idMap.has(el.id)) { @@ -912,7 +912,6 @@ function preLoadCss(cssUrl) { const oldHref = `#${el.id}`; const newHref = `#${el.id}-${i}`; el.id = `${el.id}-${i}`; - // @ts-expect-error onEachLazy(template.content.querySelectorAll("a[href]"), link => { if (link.getAttribute("href") === oldHref) { link.href = newHref; @@ -933,7 +932,6 @@ function preLoadCss(cssUrl) { // @ts-expect-error sidebarTraitList.append(li); } else { - // @ts-expect-error onEachLazy(templateAssocItems, item => { let block = hasClass(item, "associatedtype") ? associatedTypes : ( hasClass(item, "associatedconstant") ? associatedConstants : ( @@ -1040,7 +1038,6 @@ function preLoadCss(cssUrl) { function expandAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); removeClass(innerToggle, "will-expand"); - // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) { e.open = true; @@ -1053,7 +1050,6 @@ function preLoadCss(cssUrl) { function collapseAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); addClass(innerToggle, "will-expand"); - // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (e.parentNode.id !== "implementations-list" || (!hasClass(e, "implementors-toggle") && @@ -1092,7 +1088,6 @@ function preLoadCss(cssUrl) { function setImplementorsTogglesOpen(id, open) { const list = document.getElementById(id); if (list !== null) { - // @ts-expect-error onEachLazy(list.getElementsByClassName("implementors-toggle"), e => { e.open = open; }); @@ -1104,7 +1099,6 @@ function preLoadCss(cssUrl) { setImplementorsTogglesOpen("blanket-implementations-list", false); } - // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) { e.open = true; @@ -1124,7 +1118,6 @@ function preLoadCss(cssUrl) { } onEachLazy(document.querySelectorAll( ":not(.scraped-example) > .example-wrap > pre:not(.example-line-numbers)", - // @ts-expect-error ), x => { const parent = x.parentNode; const line_numbers = parent.querySelectorAll(".example-line-numbers"); @@ -1145,7 +1138,6 @@ function preLoadCss(cssUrl) { // @ts-expect-error window.rustdoc_remove_line_numbers_from_examples = () => { - // @ts-expect-error onEachLazy(document.querySelectorAll(".example-wrap > .example-line-numbers"), x => { x.parentNode.removeChild(x); }); @@ -1157,16 +1149,13 @@ function preLoadCss(cssUrl) { } function showSidebar() { - // @ts-expect-error window.hideAllModals(false); const sidebar = document.getElementsByClassName("sidebar")[0]; - // @ts-expect-error addClass(sidebar, "shown"); } function hideSidebar() { const sidebar = document.getElementsByClassName("sidebar")[0]; - // @ts-expect-error removeClass(sidebar, "shown"); } @@ -1193,7 +1182,6 @@ function preLoadCss(cssUrl) { mainElem.addEventListener("click", hideSidebar); } - // @ts-expect-error onEachLazy(document.querySelectorAll("a[href^='#']"), el => { // For clicks on internal links ( tags with a hash property), we expand the section we're // jumping to *before* jumping there. We can't do this in onHashChange, because it changes @@ -1204,7 +1192,6 @@ function preLoadCss(cssUrl) { }); }); - // @ts-expect-error onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"), el => { // @ts-expect-error el.addEventListener("click", e => { @@ -1241,7 +1228,6 @@ function preLoadCss(cssUrl) { clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); return; } - // @ts-expect-error window.hideAllModals(false); const wrapper = document.createElement("div"); if (notable_ty) { @@ -1422,7 +1408,6 @@ function preLoadCss(cssUrl) { } } - // @ts-expect-error onEachLazy(document.getElementsByClassName("tooltip"), e => { e.onclick = () => { e.TOOLTIP_FORCE_VISIBLE = e.TOOLTIP_FORCE_VISIBLE ? false : true; @@ -1527,7 +1512,6 @@ function preLoadCss(cssUrl) { // @ts-expect-error !getSettingsButton().contains(event.relatedTarget) ) { - // @ts-expect-error window.hidePopoverMenus(); } } @@ -1626,10 +1610,8 @@ function preLoadCss(cssUrl) { * * Pass "true" to reset focus for tooltip popovers. */ - // @ts-expect-error window.hideAllModals = switchFocus => { hideSidebar(); - // @ts-expect-error window.hidePopoverMenus(); hideTooltip(switchFocus); }; @@ -1637,9 +1619,7 @@ function preLoadCss(cssUrl) { /** * Hide all the popover menus. */ - // @ts-expect-error window.hidePopoverMenus = () => { - // @ts-expect-error onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"), elem => { elem.style.display = "none"; }); @@ -1708,7 +1688,6 @@ function preLoadCss(cssUrl) { if (shouldShowHelp) { showHelp(); } else { - // @ts-expect-error window.hidePopoverMenus(); } }); @@ -1780,30 +1759,42 @@ function preLoadCss(cssUrl) { }); } - // Pointer capture. - // - // Resizing is a single-pointer gesture. Any secondary pointer is ignored - // @ts-expect-error + /** + * Pointer capture. + * + * Resizing is a single-pointer gesture. Any secondary pointer is ignored + * + * @type {null|number} + */ let currentPointerId = null; - // "Desired" sidebar size. - // - // This is stashed here for window resizing. If the sidebar gets - // shrunk to maintain BODY_MIN, and then the user grows the window again, - // it gets the sidebar to restore its size. - // @ts-expect-error + /** + * "Desired" sidebar size. + * + * This is stashed here for window resizing. If the sidebar gets + * shrunk to maintain BODY_MIN, and then the user grows the window again, + * it gets the sidebar to restore its size. + * + * @type {null|number} + */ let desiredSidebarSize = null; - // Sidebar resize debouncer. - // - // The sidebar itself is resized instantly, but the body HTML can be too - // big for that, causing reflow jank. To reduce this, we queue up a separate - // animation frame and throttle it. + /** + * Sidebar resize debouncer. + * + * The sidebar itself is resized instantly, but the body HTML can be too + * big for that, causing reflow jank. To reduce this, we queue up a separate + * animation frame and throttle it. + * + * @type {false|ReturnType} + */ let pendingSidebarResizingFrame = false; - // If this page has no sidebar at all, bail out. + /** @type {HTMLElement|null} */ const resizer = document.querySelector(".sidebar-resizer"); + /** @type {HTMLElement|null} */ const sidebar = document.querySelector(".sidebar"); + // If this page has no sidebar at all, bail out. if (!resizer || !sidebar) { return; } @@ -1820,11 +1811,9 @@ function preLoadCss(cssUrl) { // from settings.js, which uses a separate function. It's done here because // the minimum sidebar size is rather uncomfortable, and it must pass // through that size when using the shrink-to-nothing gesture. - function hideSidebar() { + const hideSidebar = function() { if (isSrcPage) { - // @ts-expect-error window.rustdocCloseSourceSidebar(); - // @ts-expect-error updateLocalStorage("src-sidebar-width", null); // [RUSTDOCIMPL] CSS variable fast path // @@ -1837,22 +1826,17 @@ function preLoadCss(cssUrl) { // // So, to clear it, we need to clear all three. document.documentElement.style.removeProperty("--src-sidebar-width"); - // @ts-expect-error sidebar.style.removeProperty("--src-sidebar-width"); - // @ts-expect-error resizer.style.removeProperty("--src-sidebar-width"); } else { addClass(document.documentElement, "hide-sidebar"); updateLocalStorage("hide-sidebar", "true"); - // @ts-expect-error updateLocalStorage("desktop-sidebar-width", null); document.documentElement.style.removeProperty("--desktop-sidebar-width"); - // @ts-expect-error sidebar.style.removeProperty("--desktop-sidebar-width"); - // @ts-expect-error resizer.style.removeProperty("--desktop-sidebar-width"); } - } + }; // Call this function to show the sidebar from the resize handle. // On docs pages, this can only happen if the user has grabbed the resize @@ -1860,15 +1844,14 @@ function preLoadCss(cssUrl) { // the visible range without releasing it. You can, however, grab the // resize handle on a source page with the sidebar closed, because it // remains visible all the time on there. - function showSidebar() { + const showSidebar = function() { if (isSrcPage) { - // @ts-expect-error window.rustdocShowSourceSidebar(); } else { removeClass(document.documentElement, "hide-sidebar"); updateLocalStorage("hide-sidebar", "false"); } - } + }; /** * Call this to set the correct CSS variable and setting. @@ -1876,44 +1859,40 @@ function preLoadCss(cssUrl) { * * @param {number} size - CSS px width of the sidebar. */ - function changeSidebarSize(size) { + const changeSidebarSize = function(size) { if (isSrcPage) { - // @ts-expect-error - updateLocalStorage("src-sidebar-width", size); + updateLocalStorage("src-sidebar-width", size.toString()); // [RUSTDOCIMPL] CSS variable fast path // // While this property is set on the HTML element at load time, // because the sidebar isn't actually loaded yet, // we scope this update to the sidebar to avoid hitting a slow // path in WebKit. - // @ts-expect-error sidebar.style.setProperty("--src-sidebar-width", size + "px"); - // @ts-expect-error resizer.style.setProperty("--src-sidebar-width", size + "px"); } else { - // @ts-expect-error - updateLocalStorage("desktop-sidebar-width", size); - // @ts-expect-error + updateLocalStorage("desktop-sidebar-width", size.toString()); sidebar.style.setProperty("--desktop-sidebar-width", size + "px"); - // @ts-expect-error resizer.style.setProperty("--desktop-sidebar-width", size + "px"); } - } + }; // Check if the sidebar is hidden. Since src pages and doc pages have // different settings, this function has to check that. - function isSidebarHidden() { + const isSidebarHidden = function() { return isSrcPage ? !hasClass(document.documentElement, "src-sidebar-expanded") : hasClass(document.documentElement, "hide-sidebar"); - } + }; - // Respond to the resize handle event. - // This function enforces size constraints, and implements the - // shrink-to-nothing gesture based on thresholds defined above. - // @ts-expect-error - function resize(e) { - // @ts-expect-error + /** + * Respond to the resize handle event. + * This function enforces size constraints, and implements the + * shrink-to-nothing gesture based on thresholds defined above. + * + * @param {PointerEvent} e + */ + const resize = function(e) { if (currentPointerId === null || currentPointerId !== e.pointerId) { return; } @@ -1931,97 +1910,83 @@ function preLoadCss(cssUrl) { changeSidebarSize(constrainedPos); desiredSidebarSize = constrainedPos; if (pendingSidebarResizingFrame !== false) { - // @ts-expect-error clearTimeout(pendingSidebarResizingFrame); } - // @ts-expect-error pendingSidebarResizingFrame = setTimeout(() => { - // @ts-expect-error if (currentPointerId === null || pendingSidebarResizingFrame === false) { return; } pendingSidebarResizingFrame = false; document.documentElement.style.setProperty( "--resizing-sidebar-width", - // @ts-expect-error desiredSidebarSize + "px", ); }, 100); } - } + }; // Respond to the window resize event. window.addEventListener("resize", () => { if (window.innerWidth < RUSTDOC_MOBILE_BREAKPOINT) { return; } stopResize(); - // @ts-expect-error - if (desiredSidebarSize >= (window.innerWidth - BODY_MIN)) { + if (desiredSidebarSize !== null && desiredSidebarSize >= (window.innerWidth - BODY_MIN)) { changeSidebarSize(window.innerWidth - BODY_MIN); - // @ts-expect-error } else if (desiredSidebarSize !== null && desiredSidebarSize > SIDEBAR_MIN) { - // @ts-expect-error changeSidebarSize(desiredSidebarSize); } }); - // @ts-expect-error - function stopResize(e) { - // @ts-expect-error + + /** + * @param {PointerEvent=} e + */ + const stopResize = function(e) { if (currentPointerId === null) { return; } if (e) { e.preventDefault(); } - // @ts-expect-error desiredSidebarSize = sidebar.getBoundingClientRect().width; - // @ts-expect-error removeClass(resizer, "active"); window.removeEventListener("pointermove", resize, false); window.removeEventListener("pointerup", stopResize, false); removeClass(document.documentElement, "sidebar-resizing"); document.documentElement.style.removeProperty( "--resizing-sidebar-width"); - // @ts-expect-error if (resizer.releasePointerCapture) { - // @ts-expect-error resizer.releasePointerCapture(currentPointerId); currentPointerId = null; } - } - // @ts-expect-error - function initResize(e) { - // @ts-expect-error + }; + + /** + * @param {PointerEvent} e + */ + const initResize = function(e) { if (currentPointerId !== null || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { return; } - // @ts-expect-error if (resizer.setPointerCapture) { - // @ts-expect-error resizer.setPointerCapture(e.pointerId); - // @ts-expect-error if (!resizer.hasPointerCapture(e.pointerId)) { // unable to capture pointer; something else has it // on iOS, this usually means you long-clicked a link instead - // @ts-expect-error resizer.releasePointerCapture(e.pointerId); return; } currentPointerId = e.pointerId; } - // @ts-expect-error window.hideAllModals(false); e.preventDefault(); window.addEventListener("pointermove", resize, false); window.addEventListener("pointercancel", stopResize, false); window.addEventListener("pointerup", stopResize, false); - // @ts-expect-error addClass(resizer, "active"); addClass(document.documentElement, "sidebar-resizing"); - // @ts-expect-error const pos = e.clientX - sidebar.offsetLeft - 3; document.documentElement.style.setProperty( "--resizing-sidebar-width", pos + "px"); desiredSidebarSize = null; - } + }; resizer.addEventListener("pointerdown", initResize, false); }()); @@ -2029,8 +1994,13 @@ function preLoadCss(cssUrl) { // and the copy buttons on the code examples. (function() { // Common functions to copy buttons. - // @ts-expect-error + /** + * @param {string|null} content + */ function copyContentToClipboard(content) { + if (content === null) { + return; + } const el = document.createElement("textarea"); el.value = content; el.setAttribute("readonly", ""); @@ -2044,15 +2014,17 @@ function preLoadCss(cssUrl) { document.body.removeChild(el); } - // @ts-expect-error + /** + * @param {HTMLElement & {reset_button_timeout?: ReturnType}} button + */ function copyButtonAnimation(button) { button.classList.add("clicked"); if (button.reset_button_timeout !== undefined) { - window.clearTimeout(button.reset_button_timeout); + clearTimeout(button.reset_button_timeout); } - button.reset_button_timeout = window.setTimeout(() => { + button.reset_button_timeout = setTimeout(() => { button.reset_button_timeout = undefined; button.classList.remove("clicked"); }, 1000); @@ -2067,9 +2039,7 @@ function preLoadCss(cssUrl) { // Most page titles are ' in - Rust', except // modules (which don't have the first part) and keywords/primitives // (which don't have a module path) - // @ts-expect-error - const title = document.querySelector("title").textContent.replace(" - Rust", ""); - const [item, module] = title.split(" in "); + const [item, module] = document.title.split(" in "); const path = [item]; if (module !== undefined) { path.unshift(module); @@ -2079,8 +2049,10 @@ function preLoadCss(cssUrl) { copyButtonAnimation(but); }; - // Copy buttons on code examples. - // @ts-expect-error + /** + * Copy buttons on code examples. + * @param {HTMLElement|null} codeElem + */ function copyCode(codeElem) { if (!codeElem) { // Should never happen, but the world is a dark and dangerous place. @@ -2089,23 +2061,34 @@ function preLoadCss(cssUrl) { copyContentToClipboard(codeElem.textContent); } - // @ts-expect-error + /** + * @param {UIEvent} event + * @returns {HTMLElement|null} + */ function getExampleWrap(event) { - let elem = event.target; - while (!hasClass(elem, "example-wrap")) { - if (elem === document.body || - elem.tagName === "A" || - elem.tagName === "BUTTON" || - hasClass(elem, "docblock") - ) { - return null; + const target = event.target; + if (target instanceof HTMLElement) { + /** @type {HTMLElement|null} */ + let elem = target; + while (elem !== null && !hasClass(elem, "example-wrap")) { + if (elem === document.body || + elem.tagName === "A" || + elem.tagName === "BUTTON" || + hasClass(elem, "docblock") + ) { + return null; + } + elem = elem.parentElement; } - elem = elem.parentElement; + return elem; + } else { + return null; } - return elem; } - // @ts-expect-error + /** + * @param {UIEvent} event + */ function addCopyButton(event) { const elem = getExampleWrap(event); if (elem === null) { @@ -2132,15 +2115,17 @@ function preLoadCss(cssUrl) { }); parent.appendChild(copyButton); - if (!elem.parentElement.classList.contains("scraped-example")) { + if (!elem.parentElement || !elem.parentElement.classList.contains("scraped-example") || + !window.updateScrapedExample) { return; } const scrapedWrapped = elem.parentElement; - // @ts-expect-error window.updateScrapedExample(scrapedWrapped, parent); } - // @ts-expect-error + /** + * @param {UIEvent} event + */ function showHideCodeExampleButtons(event) { const elem = getExampleWrap(event); if (elem === null) { @@ -2159,7 +2144,6 @@ function preLoadCss(cssUrl) { buttons.classList.toggle("keep-visible"); } - // @ts-expect-error onEachLazy(document.querySelectorAll(".docblock .example-wrap"), elem => { elem.addEventListener("mouseover", addCopyButton); elem.addEventListener("click", showHideCodeExampleButtons); diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts index 18a3e22113b8..acea7828e86a 100644 --- a/src/librustdoc/html/static/js/rustdoc.d.ts +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -20,6 +20,28 @@ declare global { * As a multi-page application, we know this never changes once set. */ currentCrate: string|null; + /** + * Hide popovers, tooltips, or the mobile sidebar. + */ + hideAllModals: function(boolean), + /** + * Hide popovers, but leave other modals alone. + */ + hidePopoverMenus: function(), + /** + * Hide the source page sidebar. If it's already closed, + * or if this is a docs page, this function does nothing. + */ + rustdocCloseSourceSidebar: function(), + /** + * Show the source page sidebar. If it's already opened, + * or if this is a docs page, this function does nothing. + */ + rustdocShowSourceSidebar: function(), + /** + * Set up event listeners for a scraped source example. + */ + updateScrapedExample?: function(HTMLElement, HTMLElement), } interface HTMLElement { /** Used by the popover tooltip code. */ diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 662e95106997..121a43e3d92e 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -4732,10 +4732,8 @@ function printTab(nb) { // Corrections only kick in on type-based searches. const correctionsElem = document.getElementsByClassName("search-corrections"); if (isTypeSearch) { - // @ts-expect-error removeClass(correctionsElem[0], "hidden"); } else { - // @ts-expect-error addClass(correctionsElem[0], "hidden"); } } else if (nb !== 0) { diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index 10369e77320f..3042373fb096 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -59,7 +59,7 @@ function hasClass(elem, className) { * Add a class to a DOM Element. If `elem` is null, * does nothing. This function is idempotent. * - * @param {HTMLElement|null} elem + * @param {Element|null} elem * @param {string} className */ function addClass(elem, className) { @@ -72,7 +72,7 @@ function addClass(elem, className) { * Remove a class from a DOM Element. If `elem` is null, * does nothing. This function is idempotent. * - * @param {HTMLElement|null} elem + * @param {Element|null} elem * @param {string} className */ // eslint-disable-next-line no-unused-vars @@ -85,7 +85,7 @@ function removeClass(elem, className) { /** * Run a callback for every element of an Array. * @param {Array} arr - The array to iterate over - * @param {function(?): boolean|undefined} func - The callback + * @param {function(?): boolean|void} func - The callback */ function onEach(arr, func) { for (const elem of arr) { @@ -103,7 +103,7 @@ function onEach(arr, func) { * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection * https://developer.mozilla.org/en-US/docs/Web/API/NodeList * @param {NodeList|HTMLCollection} lazyArray - An array to iterate over - * @param {function(?): boolean} func - The callback + * @param {function(?): boolean|void} func - The callback */ // eslint-disable-next-line no-unused-vars function onEachLazy(lazyArray, func) { @@ -119,11 +119,15 @@ function onEachLazy(lazyArray, func) { * If localStorage is disabled, this function does nothing. * * @param {string} name - * @param {string} value + * @param {string|null} value */ function updateLocalStorage(name, value) { try { - window.localStorage.setItem("rustdoc-" + name, value); + if (value === null) { + window.localStorage.removeItem("rustdoc-" + name); + } else { + window.localStorage.setItem("rustdoc-" + name, value); + } } catch (e) { // localStorage is not accessible, do nothing } diff --git a/src/librustdoc/html/static_files.rs b/src/librustdoc/html/static_files.rs index 0bcaf11da0ce..45589a370698 100644 --- a/src/librustdoc/html/static_files.rs +++ b/src/librustdoc/html/static_files.rs @@ -8,26 +8,18 @@ use std::{fmt, str}; pub(crate) struct StaticFile { pub(crate) filename: PathBuf, - pub(crate) bytes: &'static [u8], + pub(crate) src_bytes: &'static [u8], + pub(crate) minified_bytes: &'static [u8], } impl StaticFile { - fn new(filename: &str, bytes: &'static [u8], sha256: &'static str) -> StaticFile { - Self { filename: static_filename(filename, sha256), bytes } - } - - pub(crate) fn minified(&self) -> Vec { - let extension = match self.filename.extension() { - Some(e) => e, - None => return self.bytes.to_owned(), - }; - if extension == "css" { - minifier::css::minify(str::from_utf8(self.bytes).unwrap()).unwrap().to_string().into() - } else if extension == "js" { - minifier::js::minify(str::from_utf8(self.bytes).unwrap()).to_string().into() - } else { - self.bytes.to_owned() - } + fn new( + filename: &str, + src_bytes: &'static [u8], + minified_bytes: &'static [u8], + sha256: &'static str, + ) -> StaticFile { + Self { filename: static_filename(filename, sha256), src_bytes, minified_bytes } } pub(crate) fn output_filename(&self) -> &Path { @@ -68,7 +60,7 @@ macro_rules! static_files { // sha256 files are generated in build.rs pub(crate) static STATIC_FILES: std::sync::LazyLock = std::sync::LazyLock::new(|| StaticFiles { - $($field: StaticFile::new($file_path, include_bytes!($file_path), include_str!(concat!(env!("OUT_DIR"), "/", $file_path, ".sha256"))),)+ + $($field: StaticFile::new($file_path, include_bytes!($file_path), include_bytes!(concat!(env!("OUT_DIR"), "/", $file_path, ".min")), include_str!(concat!(env!("OUT_DIR"), "/", $file_path, ".sha256"))),)+ }); pub(crate) fn for_each(f: impl Fn(&StaticFile) -> Result<(), E>) -> Result<(), E> { diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index afe81937495d..fcee29609797 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -231,11 +231,11 @@ impl FromClean for GenericArgs { use clean::GenericArgs::*; match args { AngleBracketed { args, constraints } => GenericArgs::AngleBracketed { - args: args.into_vec().into_json(renderer), + args: args.into_json(renderer), constraints: constraints.into_json(renderer), }, Parenthesized { inputs, output } => GenericArgs::Parenthesized { - inputs: inputs.into_vec().into_json(renderer), + inputs: inputs.into_json(renderer), output: output.map(|a| (*a).into_json(renderer)), }, } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index e67e17a73cb7..a92f3ded7746 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -61,6 +61,13 @@ pub struct Crate { #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ExternalCrate { /// The name of the crate. + /// + /// Note: This is the [*crate* name][crate-name], which may not be the same as the + /// [*package* name][package-name]. For example, for , + /// this field will be `regex_syntax` (which uses an `_`, not a `-`). + /// + /// [crate-name]: https://doc.rust-lang.org/stable/cargo/reference/cargo-targets.html#the-name-field + /// [package-name]: https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-name-field pub name: String, /// The root URL at which the crate's documentation lives. pub html_root_url: Option, diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 2eb09bac8d88..798f4575c2e1 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -362,18 +362,21 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { defaultness: ld, sig: lf, generics: lg, + contract: lc, body: lb, }), Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, + contract: rc, body: rb, }), ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) + && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) }, (Mod(lu, lmk), Mod(ru, rmk)) => { @@ -497,18 +500,21 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool { defaultness: ld, sig: lf, generics: lg, + contract: lc, body: lb, }), Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, + contract: rc, body: rb, }), ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) + && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) }, ( @@ -559,18 +565,21 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool { defaultness: ld, sig: lf, generics: lg, + contract: lc, body: lb, }), Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, + contract: rc, body: rb, }), ) => { eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) + && eq_opt_fn_contract(lc, rc) && both(lb.as_ref(), rb.as_ref(), |l, r| eq_block(l, r)) }, ( @@ -653,6 +662,17 @@ pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool { && eq_ext(&l.ext, &r.ext) } +pub fn eq_opt_fn_contract(l: &Option>, r: &Option>) -> bool { + match (l, r) { + (Some(l), Some(r)) => { + eq_expr_opt(l.requires.as_ref(), r.requires.as_ref()) + && eq_expr_opt(l.ensures.as_ref(), r.ensures.as_ref()) + } + (None, None) => true, + (Some(_), None) | (None, Some(_)) => false, + } +} + pub fn eq_generics(l: &Generics, r: &Generics) -> bool { over(&l.params, &r.params, eq_generic_param) && over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| { diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 0aaef91e48a6..5a3a3d0cedc4 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -179,7 +179,7 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks, _) + Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks | NullOp::ContractChecks, _) | Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 4735db48e81f..6bd1076a8a84 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1150,6 +1150,11 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { interp_ok(ecx.tcx.sess.ub_checks()) } + #[inline(always)] + fn contract_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> { + interp_ok(ecx.tcx.sess.contract_checks()) + } + #[inline(always)] fn thread_local_static_pointer( ecx: &mut MiriInterpCx<'tcx>, diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index 8d99924a2d17..33dc57cbc077 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -338,6 +338,18 @@ impl Rustc { self } + /// Specify `-C debuginfo=...`. + pub fn debuginfo(&mut self, level: &str) -> &mut Self { + self.cmd.arg(format!("-Cdebuginfo={level}")); + self + } + + /// Specify `-C split-debuginfo={packed,unpacked,off}`. + pub fn split_debuginfo(&mut self, split_kind: &str) -> &mut Self { + self.cmd.arg(format!("-Csplit-debuginfo={split_kind}")); + self + } + /// Pass the `--verbose` flag. pub fn verbose(&mut self) -> &mut Self { self.cmd.arg("--verbose"); diff --git a/src/tools/run-make-support/src/fs.rs b/src/tools/run-make-support/src/fs.rs index 7ebe4a9ca13b..ceb4ebbaca55 100644 --- a/src/tools/run-make-support/src/fs.rs +++ b/src/tools/run-make-support/src/fs.rs @@ -238,9 +238,7 @@ pub fn set_permissions>(path: P, perm: std::fs::Permissions) { )); } -/// A function which prints all file names in the directory `dir` similarly to Unix's `ls`. -/// Useful for debugging. -/// Usage: `eprintln!("{:#?}", shallow_find_dir_entries(some_dir));` +/// List directory entries immediately under the given `dir`. #[track_caller] pub fn shallow_find_dir_entries>(dir: P) -> Vec { let paths = read_dir(dir); diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index a8c9bec57fde..7e63ab3159ac 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -94,8 +94,8 @@ pub use artifact_names::{ /// Path-related helpers. pub use path_helpers::{ - cwd, filename_contains, filename_not_in_denylist, has_extension, has_prefix, has_suffix, - not_contains, path, shallow_find_files, build_root, source_root, + build_root, cwd, filename_contains, filename_not_in_denylist, has_extension, has_prefix, + has_suffix, not_contains, path, shallow_find_directories, shallow_find_files, source_root, }; /// Helpers for scoped test execution where certain properties are attempted to be maintained. diff --git a/src/tools/run-make-support/src/path_helpers.rs b/src/tools/run-make-support/src/path_helpers.rs index 1c59f2feea4c..b766e50e523b 100644 --- a/src/tools/run-make-support/src/path_helpers.rs +++ b/src/tools/run-make-support/src/path_helpers.rs @@ -59,6 +59,25 @@ pub fn shallow_find_files, F: Fn(&PathBuf) -> bool>( matching_files } +/// Browse the directory `path` non-recursively and return all directories which respect the +/// parameters outlined by `closure`. +#[track_caller] +pub fn shallow_find_directories, F: Fn(&PathBuf) -> bool>( + path: P, + filter: F, +) -> Vec { + let mut matching_files = Vec::new(); + for entry in rfs::read_dir(path) { + let entry = entry.expect("failed to read directory entry."); + let path = entry.path(); + + if path.is_dir() && filter(&path) { + matching_files.push(path); + } + } + matching_files +} + /// Returns true if the filename at `path` does not contain `expected`. pub fn not_contains>(path: P, expected: &str) -> bool { !path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().contains(expected)) diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 45b40b17ea37..e69de29bb2d1 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -1 +0,0 @@ -run-make/split-debuginfo/Makefile diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 17786ed6d2fd..39c9a148e9e5 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -2218,26 +2218,12 @@ ui/issues/issue-3993.rs ui/issues/issue-39970.rs ui/issues/issue-39984.rs ui/issues/issue-40000.rs -ui/issues/issue-40136.rs -ui/issues/issue-40235.rs ui/issues/issue-4025.rs ui/issues/issue-40288-2.rs ui/issues/issue-40288.rs -ui/issues/issue-40350.rs -ui/issues/issue-40408.rs -ui/issues/issue-40610.rs -ui/issues/issue-40749.rs -ui/issues/issue-40782.rs -ui/issues/issue-40827.rs -ui/issues/issue-40845.rs -ui/issues/issue-40861.rs -ui/issues/issue-40883.rs ui/issues/issue-40951.rs ui/issues/issue-41053.rs -ui/issues/issue-41139.rs -ui/issues/issue-41213.rs ui/issues/issue-41229-ref-str.rs -ui/issues/issue-41272.rs ui/issues/issue-41298.rs ui/issues/issue-41479.rs ui/issues/issue-41498.rs @@ -2360,7 +2346,6 @@ ui/issues/issue-4830.rs ui/issues/issue-48364.rs ui/issues/issue-48728.rs ui/issues/issue-4875.rs -ui/issues/issue-48838.rs ui/issues/issue-48984.rs ui/issues/issue-49298.rs ui/issues/issue-4935.rs diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index d66ba157d106..fe51231c4810 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -17,7 +17,7 @@ use ignore::Walk; const ENTRY_LIMIT: u32 = 901; // FIXME: The following limits should be reduced eventually. -const ISSUES_ENTRY_LIMIT: u32 = 1658; +const ISSUES_ENTRY_LIMIT: u32 = 1634; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files diff --git a/tests/assembly/auxiliary/dwarf-mixed-versions-lto-aux.rs b/tests/assembly/auxiliary/dwarf-mixed-versions-lto-aux.rs new file mode 100644 index 000000000000..faff6e7e2d05 --- /dev/null +++ b/tests/assembly/auxiliary/dwarf-mixed-versions-lto-aux.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -g --crate-type=rlib -Zdwarf-version=4 + +pub fn check_is_even(number: &u64) -> bool { + number % 2 == 0 +} diff --git a/tests/assembly/dwarf-mixed-versions-lto.rs b/tests/assembly/dwarf-mixed-versions-lto.rs new file mode 100644 index 000000000000..5b8e5ff4f4a4 --- /dev/null +++ b/tests/assembly/dwarf-mixed-versions-lto.rs @@ -0,0 +1,19 @@ +// This test ensures that if LTO occurs between crates with different DWARF versions, we +// will choose the highest DWARF version for the final binary. This matches Clang's behavior. + +//@ only-linux +//@ aux-build:dwarf-mixed-versions-lto-aux.rs +//@ compile-flags: -C lto -g -Zdwarf-version=5 +//@ assembly-output: emit-asm +//@ no-prefer-dynamic + +extern crate dwarf_mixed_versions_lto_aux; + +fn main() { + dwarf_mixed_versions_lto_aux::check_is_even(&0); +} + +// CHECK: .section .debug_info +// CHECK-NOT: {{\.(short|hword)}} 2 +// CHECK-NOT: {{\.(short|hword)}} 4 +// CHECK: {{\.(short|hword)}} 5 diff --git a/tests/mir-opt/building/enum_cast.droppy.built.after.mir b/tests/mir-opt/building/enum_cast.droppy.built.after.mir deleted file mode 100644 index f53c9199a494..000000000000 --- a/tests/mir-opt/building/enum_cast.droppy.built.after.mir +++ /dev/null @@ -1,71 +0,0 @@ -// MIR for `droppy` after built - -fn droppy() -> () { - let mut _0: (); - let _1: (); - let _2: Droppy; - let _4: Droppy; - let mut _5: isize; - let mut _6: u8; - let mut _7: bool; - let _8: Droppy; - scope 1 { - debug x => _2; - scope 2 { - debug y => _3; - } - scope 3 { - let _3: usize; - } - } - scope 4 { - debug z => _8; - } - - bb0: { - StorageLive(_1); - StorageLive(_2); - _2 = Droppy::C; - FakeRead(ForLet(None), _2); - StorageLive(_3); - StorageLive(_4); - _4 = move _2; - _5 = discriminant(_4); - _6 = copy _5 as u8 (IntToInt); - _7 = Le(copy _6, const 2_u8); - assume(move _7); - _3 = move _5 as usize (IntToInt); - drop(_4) -> [return: bb1, unwind: bb4]; - } - - bb1: { - StorageDead(_4); - FakeRead(ForLet(None), _3); - _1 = const (); - StorageDead(_3); - drop(_2) -> [return: bb2, unwind: bb5]; - } - - bb2: { - StorageDead(_2); - StorageDead(_1); - StorageLive(_8); - _8 = Droppy::B; - FakeRead(ForLet(None), _8); - _0 = const (); - drop(_8) -> [return: bb3, unwind: bb5]; - } - - bb3: { - StorageDead(_8); - return; - } - - bb4 (cleanup): { - drop(_2) -> [return: bb5, unwind terminate(cleanup)]; - } - - bb5 (cleanup): { - resume; - } -} diff --git a/tests/mir-opt/building/enum_cast.rs b/tests/mir-opt/building/enum_cast.rs index 7ff0cdbfe8df..4fb9a27e3093 100644 --- a/tests/mir-opt/building/enum_cast.rs +++ b/tests/mir-opt/building/enum_cast.rs @@ -41,27 +41,6 @@ fn far(far: Far) -> isize { far as isize } -// EMIT_MIR enum_cast.droppy.built.after.mir -enum Droppy { - A, - B, - C, -} - -impl Drop for Droppy { - fn drop(&mut self) {} -} - -fn droppy() { - { - let x = Droppy::C; - // remove this entire test once `cenum_impl_drop_cast` becomes a hard error - #[allow(cenum_impl_drop_cast)] - let y = x as usize; - } - let z = Droppy::B; -} - #[repr(i16)] enum SignedAroundZero { A = -2, diff --git a/tests/run-make/split-debuginfo/Makefile b/tests/run-make/split-debuginfo/Makefile deleted file mode 100644 index 5f463ffe8cde..000000000000 --- a/tests/run-make/split-debuginfo/Makefile +++ /dev/null @@ -1,371 +0,0 @@ -# ignore-cross-compile -# ignore-riscv64 On this platform only `-Csplit-debuginfo=off` is supported, see #120518 - -include ../tools.mk - -all: off packed unpacked - -ifeq ($(UNAME),Darwin) -# If disabled, don't run `dsymutil`. -off: - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs -g -C split-debuginfo=off - [ ! -d $(TMPDIR)/foo.dSYM ] - -# Packed by default, but only if debuginfo is requested -packed: - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs - [ ! -d $(TMPDIR)/foo.dSYM ] - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs -g - [ -d $(TMPDIR)/foo.dSYM ] - rm -rf $(TMPDIR)/*.dSYM - $(RUSTC) foo.rs -g -C split-debuginfo=packed - [ -d $(TMPDIR)/foo.dSYM ] - rm -rf $(TMPDIR)/*.dSYM - -# Object files are preserved with unpacked and `dsymutil` isn't run -unpacked: - $(RUSTC) foo.rs -g -C split-debuginfo=unpacked - ls $(TMPDIR)/*.o - [ ! -d $(TMPDIR)/foo.dSYM ] -else -ifdef IS_WINDOWS -# Windows only supports packed debuginfo - nothing to test. -off: -packed: -unpacked: -else -# Some non-Windows, non-Darwin platforms are not stable, and some are. -ifeq ($(UNAME),Linux) - UNSTABLEOPTS := -else - UNSTABLEOPTS := -Zunstable-options -endif - -# - Debuginfo in `.o` files -# - `.o` deleted -# - `.dwo` never created -# - `.dwp` never created -off: - $(RUSTC) foo.rs -g -C $(UNSTABLEOPTS) split-debuginfo=off - [ ! -f $(TMPDIR)/*.dwp ] - [ ! -f $(TMPDIR)/*.dwo ] - $(RUSTC) foo.rs -g - [ ! -f $(TMPDIR)/*.dwp ] - [ ! -f $(TMPDIR)/*.dwo ] - -packed: packed-split packed-single packed-lto packed-remapped packed-crosscrate - -# - Debuginfo in `.dwo` files -# - `.o` deleted -# - `.dwo` deleted -# - `.dwp` present -packed-split: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=split - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` deleted -# - `.dwo` never created -# - `.dwp` present -packed-single: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=packed -Zsplit-dwarf-kind=single - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - -packed-lto: packed-lto-split packed-lto-single - -# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated -# - `.o` never created -# - `.dwo` never created -# - `.dwp` never created -packed-lto-split: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split \ - --crate-type=rlib -Clinker-plugin-lto - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - -# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated -# - `.o` never created -# - `.dwo` never created -# - `.dwp` never created -packed-lto-single: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=packed -Zsplit-dwarf-kind=single \ - --crate-type=rlib -Clinker-plugin-lto - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - -packed-remapped: packed-remapped-split packed-remapped-single packed-remapped-scope packed-remapped-wrong-scope - -# - Debuginfo in `.dwo` files -# - `.o` and binary refer to remapped `.dwo` paths which do not exist -# - `.o` deleted -# - `.dwo` deleted -# - `.dwp` present -packed-remapped-split: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` and binary refer to remapped `.o` paths which do not exist -# - `.o` deleted -# - `.dwo` never created -# - `.dwp` present -packed-remapped-single: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` and binary refer to remapped `.o` paths which do not exist -# - `.o` deleted -# - `.dwo` never created -# - `.dwp` present -packed-remapped-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=debuginfo foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` and binary refer to remapped `.o` paths which do not exist -# - `.o` deleted -# - `.dwo` never created -# - `.dwp` present -packed-remapped-wrong-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=packed -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=macro foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/foo.dwp - rm $(TMPDIR)/$(call BIN,foo) - -packed-crosscrate: packed-crosscrate-split packed-crosscrate-single - -# - Debuginfo in `.dwo` files -# - (bar) `.rlib` file created, contains `.dwo` -# - (bar) `.o` deleted -# - (bar) `.dwo` deleted -# - (bar) `.dwp` never created -# - (main) `.o` deleted -# - (main) `.dwo` deleted -# - (main) `.dwp` present -packed-crosscrate-split: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ - -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=packed -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/main.dwp - rm $(TMPDIR)/$(call BIN,main) - -# - Debuginfo in `.o` files -# - (bar) `.rlib` file created, contains `.o` -# - (bar) `.o` deleted -# - (bar) `.dwo` never created -# - (bar) `.dwp` never created -# - (main) `.o` deleted -# - (main) `.dwo` never created -# - (main) `.dwp` present -packed-crosscrate-single: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=packed \ - -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=packed -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - rm $(TMPDIR)/main.dwp - rm $(TMPDIR)/$(call BIN,main) - -unpacked: unpacked-split unpacked-single unpacked-lto unpacked-remapped unpacked-crosscrate - -# - Debuginfo in `.dwo` files -# - `.o` deleted -# - `.dwo` present -# - `.dwp` never created -unpacked-split: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split - ls $(TMPDIR)/*.o && exit 1 || exit 0 - rm $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` present -# - `.dwo` never created -# - `.dwp` never created -unpacked-single: - $(RUSTC) foo.rs -g $(UNSTABLEOPTS) -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single - ls $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - -unpacked-lto: unpacked-lto-split unpacked-lto-single - -# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated -# - `.o` present (bitcode) -# - `.dwo` never created -# - `.dwp` never created -unpacked-lto-split: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=split \ - --crate-type=rlib -Clinker-plugin-lto - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - -# - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated -# - `.o` present (bitcode) -# - `.dwo` never created -# - `.dwp` never created -unpacked-lto-single: - $(RUSTC) baz.rs -g $(UNSTABLEOPTS) -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=single \ - --crate-type=rlib -Clinker-plugin-lto - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/libbaz.rlib - -unpacked-remapped: unpacked-remapped-split unpacked-remapped-single unpacked-remapped-scope unpacked-remapped-wrong-scope - -# - Debuginfo in `.dwo` files -# - `.o` and binary refer to remapped `.dwo` paths which do not exist -# - `.o` deleted -# - `.dwo` present -# - `.dwp` never created -unpacked-remapped-split: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=split --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - ls $(TMPDIR)/*.o && exit 1 || exit 0 - rm $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` and binary refer to remapped `.o` paths which do not exist -# - `.o` present -# - `.dwo` never created -# - `.dwp` never created -unpacked-remapped-single: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` and binary refer to remapped `.o` paths which do not exist -# - `.o` present -# - `.dwo` never created -# - `.dwp` never created -unpacked-remapped-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=debuginfo foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1 - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - -# - Debuginfo in `.o` files -# - `.o` and binary refer to remapped `.o` paths which do not exist -# - `.o` present -# - `.dwo` never created -# - `.dwp` never created -unpacked-remapped-wrong-scope: - $(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \ - -Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a \ - -Z remap-path-scope=macro foo.rs -g - objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (grep $(TMPDIR)) || exit 1 - rm $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,foo) - -unpacked-crosscrate: unpacked-crosscrate-split unpacked-crosscrate-single - -# - Debuginfo in `.dwo` files -# - (bar) `.rlib` file created, contains `.dwo` -# - (bar) `.o` deleted -# - (bar) `.dwo` present -# - (bar) `.dwp` never created -# - (main) `.o` deleted -# - (main) `.dwo` present -# - (main) `.dwp` never created -unpacked-crosscrate-split: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ - -Zsplit-dwarf-kind=split -C debuginfo=2 -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o && exit 1 || exit 0 - ls $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=unpacked -Zsplit-dwarf-kind=split -C debuginfo=2 -g main.rs - ls $(TMPDIR)/*.o && exit 1 || exit 0 - rm $(TMPDIR)/*.dwo - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,main) - -# - Debuginfo in `.o` files -# - (bar) `.rlib` file created, contains `.o` -# - (bar) `.o` present -# - (bar) `.dwo` never created -# - (bar) `.dwp` never created -# - (main) `.o` present -# - (main) `.dwo` never created -# - (main) `.dwp` never created -unpacked-crosscrate-single: - $(RUSTC) --crate-type lib $(UNSTABLEOPTS) -C split-debuginfo=unpacked \ - -Zsplit-dwarf-kind=single -C debuginfo=2 -g bar.rs - ls $(TMPDIR)/*.rlib - ls $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - $(RUSTC) --extern bar=$(TMPDIR)/libbar.rlib $(UNSTABLEOPTS) \ - -C split-debuginfo=unpacked -Zsplit-dwarf-kind=single -C debuginfo=2 -g main.rs - ls $(TMPDIR)/*.o - ls $(TMPDIR)/*.dwo && exit 1 || exit 0 - ls $(TMPDIR)/*.dwp && exit 1 || exit 0 - rm $(TMPDIR)/$(call BIN,main) -endif -endif diff --git a/tests/run-make/split-debuginfo/rmake.rs b/tests/run-make/split-debuginfo/rmake.rs new file mode 100644 index 000000000000..530a5d119f13 --- /dev/null +++ b/tests/run-make/split-debuginfo/rmake.rs @@ -0,0 +1,1618 @@ +// ignore-tidy-linelength +//! Basic smoke tests for behavior of `-C split-debuginfo` and the combined behavior when used in +//! conjunction with other flags such as: +//! +//! - `--remap-path-prefix`: see +//! . +//! - `-Z remap-path-scope`: see +//! - +//! - +//! - RFC #3127 trim-paths: +//! - `-Z split-dwarf-kind`: see . +//! - `-Clinker-plugin-lto`: see . +//! +//! # Test implementation remark +//! +//! - The pattern match on enum variants are intentional, because I find that they are very +//! revealing with respect to the kind of test coverage that we have and don't have. +//! +//! # Known limitations +//! +//! - The linux test coverage of cross-interactions between `-C split-debuginfo` and other flags are +//! significantly higher than the lack of such coverage for Windows and Darwin. +//! - windows-gnu is not tested at all, see the `FIXME(#135531)`s below. +//! - This test for the most part merely checks for existence/absence of certain artifacts, it does +//! not sanity check if the debuginfo artifacts are actually usable or contains the expected +//! amount/quality of debuginfo, especially on windows-msvc and darwin. +//! - FIXME(#111540): this test has insufficient coverage in relation to trim-paths RFC, see also +//! the comment . The +//! basic `llvm-dwarfdump` textual output inspection here is very fragile. The original `Makefile` +//! version used `objdump` (not to be confused with `llvm-objdump`) but inspected the wrong line +//! because it was looking at `DW_AT_GNU_dwo_name` when it should've been looking at +//! `DW_AT_comp_dir`. +//! - This test does not have good coverage for what values of `-Csplit-debuginfo` are stable vs +//! non-stable for the various targets, i.e. which values *should* be gated behind +//! `-Zunstable-options` for a given target. The `Makefile` version yolo'd a `-Zunstable-options` +//! for non-windows + non-linux + non-darwin, but had a misplaced interpolation which suggested to +//! me that that conditional `-Zunstable-options` never actually materialized. +//! +//! # Additional references +//! +//! - Apple `.dSYM` debug symbol bundles: . +//! - LLVM `dsymutil`: . + +// NOTE: this is a host test +//@ ignore-cross-compile + +// NOTE: this seems to be a host test, and testing on host `riscv64-gc-unknown-linux-gnu` reveals +// that this test is failing because of [MC: "error: A dwo section may not contain relocations" when +// building with fission + RISCV64 #56642](https://github.com/llvm/llvm-project/issues/56642). This +// test is ignored for now to unblock efforts to bring riscv64 targets to be exercised in CI, cf. +// [Enable riscv64gc-gnu testing #126641](https://github.com/rust-lang/rust/pull/126641). +//@ ignore-riscv64 (https://github.com/llvm/llvm-project/issues/56642) + +// FIXME(#135531): the `Makefile` version practically didn't test `-C split-debuginfo` on Windows +// at all, and lumped windows-msvc and windows-gnu together at that. +//@ ignore-windows-gnu + +#![deny(warnings)] + +use std::collections::BTreeSet; + +use run_make_support::rustc::Rustc; +use run_make_support::{ + cwd, has_extension, is_darwin, is_msvc, is_windows, llvm_dwarfdump, run_in_tmpdir, rustc, + shallow_find_directories, shallow_find_files, uname, +}; + +/// `-C debuginfo`. See . +#[derive(Debug, PartialEq, Copy, Clone)] +enum DebuginfoLevel { + /// `-C debuginfo=0` or `-C debuginfo=none` aka no debuginfo at all, default. + None, + /// `-C debuginfo=2` aka full debuginfo, aliased via `-g`. + Full, + /// The cli flag is not explicitly provided; default. + Unspecified, +} + +impl DebuginfoLevel { + fn cli_value(&self) -> &'static str { + // `-Cdebuginfo=...` + match self { + DebuginfoLevel::None => "none", + DebuginfoLevel::Full => "2", + DebuginfoLevel::Unspecified => unreachable!(), + } + } +} + +/// `-C split-debuginfo`. See +/// . +/// +/// Note that all three options are supported on Linux and Apple platforms, packed is supported on +/// Windows-MSVC, and all other platforms support off. Attempting to use an unsupported option +/// requires using the nightly channel with the `-Z unstable-options` flag. +#[derive(Debug, PartialEq, Copy, Clone)] +enum SplitDebuginfo { + /// `-C split-debuginfo=off`. Default for platforms with ELF binaries and windows-gnu (not + /// Windows MSVC and not macOS). Typically DWARF debug information can be found in the final + /// artifact in sections of the executable. + /// + /// - Not supported on Windows MSVC. + /// - On macOS this options prevents the final execution of `dsymutil` to generate debuginfo. + Off, + /// `-C split-debuginfo=unpacked`. Debug information will be found in separate files for each + /// compilation unit (object file). + /// + /// - Not supported on Windows MSVC. + /// - On macOS this means the original object files will contain debug information. + /// - On other Unix platforms this means that `*.dwo` files will contain debug information. + Unpacked, + /// `-C split-debuginfo=packed`. Default for Windows MSVC and macOS. "Packed" here means that + /// all the debug information is packed into a separate file from the main executable. + /// + /// - On Windows MSVC this is a `*.pdb` file. + /// - On macOS this is a `*.dSYM` folder. + /// - On other platforms this is a `*.dwp` file. + Packed, + /// The cli flag is not explicitly provided; uses platform default. + Unspecified, +} + +impl SplitDebuginfo { + fn cli_value(&self) -> &'static str { + // `-Csplit-debuginfo=...` + match self { + SplitDebuginfo::Off => "off", + SplitDebuginfo::Unpacked => "unpacked", + SplitDebuginfo::Packed => "packed", + SplitDebuginfo::Unspecified => unreachable!(), + } + } +} + +/// `-Z split-dwarf-kind` +#[derive(Debug, PartialEq, Copy, Clone)] +enum SplitDwarfKind { + /// `-Zsplit-dwarf-kind=split` + Split, + /// `-Zsplit-dwarf-kind=single` + Single, + Unspecified, +} + +impl SplitDwarfKind { + fn cli_value(&self) -> &'static str { + // `-Zsplit-dwarf-kind=...` + match self { + SplitDwarfKind::Split => "split", + SplitDwarfKind::Single => "single", + SplitDwarfKind::Unspecified => unreachable!(), + } + } +} + +/// `-C linker-plugin-lto` +#[derive(Debug, PartialEq, Copy, Clone)] +enum LinkerPluginLto { + /// Pass `-C linker-plugin-lto`. + Yes, + /// Don't pass `-C linker-plugin-lto`. + Unspecified, +} + +/// `--remap-path-prefix` or not. +#[derive(Debug, Clone)] +enum RemapPathPrefix { + /// `--remap-path-prefix=$prefix=$remapped_prefix`. + Yes { remapped_prefix: &'static str }, + /// Don't pass `--remap-path-prefix`. + Unspecified, +} + +/// `-Zremap-path-scope`. See +/// . +#[derive(Debug, Clone)] +enum RemapPathScope { + /// Comma-separated list of remap scopes: `macro`, `diagnostics`, `debuginfo`, `object`, `all`. + Yes(&'static str), + Unspecified, +} + +/// Whether to pass `-Zunstable-options`. +#[derive(Debug, PartialEq, Copy, Clone)] +enum UnstableOptions { + Yes, + Unspecified, +} + +#[track_caller] +fn cwd_filenames() -> BTreeSet { + let files = shallow_find_files(cwd(), |path| { + // Fiilter out source files + !has_extension(path, "rs") + }); + files.iter().map(|p| p.file_name().unwrap().to_os_string().into_string().unwrap()).collect() +} + +#[track_caller] +fn cwd_dwo_filenames() -> BTreeSet { + let files = shallow_find_files(cwd(), |path| has_extension(path, "dwo")); + files.iter().map(|p| p.file_name().unwrap().to_os_string().into_string().unwrap()).collect() +} + +#[track_caller] +fn cwd_object_filenames() -> BTreeSet { + let files = shallow_find_files(cwd(), |path| has_extension(path, "o")); + files.iter().map(|p| p.file_name().unwrap().to_os_string().into_string().unwrap()).collect() +} + +#[must_use] +struct FileAssertions<'expected> { + expected_files: BTreeSet<&'expected str>, +} + +impl<'expected> FileAssertions<'expected> { + #[track_caller] + fn assert_on(self, found_files: &BTreeSet) { + let found_files: BTreeSet<_> = found_files.iter().map(|f| f.as_str()).collect(); + assert!( + found_files.is_superset(&self.expected_files), + "expected {:?} to exist, but only found {:?}", + self.expected_files, + found_files + ); + + let unexpected_files: BTreeSet<_> = + found_files.difference(&self.expected_files).copied().collect(); + assert!(unexpected_files.is_empty(), "found unexpected files: {:?}", unexpected_files); + } +} + +/// Windows MSVC only supports packed debuginfo. +mod windows_msvc_tests { + use super::*; + + pub(crate) fn split_debuginfo(split_kind: SplitDebuginfo, level: DebuginfoLevel) { + // NOTE: `-C debuginfo` and other flags are not exercised here on Windows MSVC. + run_in_tmpdir(|| { + println!("checking: split_kind={:?} + level={:?}", split_kind, level); + match (split_kind, level) { + (SplitDebuginfo::Off, _) => { + rustc() + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .run_fail() + .assert_stderr_contains( + "error: `-Csplit-debuginfo=off` is unstable on this platform", + ); + } + (SplitDebuginfo::Unpacked, _) => { + rustc() + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .run_fail() + .assert_stderr_contains( + "error: `-Csplit-debuginfo=unpacked` is unstable on this platform", + ); + } + (SplitDebuginfo::Packed, _) => { + rustc().input("foo.rs").split_debuginfo(split_kind.cli_value()).run(); + + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo.exe", "foo.pdb"]) } + .assert_on(&found_files); + } + (split_kind, level) => { + panic!( + "split_kind={:?} + level={:?} is not handled on Windows MSVC", + split_kind, level + ) + } + } + }); + } +} + +mod darwin_tests { + use super::*; + + pub(crate) fn split_debuginfo(split_kind: SplitDebuginfo, level: DebuginfoLevel) { + run_in_tmpdir(|| { + println!("checking: split_kind={:?} + level={:?}", split_kind, level); + + let dsym_directories = + || shallow_find_directories(cwd(), |path| has_extension(path, "dSYM")); + + match (split_kind, level) { + (_, DebuginfoLevel::Unspecified) => { + rustc().input("foo.rs").run(); + let directories = + shallow_find_directories(cwd(), |path| has_extension(path, "dSYM")); + assert!( + directories.is_empty(), + "expected no `*.dSYM` folder to be generated when `-Cdebuginfo` is not specified" + ); + } + (_, DebuginfoLevel::None) => { + rustc().input("foo.rs").debuginfo(level.cli_value()).run(); + let directories = dsym_directories(); + assert!( + directories.is_empty(), + "expected no `*.dSYM` folder to be generated when `-Cdebuginfo=none`" + ); + } + (SplitDebuginfo::Off, _) => { + rustc() + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .run(); + let directories = dsym_directories(); + assert!( + directories.is_empty(), + "expected no `*.dSYM` folder to be generated since we expect `-Csplit-debuginfo=off` to inhibit final debuginfo generation on macOS" + ); + } + (SplitDebuginfo::Unpacked, _) => { + rustc().input("foo.rs").split_debuginfo(split_kind.cli_value()).run(); + let directories = dsym_directories(); + assert!( + directories.is_empty(), + "expected no `*.dSYM` folder to be generated since we expect on macOS the object files to contain debuginfo instead" + ); + } + (SplitDebuginfo::Packed, DebuginfoLevel::Full) => { + rustc() + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .run(); + let directories = shallow_find_directories(cwd(), |path| { + path.file_name().unwrap() == "foo.dSYM" + }); + assert_eq!(directories.len(), 1, "failed to find `foo.dSYM`"); + } + (SplitDebuginfo::Unspecified, DebuginfoLevel::Full) => { + rustc().input("foo.rs").debuginfo(level.cli_value()).run(); + let directories = shallow_find_directories(cwd(), |path| { + path.file_name().unwrap() == "foo.dSYM" + }); + assert_eq!(directories.len(), 1, "failed to find `foo.dSYM`"); + } + } + }); + } +} + +mod shared_linux_other_tests { + use std::path::PathBuf; + + use super::*; + + fn rustc(unstable_options: UnstableOptions) -> Rustc { + if unstable_options == UnstableOptions::Yes { + let mut rustc = run_make_support::rustc(); + rustc.arg("-Zunstable-options"); + rustc + } else { + run_make_support::rustc() + } + } + + #[derive(PartialEq)] + pub(crate) enum CrossCrateTest { + Yes, + No, + } + + pub(crate) fn split_debuginfo( + cross_crate_test: CrossCrateTest, + unstable_options: UnstableOptions, + split_kind: SplitDebuginfo, + level: DebuginfoLevel, + split_dwarf_kind: SplitDwarfKind, + lto: LinkerPluginLto, + remap_path_prefix: RemapPathPrefix, + remap_path_scope: RemapPathScope, + ) { + run_in_tmpdir(|| { + println!( + "checking: unstable_options={:?} + split_kind={:?} + level={:?} + split_dwarf_kind={:?} + lto={:?} + remap_path_prefix={:?} + remap_path_scope={:?}", + unstable_options, + split_kind, + level, + split_dwarf_kind, + lto, + remap_path_prefix, + remap_path_scope + ); + + match cross_crate_test { + CrossCrateTest::Yes => cross_crate_split_debuginfo( + unstable_options, + split_kind, + level, + split_dwarf_kind, + lto, + remap_path_prefix, + remap_path_scope, + ), + CrossCrateTest::No => simple_split_debuginfo( + unstable_options, + split_kind, + level, + split_dwarf_kind, + lto, + remap_path_prefix, + remap_path_scope, + ), + } + }); + } + + fn cross_crate_split_debuginfo( + unstable_options: UnstableOptions, + split_kind: SplitDebuginfo, + level: DebuginfoLevel, + split_dwarf_kind: SplitDwarfKind, + lto: LinkerPluginLto, + remap_path_prefix: RemapPathPrefix, + remap_path_scope: RemapPathScope, + ) { + match (split_kind, level, split_dwarf_kind, lto, remap_path_prefix, remap_path_scope) { + // packed-crosscrate-split + // - Debuginfo in `.dwo` files + // - (bar) `.rlib` file created, contains `.dwo` + // - (bar) `.o` deleted + // - (bar) `.dwo` deleted + // - (bar) `.dwp` never created + // - (main) `.o` deleted + // - (main) `.dwo` deleted + // - (main) `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("bar.rs") + .crate_type("lib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["libbar.rlib"]) } + .assert_on(&found_files); + + rustc(unstable_options) + .extern_("bar", "libbar.rlib") + .input("main.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let found_files = cwd_filenames(); + FileAssertions { + expected_files: BTreeSet::from(["libbar.rlib", "main", "main.dwp"]), + } + .assert_on(&found_files); + } + + // packed-crosscrate-single + // - Debuginfo in `.o` files + // - (bar) `.rlib` file created, contains `.o` + // - (bar) `.o` deleted + // - (bar) `.dwo` never created + // - (bar) `.dwp` never created + // - (main) `.o` deleted + // - (main) `.dwo` never created + // - (main) `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("bar.rs") + .crate_type("lib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["libbar.rlib"]) } + .assert_on(&found_files); + + rustc(unstable_options) + .extern_("bar", "libbar.rlib") + .input("main.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let found_files = cwd_filenames(); + FileAssertions { + expected_files: BTreeSet::from(["libbar.rlib", "main", "main.dwp"]), + } + .assert_on(&found_files); + } + + // unpacked-crosscrate-split + // - Debuginfo in `.dwo` files + // - (bar) `.rlib` file created, contains `.dwo` + // - (bar) `.o` deleted + // - (bar) `.dwo` present + // - (bar) `.dwp` never created + // - (main) `.o` deleted + // - (main) `.dwo` present + // - (main) `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("bar.rs") + .crate_type("lib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let bar_found_files = cwd_filenames(); + + let bar_dwo_files = cwd_dwo_filenames(); + assert_eq!(bar_dwo_files.len(), 1); + + let mut bar_expected_files = BTreeSet::new(); + bar_expected_files.extend(bar_dwo_files); + bar_expected_files.insert("libbar.rlib".to_string()); + + FileAssertions { + expected_files: bar_expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&bar_found_files); + + rustc(unstable_options) + .extern_("bar", "libbar.rlib") + .input("main.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let overall_found_files = cwd_filenames(); + + let overall_dwo_files = cwd_dwo_filenames(); + assert_eq!(overall_dwo_files.len(), 2); + + let mut overall_expected_files = BTreeSet::new(); + overall_expected_files.extend(overall_dwo_files); + overall_expected_files.insert("main".to_string()); + overall_expected_files.insert("libbar.rlib".to_string()); + + FileAssertions { + expected_files: overall_expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&overall_found_files); + } + + // unpacked-crosscrate-single + // - Debuginfo in `.o` files + // - (bar) `.rlib` file created, contains `.o` + // - (bar) `.o` present + // - (bar) `.dwo` never created + // - (bar) `.dwp` never created + // - (main) `.o` present + // - (main) `.dwo` never created + // - (main) `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("bar.rs") + .crate_type("lib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let bar_found_files = cwd_filenames(); + + let bar_object_files = cwd_object_filenames(); + assert_eq!(bar_object_files.len(), 1); + + let mut bar_expected_files = BTreeSet::new(); + bar_expected_files.extend(bar_object_files); + bar_expected_files.insert("libbar.rlib".to_string()); + + FileAssertions { + expected_files: bar_expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&bar_found_files); + + rustc(unstable_options) + .extern_("bar", "libbar.rlib") + .input("main.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + + let overall_found_files = cwd_filenames(); + + let overall_object_files = cwd_object_filenames(); + assert_eq!(overall_object_files.len(), 2); + + let mut overall_expected_files = BTreeSet::new(); + overall_expected_files.extend(overall_object_files); + overall_expected_files.insert("main".to_string()); + overall_expected_files.insert("libbar.rlib".to_string()); + + FileAssertions { + expected_files: overall_expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&overall_found_files); + } + + _ => {} + } + } + + fn simple_split_debuginfo( + unstable_options: UnstableOptions, + split_kind: SplitDebuginfo, + level: DebuginfoLevel, + split_dwarf_kind: SplitDwarfKind, + lto: LinkerPluginLto, + remap_path_prefix: RemapPathPrefix, + remap_path_scope: RemapPathScope, + ) { + match (split_kind, level, split_dwarf_kind, lto, remap_path_prefix, remap_path_scope) { + // off (unspecified): + // - Debuginfo in `.o` files + // - `.o` deleted + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unspecified, + DebuginfoLevel::Full, + SplitDwarfKind::Unspecified, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options).input("foo.rs").debuginfo(level.cli_value()).run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo"]) }.assert_on(&found_files); + } + + // off: + // - Debuginfo in `.o` files + // - `.o` deleted + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Off, + DebuginfoLevel::Full, + SplitDwarfKind::Unspecified, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo"]) }.assert_on(&found_files); + } + + // packed-split: + // - Debuginfo in `.dwo` files + // - `.o` deleted + // - `.dwo` deleted + // - `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } + .assert_on(&found_files); + } + + // packed-single: + // - Debuginfo in `.o` files + // - `.o` deleted + // - `.dwo` never created + // - `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } + .assert_on(&found_files); + } + + // packed-lto-split:: + // - `rmeta` file added to `rlib`, no object files are generated and thus no + // debuginfo is generated. + // - `.o` never created + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("baz.rs") + .crate_type("rlib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .arg("-Clinker-plugin-lto") + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["libbaz.rlib"]) } + .assert_on(&found_files); + } + + // packed-lto-single: + // - `rmeta` file added to `rlib`, no object files are generated and thus no + // debuginfo is generated + // - `.o` never created + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("baz.rs") + .crate_type("rlib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .arg("-Clinker-plugin-lto") + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["libbaz.rlib"]) } + .assert_on(&found_files); + } + + // packed-remapped-split: + // - Debuginfo in `.dwo` files + // - `.o` and binary refer to remapped `.dwo` paths which do not exist + // - `.o` deleted + // - `.dwo` deleted + // - `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::Remapped); + } + + // packed-remapped-single: + // - `.o` and binary refer to remapped `.o` paths which do not exist + // - `.o` deleted + // - `.dwo` never created + // - `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::Remapped); + } + + // packed-remapped-scope: + // - Debuginfo in `.o` files + // - `.o` and binary refer to remapped `.o` paths which do not exist + // - `.o` deleted + // - `.dwo` never created + // - `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Yes(scope @ "debuginfo"), + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .arg(format!("-Zremap-path-scope={scope}")) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::Remapped); + } + + // packed-remapped-wrong-scope: + // - `.o` and binary refer to un-remapped `.o` paths because remap path scope is + // macro. + // - `.o` deleted + // - `.dwo` never created + // - `.dwp` present + ( + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Yes(scope @ "macro"), + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .arg(format!("-Zremap-path-scope={scope}")) + .run(); + let found_files = cwd_filenames(); + FileAssertions { expected_files: BTreeSet::from(["foo", "foo.dwp"]) } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::NoRemap); + } + + // unpacked-split + // - Debuginfo in `.dwo` files + // - `.o` deleted + // - `.dwo` present + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + let found_files = cwd_filenames(); + + let dwo_files = cwd_dwo_filenames(); + assert_eq!(dwo_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(dwo_files); + expected_files.insert("foo".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_str).collect(), + } + .assert_on(&found_files); + } + + // unpacked-single + // - Debuginfo in `.o` files + // - `.o` present + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .run(); + let found_files = cwd_filenames(); + + let object_files = cwd_object_filenames(); + assert_eq!(object_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(object_files); + expected_files.insert("foo".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_str).collect(), + } + .assert_on(&found_files); + } + + // unpacked-lto-split + // - `rmeta` file added to `rlib`, no object files are generated and thus no debuginfo + // is generated + // - `.o` not present + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("baz.rs") + .crate_type("rlib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .arg("-Clinker-plugin-lto") + .run(); + + let found_files = cwd_filenames(); + + FileAssertions { expected_files: BTreeSet::from(["libbaz.rlib"]) } + .assert_on(&found_files); + } + + // unpacked-lto-single + // - rmeta file added to rlib, no object files are generated and thus no debuginfo is generated + // - `.o` present (bitcode) + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("baz.rs") + .crate_type("rlib") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .arg("-Clinker-plugin-lto") + .run(); + + let found_files = cwd_filenames(); + + let object_files = cwd_object_filenames(); + assert_eq!(object_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(object_files); + expected_files.insert("libbaz.rlib".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&found_files); + } + + // unpacked-remapped-split + // - Debuginfo in `.dwo` files + // - `.o` and binary refer to remapped `.dwo` paths which do not exist + // - `.o` deleted + // - `.dwo` present + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .run(); + + let found_files = cwd_filenames(); + + let dwo_files = cwd_dwo_filenames(); + assert_eq!(dwo_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(dwo_files); + expected_files.insert("foo".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::Remapped); + } + + // unpacked-remapped-single + // - Debuginfo in `.o` files + // - `.o` and binary refer to remapped `.o` paths which do not exist + // - `.o` present + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Unspecified, + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .run(); + + let found_files = cwd_filenames(); + + let object_files = cwd_object_filenames(); + assert_eq!(object_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(object_files); + expected_files.insert("foo".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::Remapped); + } + + // unpacked-remapped-scope + // - Debuginfo in `.o` files + // - `.o` and binary refer to remapped `.o` paths which do not exist + // - `.o` present + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Yes(scope @ "debuginfo"), + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .arg(format!("-Zremap-path-scope={scope}")) + .run(); + + let found_files = cwd_filenames(); + + let object_files = cwd_object_filenames(); + assert_eq!(object_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(object_files); + expected_files.insert("foo".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::Remapped); + } + + // unpacked-remapped-wrong-scope + // - Debuginfo in `.o` files + // - `.o` and binary refer to un-remapped `.o` paths because remap path scope is macro + // - `.o` present + // - `.dwo` never created + // - `.dwp` never created + ( + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix }, + RemapPathScope::Yes(scope @ "macro"), + ) => { + rustc(unstable_options) + .input("foo.rs") + .split_debuginfo(split_kind.cli_value()) + .debuginfo(level.cli_value()) + .arg(format!("-Zsplit-dwarf-kind={}", split_dwarf_kind.cli_value())) + .remap_path_prefix(cwd(), remapped_prefix) + .arg(format!("-Zremap-path-scope={scope}")) + .run(); + + let found_files = cwd_filenames(); + + let object_files = cwd_object_filenames(); + assert_eq!(object_files.len(), 1); + + let mut expected_files = BTreeSet::new(); + expected_files.extend(object_files); + expected_files.insert("foo".to_string()); + + FileAssertions { + expected_files: expected_files.iter().map(String::as_ref).collect(), + } + .assert_on(&found_files); + + check_path_remap(cwd(), RemapExpectation::NoRemap); + } + + (split_kind, level, split_dwarf_kind, lto, remap_path_prefix, remap_path_scope) => { + panic!( + "split_kind={:?} + level={:?} + split_dwarf_kind={:?} + lto={:?} + remap_path_prefix={:?} + remap_path_scope={:?} is not handled on linux/other", + split_kind, level, split_dwarf_kind, lto, remap_path_prefix, remap_path_scope + ) + } + } + } + + #[derive(PartialEq)] + enum RemapExpectation { + Remapped, + NoRemap, + } + + #[track_caller] + fn check_path_remap(cwd_path: PathBuf, remap_expectation: RemapExpectation) { + let cwd_path = cwd_path.to_str().unwrap(); + let output = llvm_dwarfdump().input("foo").arg("--debug-info").run().stdout_utf8(); + let output_lines: Vec<_> = output.lines().collect(); + + // Look for `DW_AT_comp_dir` and `DW_AT_GNU_dwo_name` via `llvm-dwarfdump`. Note: space + // between uses tabs. + // + // ```text + // 0x0000000b: DW_TAG_compile_unit + // DW_AT_stmt_list (0x00000000) + // DW_AT_comp_dir ("/__MY_REMAPPED_PATH") # this could be e.g. /home/repos/rust/ if not remapped + // DW_AT_GNU_dwo_name ("foo.foo.fc848df41df7a00d-cgu.0.rcgu.dwo") + // ``` + // + // FIXME: this is very fragile because the output format can be load-bearing, but doing this + // via `object` + `gimli` is rather difficult. + let mut window = output_lines.windows(2); + while let Some([first_ln, second_ln]) = window.next() { + let first_ln = first_ln.trim(); + let second_ln = second_ln.trim(); + + if !second_ln.starts_with("DW_AT_GNU_dwo_name") { + continue; + } + + let Some((comp_dir_attr_name, comp_dir_attr_val)) = first_ln.split_once("\t") else { + continue; + }; + + println!("comp_dir_attr_name: `{}`", comp_dir_attr_name); + println!("cwd_path_string: `{}`", cwd_path); + + if comp_dir_attr_name != "DW_AT_comp_dir" { + continue; + } + + println!("comp_dir_attr_val: `{}`", comp_dir_attr_val); + + // Possibly `("/__MY_REMAPPED_PATH")` or `($cwd_path_string)`. + // + // FIXME: this check is insufficiently precise, it should probably also match on suffix + // (`.o` vs `.dwo` reference). But also, string matching is just very fragile. + let comp_dir_attr_val = comp_dir_attr_val.trim(); + + match remap_expectation { + RemapExpectation::Remapped => { + assert!( + !comp_dir_attr_val.contains(&cwd_path), + "unexpected non-remapped path found in `DW_AT_comp_dir`: {}", + comp_dir_attr_val + ); + } + RemapExpectation::NoRemap => { + assert!( + comp_dir_attr_val.contains(&cwd_path), + "failed to find un-remapped path in `DW_AT_comp_dir`: {}", + comp_dir_attr_val + ); + } + } + } + } +} + +fn main() { + // ENHANCEMENT: we are only checking that split-debuginfo is splitting is some way, shape or + // form, we do not sanity check the actual debuginfo artifacts on non-Linux! It may be possible + // to also sanity check the debuginfo artifacts, but I did not want to do that during the port + // to rmake.rs initially. + + // ENHANCEMENT: the Linux checks have significantly more coverage for interaction between `-C + // split-debuginfo` and other flags compared to windows-msvc or windows-gnu or darwin or some + // other non-linux targets. It would be cool if their test coverage could be improved. + + // NOTE: these combinations are not exhaustive, because while porting to rmake.rs initially I + // tried to preserve the existing test behavior closely. Notably, no attempt was made to + // exhaustively cover all cases in the 6-fold Cartesian product of `{,-Csplit=debuginfo=...}` x + // `{,-Cdebuginfo=...}` x `{,--remap-path-prefix}` x `{,-Zremap-path-scope=...}` x + // `{,-Zsplit-dwarf-kind=...}` x `{,-Clinker-plugin-lto}`. If you really want to, you can + // identify which combination isn't exercised with a 6-layers nested for loop iterating through + // each of the cli flag enum variants. + + if is_msvc() { + // FIXME: the windows-msvc test coverage is sparse at best. + + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::Unspecified); + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Unpacked, DebuginfoLevel::Unspecified); + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Packed, DebuginfoLevel::Unspecified); + + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::None); + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Unpacked, DebuginfoLevel::None); + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Packed, DebuginfoLevel::None); + + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::Full); + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Unpacked, DebuginfoLevel::Full); + windows_msvc_tests::split_debuginfo(SplitDebuginfo::Packed, DebuginfoLevel::Full); + } else if is_windows() { + // FIXME(#135531): the `Makefile` version didn't test windows at all. I don't know about the + // intended behavior on windows-gnu to expand test coverage while porting this to rmake.rs, + // but the test coverage here really should be expanded since some windows-gnu targets are + // Tier 1. + } else if is_darwin() { + // FIXME: the darwin test coverage is sparse at best. + + // Expect no `.dSYM` generation if debuginfo is not requested (special case). + darwin_tests::split_debuginfo(SplitDebuginfo::Unspecified, DebuginfoLevel::Unspecified); + + darwin_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::Unspecified); + darwin_tests::split_debuginfo(SplitDebuginfo::Unpacked, DebuginfoLevel::Unspecified); + darwin_tests::split_debuginfo(SplitDebuginfo::Packed, DebuginfoLevel::Unspecified); + + darwin_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::None); + darwin_tests::split_debuginfo(SplitDebuginfo::Unpacked, DebuginfoLevel::None); + darwin_tests::split_debuginfo(SplitDebuginfo::Packed, DebuginfoLevel::None); + + darwin_tests::split_debuginfo(SplitDebuginfo::Off, DebuginfoLevel::Full); + darwin_tests::split_debuginfo(SplitDebuginfo::Unpacked, DebuginfoLevel::Full); + darwin_tests::split_debuginfo(SplitDebuginfo::Packed, DebuginfoLevel::Full); + } else { + // Unix as well as the non-linux + non-windows + non-darwin targets. + + // FIXME: this `uname` check is very funny, it really should be refined (e.g. llvm bug + // for riscv64 targets). + + // NOTE: some options are not stable on non-linux + non-windows + non-darwin targets... + let unstable_options = + if uname() == "Linux" { UnstableOptions::Unspecified } else { UnstableOptions::Yes }; + + // FIXME: we should add a test with scope `split-debuginfo,split-debuginfo-path` that greps + // the entire `.dwp` file for remapped paths (i.e. without going through objdump or + // readelf). See . + + use shared_linux_other_tests::CrossCrateTest; + + // unspecified `-Csplit-debuginfo` + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unspecified, + DebuginfoLevel::Full, + SplitDwarfKind::Unspecified, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // off + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Off, + DebuginfoLevel::Full, + SplitDwarfKind::Unspecified, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // packed-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // packed-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // packed-lto-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // packed-lto-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // FIXME: the remapping tests probably need to be reworked, see + // . + + // packed-remapped-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Unspecified, + ); + + // packed-remapped-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Unspecified, + ); + + // packed-remapped-scope + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Yes("debuginfo"), + ); + + // packed-remapped-wrong-scope + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Yes("macro"), + ); + + // packed-crosscrate-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::Yes, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // packed-crosscrate-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::Yes, + unstable_options, + SplitDebuginfo::Packed, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // unpacked-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // unpacked-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // unpacked-lto-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // unpacked-lto-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Yes, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // unpacked-remapped-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Unspecified, + ); + + // unpacked-remapped-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Unspecified, + ); + + // unpacked-remapped-scope + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Yes("debuginfo"), + ); + + // unpacked-remapped-wrong-scope + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::No, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Yes { remapped_prefix: "/__MY_REMAPPED_PATH__" }, + RemapPathScope::Yes("macro"), + ); + + // unpacked-crosscrate-split + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::Yes, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Split, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + + // unpacked-crosscrate-single + shared_linux_other_tests::split_debuginfo( + CrossCrateTest::Yes, + unstable_options, + SplitDebuginfo::Unpacked, + DebuginfoLevel::Full, + SplitDwarfKind::Single, + LinkerPluginLto::Unspecified, + RemapPathPrefix::Unspecified, + RemapPathScope::Unspecified, + ); + } +} diff --git a/tests/ui/cenum_impl_drop_cast.rs b/tests/ui/cenum_impl_drop_cast.rs index 96e3d967e2c6..f681434dd86a 100644 --- a/tests/ui/cenum_impl_drop_cast.rs +++ b/tests/ui/cenum_impl_drop_cast.rs @@ -1,5 +1,3 @@ -#![deny(cenum_impl_drop_cast)] - enum E { A = 0, } @@ -14,5 +12,4 @@ fn main() { let e = E::A; let i = e as u32; //~^ ERROR cannot cast enum `E` into integer `u32` because it implements `Drop` - //~| WARN this was previously accepted } diff --git a/tests/ui/cenum_impl_drop_cast.stderr b/tests/ui/cenum_impl_drop_cast.stderr index 541d15d021d3..35c69f4b4b78 100644 --- a/tests/ui/cenum_impl_drop_cast.stderr +++ b/tests/ui/cenum_impl_drop_cast.stderr @@ -1,31 +1,8 @@ error: cannot cast enum `E` into integer `u32` because it implements `Drop` - --> $DIR/cenum_impl_drop_cast.rs:15:13 + --> $DIR/cenum_impl_drop_cast.rs:13:13 | LL | let i = e as u32; | ^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #73333 -note: the lint level is defined here - --> $DIR/cenum_impl_drop_cast.rs:1:9 - | -LL | #![deny(cenum_impl_drop_cast)] - | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error -Future incompatibility report: Future breakage diagnostic: -error: cannot cast enum `E` into integer `u32` because it implements `Drop` - --> $DIR/cenum_impl_drop_cast.rs:15:13 - | -LL | let i = e as u32; - | ^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #73333 -note: the lint level is defined here - --> $DIR/cenum_impl_drop_cast.rs:1:9 - | -LL | #![deny(cenum_impl_drop_cast)] - | ^^^^^^^^^^^^^^^^^^^^ - diff --git a/tests/ui/check-cfg/cargo-build-script.stderr b/tests/ui/check-cfg/cargo-build-script.stderr index df0bc47571c7..03a7156a4d69 100644 --- a/tests/ui/check-cfg/cargo-build-script.stderr +++ b/tests/ui/check-cfg/cargo-build-script.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `has_foo` LL | #[cfg(has_foo)] | ^^^^^^^ | - = help: expected names are: `has_bar` and 30 more + = help: expected names are: `has_bar` and 31 more = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] diff --git a/tests/ui/check-cfg/cargo-feature.none.stderr b/tests/ui/check-cfg/cargo-feature.none.stderr index 58813a1f6770..b83d1794984d 100644 --- a/tests/ui/check-cfg/cargo-feature.none.stderr +++ b/tests/ui/check-cfg/cargo-feature.none.stderr @@ -25,7 +25,7 @@ warning: unexpected `cfg` condition name: `tokio_unstable` LL | #[cfg(tokio_unstable)] | ^^^^^^^^^^^^^^ | - = help: expected names are: `docsrs`, `feature`, and `test` and 30 more + = help: expected names are: `docsrs`, `feature`, and `test` and 31 more = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] diff --git a/tests/ui/check-cfg/cargo-feature.some.stderr b/tests/ui/check-cfg/cargo-feature.some.stderr index 5a12be813387..2cddcbbcd7f9 100644 --- a/tests/ui/check-cfg/cargo-feature.some.stderr +++ b/tests/ui/check-cfg/cargo-feature.some.stderr @@ -25,7 +25,7 @@ warning: unexpected `cfg` condition name: `tokio_unstable` LL | #[cfg(tokio_unstable)] | ^^^^^^^^^^^^^^ | - = help: expected names are: `CONFIG_NVME`, `docsrs`, `feature`, and `test` and 30 more + = help: expected names are: `CONFIG_NVME`, `docsrs`, `feature`, and `test` and 31 more = help: consider using a Cargo feature instead = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint: [lints.rust] diff --git a/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr b/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr index 7c276c581707..68e1259dbb84 100644 --- a/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr +++ b/tests/ui/check-cfg/cfg-value-for-cfg-name-duplicate.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `value` LL | #[cfg(value)] | ^^^^^ | - = help: expected names are: `bar`, `bee`, `cow`, and `foo` and 30 more + = help: expected names are: `bar`, `bee`, `cow`, and `foo` and 31 more = help: to expect this configuration use `--check-cfg=cfg(value)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr b/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr index 9687a043e832..138c7fc75849 100644 --- a/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr +++ b/tests/ui/check-cfg/cfg-value-for-cfg-name-multiple.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_value` LL | #[cfg(my_value)] | ^^^^^^^^ | - = help: expected names are: `bar` and `foo` and 30 more + = help: expected names are: `bar` and `foo` and 31 more = help: to expect this configuration use `--check-cfg=cfg(my_value)` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr index 10302f0a7e46..af66cbd81894 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.feature.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.feature.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/exhaustive-names-values.full.stderr b/tests/ui/check-cfg/exhaustive-names-values.full.stderr index 10302f0a7e46..af66cbd81894 100644 --- a/tests/ui/check-cfg/exhaustive-names-values.full.stderr +++ b/tests/ui/check-cfg/exhaustive-names-values.full.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `unknown_key` LL | #[cfg(unknown_key = "value")] | ^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = help: to expect this configuration use `--check-cfg=cfg(unknown_key, values("value"))` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default diff --git a/tests/ui/check-cfg/mix.stderr b/tests/ui/check-cfg/mix.stderr index 033aaef848f7..be4d7c772763 100644 --- a/tests/ui/check-cfg/mix.stderr +++ b/tests/ui/check-cfg/mix.stderr @@ -44,7 +44,7 @@ warning: unexpected `cfg` condition name: `uu` LL | #[cfg_attr(uu, unix)] | ^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = help: to expect this configuration use `--check-cfg=cfg(uu)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/raw-keywords.edition2015.stderr b/tests/ui/check-cfg/raw-keywords.edition2015.stderr index f19ded9cb671..8ca33e088fc9 100644 --- a/tests/ui/check-cfg/raw-keywords.edition2015.stderr +++ b/tests/ui/check-cfg/raw-keywords.edition2015.stderr @@ -14,7 +14,7 @@ warning: unexpected `cfg` condition name: `r#false` LL | #[cfg(r#false)] | ^^^^^^^ | - = help: expected names are: `async`, `edition2015`, `edition2021`, and `r#true` and 30 more + = help: expected names are: `async`, `edition2015`, `edition2021`, and `r#true` and 31 more = help: to expect this configuration use `--check-cfg=cfg(r#false)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/raw-keywords.edition2021.stderr b/tests/ui/check-cfg/raw-keywords.edition2021.stderr index 6096148a259b..cce55720bdd1 100644 --- a/tests/ui/check-cfg/raw-keywords.edition2021.stderr +++ b/tests/ui/check-cfg/raw-keywords.edition2021.stderr @@ -14,7 +14,7 @@ warning: unexpected `cfg` condition name: `r#false` LL | #[cfg(r#false)] | ^^^^^^^ | - = help: expected names are: `r#async`, `edition2015`, `edition2021`, and `r#true` and 30 more + = help: expected names are: `r#async`, `edition2015`, `edition2021`, and `r#true` and 31 more = help: to expect this configuration use `--check-cfg=cfg(r#false)` = note: see for more information about checking conditional configuration diff --git a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr index a6584d777a3a..989a01f22441 100644 --- a/tests/ui/check-cfg/report-in-external-macros.cargo.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.cargo.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_lib_cfg` LL | cfg_macro::my_lib_macro!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro` crate for guidance on how handle this unexpected cfg = help: the macro `cfg_macro::my_lib_macro` may come from an old version of the `cfg_macro` crate, try updating your dependency with `cargo update -p cfg_macro` diff --git a/tests/ui/check-cfg/report-in-external-macros.rustc.stderr b/tests/ui/check-cfg/report-in-external-macros.rustc.stderr index 914b5a0efe36..95d10e014f33 100644 --- a/tests/ui/check-cfg/report-in-external-macros.rustc.stderr +++ b/tests/ui/check-cfg/report-in-external-macros.rustc.stderr @@ -4,7 +4,7 @@ warning: unexpected `cfg` condition name: `my_lib_cfg` LL | cfg_macro::my_lib_macro!(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: expected names are: `feature` and 30 more + = help: expected names are: `feature` and 31 more = note: using a cfg inside a macro will use the cfgs from the destination crate and not the ones from the defining crate = help: try referring to `cfg_macro::my_lib_macro` crate for guidance on how handle this unexpected cfg = help: to expect this configuration use `--check-cfg=cfg(my_lib_cfg)` diff --git a/tests/ui/check-cfg/well-known-names.stderr b/tests/ui/check-cfg/well-known-names.stderr index 4ff90261158b..000315443f81 100644 --- a/tests/ui/check-cfg/well-known-names.stderr +++ b/tests/ui/check-cfg/well-known-names.stderr @@ -5,6 +5,7 @@ LL | #[cfg(list_all_well_known_cfgs)] | ^^^^^^^^^^^^^^^^^^^^^^^^ | = help: expected names are: `clippy` +`contract_checks` `debug_assertions` `doc` `doctest` diff --git a/tests/ui/issues/issue-40883.rs b/tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs similarity index 100% rename from tests/ui/issues/issue-40883.rs rename to tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs diff --git a/tests/ui/contracts/contract-annotation-limitations.rs b/tests/ui/contracts/contract-annotation-limitations.rs new file mode 100644 index 000000000000..10b3bacab5cf --- /dev/null +++ b/tests/ui/contracts/contract-annotation-limitations.rs @@ -0,0 +1,28 @@ +//! Test for some of the existing limitations and the current error messages. +//! Some of these limitations may be removed in the future. + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] +#![allow(dead_code)] + +/// Represent a 5-star system. +struct Stars(u8); + +impl Stars { + fn is_valid(&self) -> bool { + self.0 <= 5 + } +} + +trait ParseStars { + #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + //~^ ERROR contract annotations is only supported in functions with bodies + fn parse_string(input: String) -> Option; + + #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + //~^ ERROR contract annotations is only supported in functions with bodies + fn parse(input: T) -> Option where T: for<'a> Into<&'a str>; +} + +fn main() { +} diff --git a/tests/ui/contracts/contract-annotation-limitations.stderr b/tests/ui/contracts/contract-annotation-limitations.stderr new file mode 100644 index 000000000000..14338cf4b868 --- /dev/null +++ b/tests/ui/contracts/contract-annotation-limitations.stderr @@ -0,0 +1,23 @@ +error: contract annotations is only supported in functions with bodies + --> $DIR/contract-annotation-limitations.rs:18:5 + | +LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations is only supported in functions with bodies + --> $DIR/contract-annotation-limitations.rs:22:5 + | +LL | #[core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-annotation-limitations.rs:4:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 2 previous errors; 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr b/tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr new file mode 100644 index 000000000000..0630811d4f7e --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_const_fail.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr b/tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr new file mode 100644 index 000000000000..0630811d4f7e --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr new file mode 100644 index 000000000000..0630811d4f7e --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.chk_pass.stderr b/tests/ui/contracts/contract-attributes-generics.chk_pass.stderr new file mode 100644 index 000000000000..0630811d4f7e --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-generics.rs b/tests/ui/contracts/contract-attributes-generics.rs new file mode 100644 index 000000000000..fd79c6abedd8 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.rs @@ -0,0 +1,71 @@ +//! Test that contracts can be applied to generic functions. + +//@ revisions: unchk_pass chk_pass chk_fail_pre chk_fail_post chk_const_fail +// +//@ [unchk_pass] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +//@ [chk_const_fail] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes +//@ [chk_const_fail] compile-flags: -Zcontract-checks=yes + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +use std::ops::Sub; + +/// Dummy fn contract that precondition fails for val < 0, and post-condition fail for val == 1 +#[core::contracts::requires(val > 0u8.into())] +#[core::contracts::ensures(|ret| *ret > 0u8.into())] +fn decrement(val: T) -> T +where T: PartialOrd + Sub + From +{ + val - 1u8.into() +} + +/// Create a structure that takes a constant parameter. +#[allow(dead_code)] +struct Capped(usize); + +/// Now declare a function to create stars which shouldn't exceed 5 stars. +// Add redundant braces to ensure the built-in macro can handle this syntax. +#[allow(unused_braces)] +#[core::contracts::requires(num <= 5)] +unsafe fn stars_unchecked(num: usize) -> Capped<{ 5 }> { + Capped(num) +} + + +fn main() { + check_decrement(); + check_stars(); +} + +fn check_stars() { + // This should always pass. + let _ = unsafe { stars_unchecked(3) }; + + // This violates the contract. + #[cfg(any(unchk_pass, chk_const_fail))] + let _ = unsafe { stars_unchecked(10) }; +} + +fn check_decrement() { + // This should always pass + assert_eq!(decrement(10u8), 9u8); + + // This should fail requires but pass with no contract check. + #[cfg(any(unchk_pass, chk_fail_pre))] + assert_eq!(decrement(-2i128), -3i128); + + // This should fail ensures but pass with no contract check. + #[cfg(any(unchk_pass, chk_fail_post))] + assert_eq!(decrement(1i32), 0i32); +} diff --git a/tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr b/tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr new file mode 100644 index 000000000000..0630811d4f7e --- /dev/null +++ b/tests/ui/contracts/contract-attributes-generics.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-generics.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr b/tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr new file mode 100644 index 000000000000..9ca95b8bb01a --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr new file mode 100644 index 000000000000..9ca95b8bb01a --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.chk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.chk_pass.stderr b/tests/ui/contracts/contract-attributes-nest.chk_pass.stderr new file mode 100644 index 000000000000..9ca95b8bb01a --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.rs b/tests/ui/contracts/contract-attributes-nest.rs new file mode 100644 index 000000000000..e1e61b88f282 --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.rs @@ -0,0 +1,45 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures(|ret| *ret > 100)] +fn nest(x: Baz) -> i32 +{ + loop { + return x.baz + 50; + } +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(nest(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + nest(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + nest(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr b/tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr new file mode 100644 index 000000000000..9ca95b8bb01a --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.unchk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr new file mode 100644 index 000000000000..9ca95b8bb01a --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.unchk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr b/tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr new file mode 100644 index 000000000000..9ca95b8bb01a --- /dev/null +++ b/tests/ui/contracts/contract-attributes-nest.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-nest.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr b/tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr new file mode 100644 index 000000000000..f87e7e19fa3d --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr new file mode 100644 index 000000000000..f87e7e19fa3d --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.chk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.chk_pass.stderr b/tests/ui/contracts/contract-attributes-tail.chk_pass.stderr new file mode 100644 index 000000000000..f87e7e19fa3d --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.rs b/tests/ui/contracts/contract-attributes-tail.rs new file mode 100644 index 000000000000..ce4a6be5b82f --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.rs @@ -0,0 +1,43 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures(|ret| *ret > 100)] +fn tail(x: Baz) -> i32 +{ + x.baz + 50 +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(tail(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + tail(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + tail(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr b/tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr new file mode 100644 index 000000000000..f87e7e19fa3d --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.unchk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr b/tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr new file mode 100644 index 000000000000..f87e7e19fa3d --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.unchk_fail_pre.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr b/tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr new file mode 100644 index 000000000000..f87e7e19fa3d --- /dev/null +++ b/tests/ui/contracts/contract-attributes-tail.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-attributes-tail.rs:19:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-captures-via-closure-copy.rs b/tests/ui/contracts/contract-captures-via-closure-copy.rs new file mode 100644 index 000000000000..32c6d2bf4fe1 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-copy.rs @@ -0,0 +1,26 @@ +//@ run-fail +//@ compile-flags: -Zcontract-checks=yes + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +struct Baz { + baz: i32 +} + +#[track_caller] +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures({let old = x.baz; move |ret:&Baz| ret.baz == old*2 })] +// Relevant thing is this: ^^^^^^^^^^^^^^^ +// because we are capturing state that is Copy +fn doubler(x: Baz) -> Baz { + Baz { baz: x.baz + 10 } +} + +fn main() { + assert_eq!(doubler(Baz { baz: 10 }).baz, 20); + assert_eq!(doubler(Baz { baz: 100 }).baz, 200); + // This is a *run-fail* test because it is still exercising the + // contract machinery, specifically because this second invocation + // of `doubler` shows how the code does not meet its contract. +} diff --git a/tests/ui/contracts/contract-captures-via-closure-copy.stderr b/tests/ui/contracts/contract-captures-via-closure-copy.stderr new file mode 100644 index 000000000000..d92db601608f --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-copy.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-captures-via-closure-copy.rs:4:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.rs b/tests/ui/contracts/contract-captures-via-closure-noncopy.rs new file mode 100644 index 000000000000..976f64c7fd91 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.rs @@ -0,0 +1,23 @@ +//@ compile-flags: -Zcontract-checks=yes + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +struct Baz { + baz: i32 +} + +#[track_caller] +#[core::contracts::requires(x.baz > 0)] +#[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] +// Relevant thing is this: ^^^^^^^^^^^ +// because we are capturing state that is non-Copy. +//~^^^ ERROR trait bound `Baz: std::marker::Copy` is not satisfied +fn doubler(x: Baz) -> Baz { + Baz { baz: x.baz + 10 } +} + +fn main() { + assert_eq!(doubler(Baz { baz: 10 }).baz, 20); + assert_eq!(doubler(Baz { baz: 100 }).baz, 200); +} diff --git a/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr new file mode 100644 index 000000000000..4a47671fee19 --- /dev/null +++ b/tests/ui/contracts/contract-captures-via-closure-noncopy.stderr @@ -0,0 +1,36 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-captures-via-closure-noncopy.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the trait bound `Baz: std::marker::Copy` is not satisfied in `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}` + --> $DIR/contract-captures-via-closure-noncopy.rs:12:1 + | +LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------------------------------------^^^^ + | | | + | | within this `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}` + | | this tail expression is of type `{closure@contract-captures-via-closure-noncopy.rs:12:42}` + | unsatisfied trait bound + | + = help: within `{closure@$DIR/contract-captures-via-closure-noncopy.rs:12:42: 12:57}`, the trait `std::marker::Copy` is not implemented for `Baz` +note: required because it's used within this closure + --> $DIR/contract-captures-via-closure-noncopy.rs:12:42 + | +LL | #[core::contracts::ensures({let old = x; move |ret:&Baz| ret.baz == old.baz*2 })] + | ^^^^^^^^^^^^^^^ +note: required by a bound in `build_check_ensures` + --> $SRC_DIR/core/src/contracts.rs:LL:COL +help: consider annotating `Baz` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct Baz { + | + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr new file mode 100644 index 000000000000..d693fad446a4 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_ret.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr new file mode 100644 index 000000000000..d693fad446a4 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_try.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr new file mode 100644 index 000000000000..d693fad446a4 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_fail_yeet.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr new file mode 100644 index 000000000000..d693fad446a4 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.rs b/tests/ui/contracts/contracts-ensures-early-fn-exit.rs new file mode 100644 index 000000000000..034cead3b4e9 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.rs @@ -0,0 +1,49 @@ +//@ revisions: unchk_pass chk_pass chk_fail_try chk_fail_ret chk_fail_yeet +// +//@ [unchk_pass] run-pass +//@ [chk_pass] run-pass +//@ [chk_fail_try] run-fail +//@ [chk_fail_ret] run-fail +//@ [chk_fail_yeet] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_try] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_ret] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_yeet] compile-flags: -Zcontract-checks=yes +//! This test ensures that ensures clauses are checked for different return points of a function. + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] +#![feature(yeet_expr)] + +/// This ensures will fail in different return points depending on the input. +#[core::contracts::ensures(|ret: &Option| ret.is_some())] +fn try_sum(x: u32, y: u32, z: u32) -> Option { + // Use Yeet to return early. + if x == u32::MAX && (y > 0 || z > 0) { do yeet } + + // Use `?` to early return. + let partial = x.checked_add(y)?; + + // Explicitly use `return` clause. + if u32::MAX - partial < z { + return None; + } + + Some(partial + z) +} + +fn main() { + // This should always succeed + assert_eq!(try_sum(0, 1, 2), Some(3)); + + #[cfg(any(unchk_pass, chk_fail_yeet))] + assert_eq!(try_sum(u32::MAX, 1, 1), None); + + #[cfg(any(unchk_pass, chk_fail_try))] + assert_eq!(try_sum(u32::MAX - 10, 12, 0), None); + + #[cfg(any(unchk_pass, chk_fail_ret))] + assert_eq!(try_sum(u32::MAX - 10, 2, 100), None); +} diff --git a/tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr b/tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr new file mode 100644 index 000000000000..d693fad446a4 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-early-fn-exit.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-early-fn-exit.rs:16:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs new file mode 100644 index 000000000000..f01a852fbff3 --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.rs @@ -0,0 +1,15 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +#[core::contracts::ensures(|ret| *ret > 0)] +fn outer() -> i32 { + let inner_closure = || -> i32 { 0 }; + inner_closure(); + 10 +} + +fn main() { + outer(); +} diff --git a/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr new file mode 100644 index 000000000000..49a372b53c7d --- /dev/null +++ b/tests/ui/contracts/contracts-ensures-is-not-inherited-when-nesting.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-ensures-is-not-inherited-when-nesting.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs new file mode 100644 index 000000000000..2c2a4a697855 --- /dev/null +++ b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.rs @@ -0,0 +1,17 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] + +struct Outer { outer: std::cell::Cell } + +#[core::contracts::requires(x.outer.get() > 0)] +fn outer(x: Outer) { + let inner_closure = || { }; + x.outer.set(0); + inner_closure(); +} + +fn main() { + outer(Outer { outer: 1.into() }); +} diff --git a/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr new file mode 100644 index 000000000000..48898c4434ad --- /dev/null +++ b/tests/ui/contracts/contracts-requires-is-not-inherited-when-nesting.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contracts-requires-is-not-inherited-when-nesting.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs new file mode 100644 index 000000000000..69be906782a6 --- /dev/null +++ b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.rs @@ -0,0 +1,53 @@ +//! Checks for compilation errors related to adding contracts to non-function items. + +#![feature(contracts)] +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] +#![allow(dead_code)] + +#[core::contracts::requires(true)] +//~^ ERROR contract annotations can only be used on functions +struct Dummy(usize); + +#[core::contracts::ensures(|v| v == 100)] +//~^ ERROR contract annotations can only be used on functions +const MAX_VAL: usize = 100; + +// FIXME: Improve the error message here. The macro thinks this is a function. +#[core::contracts::ensures(|v| v == 100)] +//~^ ERROR contract annotations is only supported in functions with bodies +type NewDummy = fn(usize) -> Dummy; + +#[core::contracts::ensures(|v| v == 100)] +//~^ ERROR contract annotations is only supported in functions with bodies +const NEW_DUMMY_FN : fn(usize) -> Dummy = || { Dummy(0) }; + +#[core::contracts::requires(true)] +//~^ ERROR contract annotations can only be used on functions +impl Dummy { + + // This should work + #[core::contracts::ensures(|ret| ret.0 == v)] + fn new(v: usize) -> Dummy { + Dummy(v) + } +} + +#[core::contracts::ensures(|dummy| dummy.0 > 0)] +//~^ ERROR contract annotations can only be used on functions +impl From for Dummy { + // This should work. + #[core::contracts::ensures(|ret| ret.0 == v)] + fn from(value: usize) -> Self { + Dummy::new(value) + } +} + +/// You should not be able to annotate a trait either. +#[core::contracts::requires(true)] +//~^ ERROR contract annotations can only be used on functions +pub trait DummyBuilder { + fn build() -> Dummy; +} + +fn main() { +} diff --git a/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr new file mode 100644 index 000000000000..0a7fff8183e0 --- /dev/null +++ b/tests/ui/contracts/disallow-contract-annotation-on-non-fn.stderr @@ -0,0 +1,53 @@ +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:7:1 + | +LL | #[core::contracts::requires(true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:11:1 + | +LL | #[core::contracts::ensures(|v| v == 100)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations is only supported in functions with bodies + --> $DIR/disallow-contract-annotation-on-non-fn.rs:16:1 + | +LL | #[core::contracts::ensures(|v| v == 100)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations is only supported in functions with bodies + --> $DIR/disallow-contract-annotation-on-non-fn.rs:20:1 + | +LL | #[core::contracts::ensures(|v| v == 100)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:24:1 + | +LL | #[core::contracts::requires(true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:35:1 + | +LL | #[core::contracts::ensures(|dummy| dummy.0 > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: contract annotations can only be used on functions + --> $DIR/disallow-contract-annotation-on-non-fn.rs:46:1 + | +LL | #[core::contracts::requires(true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/disallow-contract-annotation-on-non-fn.rs:3:12 + | +LL | #![feature(contracts)] + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +error: aborting due to 7 previous errors; 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs new file mode 100644 index 000000000000..6d8cd3949eed --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-nest.rs @@ -0,0 +1,44 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(contracts_internals)] + +fn nest(x: Baz) -> i32 + contract_requires(|| x.baz > 0) + contract_ensures(|ret| *ret > 100) +{ + loop { + return x.baz + 50; + } +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(nest(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + nest(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + nest(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs new file mode 100644 index 000000000000..07ec26f921b8 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-ast-extensions-tail.rs @@ -0,0 +1,42 @@ +//@ revisions: unchk_pass unchk_fail_pre unchk_fail_post chk_pass chk_fail_pre chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_pre] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_pre] run-fail +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_pre] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_pre] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(contracts_internals)] + +fn tail(x: Baz) -> i32 + contract_requires(|| x.baz > 0) + contract_ensures(|ret| *ret > 100) +{ + x.baz + 50 +} + +struct Baz { baz: i32 } + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; +#[cfg(any(unchk_fail_pre, chk_fail_pre))] +const BAZ_FAIL_PRE: Baz = Baz { baz: -10 }; + +fn main() { + assert_eq!(tail(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_pre, chk_fail_pre))] + tail(BAZ_FAIL_PRE); + #[cfg(any(unchk_fail_post, chk_fail_post))] + tail(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/internal_machinery/contract-intrinsics.rs b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs new file mode 100644 index 000000000000..ae692afd146f --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-intrinsics.rs @@ -0,0 +1,36 @@ +//@ revisions: default unchk_pass chk_pass chk_fail_ensures chk_fail_requires +// +//@ [default] run-pass +//@ [unchk_pass] run-pass +//@ [chk_pass] run-pass +//@ [chk_fail_requires] run-fail +//@ [chk_fail_ensures] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_requires] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_ensures] compile-flags: -Zcontract-checks=yes +#![feature(cfg_contract_checks, contracts_internals, core_intrinsics)] + +fn main() { + #[cfg(any(default, unchk_pass))] // default: disabled + assert_eq!(core::intrinsics::contract_checks(), false); + + #[cfg(chk_pass)] // explicitly enabled + assert_eq!(core::intrinsics::contract_checks(), true); + + // always pass + core::intrinsics::contract_check_requires(|| true); + + // fail if enabled + #[cfg(any(default, unchk_pass, chk_fail_requires))] + core::intrinsics::contract_check_requires(|| false); + + let doubles_to_two = { let old = 2; move |ret| ret + ret == old }; + // Always pass + core::intrinsics::contract_check_ensures(&1, doubles_to_two); + + // Fail if enabled + #[cfg(any(default, unchk_pass, chk_fail_ensures))] + core::intrinsics::contract_check_ensures(&2, doubles_to_two); +} diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr new file mode 100644 index 000000000000..a60ce1602659 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr new file mode 100644 index 000000000000..a60ce1602659 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.chk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.rs b/tests/ui/contracts/internal_machinery/contract-lang-items.rs new file mode 100644 index 000000000000..e91bbed294d1 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.rs @@ -0,0 +1,39 @@ +//@ revisions: unchk_pass unchk_fail_post chk_pass chk_fail_post +// +//@ [unchk_pass] run-pass +//@ [unchk_fail_post] run-pass +//@ [chk_pass] run-pass +// +//@ [chk_fail_post] run-fail +// +//@ [unchk_pass] compile-flags: -Zcontract-checks=no +//@ [unchk_fail_post] compile-flags: -Zcontract-checks=no +// +//@ [chk_pass] compile-flags: -Zcontract-checks=yes +//@ [chk_fail_post] compile-flags: -Zcontract-checks=yes + +#![feature(contracts)] // to access core::contracts +//~^ WARN the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes [incomplete_features] +#![feature(contracts_internals)] // to access check_requires lang item + +fn foo(x: Baz) -> i32 { + let injected_checker = { + core::contracts::build_check_ensures(|ret| *ret > 100) + }; + + let ret = x.baz + 50; + injected_checker(ret) +} + +struct Baz { baz: i32 } + + +const BAZ_PASS_PRE_POST: Baz = Baz { baz: 100 }; +#[cfg(any(unchk_fail_post, chk_fail_post))] +const BAZ_FAIL_POST: Baz = Baz { baz: 10 }; + +fn main() { + assert_eq!(foo(BAZ_PASS_PRE_POST), 150); + #[cfg(any(unchk_fail_post, chk_fail_post))] + foo(BAZ_FAIL_POST); +} diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr new file mode 100644 index 000000000000..a60ce1602659 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_fail_post.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr new file mode 100644 index 000000000000..a60ce1602659 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contract-lang-items.unchk_pass.stderr @@ -0,0 +1,11 @@ +warning: the feature `contracts` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/contract-lang-items.rs:15:12 + | +LL | #![feature(contracts)] // to access core::contracts + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs new file mode 100644 index 000000000000..960ccaed3588 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-ensures-is-not-inherited-when-nesting.rs @@ -0,0 +1,15 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts_internals)] + +fn outer() -> i32 + contract_ensures(|ret| *ret > 0) +{ + let inner_closure = || -> i32 { 0 }; + inner_closure(); + 10 +} + +fn main() { + outer(); +} diff --git a/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs new file mode 100644 index 000000000000..bee703de16a0 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/contracts-lowering-requires-is-not-inherited-when-nesting.rs @@ -0,0 +1,17 @@ +//@ run-pass +//@ compile-flags: -Zcontract-checks=yes +#![feature(contracts_internals)] + +struct Outer { outer: std::cell::Cell } + +fn outer(x: Outer) + contract_requires(|| x.outer.get() > 0) +{ + let inner_closure = || { }; + x.outer.set(0); + inner_closure(); +} + +fn main() { + outer(Outer { outer: 1.into() }); +} diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.rs b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs new file mode 100644 index 000000000000..1b76eef6780f --- /dev/null +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.rs @@ -0,0 +1,20 @@ +// gate-test-contracts_internals + +fn main() { + // intrinsics are guarded by contracts_internals feature gate. + core::intrinsics::contract_checks(); + //~^ ERROR use of unstable library feature `contracts_internals` + core::intrinsics::contract_check_requires(|| true); + //~^ ERROR use of unstable library feature `contracts_internals` + core::intrinsics::contract_check_ensures(&1, |_|true); + //~^ ERROR use of unstable library feature `contracts_internals` + + core::contracts::build_check_ensures(|_: &()| true); + //~^ ERROR use of unstable library feature `contracts_internals` + + // ast extensions are guarded by contracts_internals feature gate + fn identity_1() -> i32 contract_requires(|| true) { 10 } + //~^ ERROR contract internal machinery is for internal use only + fn identity_2() -> i32 contract_ensures(|_| true) { 10 } + //~^ ERROR contract internal machinery is for internal use only +} diff --git a/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr new file mode 100644 index 000000000000..c0e1522f54c6 --- /dev/null +++ b/tests/ui/contracts/internal_machinery/internal-feature-gating.stderr @@ -0,0 +1,63 @@ +error[E0658]: contract internal machinery is for internal use only + --> $DIR/internal-feature-gating.rs:16:45 + | +LL | fn identity_1() -> i32 contract_requires(|| true) { 10 } + | ^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: contract internal machinery is for internal use only + --> $DIR/internal-feature-gating.rs:18:44 + | +LL | fn identity_2() -> i32 contract_ensures(|_| true) { 10 } + | ^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `contracts_internals` + --> $DIR/internal-feature-gating.rs:5:5 + | +LL | core::intrinsics::contract_checks(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `contracts_internals` + --> $DIR/internal-feature-gating.rs:7:5 + | +LL | core::intrinsics::contract_check_requires(|| true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `contracts_internals` + --> $DIR/internal-feature-gating.rs:9:5 + | +LL | core::intrinsics::contract_check_ensures(&1, |_|true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `contracts_internals` + --> $DIR/internal-feature-gating.rs:12:5 + | +LL | core::contracts::build_check_ensures(|_: &()| true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs new file mode 100644 index 000000000000..36d3e712dc0e --- /dev/null +++ b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2021.rs @@ -0,0 +1,23 @@ +//@ edition: 2021 + +#[macro_export] +macro_rules! make_macro_with_input { + ($i:ident) => { + macro_rules! macro_inner_input { + () => { + pub fn $i() {} + }; + } + }; +} + +#[macro_export] +macro_rules! make_macro { + () => { + macro_rules! macro_inner { + () => { + pub fn gen() {} + }; + } + }; +} diff --git a/tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs new file mode 100644 index 000000000000..4012398fe662 --- /dev/null +++ b/tests/ui/editions/auxiliary/nested_macro_rules_dep_2024.rs @@ -0,0 +1,23 @@ +//@ edition: 2024 + +#[macro_export] +macro_rules! make_macro_with_input { + ($i:ident) => { + macro_rules! macro_inner_input { + () => { + pub fn $i() {} + }; + } + }; +} + +#[macro_export] +macro_rules! make_macro { + () => { + macro_rules! macro_inner { + () => { + pub fn gen() {} + }; + } + }; +} diff --git a/tests/ui/editions/nested-macro-rules-edition.e2021.stderr b/tests/ui/editions/nested-macro-rules-edition.e2021.stderr new file mode 100644 index 000000000000..eac80262364b --- /dev/null +++ b/tests/ui/editions/nested-macro-rules-edition.e2021.stderr @@ -0,0 +1,27 @@ +error: expected identifier, found reserved keyword `gen` + --> $DIR/nested-macro-rules-edition.rs:30:5 + | +LL | macro_inner_input!{} + | ^^^^^^^^^^^^^^^^^^^^ expected identifier, found reserved keyword + | + = note: this error originates in the macro `macro_inner_input` (in Nightly builds, run with -Z macro-backtrace for more info) +help: escape `gen` to use it as an identifier + | +LL | nested_macro_rules_dep::make_macro_with_input!{r#gen} + | ++ + +error: expected identifier, found reserved keyword `gen` + --> $DIR/nested-macro-rules-edition.rs:35:5 + | +LL | macro_inner!{} + | ^^^^^^^^^^^^^^ expected identifier, found reserved keyword + | + = note: this error originates in the macro `macro_inner` (in Nightly builds, run with -Z macro-backtrace for more info) +help: escape `gen` to use it as an identifier + --> $DIR/auxiliary/nested_macro_rules_dep_2024.rs:19:24 + | +LL | pub fn r#gen() {} + | ++ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/editions/nested-macro-rules-edition.rs b/tests/ui/editions/nested-macro-rules-edition.rs new file mode 100644 index 000000000000..28d568f77501 --- /dev/null +++ b/tests/ui/editions/nested-macro-rules-edition.rs @@ -0,0 +1,39 @@ +// This checks the behavior of how nested macro_rules definitions are handled +// with regards to edition spans. Prior to https://github.com/rust-lang/rust/pull/133274, +// the compiler would compile the inner macro with the edition of the local crate. +// Afterwards, it uses the edition where the macro was *defined*. +// +// Unfortunately macro_rules compiler discards the edition of any *input* that +// was used to generate the macro. This is possibly not the behavior that we +// want. If we want to keep with the philosophy that code should follow the +// edition rules of the crate where it is written, then presumably we would +// want the input tokens to retain the edition of where they were written. +// +// See https://github.com/rust-lang/rust/issues/135669 for more. +// +// This has two revisions, one where local=2021 and the dep=2024. The other +// revision is vice-versa. + +//@ revisions: e2021 e2024 +//@[e2021] edition:2021 +//@[e2024] edition:2024 +//@[e2021] aux-crate: nested_macro_rules_dep=nested_macro_rules_dep_2024.rs +//@[e2024] aux-crate: nested_macro_rules_dep=nested_macro_rules_dep_2021.rs +//@[e2024] check-pass + +mod with_input { + // If we change the macro_rules input behavior, then this should pass when + // local edition is 2021 because `gen` is written in a context with 2021 + // behavior. For local edition 2024, the reverse would be true and this + // should fail. + nested_macro_rules_dep::make_macro_with_input!{gen} + macro_inner_input!{} + //[e2021]~^ ERROR found reserved keyword +} +mod no_input { + nested_macro_rules_dep::make_macro!{} + macro_inner!{} + //[e2021]~^ ERROR found reserved keyword +} + +fn main() {} diff --git a/tests/ui/issues/issue-48838.rs b/tests/ui/enum/closure-in-enum-issue-48838.rs similarity index 100% rename from tests/ui/issues/issue-48838.rs rename to tests/ui/enum/closure-in-enum-issue-48838.rs diff --git a/tests/ui/issues/issue-48838.stderr b/tests/ui/enum/closure-in-enum-issue-48838.stderr similarity index 66% rename from tests/ui/issues/issue-48838.stderr rename to tests/ui/enum/closure-in-enum-issue-48838.stderr index 504ea3e80103..17e6c3433343 100644 --- a/tests/ui/issues/issue-48838.stderr +++ b/tests/ui/enum/closure-in-enum-issue-48838.stderr @@ -1,11 +1,11 @@ error[E0308]: mismatched types - --> $DIR/issue-48838.rs:2:14 + --> $DIR/closure-in-enum-issue-48838.rs:2:14 | LL | Square = |x| x, | ^^^^^ expected `isize`, found closure | = note: expected type `isize` - found closure `{closure@$DIR/issue-48838.rs:2:14: 2:17}` + found closure `{closure@$DIR/closure-in-enum-issue-48838.rs:2:14: 2:17}` error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-40350.rs b/tests/ui/enum/enum-inside-enum-issue-40350.rs similarity index 100% rename from tests/ui/issues/issue-40350.rs rename to tests/ui/enum/enum-inside-enum-issue-40350.rs diff --git a/tests/ui/issues/issue-41272.rs b/tests/ui/expr/if/if-let-no-match-guards-issue-41272.rs similarity index 100% rename from tests/ui/issues/issue-41272.rs rename to tests/ui/expr/if/if-let-no-match-guards-issue-41272.rs diff --git a/tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs new file mode 100644 index 000000000000..cd9bf12b5e77 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.rs @@ -0,0 +1,5 @@ +#![crate_type = "lib"] + +pub fn contract_checks_are_enabled() -> bool { + cfg!(contract_checks) //~ ERROR `cfg(contract_checks)` is experimental +} diff --git a/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr new file mode 100644 index 000000000000..89c6d077f97e --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-cfg-contract-checks.stderr @@ -0,0 +1,13 @@ +error[E0658]: `cfg(contract_checks)` is experimental and subject to change + --> $DIR/feature-gate-cfg-contract-checks.rs:4:10 + | +LL | cfg!(contract_checks) + | ^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(cfg_contract_checks)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-contracts.rs b/tests/ui/feature-gates/feature-gate-contracts.rs new file mode 100644 index 000000000000..5544f1d82ee4 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-contracts.rs @@ -0,0 +1,11 @@ +#![crate_type = "lib"] + +#[core::contracts::requires(x > 0)] +pub fn requires_needs_it(x: i32) { } +//~^^ ERROR use of unstable library feature `contracts` +//~^^^ ERROR contracts are incomplete + +#[core::contracts::ensures(|ret| *ret > 0)] +pub fn ensures_needs_it() -> i32 { 10 } +//~^^ ERROR use of unstable library feature `contracts` +//~^^^ ERROR contracts are incomplete diff --git a/tests/ui/feature-gates/feature-gate-contracts.stderr b/tests/ui/feature-gates/feature-gate-contracts.stderr new file mode 100644 index 000000000000..4403e7df50b4 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-contracts.stderr @@ -0,0 +1,43 @@ +error[E0658]: use of unstable library feature `contracts` + --> $DIR/feature-gate-contracts.rs:3:3 + | +LL | #[core::contracts::requires(x > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `contracts` + --> $DIR/feature-gate-contracts.rs:8:3 + | +LL | #[core::contracts::ensures(|ret| *ret > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: contracts are incomplete + --> $DIR/feature-gate-contracts.rs:3:1 + | +LL | #[core::contracts::requires(x > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: contracts are incomplete + --> $DIR/feature-gate-contracts.rs:8:1 + | +LL | #[core::contracts::ensures(|ret| *ret > 0)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #128044 for more information + = help: add `#![feature(contracts)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/inference/issue-72616.stderr b/tests/ui/inference/issue-72616.stderr index a26f9a1ff561..3848fcf61d90 100644 --- a/tests/ui/inference/issue-72616.stderr +++ b/tests/ui/inference/issue-72616.stderr @@ -26,7 +26,7 @@ LL | if String::from("a") == "a".try_into().unwrap() {} | ^^^^^^^^ | = note: multiple `impl`s satisfying `_: TryFrom<&str>` found in the following crates: `core`, `std`: - - impl TryFrom<&str> for std::sys_common::net::LookupHost; + - impl TryFrom<&str> for std::sys::net::connection::socket::LookupHost; - impl TryFrom for T where U: Into; = note: required for `&str` to implement `TryInto<_>` diff --git a/tests/ui/issues/issue-40408.rs b/tests/ui/lexer/floating-point-0e10-issue-40408.rs similarity index 100% rename from tests/ui/issues/issue-40408.rs rename to tests/ui/lexer/floating-point-0e10-issue-40408.rs diff --git a/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs b/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs new file mode 100644 index 000000000000..3c81127ee65c --- /dev/null +++ b/tests/ui/lto/auxiliary/dwarf-mixed-versions-lto-aux.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -g --crate-type=rlib -Zdwarf-version=4 + +pub fn say_hi() { + println!("hello there") +} diff --git a/tests/ui/lto/dwarf-mixed-versions-lto.rs b/tests/ui/lto/dwarf-mixed-versions-lto.rs new file mode 100644 index 000000000000..14ef65a868e0 --- /dev/null +++ b/tests/ui/lto/dwarf-mixed-versions-lto.rs @@ -0,0 +1,15 @@ +// This test verifies that we do not produce a warning when performing LTO on a +// crate graph that contains a mix of different DWARF version settings. This +// matches Clang's behavior. + +//@ ignore-msvc Platform must use DWARF +//@ aux-build:dwarf-mixed-versions-lto-aux.rs +//@ compile-flags: -C lto -g -Zdwarf-version=5 +//@ no-prefer-dynamic +//@ build-pass + +extern crate dwarf_mixed_versions_lto_aux; + +fn main() { + dwarf_mixed_versions_lto_aux::say_hi(); +} diff --git a/tests/ui/issues/issue-40136.rs b/tests/ui/macros/const-expr-invocations-issue-40136.rs similarity index 100% rename from tests/ui/issues/issue-40136.rs rename to tests/ui/macros/const-expr-invocations-issue-40136.rs diff --git a/tests/ui/issues/issue-40845.rs b/tests/ui/macros/macros-in-trait-positions-issue-40845.rs similarity index 100% rename from tests/ui/issues/issue-40845.rs rename to tests/ui/macros/macros-in-trait-positions-issue-40845.rs diff --git a/tests/ui/issues/issue-40845.stderr b/tests/ui/macros/macros-in-trait-positions-issue-40845.stderr similarity index 65% rename from tests/ui/issues/issue-40845.stderr rename to tests/ui/macros/macros-in-trait-positions-issue-40845.stderr index 66bf053204c0..5c1b5f51e44b 100644 --- a/tests/ui/issues/issue-40845.stderr +++ b/tests/ui/macros/macros-in-trait-positions-issue-40845.stderr @@ -1,11 +1,11 @@ error: cannot find macro `m` in this scope - --> $DIR/issue-40845.rs:1:11 + --> $DIR/macros-in-trait-positions-issue-40845.rs:1:11 | LL | trait T { m!(); } | ^ error: cannot find macro `m` in this scope - --> $DIR/issue-40845.rs:4:10 + --> $DIR/macros-in-trait-positions-issue-40845.rs:4:10 | LL | impl S { m!(); } | ^ diff --git a/tests/ui/issues/issue-41213.rs b/tests/ui/match/enum-and-break-in-match-issue-41213.rs similarity index 95% rename from tests/ui/issues/issue-41213.rs rename to tests/ui/match/enum-and-break-in-match-issue-41213.rs index 97f80a99a832..7c42a3629c9c 100644 --- a/tests/ui/issues/issue-41213.rs +++ b/tests/ui/match/enum-and-break-in-match-issue-41213.rs @@ -1,4 +1,4 @@ -//@ run-pass +//@ check-pass #![allow(dead_code)] enum A { A1, diff --git a/tests/ui/self/arbitrary_self_type_infinite_recursion.rs b/tests/ui/self/arbitrary_self_type_infinite_recursion.rs new file mode 100644 index 000000000000..6fbf35c0b867 --- /dev/null +++ b/tests/ui/self/arbitrary_self_type_infinite_recursion.rs @@ -0,0 +1,24 @@ +#![feature(arbitrary_self_types)] + +struct MySmartPtr(T); + +impl core::ops::Receiver for MySmartPtr { + type Target = MySmartPtr; +} + +struct Content; + +impl Content { + fn method(self: MySmartPtr) { // note self type + //~^ reached the recursion limit + //~| reached the recursion limit + //~| invalid `self` parameter type + } +} + +fn main() { + let p = MySmartPtr(Content); + p.method(); + //~^ reached the recursion limit + //~| no method named `method` +} diff --git a/tests/ui/self/arbitrary_self_type_infinite_recursion.stderr b/tests/ui/self/arbitrary_self_type_infinite_recursion.stderr new file mode 100644 index 000000000000..5e652efb3645 --- /dev/null +++ b/tests/ui/self/arbitrary_self_type_infinite_recursion.stderr @@ -0,0 +1,47 @@ +error[E0055]: reached the recursion limit while auto-dereferencing `MySmartPtr` + --> $DIR/arbitrary_self_type_infinite_recursion.rs:12:19 + | +LL | fn method(self: MySmartPtr) { // note self type + | ^^^^^^^^^^^^^^^^ deref recursion limit reached + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`arbitrary_self_type_infinite_recursion`) + +error[E0055]: reached the recursion limit while auto-dereferencing `MySmartPtr` + --> $DIR/arbitrary_self_type_infinite_recursion.rs:12:19 + | +LL | fn method(self: MySmartPtr) { // note self type + | ^^^^^^^^^^^^^^^^ deref recursion limit reached + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`arbitrary_self_type_infinite_recursion`) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0307]: invalid `self` parameter type: `MySmartPtr` + --> $DIR/arbitrary_self_type_infinite_recursion.rs:12:19 + | +LL | fn method(self: MySmartPtr) { // note self type + | ^^^^^^^^^^^^^^^^ + | + = note: type of `self` must be `Self` or some type implementing `Receiver` + = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box`, `self: Rc`, or `self: Arc` + +error[E0055]: reached the recursion limit while auto-dereferencing `MySmartPtr` + --> $DIR/arbitrary_self_type_infinite_recursion.rs:21:5 + | +LL | p.method(); + | ^^^^^^ deref recursion limit reached + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`arbitrary_self_type_infinite_recursion`) + +error[E0599]: no method named `method` found for struct `MySmartPtr` in the current scope + --> $DIR/arbitrary_self_type_infinite_recursion.rs:21:5 + | +LL | struct MySmartPtr(T); + | -------------------- method `method` not found for this struct +... +LL | p.method(); + | ^^^^^^ method not found in `MySmartPtr` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0055, E0307, E0599. +For more information about an error, try `rustc --explain E0055`. diff --git a/tests/ui/issues/issue-40782.fixed b/tests/ui/suggestions/for-loop-missing-in.fixed similarity index 100% rename from tests/ui/issues/issue-40782.fixed rename to tests/ui/suggestions/for-loop-missing-in.fixed diff --git a/tests/ui/issues/issue-40782.rs b/tests/ui/suggestions/for-loop-missing-in.rs similarity index 100% rename from tests/ui/issues/issue-40782.rs rename to tests/ui/suggestions/for-loop-missing-in.rs diff --git a/tests/ui/issues/issue-40782.stderr b/tests/ui/suggestions/for-loop-missing-in.stderr similarity index 82% rename from tests/ui/issues/issue-40782.stderr rename to tests/ui/suggestions/for-loop-missing-in.stderr index 614980202385..61830b800a61 100644 --- a/tests/ui/issues/issue-40782.stderr +++ b/tests/ui/suggestions/for-loop-missing-in.stderr @@ -1,5 +1,5 @@ error: missing `in` in `for` loop - --> $DIR/issue-40782.rs:4:11 + --> $DIR/for-loop-missing-in.rs:4:11 | LL | for _i 0..2 { | ^ @@ -10,7 +10,7 @@ LL | for _i in 0..2 { | ++ error: missing `in` in `for` loop - --> $DIR/issue-40782.rs:6:12 + --> $DIR/for-loop-missing-in.rs:6:12 | LL | for _i of 0..2 { | ^^ diff --git a/tests/ui/suggestions/suggest-std-when-using-type.fixed b/tests/ui/suggestions/suggest-std-when-using-type.fixed index ebbb854175bd..0547940f94d3 100644 --- a/tests/ui/suggestions/suggest-std-when-using-type.fixed +++ b/tests/ui/suggestions/suggest-std-when-using-type.fixed @@ -1,8 +1,5 @@ //@ run-rustfix fn main() { let pi = std::f32::consts::PI; //~ ERROR ambiguous associated type - let bytes = "hello world".as_bytes(); - let string = std::str::from_utf8(bytes).unwrap(); - //~^ ERROR no function or associated item named `from_utf8` found - println!("{pi} {bytes:?} {string}"); + println!("{pi}"); } diff --git a/tests/ui/suggestions/suggest-std-when-using-type.rs b/tests/ui/suggestions/suggest-std-when-using-type.rs index 06aab63d6885..cd55c5d13bd1 100644 --- a/tests/ui/suggestions/suggest-std-when-using-type.rs +++ b/tests/ui/suggestions/suggest-std-when-using-type.rs @@ -1,8 +1,5 @@ //@ run-rustfix fn main() { let pi = f32::consts::PI; //~ ERROR ambiguous associated type - let bytes = "hello world".as_bytes(); - let string = str::from_utf8(bytes).unwrap(); - //~^ ERROR no function or associated item named `from_utf8` found - println!("{pi} {bytes:?} {string}"); + println!("{pi}"); } diff --git a/tests/ui/suggestions/suggest-std-when-using-type.stderr b/tests/ui/suggestions/suggest-std-when-using-type.stderr index 6f890b87b24b..7f8545f461d9 100644 --- a/tests/ui/suggestions/suggest-std-when-using-type.stderr +++ b/tests/ui/suggestions/suggest-std-when-using-type.stderr @@ -9,18 +9,6 @@ help: you are looking for the module in `std`, not the primitive type LL | let pi = std::f32::consts::PI; | +++++ -error[E0599]: no function or associated item named `from_utf8` found for type `str` in the current scope - --> $DIR/suggest-std-when-using-type.rs:5:23 - | -LL | let string = str::from_utf8(bytes).unwrap(); - | ^^^^^^^^^ function or associated item not found in `str` - | -help: you are looking for the module in `std`, not the primitive type - | -LL | let string = std::str::from_utf8(bytes).unwrap(); - | +++++ +error: aborting due to 1 previous error -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0223, E0599. -For more information about an error, try `rustc --explain E0223`. +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/issues/issue-40827.rs b/tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.rs similarity index 100% rename from tests/ui/issues/issue-40827.rs rename to tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.rs diff --git a/tests/ui/issues/issue-40827.stderr b/tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.stderr similarity index 76% rename from tests/ui/issues/issue-40827.stderr rename to tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.stderr index 7f5c578ae4ff..7b59fe72f431 100644 --- a/tests/ui/issues/issue-40827.stderr +++ b/tests/ui/trait-bounds/deep-level-Send-bound-check-issue-40827.stderr @@ -1,5 +1,5 @@ error[E0277]: `Rc` cannot be shared between threads safely - --> $DIR/issue-40827.rs:14:7 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:14:7 | LL | f(Foo(Arc::new(Bar::B(None)))); | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc` cannot be shared between threads safely @@ -8,24 +8,24 @@ LL | f(Foo(Arc::new(Bar::B(None)))); | = help: within `Bar`, the trait `Sync` is not implemented for `Rc` note: required because it appears within the type `Bar` - --> $DIR/issue-40827.rs:6:6 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:6:6 | LL | enum Bar { | ^^^ = note: required for `Arc` to implement `Send` note: required because it appears within the type `Foo` - --> $DIR/issue-40827.rs:4:8 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:4:8 | LL | struct Foo(Arc); | ^^^ note: required by a bound in `f` - --> $DIR/issue-40827.rs:11:9 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:11:9 | LL | fn f(_: T) {} | ^^^^ required by this bound in `f` error[E0277]: `Rc` cannot be sent between threads safely - --> $DIR/issue-40827.rs:14:7 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:14:7 | LL | f(Foo(Arc::new(Bar::B(None)))); | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Rc` cannot be sent between threads safely @@ -34,18 +34,18 @@ LL | f(Foo(Arc::new(Bar::B(None)))); | = help: within `Bar`, the trait `Send` is not implemented for `Rc` note: required because it appears within the type `Bar` - --> $DIR/issue-40827.rs:6:6 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:6:6 | LL | enum Bar { | ^^^ = note: required for `Arc` to implement `Send` note: required because it appears within the type `Foo` - --> $DIR/issue-40827.rs:4:8 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:4:8 | LL | struct Foo(Arc); | ^^^ note: required by a bound in `f` - --> $DIR/issue-40827.rs:11:9 + --> $DIR/deep-level-Send-bound-check-issue-40827.rs:11:9 | LL | fn f(_: T) {} | ^^^^ required by this bound in `f` diff --git a/tests/ui/type/pattern_types/signed_ranges.rs b/tests/ui/type/pattern_types/signed_ranges.rs new file mode 100644 index 000000000000..3725fbda7292 --- /dev/null +++ b/tests/ui/type/pattern_types/signed_ranges.rs @@ -0,0 +1,24 @@ +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +type Sign = pattern_type!(u32 is -10..); +//~^ ERROR: cannot apply unary operator `-` + +type SignedChar = pattern_type!(char is -'A'..); +//~^ ERROR: cannot apply unary operator `-` + +fn main() { + match 42_u8 { + -10..253 => {} + //~^ ERROR `u8: Neg` is not satisfied + _ => {} + } + + match 'A' { + -'\0'..'a' => {} + //~^ ERROR `char: Neg` is not satisfied + _ => {} + } +} diff --git a/tests/ui/type/pattern_types/signed_ranges.stderr b/tests/ui/type/pattern_types/signed_ranges.stderr new file mode 100644 index 000000000000..79bf8501b639 --- /dev/null +++ b/tests/ui/type/pattern_types/signed_ranges.stderr @@ -0,0 +1,41 @@ +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/signed_ranges.rs:14:9 + | +LL | -10..253 => {} + | ^^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `char: Neg` is not satisfied + --> $DIR/signed_ranges.rs:20:9 + | +LL | -'\0'..'a' => {} + | ^^^^^ the trait `Neg` is not implemented for `char` + +error[E0600]: cannot apply unary operator `-` to type `u32` + --> $DIR/signed_ranges.rs:6:34 + | +LL | type Sign = pattern_type!(u32 is -10..); + | ^^^ cannot apply unary operator `-` + | + = note: unsigned values cannot be negated + +error[E0600]: cannot apply unary operator `-` to type `char` + --> $DIR/signed_ranges.rs:9:41 + | +LL | type SignedChar = pattern_type!(char is -'A'..); + | ^^^^ cannot apply unary operator `-` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0600. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/issues/issue-40610.rs b/tests/ui/typeck/coercion-check-for-addition-issue-40610.rs similarity index 100% rename from tests/ui/issues/issue-40610.rs rename to tests/ui/typeck/coercion-check-for-addition-issue-40610.rs diff --git a/tests/ui/issues/issue-40610.stderr b/tests/ui/typeck/coercion-check-for-addition-issue-40610.stderr similarity index 79% rename from tests/ui/issues/issue-40610.stderr rename to tests/ui/typeck/coercion-check-for-addition-issue-40610.stderr index 1bd1c4dd57d7..5e424862d97a 100644 --- a/tests/ui/issues/issue-40610.stderr +++ b/tests/ui/typeck/coercion-check-for-addition-issue-40610.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot add `()` to `()` - --> $DIR/issue-40610.rs:4:8 + --> $DIR/coercion-check-for-addition-issue-40610.rs:4:8 | LL | () + f(&[1.0]); | -- ^ --------- () diff --git a/tests/ui/issues/issue-40861.rs b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.rs similarity index 100% rename from tests/ui/issues/issue-40861.rs rename to tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.rs diff --git a/tests/ui/issues/issue-40861.stderr b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr similarity index 81% rename from tests/ui/issues/issue-40861.stderr rename to tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr index dec9af4b6d1c..13bc0cd94f37 100644 --- a/tests/ui/issues/issue-40861.stderr +++ b/tests/ui/typeck/coercion-check-for-indexing-expression-issue-40861.stderr @@ -1,5 +1,5 @@ error[E0608]: cannot index into a value of type `()` - --> $DIR/issue-40861.rs:4:7 + --> $DIR/coercion-check-for-indexing-expression-issue-40861.rs:4:7 | LL | ()[f(&[1.0])]; | ^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-41139.rs b/tests/ui/typeck/unsized-rvalue-issue-41139.rs similarity index 100% rename from tests/ui/issues/issue-41139.rs rename to tests/ui/typeck/unsized-rvalue-issue-41139.rs diff --git a/tests/ui/issues/issue-41139.stderr b/tests/ui/typeck/unsized-rvalue-issue-41139.stderr similarity index 92% rename from tests/ui/issues/issue-41139.stderr rename to tests/ui/typeck/unsized-rvalue-issue-41139.stderr index d7b35245d8f5..aba0423eeb3e 100644 --- a/tests/ui/issues/issue-41139.stderr +++ b/tests/ui/typeck/unsized-rvalue-issue-41139.stderr @@ -1,5 +1,5 @@ error[E0618]: expected function, found `&dyn Fn() -> (dyn Trait + 'static)` - --> $DIR/issue-41139.rs:10:26 + --> $DIR/unsized-rvalue-issue-41139.rs:10:26 | LL | fn get_function<'a>() -> &'a dyn Fn() -> dyn Trait { | -------------------------------------------------- `get_function` defined here returns `&dyn Fn() -> (dyn Trait + 'static)` diff --git a/tests/ui/issues/issue-40749.rs b/tests/ui/wf/range-expr-root-of-constant-issue-40749.rs similarity index 100% rename from tests/ui/issues/issue-40749.rs rename to tests/ui/wf/range-expr-root-of-constant-issue-40749.rs diff --git a/tests/ui/issues/issue-40749.stderr b/tests/ui/wf/range-expr-root-of-constant-issue-40749.stderr similarity index 84% rename from tests/ui/issues/issue-40749.stderr rename to tests/ui/wf/range-expr-root-of-constant-issue-40749.stderr index f7770e00013c..482773a39440 100644 --- a/tests/ui/issues/issue-40749.stderr +++ b/tests/ui/wf/range-expr-root-of-constant-issue-40749.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-40749.rs:2:9 + --> $DIR/range-expr-root-of-constant-issue-40749.rs:2:9 | LL | [0; ..10]; | ^^^^ expected `usize`, found `RangeTo<{integer}>` diff --git a/tests/ui/issues/issue-40235.rs b/tests/ui/while/while-let-scope-issue-40235.rs similarity index 88% rename from tests/ui/issues/issue-40235.rs rename to tests/ui/while/while-let-scope-issue-40235.rs index 2bdbb2f229e1..7d5dfc64a905 100644 --- a/tests/ui/issues/issue-40235.rs +++ b/tests/ui/while/while-let-scope-issue-40235.rs @@ -1,4 +1,4 @@ -//@ run-pass +//@ check-pass #![allow(unused_variables)] fn foo() {}