diff --git a/Cargo.lock b/Cargo.lock index f55daf9e01c5f..02e28b94d0740 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6336,9 +6336,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.26" +version = "0.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51a12709376d4ce64f472699500db3b0e5902cc2bef16fb6ca3098bfdac032fa" +checksum = "5fd1ba338b2c06654988a76b13f38cad9a1c738e3275bf71ecf70eeac5d98eb4" dependencies = [ "anyhow", "clap", @@ -6347,12 +6347,12 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.253.0", + "wasmparser 0.254.0", "wat", "windows-sys 0.61.2", "winsplit", - "wit-component 0.253.0", - "wit-parser 0.253.0", + "wit-component 0.254.0", + "wit-parser 0.254.0", ] [[package]] @@ -6384,12 +6384,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.253.0" +version = "0.254.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59972d6cd272259de647b7c1f1912e45e289c75ffd4be04e10695507cd7e1b59" +checksum = "09480d646178e5fdd12bb06e812d0af9a3a191dbc9cd697fdc86687beade7393" dependencies = [ "leb128fmt", - "wasmparser 0.253.0", + "wasmparser 0.254.0", ] [[package]] @@ -6406,14 +6406,14 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.253.0" +version = "0.254.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3f45816ef616806f48498bcd831377de578c4fa51db0c83ab8ceb78cc13523b" +checksum = "b01df5f3b4ca7881e843f3bc0fb8a3905d79c68692250dcb8e33e698705ccdb6" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.253.0", - "wasmparser 0.253.0", + "wasm-encoder 0.254.0", + "wasmparser 0.254.0", ] [[package]] @@ -6450,9 +6450,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.253.0" +version = "0.254.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19db11f87d2486580e1e8b6f494c54df7e0566b87d0b599db843c24019667339" +checksum = "d5769a29f799fbab136aaf65b4fe5384cd7d93fe6fc9ba0dcb6c8382a1f16e27" dependencies = [ "bitflags", "hashbrown 0.17.0", @@ -6463,22 +6463,22 @@ dependencies = [ [[package]] name = "wast" -version = "253.0.0" +version = "254.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3264542f8965c5d84fb1085d924bfba9a6314bb228eff13a2de14d7627664d0" +checksum = "e7ed4dfc8f6b9fc38b231065e2cdfbf7359af5ab945990abf09658dcc63c3e32" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.2", - "wasm-encoder 0.253.0", + "wasm-encoder 0.254.0", ] [[package]] name = "wat" -version = "1.253.0" +version = "1.254.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bfc5ce906144200c972ec617470aa35bd847472e170b26dde3e80541c674055" +checksum = "7127f7f9b8f127c879991cecd35f494e4628bae1b0874c681414d8d8831e952c" dependencies = [ "wast", ] @@ -6926,9 +6926,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.253.0" +version = "0.254.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbbd2500ac3488489ee8c6e59b79d7e47e6da5bfb019efd35d5dca57b78af624" +checksum = "b0e65bb94c369b3c4741ce3d1d2704b1fec93db7c540df0e521a097e7ceeb5be" dependencies = [ "anyhow", "bitflags", @@ -6937,10 +6937,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.253.0", - "wasm-metadata 0.253.0", - "wasmparser 0.253.0", - "wit-parser 0.253.0", + "wasm-encoder 0.254.0", + "wasm-metadata 0.254.0", + "wasmparser 0.254.0", + "wit-parser 0.254.0", ] [[package]] @@ -6963,9 +6963,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.253.0" +version = "0.254.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d997b8e5920fcbeec742b58e583325d6419a6aca617ae8075c406a61c65ba8a" +checksum = "1655131e4f7d3f0cb141f6eca71315ca40eff0f3d4de7cff0a82bacedd8c89b4" dependencies = [ "anyhow", "hashbrown 0.17.0", @@ -6977,7 +6977,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-ident", - "wasmparser 0.253.0", + "wasmparser 0.254.0", ] [[package]] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index a36e7f3248dd0..38cc0177821d8 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2339,27 +2339,7 @@ pub struct FnSig { impl FnSig { /// Return a span encompassing the header, or where to insert it if empty. pub fn header_span(&self) -> Span { - match self.header.ext { - Extern::Implicit(span) | Extern::Explicit(_, span) => { - return self.span.with_hi(span.hi()); - } - Extern::None => {} - } - - match self.header.safety { - Safety::Unsafe(span) | Safety::Safe(span) => return self.span.with_hi(span.hi()), - Safety::Default => {} - }; - - if let Some(coroutine_kind) = self.header.coroutine_kind { - return self.span.with_hi(coroutine_kind.span().hi()); - } - - if let Const::Yes(span) = self.header.constness { - return self.span.with_hi(span.hi()); - } - - self.span.shrink_to_lo() + self.header.span().unwrap_or(self.span.shrink_to_lo()) } /// The span of the header's safety, or where to insert it if empty. @@ -2382,6 +2362,19 @@ impl FnSig { pub fn extern_span(&self) -> Span { self.header.ext.span().unwrap_or(self.safety_span().shrink_to_hi()) } + + pub fn as_borrowed(&self) -> BorrowedFnSig<'_> { + BorrowedFnSig { header: self.header, decl: &self.decl, span: self.span } + } +} + +/// A borrowed version of `FnSig`, used to share logic between function declarations and function +/// pointer types. +#[derive(Clone, Debug)] +pub struct BorrowedFnSig<'a> { + pub header: FnHeader, + pub decl: &'a FnDecl, + pub span: Span, } /// A constraint on an associated item. @@ -2487,6 +2480,16 @@ pub struct FnPtrTy { pub decl_span: Span, } +impl FnPtrTy { + pub fn header(&self) -> FnHeader { + FnHeader { constness: Const::No, coroutine_kind: None, safety: self.safety, ext: self.ext } + } + + pub fn as_borrowed_fn_sig<'a>(&'a self) -> BorrowedFnSig<'a> { + BorrowedFnSig { header: self.header(), decl: &self.decl, span: self.decl_span } + } +} + #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub struct UnsafeBinderTy { pub generic_params: ThinVec, @@ -3836,6 +3839,30 @@ impl FnHeader { || matches!(constness, Const::Yes(_)) || !matches!(ext, Extern::None) } + + pub fn span(&self) -> Option { + let mut spans = smallvec::SmallVec::<[Span; 4]>::new(); + + match self.ext { + Extern::Implicit(span) | Extern::Explicit(_, span) => spans.push(span), + Extern::None => {} + } + + match self.safety { + Safety::Unsafe(span) | Safety::Safe(span) => spans.push(span), + Safety::Default => {} + }; + + if let Some(coroutine_kind) = self.coroutine_kind { + spans.push(coroutine_kind.span()); + } + + if let Const::Yes(span) = self.constness { + spans.push(span) + } + + spans.into_iter().reduce(Span::to) + } } impl Default for FnHeader { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 1276aa8f4fb96..0e47424ba1aa4 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -546,7 +546,13 @@ impl<'a> AstValidator<'a> { } /// Check that the signature of this function does not violate the constraints of its ABI. - fn check_extern_fn_signature(&self, abi: ExternAbi, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) { + fn check_extern_fn_signature( + &self, + abi: ExternAbi, + ctxt: FnCtxt, + opt_function_name: Option<&Ident>, // None for function pointers + sig: &BorrowedFnSig<'_>, + ) { match AbiMap::from_target(&self.sess.target).canonize_abi(abi, false) { AbiMapping::Direct(canon_abi) | AbiMapping::Deprecated(canon_abi) => { match canon_abi { @@ -569,13 +575,13 @@ impl<'a> AstValidator<'a> { CanonAbi::Custom => { // An `extern "custom"` function must be unsafe. - self.reject_safe_fn(abi, ctxt, sig); + self.reject_safe_fn(abi, ctxt, sig, opt_function_name.is_none()); // An `extern "custom"` function cannot be `async` and/or `gen`. self.reject_coroutine(abi, sig); // An `extern "custom"` function must have type `fn()`. - self.reject_params_or_return(abi, ident, sig); + self.reject_params_or_return(abi, opt_function_name, sig); } CanonAbi::Interrupt(interrupt_kind) => { @@ -600,7 +606,7 @@ impl<'a> AstValidator<'a> { self.reject_return(abi, sig); } else { // An `extern "interrupt"` function must have type `fn()`. - self.reject_params_or_return(abi, ident, sig); + self.reject_params_or_return(abi, opt_function_name, sig); } } } @@ -609,18 +615,27 @@ impl<'a> AstValidator<'a> { } } - fn reject_safe_fn(&self, abi: ExternAbi, ctxt: FnCtxt, sig: &FnSig) { + fn reject_safe_fn( + &self, + abi: ExternAbi, + ctxt: FnCtxt, + sig: &BorrowedFnSig<'_>, + is_fn_ptr: bool, + ) { let dcx = self.dcx(); match sig.header.safety { Safety::Unsafe(_) => { /* all good */ } Safety::Safe(safe_span) => { - let source_map = self.sess.psess.source_map(); - let safe_span = source_map.span_until_non_whitespace(safe_span.to(sig.span)); - dcx.emit_err(diagnostics::AbiCustomSafeForeignFunction { - span: sig.span, - safe_span, - }); + // Function pointers already error when `safe` is used. + if !is_fn_ptr { + let source_map = self.sess.psess.source_map(); + let safe_span = source_map.span_until_non_whitespace(safe_span.to(sig.span)); + dcx.emit_err(diagnostics::AbiCustomSafeForeignFunction { + span: sig.span, + safe_span, + }); + } } Safety::Default => match ctxt { FnCtxt::Foreign => { /* all good */ } @@ -635,7 +650,7 @@ impl<'a> AstValidator<'a> { } } - fn reject_coroutine(&self, abi: ExternAbi, sig: &FnSig) { + fn reject_coroutine(&self, abi: ExternAbi, sig: &BorrowedFnSig<'_>) { if let Some(coroutine_kind) = sig.header.coroutine_kind { let coroutine_kind_span = self .sess @@ -652,7 +667,7 @@ impl<'a> AstValidator<'a> { } } - fn reject_return(&self, abi: ExternAbi, sig: &FnSig) { + fn reject_return(&self, abi: ExternAbi, sig: &BorrowedFnSig<'_>) { if let FnRetTy::Ty(ref ret_ty) = sig.decl.output && match &ret_ty.kind { TyKind::Never => false, @@ -664,29 +679,41 @@ impl<'a> AstValidator<'a> { } } - fn reject_params_or_return(&self, abi: ExternAbi, ident: &Ident, sig: &FnSig) { + fn reject_params_or_return( + &self, + abi: ExternAbi, + opt_function_name: Option<&Ident>, // None for function pointers + sig: &BorrowedFnSig<'_>, + ) { let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect(); + + let allowed_return = |ret_ty: &Ty| match &ret_ty.kind { + TyKind::Never if abi != ExternAbi::Custom => true, + TyKind::Tup(tup) if tup.is_empty() => true, + _ => false, + }; + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output - && match &ret_ty.kind { - TyKind::Never => false, - TyKind::Tup(tup) if tup.is_empty() => false, - _ => true, - } + && !allowed_return(ret_ty) { spans.push(ret_ty.span); } if !spans.is_empty() { - let header_span = sig.header_span(); + let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo()); let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span()); let padding = if header_span.is_empty() { "" } else { " " }; self.dcx().emit_err(diagnostics::AbiMustNotHaveParametersOrReturnType { spans, - symbol: ident.name, + abi, + suggestion_span, padding, - abi, + symbol: match opt_function_name { + Some(ident) => format!(" {}", ident.name), + None => String::new(), + }, }); } } @@ -717,8 +744,12 @@ impl<'a> AstValidator<'a> { } fn check_fn_ptr_safety(&self, span: Span, safety: Safety) { - if matches!(safety, Safety::Safe(_)) { - self.dcx().emit_err(diagnostics::InvalidSafetyOnFnPtr { span }); + if let Safety::Safe(safe_span) = safety { + let remove_span = self.sess.source_map().span_until_non_whitespace(span); + self.dcx().emit_err(diagnostics::InvalidSafetyOnFnPtr { + span: safe_span, + safe_span: remove_span, + }); } } @@ -1138,6 +1169,24 @@ impl<'a> AstValidator<'a> { if let Extern::Implicit(extern_span) = bfty.ext { self.handle_missing_abi(extern_span, ty.id); } + + let ext = match bfty.ext { + Extern::None => None, + Extern::Implicit(_) => Some(ExternAbi::FALLBACK), + Extern::Explicit(str_lit, _) => { + ExternAbi::from_str(str_lit.symbol.as_str()).ok() + } + }; + + // Some ABIs impose special restrictions on the signature. + if let Some(extern_abi) = ext { + self.check_extern_fn_signature( + extern_abi, + FnCtxt::Free, + None, + &bfty.as_borrowed_fn_sig(), + ); + } } TyKind::TraitObject(bounds, ..) => { let mut any_lifetime_bounds = false; @@ -1664,8 +1713,8 @@ impl Visitor<'_> for AstValidator<'_> { self.check_extern_fn_signature( self.extern_mod_abi.unwrap_or(ExternAbi::FALLBACK), FnCtxt::Foreign, - ident, - sig, + Some(ident), + &sig.as_borrowed(), ); if let Some(attr) = attr::find_by_name(fi.attrs(), sym::track_caller) @@ -1870,7 +1919,12 @@ impl Visitor<'_> for AstValidator<'_> { if let Some((extern_abi, extern_abi_span)) = ext { // Some ABIs impose special restrictions on the signature. - self.check_extern_fn_signature(extern_abi, ctxt, &fun.ident, &fun.sig); + self.check_extern_fn_signature( + extern_abi, + ctxt, + Some(&fun.ident), + &fun.sig.as_borrowed(), + ); // #[track_caller] can only be used with the rust ABI. if let Some(attr) = attr::find_by_name(attrs, sym::track_caller) diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index 5bcffe96f6086..af22ead1332d1 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -382,6 +382,13 @@ pub(crate) struct InvalidSafetyOnItem { pub(crate) struct InvalidSafetyOnFnPtr { #[primary_span] pub span: Span, + #[suggestion( + "remove the `safe` qualifier", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] + pub safe_span: Span, } #[derive(Diagnostic)] @@ -1115,11 +1122,11 @@ pub(crate) struct AbiMustNotHaveParametersOrReturnType { #[suggestion( "remove the parameters and return type", applicability = "maybe-incorrect", - code = "{padding}fn {symbol}()", + code = "{padding}fn{symbol}()", style = "verbose" )] pub suggestion_span: Span, - pub symbol: Symbol, + pub symbol: String, pub padding: &'static str, } diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs index a2348eef975cf..69bdccb85c5cd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/traits.rs +++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs @@ -53,7 +53,7 @@ pub(crate) struct RustcParenSugarParser; impl NoArgsAttributeParser for RustcParenSugarParser { const PATH: &[Symbol] = &[sym::rustc_paren_sugar]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Trait)]); - const STABILITY: AttributeStability = unstable!(rustc_attrs); + const STABILITY: AttributeStability = unstable!(unboxed_closures); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcParenSugar; } diff --git a/compiler/rustc_codegen_ssa/src/back/mod.rs b/compiler/rustc_codegen_ssa/src/back/mod.rs index 0f5050a9d7a9a..68db2d0cbf0bb 100644 --- a/compiler/rustc_codegen_ssa/src/back/mod.rs +++ b/compiler/rustc_codegen_ssa/src/back/mod.rs @@ -15,6 +15,8 @@ mod symbol_edit; pub mod symbol_export; pub mod write; +pub use symbol_export::{exported_non_generic_symbols_helper, reachable_non_generics_helper}; + /// The target triple depends on the deployment target, and is required to /// enable features such as cross-language LTO, and for picking the right /// Mach-O commands. diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 42e2b646e4793..51413d3a67a68 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -54,6 +54,11 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap) -> DefIdMap { let is_compiler_builtins = tcx.is_compiler_builtins(LOCAL_CRATE); let mut reachable_non_generics: DefIdMap<_> = tcx @@ -176,6 +181,13 @@ fn exported_non_generic_symbols_provider_local<'tcx>( return &[]; } + exported_non_generic_symbols_helper(tcx) +} + +/// Exposed separately *without* the "should codegen" check so Miri can access it. +pub fn exported_non_generic_symbols_helper<'tcx>( + tcx: TyCtxt<'tcx>, +) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] { // FIXME: Sorting this is unnecessary since we are sorting later anyway. // Can we skip the later sorting? let sorted = tcx.with_stable_hashing_context(|mut hcx| { diff --git a/compiler/rustc_data_structures/src/svh.rs b/compiler/rustc_data_structures/src/svh.rs index 2c4e00c824d80..67594f6dae79d 100644 --- a/compiler/rustc_data_structures/src/svh.rs +++ b/compiler/rustc_data_structures/src/svh.rs @@ -11,17 +11,8 @@ use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash}; use crate::fingerprint::Fingerprint; -#[derive( - Copy, - Clone, - PartialEq, - Eq, - Debug, - Encodable_NoContext, - Decodable_NoContext, - Hash, - StableHash -)] +#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Encodable_NoContext, Decodable_NoContext, StableHash)] pub struct Svh { hash: Fingerprint, } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index db492475a3aa4..efe3657f21ea7 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -370,19 +370,8 @@ pub enum PeImportNameType { Undecorated, } -#[derive( - Copy, - Clone, - Debug, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Encodable, - Decodable, - PrintAttribute -)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Encodable, Decodable, PrintAttribute)] #[derive(StableHash)] pub enum NativeLibKind { /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 2e272fa3af9d0..28200bc3f4712 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -4769,7 +4769,7 @@ pub enum RestrictionKind<'hir> { /// explicitly to allow unsafe operations. #[derive(Copy, Clone, Debug, StableHash, PartialEq, Eq)] pub enum HeaderSafety { - /// A safe function annotated with `#[target_features]`. + /// A safe function annotated with `#[target_feature(..)]`. /// The type system treats this function as an unsafe function, /// but safety checking will check this enum to treat it as safe /// and allowing calling other safe target feature functions with diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 92f48cda10f9f..e6e0b3726552f 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -226,6 +226,7 @@ language_item_table! { IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1); UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None; + CovariantUnsafeCell, sym::covariant_unsafe_cell, covariant_unsafe_cell_type, Target::Struct, GenericRequirement::Exact(1); UnsafePinned, sym::unsafe_pinned, unsafe_pinned_type, Target::Struct, GenericRequirement::None; VaArgSafe, sym::va_arg_safe, va_arg_safe, Target::Trait, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 605b6e3751ef3..6c393ed3dfc6b 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -947,9 +947,6 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { let attrs = tcx.get_all_attrs(def_id); let paren_sugar = find_attr!(attrs, RustcParenSugar); - if paren_sugar && !tcx.features().unboxed_closures() { - tcx.dcx().emit_err(diagnostics::ParenSugarAttribute { span: item.span }); - } // Only regular traits can be marker. let is_marker = !is_alias && find_attr!(attrs, Marker); diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index a6e274ceb4bc6..0ea353cf14cee 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -862,16 +862,6 @@ pub(crate) struct EnumDiscriminantOverflowed { pub wrapped_discr: String, } -#[derive(Diagnostic)] -#[diag( - "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation" -)] -#[help("add `#![feature(unboxed_closures)]` to the crate attributes to use it")] -pub(crate) struct ParenSugarAttribute { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("use of SIMD type{$snip} in FFI is highly experimental and may result in invalid code")] #[help("add `#![feature(simd_ffi)]` to the crate attributes to enable")] diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index 6faeab4217e37..d22eb01467ebe 100644 --- a/compiler/rustc_hir_analysis/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs @@ -111,6 +111,7 @@ fn lang_items(tcx: TyCtxt<'_>) -> Vec<(LocalDefId, Vec)> { let all = [ (lang_items.phantom_data(), vec![ty::Covariant]), (lang_items.unsafe_cell_type(), vec![ty::Invariant]), + (lang_items.covariant_unsafe_cell_type(), vec![ty::Covariant]), ]; all.into_iter() // iterating over (Option, Variance) diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 6949090781cf6..767fa647612af 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -149,9 +149,8 @@ impl From for LintExpectationId { /// Setting for how to handle a lint. /// /// See: -#[derive( - Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable, StableHash -)] +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +#[derive(Encodable, Decodable, StableHash)] pub enum Level { /// The `allow` level will not issue any message. Allow, diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 391bd41be828c..f460fd42cb31c 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -217,9 +217,8 @@ pub struct DepKindVTable<'tcx> { /// some independent path or string that persists between runs without /// the need to be mapped or unmapped. (This ensures we can serialize /// them even in the absence of a tcx.) -#[derive( - Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, StableHash -)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Encodable, Decodable, StableHash)] pub struct WorkProductId { hash: Fingerprint, } diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 83670daf909fa..a16352e45564e 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -129,7 +129,8 @@ impl<'tcx> TypeError<'tcx> { } TypeError::IntrinsicCast => "cannot coerce intrinsics to function pointers".into(), TypeError::TargetFeatureCast(_) => { - "cannot coerce functions with `#[target_feature]` to safe function pointers".into() + "cannot coerce functions with `#[target_feature(..)]` to safe function pointers" + .into() } } } diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 3c7e3b63ee9b5..3614a67b71045 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -789,7 +789,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let mut sig = self.tcx().fn_sig(def_id).instantiate(self.tcx(), args).skip_norm_wip(); if self.tcx().codegen_fn_attrs(def_id).safe_target_features { - write!(self, "#[target_features] ")?; + write!(self, "#[target_feature(..)] ")?; sig = sig.map_bound(|mut sig| { sig.fn_sig_kind = sig.fn_sig_kind.set_safety(hir::Safety::Safe); sig diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 16f8f3496d9e7..49b5b7e9b1c87 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -338,18 +338,12 @@ where // We currently only consider a cycle coinductive if it steps // into a where-clause of a coinductive trait. CurrentGoalKind::CoinductiveTrait => PathKind::Coinductive, - // While normalizing via an impl does step into a where-clause of - // an impl, accessing the associated item immediately steps out of - // it again. This means cycles/recursive calls are not guarded - // by impls used for normalization. - // - // See tests/ui/traits/next-solver/cycles/normalizes-to-is-not-productive.rs - // for how this can go wrong. - CurrentGoalKind::ProjectionComputeAssocTermCandidate => PathKind::Inductive, // We probably want to make all traits coinductive in the future, // so we treat cycles involving where-clauses of not-yet coinductive // traits as ambiguous for now. - CurrentGoalKind::Misc => PathKind::Unknown, + CurrentGoalKind::Misc | CurrentGoalKind::ProjectionComputeAssocTermCandidate => { + PathKind::Unknown + } }, // Relating types is always unproductive. If we were to map proof trees to // corecursive functions as explained in #136824, relating types never diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs index 4918258350bf3..fb44782ffb039 100644 --- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs +++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs @@ -107,6 +107,7 @@ where response_no_constraints(cx, input, Certainty::overflow(true)) } + const FIXPOINT_OVERFLOW_AMBIGUITY_KIND: Certainty = Certainty::overflow(false); fn fixpoint_overflow_result( cx: I, input: CanonicalInput, @@ -126,14 +127,6 @@ where }) } - fn propagate_ambiguity( - cx: I, - for_input: CanonicalInput, - certainty: Certainty, - ) -> (QueryResult, AccessedOpaques) { - response_no_constraints(cx, for_input, certainty) - } - fn compute_goal( search_graph: &mut SearchGraph, cx: I, diff --git a/compiler/rustc_passes/src/canonical_symbols.rs b/compiler/rustc_passes/src/canonical_symbols.rs index 746758d3e36f2..c823453ef17f4 100644 --- a/compiler/rustc_passes/src/canonical_symbols.rs +++ b/compiler/rustc_passes/src/canonical_symbols.rs @@ -1,8 +1,8 @@ use rustc_hir::{CanonicalSymbols, ForeignItemId, find_attr}; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::{Instance, List, TyCtxt}; -use rustc_span::Symbol; use rustc_span::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::{Symbol, sym}; use crate::diagnostics::DuplicateCanonicalSymbolInCrate; @@ -57,12 +57,15 @@ fn canonical_symbols(tcx: TyCtxt<'_>, _: LocalCrate) -> CanonicalSymbols { // Initialize the collector. let mut canonical_symbols = CanonicalSymbols::new(); - // Collect canonical symbols in this crate. - let crate_items = tcx.hir_crate_items(()); - for id in crate_items.foreign_items() { - observe_item(tcx, &mut canonical_symbols, id); + // Optimization: can this crate even define canonical items? + // (But do not mark `rustc_attrs` as used while doing so) + if tcx.features().enabled_features().contains(&sym::rustc_attrs) { + // Collect canonical symbols in this crate. + let crate_items = tcx.hir_crate_items(()); + for id in crate_items.foreign_items() { + observe_item(tcx, &mut canonical_symbols, id); + } } - canonical_symbols } diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs index 49e46a05e5ccd..e950a26af91a3 100644 --- a/compiler/rustc_passes/src/diagnostic_items.rs +++ b/compiler/rustc_passes/src/diagnostic_items.rs @@ -13,8 +13,8 @@ use rustc_hir::diagnostic_items::DiagnosticItems; use rustc_hir::{CRATE_OWNER_ID, OwnerId, find_attr}; use rustc_middle::query::{LocalCrate, Providers}; use rustc_middle::ty::TyCtxt; -use rustc_span::Symbol; use rustc_span::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::{Symbol, sym}; use crate::diagnostics::DuplicateDiagnosticItemInCrate; @@ -53,15 +53,19 @@ fn report_duplicate_item( }); } -/// Traverse and collect the diagnostic items in the current +/// Traverse and collect the diagnostic items in the current crate fn diagnostic_items(tcx: TyCtxt<'_>, _: LocalCrate) -> DiagnosticItems { // Initialize the collector. let mut diagnostic_items = DiagnosticItems::default(); - // Collect diagnostic items in this crate. - let crate_items = tcx.hir_crate_items(()); - for id in crate_items.owners().chain(std::iter::once(CRATE_OWNER_ID)) { - observe_item(tcx, &mut diagnostic_items, id); + // Optimization: can this crate even define diagnostic items? + // (But do not mark `rustc_attrs` as used while doing so) + if tcx.features().enabled_features().contains(&sym::rustc_attrs) { + // Collect diagnostic items in this crate. + let crate_items = tcx.hir_crate_items(()); + for id in crate_items.owners().chain(std::iter::once(CRATE_OWNER_ID)) { + observe_item(tcx, &mut diagnostic_items, id); + } } diagnostic_items diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index fdae03ec49d9f..b8497aefb7767 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -444,6 +444,8 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their // `SymbolExportLevel::Rust` export level but may end up being exported in dylibs. + // Also note that Miri is relying on this to be able to find private `link_section` statics + // across all crates. || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER) || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) // Right now, the only way to get "foreign item symbol aliases" is by being an EII-implementation. diff --git a/compiler/rustc_public/src/compiler_interface.rs b/compiler/rustc_public/src/compiler_interface.rs index 89ef152e70f51..7abc07505d1fc 100644 --- a/compiler/rustc_public/src/compiler_interface.rs +++ b/compiler/rustc_public/src/compiler_interface.rs @@ -504,6 +504,14 @@ impl<'tcx> CompilerInterface<'tcx> { }) } + /// Create a caller location constant from a span. + pub(crate) fn span_as_caller_location(&self, span: Span) -> MirConst { + self.with_cx(|tables, cx| { + let sp = tables.spans[span]; + cx.span_as_caller_location(sp).stable(tables, cx) + }) + } + /// Create a new constant that represents the given string value. pub(crate) fn new_const_str(&self, value: &str) -> MirConst { self.with_cx(|tables, cx| cx.new_const_str(value).stable(tables, cx)) @@ -661,6 +669,14 @@ impl<'tcx> CompilerInterface<'tcx> { }) } + /// Check if this instance requires a caller location argument. + pub(crate) fn instance_requires_caller_location(&self, def: InstanceDef) -> bool { + self.with_cx(|tables, cx| { + let instance = tables.instances[def]; + cx.instance_requires_caller_location(instance) + }) + } + /// Check if this is an empty DropGlue shim. pub(crate) fn is_empty_drop_shim(&self, def: InstanceDef) -> bool { self.with_cx(|tables, cx| { diff --git a/compiler/rustc_public/src/mir/body.rs b/compiler/rustc_public/src/mir/body.rs index 20682de5f4825..def6c837a2b85 100644 --- a/compiler/rustc_public/src/mir/body.rs +++ b/compiler/rustc_public/src/mir/body.rs @@ -20,10 +20,10 @@ pub struct Body { /// The first local is the return value pointer, followed by `arg_count` /// locals for the function arguments, followed by any user-declared /// variables and temporaries. - pub(super) locals: LocalDecls, + pub(crate) locals: LocalDecls, /// The number of arguments this function takes. - pub(super) arg_count: usize, + pub(crate) arg_count: usize, /// Debug information pertaining to user variables, including captures. pub var_debug_info: Vec, @@ -31,19 +31,29 @@ pub struct Body { /// Mark an argument (which must be a tuple) as getting passed as its individual components. /// /// This is used for the "rust-call" ABI such as closures. - pub(super) spread_arg: Option, + pub(crate) spread_arg: Option, /// The span that covers the entire function body. pub span: Span, + + /// Source scope information, used by [`Body::caller_location`] for inline-aware resolution. + /// + /// Invariants: + /// - All scope indices referenced by terminators and statements must be within bounds. + /// - `inlined_parent_scope` links must not form cycles. + pub(crate) source_scopes: Vec, } pub type BasicBlockIdx = usize; impl Body { - /// Constructs a `Body`. + /// Constructs a `Body` without inlining information. + /// + /// # Warning /// - /// A constructor is required to build a `Body` from outside the crate - /// because the `arg_count` and `locals` fields are private. + /// This constructor does not accept source scope data today. + /// [`Body::caller_location`] will fall back to the terminator's span, + /// which may be incorrect when MIR inlining is involved. pub fn new( blocks: Vec, locals: LocalDecls, @@ -52,13 +62,15 @@ impl Body { spread_arg: Option, span: Span, ) -> Self { - // If locals doesn't contain enough entries, it can lead to panics in - // `ret_local`, `arg_locals`, and `inner_locals`. assert!( locals.len() > arg_count, "A Body must contain at least a local for the return value and each of the function's arguments" ); - Self { blocks, locals, arg_count, var_debug_info, spread_arg, span } + let source_scopes = vec![ + SourceScopeInfo { inlined: None, inlined_parent_scope: None }; + max_scope(&blocks) as usize + 1 + ]; + Self { blocks, locals, arg_count, var_debug_info, spread_arg, span, source_scopes } } /// Return local that holds this function's return value. @@ -119,6 +131,44 @@ impl Body { pub fn spread_arg(&self) -> Option { self.spread_arg } + + /// Resolve the caller location for a call to a `#[track_caller]` function. + /// + /// Use this when generating the implicit `&'static Location<'static>` argument + /// for a call where [`Instance::requires_caller_location`] is true. + /// + /// Pass `inherited_location` if this body belongs to a `#[track_caller]` function + /// (the implicit parameter it received). Pass `None` otherwise. + /// + /// This method accounts for MIR inlining: when inlined `#[track_caller]` functions + /// are present, the terminator's span may not be the correct location. The method + /// walks the inlined scopes to resolve the right one. + /// + /// [`Instance::requires_caller_location`]: crate::mir::mono::Instance::requires_caller_location + pub fn caller_location( + &self, + terminator: &Terminator, + inherited_location: Option, + ) -> MirConst { + let mut span = terminator.source_info.span; + let mut scope = terminator.source_info.scope; + + while let Some(scope_data) = self.source_scopes.get(scope as usize) { + if let Some((track_caller, callsite_span)) = &scope_data.inlined { + if !track_caller { + return span.as_caller_location(); + } + span = *callsite_span; + } + + match scope_data.inlined_parent_scope { + Some(parent) => scope = parent, + None => break, + } + } + + inherited_location.unwrap_or_else(|| span.as_caller_location()) + } } type LocalDecls = Vec; @@ -748,6 +798,22 @@ impl VarDebugInfo { pub type SourceScope = u32; +/// Data about a source scope, used for caller location resolution. +/// +/// Each entry corresponds to a source scope in the MIR body. Most scopes have no +/// inlined data. For scopes introduced by MIR inlining, `inlined` records whether +/// the inlined callee is `#[track_caller]` and the span of the call site. +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)] +pub(crate) struct SourceScopeInfo { + /// Present when this scope was introduced by inlining a function. + /// The `bool` is `true` if the inlined callee is `#[track_caller]`. + /// The `Span` is the call site where inlining occurred. + pub inlined: Option<(bool, Span)>, + /// Nearest (transitive) parent scope that was itself inlined. + /// Skips over intermediate scopes within the same inlined function body. + pub inlined_parent_scope: Option, +} + #[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub struct SourceInfo { pub span: Span, @@ -1103,3 +1169,15 @@ impl ProjectionElem { Ok(deref_ty.ty) } } + +/// Return the maximum scope index referenced by any terminator or statement in `blocks`. +fn max_scope(blocks: &[BasicBlock]) -> u32 { + blocks + .iter() + .flat_map(|bb| { + std::iter::once(bb.terminator.source_info.scope) + .chain(bb.statements.iter().map(|s| s.source_info.scope)) + }) + .max() + .unwrap_or(0) +} diff --git a/compiler/rustc_public/src/mir/mono.rs b/compiler/rustc_public/src/mir/mono.rs index b3caee5095733..b72e6ab3fa0d0 100644 --- a/compiler/rustc_public/src/mir/mono.rs +++ b/compiler/rustc_public/src/mir/mono.rs @@ -171,6 +171,18 @@ impl Instance { self.kind == InstanceKind::Shim && with(|cx| cx.is_empty_drop_shim(self.def)) } + /// Check whether this instance requires a caller location argument. + /// + /// Functions annotated with `#[track_caller]` have an implicit extra + /// `&'static core::panic::Location<'static>` argument appended to their ABI. + /// This argument is not present in the MIR body's signature. + /// + /// When this returns `true`, the instance's `fn_abi()` will have one additional + /// argument compared to the MIR body's parameter list. + pub fn requires_caller_location(&self) -> bool { + with(|cx| cx.instance_requires_caller_location(self.def)) + } + /// Try to constant evaluate the instance into a constant with the given type. /// /// This can be used to retrieve a constant that represents an intrinsic return such as diff --git a/compiler/rustc_public/src/mir/visit.rs b/compiler/rustc_public/src/mir/visit.rs index 8cb3237bc4c83..d80c769cd8169 100644 --- a/compiler/rustc_public/src/mir/visit.rs +++ b/compiler/rustc_public/src/mir/visit.rs @@ -408,7 +408,15 @@ macro_rules! super_body { }; ($self:ident, $body:ident, ) => { - let Body { blocks, locals: _, arg_count, var_debug_info, spread_arg: _, span } = $body; + let Body { + blocks, + locals: _, + arg_count, + var_debug_info, + spread_arg: _, + span, + source_scopes: _, + } = $body; for bb in blocks { $self.visit_basic_block(bb); diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index ece87c4e1c929..e9f2ff9a8bb3f 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -294,6 +294,11 @@ impl Span { pub fn diagnostic(&self) -> String { with(|c| c.span_to_string(*self)) } + + /// Create a `&'static core::panic::Location<'static>` constant from this span. + pub(crate) fn as_caller_location(&self) -> MirConst { + with(|c| c.span_as_caller_location(*self)) + } } #[derive(Clone, Copy, Debug, Serialize)] diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 7233df686cc82..0e901cf871126 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -7,7 +7,9 @@ use rustc_public_bridge::{Tables, bridge}; use crate::compiler_interface::BridgeTys; use crate::mir::alloc::GlobalAlloc; -use crate::mir::{ConstOperand, Statement, UserTypeProjection, VarDebugInfoFragment}; +use crate::mir::{ + ConstOperand, SourceScopeInfo, Statement, UserTypeProjection, VarDebugInfoFragment, +}; use crate::ty::{Allocation, ConstantKind, MirConst}; use crate::unstable::Stable; use crate::{Error, alloc, opaque}; @@ -20,8 +22,9 @@ impl<'tcx> Stable<'tcx> for mir::Body<'tcx> { tables: &mut Tables<'cx, BridgeTys>, cx: &CompilerCtxt<'cx, BridgeTys>, ) -> Self::T { - crate::mir::Body::new( - self.basic_blocks + crate::mir::Body { + blocks: self + .basic_blocks .iter() .map(|block| crate::mir::BasicBlock { terminator: block.terminator().stable(tables, cx), @@ -32,7 +35,8 @@ impl<'tcx> Stable<'tcx> for mir::Body<'tcx> { .collect(), }) .collect(), - self.local_decls + locals: self + .local_decls .iter() .map(|decl| crate::mir::LocalDecl { ty: decl.ty.stable(tables, cx), @@ -40,11 +44,25 @@ impl<'tcx> Stable<'tcx> for mir::Body<'tcx> { mutability: decl.mutability.stable(tables, cx), }) .collect(), - self.arg_count, - self.var_debug_info.iter().map(|info| info.stable(tables, cx)).collect(), - self.spread_arg.stable(tables, cx), - self.span.stable(tables, cx), - ) + arg_count: self.arg_count, + var_debug_info: self + .var_debug_info + .iter() + .map(|info| info.stable(tables, cx)) + .collect(), + spread_arg: self.spread_arg.stable(tables, cx), + span: self.span.stable(tables, cx), + source_scopes: self + .source_scopes + .iter() + .map(|scope_data| SourceScopeInfo { + inlined: scope_data.inlined.map(|(instance, span)| { + (instance.def.requires_caller_location(cx.tcx), span.stable(tables, cx)) + }), + inlined_parent_scope: scope_data.inlined_parent_scope.map(|s| s.as_u32()), + }) + .collect(), + } } } diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs index a1701db4f5e11..6b12da9d9c4ed 100644 --- a/compiler/rustc_public_bridge/src/context/impls.rs +++ b/compiler/rustc_public_bridge/src/context/impls.rs @@ -490,6 +490,16 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { ty::Const::zero_sized(self.tcx, ty_internal) } + /// Create a caller location constant from a span. + /// + /// This produces a `&'static core::panic::Location<'static>` constant, + /// which is the implicit extra argument for `#[track_caller]` functions. + pub fn span_as_caller_location(&self, span: Span) -> MirConst<'tcx> { + let val = self.tcx.span_as_caller_location(span); + let ty = self.tcx.caller_location_ty(); + MirConst::from_value(val, ty) + } + /// Create a new constant that represents the given string value. pub fn new_const_str(&self, value: &str) -> MirConst<'tcx> { let ty = Ty::new_static_str(self.tcx); @@ -653,6 +663,15 @@ impl<'tcx, B: Bridge> CompilerCtxt<'tcx, B> { matches!(instance.def, ty::InstanceKind::Shim(ty::ShimKind::DropGlue(_, None))) } + /// Check if this instance requires a caller location argument. + /// + /// Functions with `#[track_caller]` have an implicit extra + /// `&'static core::panic::Location<'static>` argument appended to their ABI, + /// which is not visible in their MIR body signature. + pub fn instance_requires_caller_location(&self, instance: ty::Instance<'tcx>) -> bool { + instance.def.requires_caller_location(self.tcx) + } + /// Convert a non-generic crate item into an instance. /// This function will panic if the item is generic. pub fn mono_instance(&self, def_id: DefId) -> Instance<'tcx> { diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 318d0b60e240d..b8cf472309ba5 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -2126,10 +2126,8 @@ impl fmt::Debug for SourceFile { /// /// When `SourceFile`s are exported in crate metadata, the `StableSourceFileId` /// is updated to incorporate the `StableCrateId` of the exporting crate. -#[derive( - Debug, Clone, Copy, Hash, PartialEq, Eq, StableHash, Encodable, Decodable, Default, PartialOrd, - Ord -)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Default, Ord)] +#[derive(StableHash, Encodable, Decodable)] pub struct StableSourceFileId(Hash128); impl StableSourceFileId { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 95421ba8cfdab..da19fa4c623bc 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -737,6 +737,7 @@ symbols! { cosf64, cosf128, count, + covariant_unsafe_cell, coverage, coverage_attribute, cr, diff --git a/compiler/rustc_target/src/asm/amdgpu.rs b/compiler/rustc_target/src/asm/amdgpu.rs index cc6f612cdb8b5..0f24ae5dea225 100644 --- a/compiler/rustc_target/src/asm/amdgpu.rs +++ b/compiler/rustc_target/src/asm/amdgpu.rs @@ -1,5 +1,6 @@ use std::fmt; +use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_span::Symbol; use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; @@ -9,19 +10,8 @@ use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; /// Amdgpu register classes /// /// The number is the size of the register class in bits. -#[derive( - Copy, - Clone, - rustc_macros::Encodable, - rustc_macros::Decodable, - Debug, - Eq, - PartialEq, - PartialOrd, - Hash, - rustc_macros::StableHash -)] -#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(StableHash, Encodable, Decodable)] pub enum AmdgpuInlineAsmRegClass { Sgpr(u16), Vgpr(u16), @@ -267,18 +257,8 @@ impl AmdgpuInlineAsmRegClass { /// Start index of a register. /// /// Together with the register size this gives the range occupied by a register. -#[derive( - Copy, - Clone, - rustc_macros::Encodable, - rustc_macros::Decodable, - Debug, - Eq, - PartialEq, - PartialOrd, - Hash, - rustc_macros::StableHash -)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(Encodable, Decodable, StableHash)] enum AmdgpuRegStart { /// Low 16-bit of the register at this index Low(u16), @@ -288,19 +268,8 @@ enum AmdgpuRegStart { Full(u16), } -#[derive( - Copy, - Clone, - rustc_macros::Encodable, - rustc_macros::Decodable, - Debug, - Eq, - PartialEq, - PartialOrd, - Hash, - rustc_macros::StableHash -)] -#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] +#[derive(Encodable, Decodable, StableHash)] pub struct AmdgpuInlineAsmReg { class: AmdgpuInlineAsmRegClass, range: AmdgpuRegStart, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index fc25849b2d602..3e07621a20d55 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -1219,7 +1219,7 @@ impl Target { /// These features are checked against the target features reported by LLVM based on /// `-Ctarget-cpu` and `-Ctarget-features`. Constraint violations result in a warning. /// - /// We also check features enabled via `#[target_features]` (and here, constraint violations + /// We also check features enabled via `#[target_feature(..)]` (and here, constraint violations /// emit a hard error), including features enabled indirectly via implications -- but if LLVM /// considers more features to be implied than we do, that could bypass this check! pub fn abi_required_features(&self) -> FeatureConstraints { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 69ef6c8d2a5c4..0af41423b846a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -773,17 +773,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let (lt1, sig1) = get_lifetimes(sig1); let (lt2, sig2) = get_lifetimes(sig2); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T let mut values = (DiagStyledString::normal("".to_string()), DiagStyledString::normal("".to_string())); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^^^^^^^^^^^^^^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^^^^^^^^^^^^^^^^ let fn_item_prefix_and_safety = |fn_def, sig: ty::FnSig<'_>| match fn_def { None => ("", sig.safety().prefix_str()), Some((did, _)) => { if self.tcx.codegen_fn_attrs(did).safe_target_features { - ("#[target_features] ", "") + ("#[target_feature(..)] ", "") } else { ("", sig.safety().prefix_str()) } @@ -794,19 +794,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { values.0.push(prefix1, prefix1 != prefix2); values.1.push(prefix2, prefix1 != prefix2); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^^^^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^^^ let lifetime_diff = lt1 != lt2; values.0.push(lt1, lifetime_diff); values.1.push(lt2, lifetime_diff); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^ values.0.push(safety1, safety1 != safety2); values.1.push(safety2, safety1 != safety2); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^^^^^^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^^^^^ if sig1.abi() != ExternAbi::Rust { values.0.push(format!("extern {} ", sig1.abi()), sig1.abi() != sig2.abi()); } @@ -814,13 +814,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { values.1.push(format!("extern {} ", sig2.abi()), sig1.abi() != sig2.abi()); } - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^ values.0.push_normal("fn("); values.1.push_normal("fn("); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^ let len1 = sig1.inputs().len(); let len2 = sig2.inputs().len(); let splatted_arg_index1 = sig1.splatted().map(usize::from); @@ -868,13 +868,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { values.1.push("...", !sig1.c_variadic()); } - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^ values.0.push_normal(")"); values.1.push_normal(")"); - // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T - // ^^^^^^^^ + // #[target_feature(..)] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^^^ let output1 = sig1.output(); let output2 = sig2.output(); let (x1, x2) = self.cmp(output1, output2); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index f93d28c29cdfa..7fcc53c2b4348 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -621,9 +621,9 @@ impl Trait for X { TypeError::TargetFeatureCast(def_id) => { let target_spans = find_attr!(tcx, def_id, TargetFeature{attr_span: span, was_forced: false, ..} => *span); diag.note( - "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers" + "functions with `#[target_feature(..)]` can only be coerced to `unsafe` function pointers" ); - diag.span_labels(target_spans, "`#[target_feature]` added here"); + diag.span_labels(target_spans, "`#[target_feature(..)]` added here"); } _ => {} } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index b98c3fab5bcb5..8af061168a865 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -555,7 +555,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; if is_fn_trait && is_target_feature_fn { err.note( - "`#[target_feature]` functions do not implement the `Fn` traits", + "`#[target_feature(..)]` functions do not implement the `Fn` traits", ); err.note( "try casting the function to a `fn` pointer or wrapping it in a closure", diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index bfa1c982bd0b7..fd02f017b2950 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -680,7 +680,7 @@ impl CollectAndApply for Result { impl search_graph::Cx for I { type Input = CanonicalInput; type Result = (QueryResult, AccessedOpaques); - type AmbiguityInfo = Certainty; + type AmbiguityKind = Certainty; type DepNodeIndex = I::DepNodeIndex; type Tracked = I::Tracked; diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 711e7ae1c8945..b1635e7e4097c 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -40,7 +40,7 @@ pub use global_cache::GlobalCache; pub trait Cx: Copy { type Input: Debug + Eq + Hash + Copy; type Result: Debug + Eq + Hash + Copy; - type AmbiguityInfo: Debug + Eq + Hash + Copy; + type AmbiguityKind: Debug + Eq + Hash + Copy; type DepNodeIndex; type Tracked: Debug; @@ -92,6 +92,8 @@ pub trait Delegate: Sized { cx: Self::Cx, input: ::Input, ) -> ::Result; + + const FIXPOINT_OVERFLOW_AMBIGUITY_KIND: ::AmbiguityKind; fn fixpoint_overflow_result( cx: Self::Cx, input: ::Input, @@ -99,12 +101,7 @@ pub trait Delegate: Sized { fn is_ambiguous_result( result: ::Result, - ) -> Option<::AmbiguityInfo>; - fn propagate_ambiguity( - cx: Self::Cx, - for_input: ::Input, - ambiguity_info: ::AmbiguityInfo, - ) -> ::Result; + ) -> Option<::AmbiguityKind>; fn compute_goal( search_graph: &mut SearchGraph, @@ -955,8 +952,7 @@ impl, X: Cx> SearchGraph { #[derive_where(Debug; X: Cx)] enum RebaseReason { NoCycleUsages, - Ambiguity(X::AmbiguityInfo), - Overflow, + Ambiguity(X::AmbiguityKind), /// We've actually reached a fixpoint. /// /// This either happens in the first evaluation step for the cycle head. @@ -987,10 +983,9 @@ impl, X: Cx> SearchGraph { /// cache entries to also be ambiguous. This causes some undesirable ambiguity for nested /// goals whose result doesn't actually depend on this cycle head, but that's acceptable /// to me. - #[instrument(level = "trace", skip(self, cx))] + #[instrument(level = "trace", skip(self))] fn rebase_provisional_cache_entries( &mut self, - cx: X, stack_entry: &StackEntry, rebase_reason: RebaseReason, ) { @@ -1065,18 +1060,22 @@ impl, X: Cx> SearchGraph { } // The provisional cache entry does depend on the provisional result - // of the popped cycle head. We need to mutate the result of our - // provisional cache entry in case we did not reach a fixpoint. + // of the popped cycle head. In case we didn't actually reach a fixpoint, + // we must not keep potentially incorrect provisional cache entries around. match rebase_reason { // If the cycle head does not actually depend on itself, then // the provisional result used by the provisional cache entry // is not actually equal to the final provisional result. We // need to discard the provisional cache entry in this case. RebaseReason::NoCycleUsages => return false, - RebaseReason::Ambiguity(info) => { - *result = D::propagate_ambiguity(cx, input, info); + // If we avoid rerunning a goal due to ambiguity, we only keep provisional + // results which depend on that cycle head if these are already ambiguous + // themselves. + RebaseReason::Ambiguity(kind) => { + if !D::is_ambiguous_result(*result).is_some_and(|k| k == kind) { + return false; + } } - RebaseReason::Overflow => *result = D::fixpoint_overflow_result(cx, input), RebaseReason::ReachedFixpoint(None) => {} RebaseReason::ReachedFixpoint(Some(path_kind)) => { if !popped_head.usages.is_single(path_kind) { @@ -1380,17 +1379,12 @@ impl, X: Cx> SearchGraph { // final result is equal to the initial response for that case. if let Ok(fixpoint) = self.reached_fixpoint(&stack_entry, usages, result) { self.rebase_provisional_cache_entries( - cx, &stack_entry, RebaseReason::ReachedFixpoint(fixpoint), ); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } else if usages.is_empty() { - self.rebase_provisional_cache_entries( - cx, - &stack_entry, - RebaseReason::NoCycleUsages, - ); + self.rebase_provisional_cache_entries(&stack_entry, RebaseReason::NoCycleUsages); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } @@ -1399,19 +1393,15 @@ impl, X: Cx> SearchGraph { // response in the next iteration in this case. These changes would // likely either be caused by incompleteness or can change the maybe // cause from ambiguity to overflow. Returning ambiguity always - // preserves soundness and completeness even if the goal is be known - // to succeed or fail. + // preserves soundness and completeness even if the goal could + // otherwise succeed or fail. // // This prevents exponential blowup affecting multiple major crates. // As we only get to this branch if we haven't yet reached a fixpoint, // we also taint all provisional cache entries which depend on the // current goal. - if let Some(info) = D::is_ambiguous_result(result) { - self.rebase_provisional_cache_entries( - cx, - &stack_entry, - RebaseReason::Ambiguity(info), - ); + if let Some(kind) = D::is_ambiguous_result(result) { + self.rebase_provisional_cache_entries(&stack_entry, RebaseReason::Ambiguity(kind)); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); }; @@ -1421,7 +1411,10 @@ impl, X: Cx> SearchGraph { if i >= D::FIXPOINT_STEP_LIMIT { debug!("canonical cycle overflow"); let result = D::fixpoint_overflow_result(cx, input); - self.rebase_provisional_cache_entries(cx, &stack_entry, RebaseReason::Overflow); + self.rebase_provisional_cache_entries( + &stack_entry, + RebaseReason::Ambiguity(D::FIXPOINT_OVERFLOW_AMBIGUITY_KIND), + ); return EvaluationResult::finalize(stack_entry, encountered_overflow, result); } diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 8560a644f207b..49bf941984af5 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -64,6 +64,28 @@ pub struct Global; /// of the allocator registered with the `#[global_allocator]` attribute /// if there is one, or the `std` crate’s default. /// +/// Note, however, that invoking this function is *not* equivalent to invoking the underlying +/// [`GlobalAlloc::alloc`] method of the registered allocator directly. Users of this function +/// cannot assume anything about what the allocator does, other than the documented requirements. +/// This means: +/// +/// - This function may non-deterministically entirely skip the underlying allocator, e.g. if the +/// compiler can show that this allocation can be replaced by a stack variable. The compiler may +/// also merge multiple allocation operations into one, as long as it can also adjust all +/// corresponding deallocation operations accordingly. +/// - An allocation created by invoking this function has exactly the size and minimum alignment +/// defined by `layout`, even if the underlying allocator makes stronger promises. +/// - The allocation can only be freed by invoking [`dealloc`] or [`realloc`]. In particular, +/// passing a pointer to such an allocation directly to the underlying method on [`GlobalAlloc`] is +/// not permitted. Until one of those functions is called, it is undefined behavior to access the +/// memory that backs this allocation with any pointer not derived from the return value of this +/// function (e.g., with internal pointers the allocator might keep around). +/// - This function de-initializes the contents of the allocation before handing it to the user. So even +/// if you control the underlying allocator and know that it explicitly initialized this memory, +/// you cannot rely on it being initialized. +/// +/// Users of this function have to consider that in the future, allocators may be allowed to unwind. +/// /// This function is expected to be deprecated in favor of the `allocate` method /// of the [`Global`] type when it and the [`Allocator`] trait become stable. /// @@ -109,6 +131,24 @@ pub unsafe fn alloc(layout: Layout) -> *mut u8 { /// of the allocator registered with the `#[global_allocator]` attribute /// if there is one, or the `std` crate’s default. /// +/// Note, however, that invoking this function is *not* equivalent to invoking the underlying +/// [`GlobalAlloc::dealloc`] method of the registered allocator directly. Users of this function +/// cannot assume anything about what the allocator does, other than the documented requirements. +/// This means: +/// +/// - This function may non-deterministically entirely skip the underlying allocator, e.g. if the +/// compiler can show that this allocation can be replaced by a stack variable. The compiler may +/// also merge multiple allocation operations into one, as long as it can also adjust all +/// corresponding deallocation operations accordingly. +/// - The pointer passed to this function must have been obtained by invoking [`alloc`], +/// [`alloc_zeroed`], or [`realloc`]. In particular, passing a pointer returned by the underlying +/// methods on [`GlobalAlloc`] is not permitted. +/// - This function de-initializes the contents of the allocation before handing it to the allocator. +/// So even if you know that the program previously initialized that memory, the allocator cannot +/// rely on it being initialized. +/// +/// Users of this function have to consider that in the future, allocators may be allowed to unwind. +/// /// This function is expected to be deprecated in favor of the `deallocate` method /// of the [`Global`] type when it and the [`Allocator`] trait become stable. /// @@ -135,6 +175,32 @@ unsafe fn dealloc_nonnull(ptr: NonNull, layout: Layout) { /// of the allocator registered with the `#[global_allocator]` attribute /// if there is one, or the `std` crate’s default. /// +/// Note, however, that invoking this function is *not* equivalent to invoking the underlying +/// [`GlobalAlloc::realloc`] method of the registered allocator directly. Users of this function +/// cannot assume anything about what the allocator does, other than the documented requirements. +/// This means: +/// +/// - This function may non-deterministically entirely skip the underlying allocator, e.g. if the +/// compiler can show that this allocation can be replaced by a stack variable. The compiler may +/// also merge multiple allocation operations into one, as long as it can also adjust all +/// corresponding deallocation operations accordingly. +/// - The pointer passed to this function must have been obtained by invoking [`alloc`], +/// [`alloc_zeroed`], or [`realloc`]. In particular, passing a pointer returned by the underlying +/// methods on [`GlobalAlloc`] is not permitted. +/// - An allocation created by invoking this function has exactly the size and minimum alignment +/// defined by `layout`, even if the underlying allocator makes stronger promises. +/// - The allocation can only be freed by invoking [`dealloc`] or [`realloc`]. In particular, +/// passing a pointer to such an allocation directly to the underlying method on [`GlobalAlloc`] is +/// not permitted. Until one of those functions is called, it is undefined behavior to access the +/// memory that backs this allocation with any pointer not derived from the return value of this +/// function (e.g., with internal pointers the allocator might keep around). +/// - If this grows the allocation, the contents of the grown part of the new allocation allocation +/// are de-initialized by this function before returning. +/// - If this shrinks the allocation, the contents of the removed part of the old allocation are +/// de-initialized by this function before invoking the underlying allocator. +/// +/// Users of this function have to consider that in the future, allocators may be allowed to unwind. +/// /// This function is expected to be deprecated in favor of the `grow` and `shrink` methods /// of the [`Global`] type when it and the [`Allocator`] trait become stable. /// @@ -162,6 +228,25 @@ unsafe fn realloc_nonnull(ptr: NonNull, layout: Layout, new_size: usize) -> /// of the allocator registered with the `#[global_allocator]` attribute /// if there is one, or the `std` crate’s default. /// +/// Note, however, that invoking this function is *not* equivalent to invoking the underlying +/// [`GlobalAlloc::alloc_zeroed`] method of the registered allocator directly. Users of this +/// function cannot assume anything about what the allocator does, other than the documented +/// requirements. This means: +/// +/// - This function may non-deterministically entirely skip the underlying allocator, e.g. if the +/// compiler can show that this allocation can be replaced by a stack variable. The compiler may +/// also merge multiple allocation operations into one, as long as it can also adjust all +/// corresponding deallocation operations accordingly. +/// - The allocation can only be freed by invoking [`dealloc`] or [`realloc`]. In particular, +/// passing a pointer to such an allocation directly to the underlying method on [`GlobalAlloc`] is +/// not permitted. Until one of those functions is called, it is undefined behavior to access the +/// memory that backs this allocation with any pointer not derived from the return value of this +/// function (e.g., with internal pointers the allocator might keep around). +/// - An allocation created by invoking this function has exactly the size and minimum alignment +/// defined by `layout`, even if the underlying allocator makes stronger promises. +/// +/// Users of this function have to consider that in the future, allocators may be allowed to unwind. +/// /// This function is expected to be deprecated in favor of the `allocate_zeroed` method /// of the [`Global`] type when it and the [`Allocator`] trait become stable. /// diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 58996703023ce..5190baaab8a26 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1368,7 +1368,7 @@ impl Box { /// /// unsafe { /// let non_null = NonNull::new(alloc(Layout::new::()).cast::()) - /// .expect("allocation failed"); + /// .expect("alloc should have successfully allocated memory"); /// // In general .write is required to avoid attempting to destruct /// // the (uninitialized) previous contents of `non_null`. /// non_null.write(5); diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index e6e6fcf5420f3..b340cf9566f2e 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -84,7 +84,7 @@ use crate::vec::Vec; /// /// // We are certain that our string doesn't have 0 bytes in the middle, /// // so we can .expect() -/// let c_to_print = CString::new("Hello, world!").expect("CString::new failed"); +/// let c_to_print = CString::new("Hello, world!").expect("we provided a string without NUL bytes, so CString::new should not fail"); /// unsafe { /// my_printer(c_to_print.as_ptr()); /// } @@ -242,7 +242,7 @@ impl CString { /// /// extern "C" { fn puts(s: *const c_char); } /// - /// let to_print = CString::new("Hello!").expect("CString::new failed"); + /// let to_print = CString::new("Hello!").expect("we provided a string without NUL bytes, so CString::new should not fail"); /// unsafe { /// puts(to_print.as_ptr()); /// } @@ -466,12 +466,12 @@ impl CString { /// use std::ffi::CString; /// /// let valid_utf8 = vec![b'f', b'o', b'o']; - /// let cstring = CString::new(valid_utf8).expect("CString::new failed"); - /// assert_eq!(cstring.into_string().expect("into_string() call failed"), "foo"); + /// let cstring = CString::new(valid_utf8).expect("we provided bytes that do not have a NUL byte, so CString::new should not fail"); + /// assert_eq!(cstring.into_string().expect("we provided bytes that are valid UTF-8, so `into_string` should not fail"), "foo"); /// /// let invalid_utf8 = vec![b'f', 0xff, b'o', b'o']; - /// let cstring = CString::new(invalid_utf8).expect("CString::new failed"); - /// let err = cstring.into_string().err().expect("into_string().err() failed"); + /// let cstring = CString::new(invalid_utf8).expect("we provided bytes that do not have a NUL byte, so CString::new should not fail"); + /// let err = cstring.into_string().expect_err("we provided bytes that are invalid UTF-8, so `into_string` should fail"); /// assert_eq!(err.utf8_error().valid_up_to(), 1); /// ``` #[stable(feature = "cstring_into", since = "1.7.0")] @@ -577,7 +577,7 @@ impl CString { /// let c_string = CString::from(c"foo"); /// let cstr = c_string.as_c_str(); /// assert_eq!(cstr, - /// CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed")); + /// CStr::from_bytes_with_nul(b"foo\0").expect("we provided bytes that has one NUL byte exactly at the end, so CStr::from_bytes_with_nul should not fail")); /// ``` #[inline] #[must_use] @@ -660,7 +660,7 @@ impl CString { /// use std::ffi::CString; /// assert_eq!( /// CString::from_vec_with_nul(b"abc\0".to_vec()) - /// .expect("CString::from_vec_with_nul failed"), + /// .expect("we provided bytes that has one NUL byte exactly at the end, so CString::from_vec_with_nul should not fail"), /// c"abc".to_owned() /// ); /// ``` diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 6c19ba816050d..cc321660e6ea4 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -1314,7 +1314,7 @@ impl String { /// /// Ok(output) /// } - /// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?"); + /// # process_data("rust").expect("reserving capacity for 12 bytes should never fail"); /// ``` #[stable(feature = "try_reserve", since = "1.57.0")] pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { @@ -1355,7 +1355,7 @@ impl String { /// /// Ok(output) /// } - /// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?"); + /// # process_data("rust").expect("reserving capacity for 12 bytes should never fail"); /// ``` #[stable(feature = "try_reserve", since = "1.57.0")] pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index 44a8ab6e54196..0036b7a55c8ce 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -19,7 +19,6 @@ use crate::{cmp, ptr}; /// method such as `dealloc` or by being /// passed to a reallocation method that returns a non-null pointer. /// -/// /// # Example /// /// ```standalone_crate @@ -85,39 +84,83 @@ use crate::{cmp, ptr}; /// } /// ``` /// +/// # The `#[global_allocator]` attribute +/// +/// As the example above demonstrates, the `#[global_allocator]` attribute can be used to register a +/// concrete `static` of a type that implements this trait to become *the* global allocator +/// for the current program. That global allocator can be invoked via the functions [`alloc`], +/// [`alloc_zeroed`], [`dealloc`], [`realloc`]). Note, however, that invoking those functions is +/// *not* equivalent to directly invoking the underlying methods on the declared global allocator! +/// Users of the global allocator cannot assume anything about what the allocator does (even if they know which allocator is being used), +/// and implementors of the allocator cannot assume anything about what the program does (even if they know how the allocator is being used). +/// Both can only assume the documented requirements for the respective other party of this contract. +/// This means: +/// +/// - Allocation functions may non-deterministically entirely skip the underlying allocator, e.g. if the +/// compiler can show that this allocation can be replaced by a stack variable. The compiler may +/// also merge multiple allocation operations into one, as long as it can also adjust all +/// corresponding deallocation operations accordingly. +/// - An allocation created by invoking [`alloc`], [`alloc_zeroed`], or [`realloc`] has exactly the +/// size and minimum alignment defined by `layout`, even if the underlying allocator makes +/// stronger promises. +/// - An allocation created by invoking [`alloc`], [`alloc_zeroed`], or [`realloc`] can only be +/// freed by invoking [`dealloc`] or [`realloc`]. In particular, passing a pointer to such an +/// allocation directly to the underlying method on [`GlobalAlloc`] is not permitted. Until one of +/// those functions is called, it is undefined behavior to access the memory that backs this +/// allocation with any pointer not derived from the return value of this function (e.g., with +/// internal pointers the allocator might keep around). +/// - The pointer passed to [`dealloc`] or [`realloc`] must have been obtained by invoking [`alloc`], +/// [`alloc_zeroed`], or [`realloc`]. In particular, passing a pointer returned by the underlying +/// methods on [`GlobalAlloc`] is not permitted. +/// - [`alloc`] de-initializes the contents of the allocation before handing it to the user. So even +/// if you control the underlying allocator and know that it explicitly initialized this memory, +/// you cannot rely on it being initialized. For a [`realloc`] that grows an allocation, this +/// applies to the newly allocated part. +/// - [`dealloc`] de-initializes the contents of the allocation before handing it to the allocator. +/// So even if you know that the program previously initialized that memory, the allocator cannot +/// rely on it being initialized. For a [`realloc`] that shrinks an allocation, this applies to +/// the part being removed. +/// +/// [`alloc`]: ../../std/alloc/fn.alloc.html +/// [`alloc_zeroed`]: ../../std/alloc/fn.alloc_zeroed.html +/// [`dealloc`]: ../../std/alloc/fn.dealloc.html +/// [`realloc`]: ../../std/alloc/fn.realloc.html +/// +/// The first point means that you cannot rely on global allocations actually happening, even if +/// there are explicit global allocations in the source. The optimizer may detect unused global +/// allocations that it can either eliminate entirely or move to the stack and thus never invoke the +/// global allocator. The optimizer may further assume that allocation is infallible, so code that +/// used to fail due to allocator failures may now suddenly work because the optimizer worked around +/// the need for an allocation. More concretely, the following code example is unsound, irrespective +/// of whether your custom allocator allows counting how many allocations have happened. +/// +/// ```rust,ignore (unsound and has placeholders) +/// drop(Box::new(42)); +/// let number_of_heap_allocs = /* call private allocator API */; +/// unsafe { std::hint::assert_unchecked(number_of_heap_allocs > 0); } +/// ``` +/// +/// Note that the optimizations mentioned above are not the only +/// optimization that can be applied. You may generally not rely on global allocations +/// happening if they can be removed without changing program behavior. +/// Whether allocations happen or not is not part of the program behavior, even if it +/// could be detected via an allocator that tracks allocations by printing or otherwise +/// having side effects. +/// /// # Safety /// /// The `GlobalAlloc` trait is an `unsafe` trait for a number of reasons, and /// implementors must ensure that they adhere to these contracts: /// +/// * It is undefined behavior for the allocator to read, write, or deallocate any memory that +/// is *currently allocated*. This memory is owned by the user, the allocator must not touch it. +/// /// * It's undefined behavior if global allocators unwind. This restriction may /// be lifted in the future, but currently a panic from any of these /// functions may lead to memory unsafety. /// -/// * `Layout` queries and calculations in general must be correct. Callers of -/// this trait are allowed to rely on the contracts defined on each method, -/// and implementors must ensure such contracts remain true. -/// -/// * You must not rely on allocations actually happening, even if there are explicit -/// heap allocations in the source. The optimizer may detect unused allocations that it can either -/// eliminate entirely or move to the stack and thus never invoke the allocator. The -/// optimizer may further assume that allocation is infallible, so code that used to fail due -/// to allocator failures may now suddenly work because the optimizer worked around the -/// need for an allocation. More concretely, the following code example is unsound, irrespective -/// of whether your custom allocator allows counting how many allocations have happened. -/// -/// ```rust,ignore (unsound and has placeholders) -/// drop(Box::new(42)); -/// let number_of_heap_allocs = /* call private allocator API */; -/// unsafe { std::hint::assert_unchecked(number_of_heap_allocs > 0); } -/// ``` -/// -/// Note that the optimizations mentioned above are not the only -/// optimization that can be applied. You may generally not rely on heap allocations -/// happening if they can be removed without changing program behavior. -/// Whether allocations happen or not is not part of the program behavior, even if it -/// could be detected via an allocator that tracks allocations by printing or otherwise -/// having side effects. +/// * Callers of this trait are allowed to rely on the contracts defined on each method, and +/// implementors must ensure such contracts remain true. /// /// # Re-entrance /// @@ -133,7 +176,7 @@ use crate::{cmp, ptr}; /// - [`std::thread_local`], /// - [`std::thread::current`], /// - [`std::thread::park`] and [`std::thread::Thread`]'s [`unpark`] method and -/// [`Clone`] implementation. +/// [`Clone`] implementation. /// /// [`std`]: ../../std/index.html /// [`std::sync::Mutex`]: ../../std/sync/struct.Mutex.html @@ -174,7 +217,7 @@ pub unsafe trait GlobalAlloc { /// /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`handle_alloc_error`] function, - /// rather than directly invoking `panic!` or similar. + /// rather than directly invoking `panic!` or similar (but note that both may unwind). /// /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html #[stable(feature = "global_alloc", since = "1.28.0")] diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 1b8ec2a91478a..1145a085931f4 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -259,9 +259,12 @@ use crate::pin::PinCoerceUnsized; use crate::ptr::{self, NonNull}; use crate::range; +mod covariant_unsafe_cell; mod lazy; mod once; +#[unstable(feature = "covariant_unsafe_cell", issue = "159735")] +pub use covariant_unsafe_cell::CovariantUnsafeCell; #[stable(feature = "lazy_cell", since = "1.80.0")] pub use lazy::LazyCell; #[stable(feature = "once_cell", since = "1.70.0")] @@ -2150,8 +2153,9 @@ impl fmt::Display for RefMut<'_, T> { /// use `UnsafeCell` to wrap their data. /// /// Note that only the immutability guarantee for shared references is affected by `UnsafeCell`. The -/// uniqueness guarantee for mutable references is unaffected. There is *no* legal way to obtain -/// aliasing `&mut`, not even with `UnsafeCell`. +/// uniqueness guarantee for mutable references is unaffected. As explained below, for the duration +/// of the lifetime of an `&mut`, no other reference may exist and no pointer may be used to access +/// that memory; this applies even with `UnsafeCell`. /// /// `UnsafeCell` does nothing to avoid data races; they are still undefined behavior. If multiple /// threads have access to the same `UnsafeCell`, they must follow the usual rules of the @@ -2171,11 +2175,13 @@ impl fmt::Display for RefMut<'_, T> { /// /// - If you create a safe reference with lifetime `'a` (either a `&T` or `&mut T` reference), then /// you must not access the data in any way that contradicts that reference for the remainder of -/// `'a`. For example, this means that if you take the `*mut T` from an `UnsafeCell` and cast it -/// to a `&T`, then the data in `T` must remain immutable (modulo any `UnsafeCell` data found -/// within `T`, of course) until that reference's lifetime expires. Similarly, if you create a -/// `&mut T` reference, then you must not access the data within the -/// `UnsafeCell` until that reference expires. +/// `'a`, and you must not create any contradicting references. For example, this means that if +/// you take the `*mut T` from an `UnsafeCell` and cast it to a `&T`, then the data in `T` must +/// remain immutable (modulo any `UnsafeCell` data found within `T`, of course) until that +/// reference's lifetime expires, and no `&mut` reference to this data may be created. Similarly, +/// if you create a `&mut T` reference, then you must not access the data within the `UnsafeCell` +/// with any other pointer/reference until that reference expires, and no reference of any kind +/// may be created. /// /// - For both `&T` without `UnsafeCell<_>` and `&mut T`, you must also not deallocate the data /// until the reference expires. As a special exception, given a `&T`, any part of it that is @@ -2200,7 +2206,7 @@ impl fmt::Display for RefMut<'_, T> { /// Note that whilst mutating the contents of a `&UnsafeCell` (even while other /// `&UnsafeCell` references alias the cell) is /// ok (provided you enforce the above invariants some other way), it is still undefined behavior -/// to have multiple `&mut UnsafeCell` aliases. That is, `UnsafeCell` is a wrapper +/// to have aliasing `&mut UnsafeCell` (or aliasing `&mut` of *any* type). That is, `UnsafeCell` is a wrapper /// designed to have a special interaction with _shared_ accesses (_i.e._, through an /// `&UnsafeCell<_>` reference); there is no magic whatsoever when dealing with _exclusive_ /// accesses (_e.g._, through a `&mut UnsafeCell<_>`): neither the cell nor the wrapped value @@ -2223,36 +2229,20 @@ impl fmt::Display for RefMut<'_, T> { /// order to avoid its interior mutability property from spreading from `T` into the `Outer` type, /// thus this can cause distortions in the type size in these cases. /// -/// Note that the only valid way to obtain a `*mut T` pointer to the contents of a -/// _shared_ `UnsafeCell` is through [`.get()`] or [`.raw_get()`]. A `&T` or `&mut T` reference -/// can then be obtained from that pointer, as long as the aliasing rules outlined above are obeyed. -/// Even though `T` and `UnsafeCell` have the -/// same memory layout, the following is not allowed and undefined behavior: +/// The following examples make use of this guarantee: /// -/// ```rust,compile_fail +/// ```rust /// # use std::cell::UnsafeCell; -/// unsafe fn not_allowed(ptr: &UnsafeCell) -> &mut T { +/// /// # Safety +/// /// The caller must not call `get_mut_unchecked` again (on any alias of `ptr`) for the duration +/// /// of the lifetime of the returned reference. +/// # #[allow(invalid_reference_casting)] // FIXME should the lint really fire here? +/// unsafe fn get_mut_unchecked(ptr: &UnsafeCell) -> &mut T { /// let t = ptr as *const UnsafeCell as *mut T; -/// // This is undefined behavior, because the `*mut T` pointer -/// // was not obtained through `.get()` nor `.raw_get()`: /// unsafe { &mut *t } /// } /// ``` /// -/// Instead, do this: -/// -/// ```rust -/// # use std::cell::UnsafeCell; -/// // Safety: the caller must ensure that there are no references that -/// // point to the *contents* of the `UnsafeCell`. -/// unsafe fn get_mut(ptr: &UnsafeCell) -> &mut T { -/// unsafe { &mut *ptr.get() } -/// } -/// ``` -/// -/// Converting in the other direction from a `&mut T` -/// to an `&UnsafeCell` is allowed: -/// /// ```rust /// # use std::cell::UnsafeCell; /// fn get_shared(ptr: &mut T) -> &UnsafeCell { @@ -2263,7 +2253,6 @@ impl fmt::Display for RefMut<'_, T> { /// ``` /// /// [niche]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#niche -/// [`.raw_get()`]: `UnsafeCell::raw_get` /// /// # Examples /// @@ -2425,6 +2414,9 @@ impl UnsafeCell { /// must uphold the aliasing rules; see [the type-level docs][UnsafeCell#aliasing-rules] for /// more discussion and caveats. /// + /// This is equivalent to casting `self` to a raw pointer and then casting that raw + /// pointer to `*mut T`. + /// /// # Examples /// /// ``` @@ -2442,8 +2434,7 @@ impl UnsafeCell { #[rustc_should_not_be_called_on_const_items] pub const fn get(&self) -> *mut T { // We can just cast the pointer from `UnsafeCell` to `T` because of - // #[repr(transparent)]. This exploits std's special status, there is - // no guarantee for user code that this will work in future versions of the compiler! + // #[repr(transparent)]. self as *const UnsafeCell as *const T as *mut T } @@ -2477,6 +2468,8 @@ impl UnsafeCell { /// must uphold the aliasing rules; see [the type-level docs][UnsafeCell#aliasing-rules] for /// more discussion and caveats. /// + /// This is equivalent to casting `this` to `*mut T`. + /// /// [`get`]: UnsafeCell::get() /// /// # Examples diff --git a/library/core/src/cell/covariant_unsafe_cell.rs b/library/core/src/cell/covariant_unsafe_cell.rs new file mode 100644 index 0000000000000..3876cc404cd2e --- /dev/null +++ b/library/core/src/cell/covariant_unsafe_cell.rs @@ -0,0 +1,183 @@ +use crate::cell::UnsafeCell; +use crate::fmt; +use crate::ops::CoerceUnsized; +use crate::ptr::{self, NonNull}; + +/// **Co**variant version of [`UnsafeCell`]. +#[unstable(feature = "covariant_unsafe_cell", issue = "159735")] +#[repr(transparent)] +#[rustc_pub_transparent] +// Implementation note: +// +// We could make `CovariantUnsafeCell` be the canonical lang item and make `UnsafeCell` a wrapper +// over it, with `PhantomData<*mut T>`. That would however be a huge compiler change, without clear +// benefit. +// +// As such, `CovariantUnsafeCell` is wrapping `UnsafeCell` instead. It is a lang-item only to +// hardcode its variance to be **co**variant in `T`, even though it is wrapping `UnsafeCell` which +// is **in**variant in `T`. +#[lang = "covariant_unsafe_cell"] +pub struct CovariantUnsafeCell(UnsafeCell); + +#[unstable(feature = "covariant_unsafe_cell", issue = "159735")] +impl !Sync for CovariantUnsafeCell {} + +impl CovariantUnsafeCell { + /// Constructs a new instance of `CovariantUnsafeCell` which will wrap the specified value. + /// + /// All access to the inner value through `&CovariantUnsafeCell` requires `unsafe` code. + /// + /// # Examples + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// + /// let uc = CovariantUnsafeCell::new(5); + /// ``` + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[inline(always)] + pub const fn new(value: T) -> CovariantUnsafeCell { + CovariantUnsafeCell(UnsafeCell::new(value)) + } + + /// Unwraps the value, consuming the cell. + /// + /// # Examples + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// + /// let uc = CovariantUnsafeCell::new(5); + /// + /// let five = uc.into_inner(); + /// ``` + #[inline(always)] + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + pub const fn into_inner(self) -> T { + self.0.into_inner() + } +} + +impl CovariantUnsafeCell { + /// Gets a mutable non-null pointer to the wrapped value. + /// + /// This can be cast to a pointer of any kind. When creating (shared or mutable) references, you + /// must uphold the aliasing rules; see [the `UnsafeCell` type-level docs] for more discussion + /// and caveats. + /// + /// [the `UnsafeCell` type-level docs]: super::UnsafeCell#aliasing-rules + /// + /// # Examples + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// use std::ptr::NonNull; + /// + /// let uc = CovariantUnsafeCell::new(5); + /// + /// let ptr: NonNull = uc.get(); + /// ``` + #[inline(always)] + #[rustc_as_ptr] + #[rustc_should_not_be_called_on_const_items] + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + pub const fn get(&self) -> NonNull { + // We can just cast the pointer from `CovariantUnsafeCell` to `T` because of + // #[repr(transparent)]. + // + // Note that this is also known to be allowed for user code as per + // `#[rustc_pub_transparent]`. + // SAFETY: the pointer is not null, as it comes from a reference + unsafe { NonNull::new_unchecked(ptr::from_ref(self).cast_mut() as *mut T) } + } + + /// Returns a mutable reference to the underlying data. + /// + /// This call borrows the `CovariantUnsafeCell` mutably (at compile-time) which guarantees that + /// we possess the only reference. + /// + /// # Examples + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// + /// let mut c = CovariantUnsafeCell::new(5); + /// *c.get_mut() += 1; + /// + /// assert_eq!(*c.get_mut(), 6); + /// ``` + #[inline(always)] + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + pub const fn get_mut(&mut self) -> &mut T { + self.0.get_mut() + } + + /// Gets a mutable pointer to the wrapped value. + /// The difference from [`get`] is that this function accepts a raw pointer, + /// which is useful to avoid the creation of temporary references. + /// + /// This can be cast to a pointer of any kind. When creating (shared or mutable) references, you + /// must uphold the aliasing rules; see [the `UnsafeCell` type-level docs] for more discussion + /// and caveats. + /// + /// [`get`]: CovariantUnsafeCell::get() + /// + /// # Examples + /// + /// Gradual initialization of an `CovariantUnsafeCell` requires `raw_get`, as + /// calling `get` would require creating a reference to uninitialized data: + /// + /// ``` + /// #![feature(covariant_unsafe_cell)] + /// use std::cell::CovariantUnsafeCell; + /// use std::mem::MaybeUninit; + /// + /// let m = MaybeUninit::>::uninit(); + /// unsafe { CovariantUnsafeCell::raw_get(m.as_ptr()).write(5); } + /// // avoid below which references to uninitialized data + /// // unsafe { CovariantUnsafeCell::get(&*m.as_ptr()).write(5); } + /// let uc = unsafe { m.assume_init() }; + /// + /// assert_eq!(uc.into_inner(), 5); + /// ``` + #[inline(always)] + #[unstable(feature = "covariant_unsafe_cell", issue = "159735")] + #[rustc_const_unstable(feature = "covariant_unsafe_cell", issue = "159735")] + pub const fn raw_get(this: *const Self) -> *mut T { + // We can just cast the pointer from `UnsafeCell` to `T` because of + // #[repr(transparent)]. + // + // Note that this is also known to be allowed for user code as per + // `#[rustc_pub_transparent]`. + this as *const T as *mut T + } +} + +#[unstable(feature = "coerce_unsized", issue = "18598")] +impl, U> CoerceUnsized> for CovariantUnsafeCell {} + +#[unstable(feature = "covariant_unsafe_cell", issue = "159735")] +impl fmt::Debug for CovariantUnsafeCell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("CovariantUnsafeCell").finish_non_exhaustive() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn _covarience<'short, 'long: 'short>( + x: CovariantUnsafeCell<&'long ()>, + ) -> CovariantUnsafeCell<&'short ()> { + x + } +} diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 84447c06aaecf..9d39fbf770e91 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -53,6 +53,11 @@ //! The `#[global_allocator]` can only be used once in a crate //! or its recursive dependencies. //! +//! The global allocator is invoked via the functions in this module +//! ([`alloc`][crate::alloc::alloc], [`alloc_zeroed`], [`dealloc`], [`realloc`]). Note, however, +//! that invoking those functions is *not* equivalent to directly invoking the underlying methods on +//! the declared global allocator! See the documentation of those functions for details. +//! //! [^system-alloc]: Note that the Rust standard library internals may still //! directly call [`System`] when necessary (for example for the runtime //! support typically required to implement a global allocator, see [re-entrance] on [`GlobalAlloc`] diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index ee7e5f8586700..3c32ec34dee5b 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -263,6 +263,7 @@ impl PoisonError { /// or [`RwLock::read`](crate::sync::RwLock::read). /// /// This method may panic if std was built with `panic="abort"`. + #[doc(auto_cfg = false)] #[cfg(panic = "unwind")] #[stable(feature = "sync_poison", since = "1.2.0")] pub fn new(data: T) -> PoisonError { @@ -275,6 +276,7 @@ impl PoisonError { /// or [`RwLock::read`](crate::sync::RwLock::read). /// /// This method may panic if std was built with `panic="abort"`. + #[doc(auto_cfg = false)] #[cfg(not(panic = "unwind"))] #[stable(feature = "sync_poison", since = "1.2.0")] #[track_caller] diff --git a/library/std/src/sys/fs/windows/dir.rs b/library/std/src/sys/fs/windows/dir.rs index cd86f76bbcf83..5e69515b66599 100644 --- a/library/std/src/sys/fs/windows/dir.rs +++ b/library/std/src/sys/fs/windows/dir.rs @@ -6,7 +6,7 @@ use crate::os::windows::io::{ OwnedHandle, RawHandle, }; use crate::path::Path; -use crate::sys::api::{self, SetFileInformation, UnicodeStrRef, WinError}; +use crate::sys::api::{UnicodeStrRef, WinError}; use crate::sys::fs::windows::debug_path_handle; use crate::sys::fs::{File, FileAttr, OpenOptions}; use crate::sys::handle::Handle; @@ -127,16 +127,7 @@ impl Dir { let mut opts = OpenOptions::new(); opts.access_mode(c::DELETE); let handle = self.open_file_native(path, &opts, dir)?; - let info = c::FILE_DISPOSITION_INFO_EX { Flags: c::FILE_DISPOSITION_FLAG_DELETE }; - let result = unsafe { - c::SetFileInformationByHandle( - handle.as_raw_handle(), - c::FileDispositionInfoEx, - (&info).as_ptr(), - size_of::() as _, - ) - }; - if result == 0 { Err(api::get_last_error()).io_result() } else { Ok(()) } + File::from_inner(handle).delete().io_result() } fn rename_native(&self, from: &[u16], to_dir: &Self, to: &[u16], dir: bool) -> io::Result<()> { diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 44bedaf878e04..1d2ca88a2d0bd 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -839,6 +839,10 @@ impl Step for CargoMiri { SourceType::Submodule, &[], ); + // Run subcrate tests as well. + cargo.arg("--workspace"); + // Some tests need isolation disabled. + cargo.env("MIRIFLAGS", "-Zmiri-disable-isolation"); // If we are testing stage 2+ cargo miri, make sure that it works with the in-tree cargo. // We want to do this *somewhere* to ensure that Miri + nightly cargo actually works. diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 74be2409570f8..d516c89fba03c 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -532,8 +532,6 @@ impl Config { optimized_compiler_builtins: build_optimized_compiler_builtins, jobs: build_jobs, compiletest_diff_tool: build_compiletest_diff_tool, - // No longer has any effect; kept (for now) to avoid breaking people's configs. - compiletest_use_stage0_libtest: _, tidy_extra_checks: build_tidy_extra_checks, ccache: build_ccache, exclude: build_exclude, diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs index 877dde676c605..d0fdbbed4e82d 100644 --- a/src/bootstrap/src/core/config/toml/build.rs +++ b/src/bootstrap/src/core/config/toml/build.rs @@ -72,9 +72,6 @@ define_config! { jobs: Option = "jobs", compiletest_diff_tool: Option = "compiletest-diff-tool", compiletest_allow_stage0: Option = "compiletest-allow-stage0", - /// No longer has any effect; kept (for now) to avoid breaking people's configs. - /// FIXME(#146929): Remove this in 2026. - compiletest_use_stage0_libtest: Option = "compiletest-use-stage0-libtest", tidy_extra_checks: Option = "tidy-extra-checks", ccache: Option = "ccache", exclude: Option> = "exclude", diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 0035d6f3af668..df35face8b5d8 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -1324,10 +1324,10 @@ impl Build { if let Some(map_to) = self.debuginfo_map_to(which, RemapScheme::NonCompiler) { let map = format!("{}={}", self.src.display(), map_to); - let cc = self.cc(target); - if cc.ends_with("clang") || cc.ends_with("gcc") { + let cc = self.cc_tool(target); + if cc.is_like_clang() || cc.is_like_gnu() { base.push(format!("-fdebug-prefix-map={map}")); - } else if cc.ends_with("clang-cl.exe") { + } else if cc.is_like_clang_cl() { base.push("-Xclang".into()); base.push(format!("-fdebug-prefix-map={map}")); } diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 4252f2683b3d9..f5ab16c2e0a9a 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -651,4 +651,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "A new `build.sde` configuration option has been added to support intrinsic-test.", }, + ChangeInfo { + change_id: 159878, + severity: ChangeSeverity::Warning, + summary: "Obsolete option `build.compiletest-use-stage0-libtest` has no effect and has been removed.", + }, ]; diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 25a5238f538ee..1a74dff9d23d8 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -390279b302ca98ae270f434100ae3730531d1246 +da86f4d0726be475afbbffe40cb2f65741c51ad3 diff --git a/src/doc/rustc-dev-guide/src/ast-validation.md b/src/doc/rustc-dev-guide/src/ast-validation.md index 8f10bbecf21d0..45a9d77252815 100644 --- a/src/doc/rustc-dev-guide/src/ast-validation.md +++ b/src/doc/rustc-dev-guide/src/ast-validation.md @@ -1,28 +1,27 @@ # AST validation _AST validation_ is a separate AST pass that visits each -item in the tree and performs simple checks. This pass -doesn't perform any complex analysis, type checking or +item in the tree and performs simple checks. +This pass doesn't perform any complex analysis, type checking or name resolution. -Before performing any validation, the compiler first expands -the macros. Then this pass performs validations to check -that each AST item is in the correct state. And when this pass -is done, the compiler runs the crate resolution pass. +Before performing any validation, the compiler first expands the macros. +Then this pass performs validations to check that each AST item is in the correct state. +And when this pass is done, the compiler runs the crate resolution pass. ## Validations Validations are defined in `AstValidator` type, which -itself is located in `rustc_ast_passes` crate. This -type implements various simple checks which emit errors +itself is located in `rustc_ast_passes` crate. +This type implements various simple checks which emit errors when certain language rules are broken. In addition, `AstValidator` implements `Visitor` trait that defines how to visit AST items (which can be functions, traits, enums, etc). -For each item, visitor performs specific checks. For -example, when visiting a function declaration, +For each item, visitor performs specific checks. +For example, when visiting a function declaration, `AstValidator` checks that the function has: * no more than `u16::MAX` parameters; diff --git a/src/doc/rustc-dev-guide/src/attributes.md b/src/doc/rustc-dev-guide/src/attributes.md index 1056a257c2ee8..e9703d2c6e209 100644 --- a/src/doc/rustc-dev-guide/src/attributes.md +++ b/src/doc/rustc-dev-guide/src/attributes.md @@ -25,6 +25,35 @@ For more information on these attributes, see the chapter about [attribute parsi [attr-parsing-chapter]: ./hir/attribute-parsing.md +### Note on adding new builtin attributes + +
+ +**Warning: Name resolution ambiguity potential when adding new builtin attributes** + +Please note that adding **new builtin attributes** (whose name is not reserved, i.e. a new builtin +attribute whose name does not start with `rustc`), even if *unstable*-gated, can introduce breakage +from name resolution ambiguity in stable code if (1) the stable code has a macro of the same name +which gets re-exported, or (2) or a proc-macro derive helper attribute of the same name. + +Typically, the builtin attributes probably has to start out as `#[rustc_foo]` instead of `#[foo]` to +avoid colliding with user-defined macros and proc-macro helper attributes. +Then, prior to stabilization, +a rename to `#[foo]` should be done separately with a crater run to assess fallout, +with a deliberate breakage FCP proposal for T-lang to consider. + +Remember also that crater is *not* exhaustive and does not contain all existing stable code. + +See: +- [Built-in attributes are treated differently vs prelude attributes, unstable built-in attributes + can name-collide with stable macro, and built-in attributes can break back-compat + #134963](https://github.com/rust-lang/rust/issues/134963) and backlinks within this issue, + including design discussions on how to fix this kind of breakage hazard. +- [Broken build after updating: coverage is ambiguous; ambiguous because of a name conflict with a + builtin attribute #121157](https://github.com/rust-lang/rust/issues/121157). +- [Regression: align is ambiguous #143834](https://github.com/rust-lang/rust/issues/143834). +
+ ## 'Non-builtin'/'active' attributes These attributes are defined by a crate - either the standard library, or a proc-macro crate. diff --git a/src/doc/rustc-dev-guide/src/const-eval.md b/src/doc/rustc-dev-guide/src/const-eval.md index a3fee034ec6ed..8ffd7d5ba47de 100644 --- a/src/doc/rustc-dev-guide/src/const-eval.md +++ b/src/doc/rustc-dev-guide/src/const-eval.md @@ -1,8 +1,9 @@ # Constant Evaluation -Constant evaluation is the process of computing values at compile time. For a -specific item (constant/static/array length) this happens after the MIR for the -item is borrow-checked and optimized. In many cases trying to const evaluate an +Constant evaluation is the process of computing values at compile time. +For a specific item (constant/static/array length) this happens after the MIR for the +item is borrow-checked and optimized. +In many cases trying to const evaluate an item will trigger the computation of its MIR for the first time. Prominent examples are: @@ -11,14 +12,12 @@ Prominent examples are: * Array length * needs to be known to reserve stack or heap space * Enum variant discriminants - * needs to be known to prevent two variants from having the same - discriminant + * needs to be known to prevent two variants from having the same discriminant * Patterns * need to be known to check for overlapping patterns Additionally constant evaluation can be used to reduce the workload or binary -size at runtime by precomputing complex operations at compile time and only -storing the result. +size at runtime by precomputing complex operations at compile time and only storing the result. All uses of constant evaluation can either be categorized as "influencing the type system" (array lengths, enum variant discriminants, const generic parameters), or as solely being @@ -37,7 +36,8 @@ They're the wrappers of the `const_eval` query. The `const_eval_*` functions use a [`ParamEnv`](./typing-parameter-envs.md) of environment in which the constant is evaluated (e.g. the function within which the constant is used) -and a [`GlobalId`]. The `GlobalId` is made up of an `Instance` referring to a constant +and a [`GlobalId`]. +The `GlobalId` is made up of an `Instance` referring to a constant or static or of an `Instance` of a function and an index into the function's `Promoted` table. Constant evaluation returns an [`EvalToValTreeResult`] for type system constants diff --git a/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md b/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md index 101c793525246..9acc94a174ed5 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/gpu-target.md @@ -3,14 +3,13 @@ **Github Label:** None
**Ping command:** `@rustbot ping gpu-target` -This notification group deals with linker related issues and their integration -within the compiler. +This notification group deals with linker-related issues and their integration within the compiler. The group also has an associated Zulip stream ([`#t-compiler/gpgpu-backend`]) where people can go to ask questions and discuss GPU-related topics and issues. -if you're interested in participating, feel free to sign up for this group! To -do so, open a PR against the [rust-lang/team] repository and add your GitHub +if you're interested in participating, feel free to sign up for this group! +To do so, open a PR against the [rust-lang/team] repository and add your GitHub user to [this file][gpu-target-team]. [`#t-compiler/gpgpu-backend`]: https://rust-lang.zulipchat.com/#narrow/channel/422870-t-compiler.2Fgpgpu-backend diff --git a/src/doc/rustc-dev-guide/src/notification-groups/wasi.md b/src/doc/rustc-dev-guide/src/notification-groups/wasi.md index 93962a54fdfb7..a38528e2d9b4e 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/wasi.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/wasi.md @@ -14,8 +14,8 @@ where people can go to pose questions and discuss WASI-specific topics. So, if you are interested in participating, please sign up for the WASI group! To do so, open a PR against the [rust-lang/team] repository. -Just [follow this example][eg], but change the username to your own! +Just follow [this example], but change the username to your own! [`#t-compiler/wasm`]: https://rust-lang.zulipchat.com/#narrow/stream/463513-t-compiler.2Fwasm [rust-lang/team]: https://github.com/rust-lang/team -[eg]: https://github.com/rust-lang/team/pull/1580 +[this example]: https://github.com/rust-lang/team/pull/1580 diff --git a/src/doc/rustc-dev-guide/src/notification-groups/wasm.md b/src/doc/rustc-dev-guide/src/notification-groups/wasm.md index 12e457546ba93..f903bc7114a71 100644 --- a/src/doc/rustc-dev-guide/src/notification-groups/wasm.md +++ b/src/doc/rustc-dev-guide/src/notification-groups/wasm.md @@ -10,14 +10,12 @@ WebAssembly-related issues as well as suggestions on how to resolve interesting questions regarding our WASM support. The group also has an associated Zulip channel ([`#t-compiler/wasm`]) -where people can go to pose questions and discuss WASM-specific -topics. +where people can go to pose questions and discuss WASM-specific topics. -So, if you are interested in participating, please sign up for the -WASM group! To do so, open a PR against the [rust-lang/team] -repository. Just [follow this example][eg], but change the username to -your own! +So, if you are interested in participating, please sign up for the WASM group! +To do so, open a PR against the [rust-lang/team] repository. +Just follow [this example], but change the username to your own! [`#t-compiler/wasm`]: https://rust-lang.zulipchat.com/#narrow/stream/463513-t-compiler.2Fwasm [rust-lang/team]: https://github.com/rust-lang/team -[eg]: https://github.com/rust-lang/team/pull/1581 +[this example]: https://github.com/rust-lang/team/pull/1581 diff --git a/src/doc/rustc-dev-guide/src/pat-exhaustive-checking.md b/src/doc/rustc-dev-guide/src/pat-exhaustive-checking.md index e953931aa78c2..b7edb51ac8545 100644 --- a/src/doc/rustc-dev-guide/src/pat-exhaustive-checking.md +++ b/src/doc/rustc-dev-guide/src/pat-exhaustive-checking.md @@ -1,7 +1,7 @@ # Pattern and exhaustiveness checking -In Rust, pattern matching and bindings have a few very helpful properties. The -compiler will check that bindings are irrefutable when made and that match arms +In Rust, pattern matching and bindings have a few very helpful properties. +The compiler will check that bindings are irrefutable when made and that match arms are exhaustive. ## Pattern usefulness @@ -35,8 +35,7 @@ match x { } ``` -Thus usefulness is used for two purposes: -detecting unreachable code (which is useful to the user), +Thus usefulness is used for two purposes: detecting unreachable code (which is useful to the user), and ensuring that matches are exhaustive (which is important for soundness, because a match expression can return a value). @@ -88,22 +87,26 @@ That file contains a detailed description of the algorithm. ### Constructors and fields In the value `Pair(Some(0), true)`, `Pair` is called the constructor of the value, and `Some(0)` and -`true` are its fields. Every matchable value can be decomposed in this way. Examples of -constructors are: `Some`, `None`, `(,)` (the 2-tuple constructor), `Foo {..}` (the constructor for -a struct `Foo`), and `2` (the constructor for the number `2`). - -Each constructor takes a fixed number of fields; this is called its arity. `Pair` and `(,)` have -arity 2, `Some` has arity 1, `None` and `42` have arity 0. Each type has a known set of -constructors. Some types have many constructors (like `u64`) or even an infinitely many (like `&str` -and `&[T]`). - -Patterns are similar: `Pair(Some(_), _)` has constructor `Pair` and two fields. The difference is -that we get some extra pattern-only constructors, namely: the wildcard `_`, variable bindings, -integer ranges like `0..=10`, and variable-length slices like `[_, .., _]`. We treat or-patterns -separately. +`true` are its fields. +Every matchable value can be decomposed in this way. +Examples of constructors are: +`Some`, `None`, `(,)` (the 2-tuple constructor), `Foo {..}` (the constructor for a struct `Foo`), +and `2` (the constructor for the number `2`). + +Each constructor takes a fixed number of fields; this is called its arity. +`Pair` and `(,)` have arity 2, `Some` has arity 1, `None` and `42` have arity 0. +Each type has a known set of constructors. +Some types have many constructors (like `u64`) or even an infinitely many (like `&str` and `&[T]`). + +Patterns are similar: `Pair(Some(_), _)` has constructor `Pair` and two fields. +The difference is that we get some extra pattern-only constructors, namely: +the wildcard `_`, variable bindings, +integer ranges like `0..=10`, and variable-length slices like `[_, .., _]`. +We treat or-patterns separately. Now to check if a value `v` matches a pattern `p`, we check if `v`'s constructor matches `p`'s -constructor, then recursively compare their fields if necessary. A few representative examples: +constructor, then recursively compare their fields if necessary. +A few representative examples: - `matches!(v, _) := true` - `matches!((v0, v1), (p0, p1)) := matches!(v0, p0) && matches!(v1, p1)` @@ -114,8 +117,9 @@ constructor, then recursively compare their fields if necessary. A few represent - `matches!([v0], [p0, .., p1]) := false` (incompatible lengths) - `matches!([v0, v1, v2], [p0, .., p1]) := matches!(v0, p0) && matches!(v2, p1)` -This concept is absolutely central to pattern analysis. The [`constructor`] module provides -functions to extract, list and manipulate constructors. This is a useful enough concept that +This concept is absolutely central to pattern analysis. +The [`constructor`] module provides functions to extract, list, and manipulate constructors. +This is a useful enough concept that variations of it can be found in other places of the compiler, like in the MIR-lowering of a match expression and in some clippy lints. @@ -125,7 +129,8 @@ The pattern-only constructors (`_`, ranges and variable-length slices) each stan normal constructors, e.g. `_: Option` stands for the set {`None`, `Some`} and `[_, .., _]` stands for the infinite set {`[,]`, `[,,]`, `[,,,]`, ...} of the slice constructors of arity >= 2. -In order to manage these constructors, we keep them as grouped as possible. For example: +In order to manage these constructors, we keep them as grouped as possible. +For example: ```rust match (0, false) { @@ -137,7 +142,8 @@ match (0, false) { In this example, all of `0`, `1`, .., `49` match the same arms, and thus can be treated as a group. In fact, in this match, the only ranges we need to consider are: `0..50`, `50..=100`, -`101..=150`,`151..=200` and `201..`. Similarly: +`101..=150`,`151..=200` and `201..`. +Similarly: ```rust enum Direction { North, South, East, West } @@ -156,10 +162,11 @@ time. ### Usefulness vs reachability in the presence of empty types -This is likely the subtlest aspect of exhaustiveness. To be fully precise, a match doesn't operate -on a value, it operates on a place. In certain unsafe circumstances, it is possible for a place to -not contain valid data for its type. This has subtle consequences for empty types. Take the -following: +This is likely the subtlest aspect of exhaustiveness. +To be fully precise, a match doesn't operate on a value; it operates on a place. +In certain unsafe circumstances, it is possible for a place to not contain valid data for its type. +This has subtle consequences for empty types. +Take the following: ```rust enum Void {} @@ -172,10 +179,11 @@ unsafe { } ``` -In this example, `ptr` is a valid pointer pointing to a place with invalid data. The `_` pattern -does not look at the contents of the place `*ptr`, so this code is ok and the arm is taken. In other -words, despite the place we are inspecting being of type `Void`, there is a reachable arm. If the -arm had a binding however: +In this example, `ptr` is a valid pointer pointing to a place with invalid data. +The `_` pattern does not look at the contents of the place `*ptr`, +so this code is ok and the arm is taken. +In other words, despite the place we are inspecting being of type `Void`, there is a reachable arm. +If the arm had a binding however: ```rust # #[derive(Copy, Clone)] @@ -189,25 +197,31 @@ match *ptr { # } ``` -Here the binding loads the value of type `Void` from the `*ptr` place. In this example, this causes -UB since the data is not valid. In the general case, this asserts validity of the data at `*ptr`. +Here the binding loads the value of type `Void` from the `*ptr` place. +In this example, this causes UB since the data is not valid. +In the general case, this asserts validity of the data at `*ptr`. Either way, this arm will never be taken. -Finally, let's consider the empty match `match *ptr {}`. If we consider this exhaustive, then -having invalid data at `*ptr` is invalid. In other words, the empty match is semantically -equivalent to the `_a => ...` match. In the interest of explicitness, we prefer the case with an -arm, hence we won't tell the user to remove the `_a` arm. In other words, the `_a` arm is -unreachable yet not redundant. This is why we lint on redundant arms rather than unreachable +Finally, let's consider the empty match `match *ptr {}`. +If we consider this exhaustive, then having invalid data at `*ptr` is invalid. +In other words, the empty match is semantically equivalent to the `_a => ...` match. +In the interest of explicitness, we prefer the case with an +arm, hence we won't tell the user to remove the `_a` arm. +In other words, the `_a` arm is unreachable yet not redundant. +This is why we lint on redundant arms rather than unreachable arms, despite the fact that the lint says "unreachable". These considerations only affects certain places, namely those that can contain non-valid data -without UB. These are: pointer dereferences, reference dereferences, and union field accesses. We -track during exhaustiveness checking whether a given place is known to contain valid data. +without UB. +These are: pointer dereferences, reference dereferences, and union field accesses. +We track during exhaustiveness checking whether a given place is known to contain valid data. Having said all that, the current implementation of exhaustiveness checking does not follow the -above considerations. On stable, empty types are for the most part treated as non-empty. The -[`exhaustive_patterns`] feature errs on the other end: it allows omitting arms that could be -reachable in unsafe situations. The [`never_patterns`] experimental feature aims to fix this and +above considerations. +On stable, empty types are for the most part treated as non-empty. +The [`exhaustive_patterns`] feature errs on the other end: it allows omitting arms that could be +reachable in unsafe situations. +The [`never_patterns`] experimental feature aims to fix this and permit the correct behavior of empty types in patterns. [`check_match`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/thir/pattern/check_match/index.html diff --git a/src/doc/rustc-dev-guide/src/profiling/with-perf.md b/src/doc/rustc-dev-guide/src/profiling/with-perf.md index 3285a486eb452..becaec831230c 100644 --- a/src/doc/rustc-dev-guide/src/profiling/with-perf.md +++ b/src/doc/rustc-dev-guide/src/profiling/with-perf.md @@ -17,10 +17,9 @@ This is a guide for how to profile rustc with [perf](https://perf.wiki.kernel.or ## Gathering a perf profile -perf is an excellent tool on linux that can be used to gather and -analyze all kinds of information. Mostly it is used to figure out -where a program spends its time. It can also be used for other sorts -of events, though, like cache misses and so forth. +perf is an excellent tool on Linux that can be used to gather and analyze all kinds of information. +It is mostly used to figure out where a program spends its time. +It can also be used for other sorts of events, though, like cache misses and so forth. ### The basics @@ -30,26 +29,26 @@ The basic `perf` command is this: perf record -F99 --call-graph dwarf XXX ``` -The `-F99` tells perf to sample at 99 Hz, which avoids generating too -much data for longer runs (why 99 Hz you ask? It is often chosen -because it is unlikely to be in lockstep with other periodic -activity). The `--call-graph dwarf` tells perf to get call-graph -information from debuginfo, which is accurate. The `XXX` is the -command you want to profile. So, for example, you might do: +The `-F99` tells perf to sample at 99 Hz, which avoids generating too much data for longer runs. + +> 99 Hz is often chosen because it is unlikely to be in lockstep with other periodic activity. + +The `--call-graph dwarf` tells perf to get call-graph information from debuginfo, which is accurate. +The `XXX` is the command you want to profile. +So, for example, you might do: ```bash perf record -F99 --call-graph dwarf cargo + rustc ``` -to run `cargo` -- here `` should be the name of the toolchain -you made in the beginning. But there are some things to be aware of: +to run `cargo` -- here `` should be the name of the toolchain you made in the beginning. +But there are some things to be aware of: -- You probably don't want to profile the time spend building - dependencies. So something like `cargo build; cargo clean -p $C` may - be helpful (where `$C` is the crate name) - - Though usually I just do `touch src/lib.rs` and rebuild instead. =) -- You probably don't want incremental messing about with your - profile. So something like `CARGO_INCREMENTAL=0` can be helpful. +- You probably don't want to profile the time spent building dependencies. + So something like `cargo build; cargo clean --package $crate` may be helpful + - Though usually I just do `touch src/lib.rs` and rebuild instead =) +- You probably don't want incremental messing about with your profile. + So something like `CARGO_INCREMENTAL=0` can be helpful. In case to avoid the issue of `addr2line xxx/elf: could not read first record` when reading collected data from `cargo`, you may need use the latest version of `addr2line`: @@ -61,20 +60,20 @@ cargo install addr2line --features="bin" ### Gathering a perf profile from a `perf.rust-lang.org` test Often we want to analyze a specific test from `perf.rust-lang.org`. -The easiest way to do that is to use the [rustc-perf][rustc-perf] +The easiest way to do that is to use the [rustc-perf] benchmarking suite, this approach is described [here](with-rustc-perf.md). -Instead of using the benchmark suite CLI, you can also profile the benchmarks manually. First, -you need to clone the [rustc-perf][rustc-perf] repository: +Instead of using the benchmark suite CLI, you can also profile the benchmarks manually. +First, you need to clone the [rustc-perf] repository: ```bash $ git clone https://github.com/rust-lang/rustc-perf ``` -and then find the source code of the test that you want to profile. Sources for the tests -are found in [the `collector/compile-benchmarks` directory][compile-time dir] -and [the `collector/runtime-benchmarks` directory][runtime dir]. So let's -go into the directory of a specific test; we'll use `clap-rs` as an example: +and then find the source code of the test that you want to profile. +Sources for the tests are found in [the `collector/compile-benchmarks` directory][compile-time dir] +and [the `collector/runtime-benchmarks` directory][runtime dir]. +So let's go into the directory of a specific test; we'll use `clap-rs` as an example: [rustc-perf]: https://github.com/rust-lang/rustc-perf [compile-time dir]: https://github.com/rust-lang/rustc-perf/tree/master/collector/compile-benchmarks @@ -84,9 +83,8 @@ go into the directory of a specific test; we'll use `clap-rs` as an example: cd collector/compile-benchmarks/clap-3.1.6 ``` -In this case, let's say we want to profile the `cargo check` -performance. In that case, I would first run some basic commands to -build the dependencies: +In this case, let's say we want to profile the `cargo check` performance. +In that case, I would first run some basic commands to build the dependencies: ```bash # Setup: first clean out any old results and build the dependencies: @@ -94,11 +92,11 @@ cargo + clean CARGO_INCREMENTAL=0 cargo + check ``` -(Again, `` should be replaced with the name of the -toolchain we made in the first step.) +(Again, `` should be replaced with the name of the toolchain we made in the first step.) Next: we want record the execution time for *just* the clap-rs crate, -running cargo check. I tend to use `cargo rustc` for this, since it +running cargo check. +I tend to use `cargo rustc` for this, since it also allows me to add explicit flags, which we'll do later on. ```bash @@ -106,26 +104,26 @@ touch src/lib.rs CARGO_INCREMENTAL=0 perf record -F99 --call-graph dwarf cargo rustc --profile check --lib ``` -Note that final command: it's a doozy! It uses the `cargo rustc` -command, which executes rustc with (potentially) additional options; +Note that final command: it's a doozy! +It uses the `cargo rustc` command, which executes rustc with (potentially) additional options; the `--profile check` and `--lib` options specify that we are doing a `cargo check` execution, and that this is a library (not a binary). -At this point, we can use `perf` tooling to analyze the results. For example: +At this point, we can use `perf` tooling to analyze the results. +For example: ```bash perf report ``` -will open up an interactive TUI program. In simple cases, that can be -helpful. For more detailed examination, the [`perf-focus` tool][pf] -can be helpful; it is covered below. +This opens up an interactive TUI program. +In simple cases, that can be helpful. +For more detailed examination, the [`perf-focus` tool][pf] can be helpful; it is covered below. -**A note of caution.** Each of the rustc-perf tests is its own special - snowflake. In particular, some of them are not libraries, in which - case you would want to do `touch src/main.rs` and avoid passing - `--lib`. I'm not sure how best to tell which test is which to be - honest. +**A note of caution.** Each of the rustc-perf tests is its own special snowflake. + In particular, some of them are not libraries, in which + case you would want to do `touch src/main.rs` and avoid passing `--lib`. + I'm not sure how best to tell which test is which to be honest. ### Gathering NLL data @@ -141,23 +139,22 @@ CARGO_INCREMENTAL=0 perf record -F99 --call-graph dwarf cargo rustc --profile ch ## Analyzing a perf profile with `perf focus` -Once you've gathered a perf profile, we want to get some information -about it. For this, I personally use [perf focus][pf]. It's a kind of -simple but useful tool that lets you answer queries like: +Once you've gathered a perf profile, we want to get some information about it. +For this, I personally use [perf focus][pf]. +It's a kind of simple but useful tool that lets you answer queries like: - "how much time was spent in function F" (no matter where it was called from) - "how much time was spent in function F when it was called from G" - "how much time was spent in function F *excluding* time spent in G" - "what functions does F call and how much time does it spend in them" -To understand how it works, you have to know just a bit about -perf. Basically, perf works by *sampling* your process on a regular -basis (or whenever some event occurs). For each sample, perf gathers a -backtrace. `perf focus` lets you write a regular expression that tests +To understand how it works, you have to know just a bit about perf. +Basically, perf works by *sampling* your process on a regular basis (or whenever some event occurs). +For each sample, perf gathers a backtrace. +`perf focus` lets you write a regular expression that tests which functions appear in that backtrace, and then tells you which -percentage of samples had a backtrace that met the regular -expression. It's probably easiest to explain by walking through how I -would analyze NLL performance. +percentage of samples had a backtrace that met the regular expression. +It's probably easiest to explain by walking through how I would analyze NLL performance. ### Installing `perf-focus` @@ -169,9 +166,9 @@ cargo install perf-focus ### Example: How much time is spent in MIR borrowck? -Let's say we've gathered the NLL data for a test. We'd like to know -how much time it is spending in the MIR borrow-checker. The "main" -function of the MIR borrowck is called `do_mir_borrowck`, so we can do +Let's say we've gathered the NLL data for a test. +We'd like to know how much time it is spending in the MIR borrow-checker. +The "main" function of the MIR borrowck is called `do_mir_borrowck`, so we can do this command: ```bash @@ -183,37 +180,38 @@ Percentage : 29% ``` The `'{do_mir_borrowck}'` argument is called the **matcher**. It -specifies the test to be applied on the backtrace. In this case, the -`{X}` indicates that there must be *some* function on the backtrace -that meets the regular expression `X`. In this case, that regex is -just the name of the function we want (in fact, it's a subset of the name; -the full name includes a bunch of other stuff, like the module -path). In this mode, perf-focus just prints out the percentage of +specifies the test to be applied on the backtrace. +In this case, the `{X}` indicates that there must be *some* function on the backtrace +that meets the regular expression `X`. +In this case, that regex is just the name of the function we want +(in fact, it's a subset of the name; +the full name includes a bunch of other stuff, like the module path). +In this mode, perf-focus just prints out the percentage of samples where `do_mir_borrowck` was on the stack: in this case, 29%. **A note about c++filt.** To get the data from `perf`, `perf focus` - currently executes `perf script` (perhaps there is a better - way...). I've sometimes found that `perf script` outputs C++ mangled + currently executes `perf script` (perhaps there is a better way...). + I've sometimes found that `perf script` outputs C++ mangled names. This is annoying. You can tell by running `perf script | head` yourself — if you see names like `5rustc6middle` instead of - `rustc::middle`, then you have the same problem. You can solve this - by doing: + `rustc::middle`, then you have the same problem. + You can solve this by doing: ```bash perf script | c++filt | perf focus --from-stdin ... ``` This will pipe the output from `perf script` through `c++filt` and -should mostly convert those names into a more friendly format. The -`--from-stdin` flag to `perf focus` tells it to get its data from -stdin, rather than executing `perf focus`. We should make this more -convenient (at worst, maybe add a `c++filt` option to `perf focus`, or +should mostly convert those names into a more friendly format. +The `--from-stdin` flag to `perf focus` tells it to get its data from +stdin, rather than executing `perf focus`. +We should make this more convenient (at worst, maybe add a `c++filt` option to `perf focus`, or just always use it — it's pretty harmless). ### Example: How much time does MIR borrowck spend solving traits? -Perhaps we'd like to know how much time MIR borrowck spends in the -trait checker. We can ask this using a more complex regex: +Perhaps we'd like to know how much time MIR borrowck spends in the trait checker. +We can ask this using a more complex regex: ```bash $ perf focus '{do_mir_borrowck}..{^rustc::traits}' @@ -225,23 +223,21 @@ Percentage : 0% Here we used the `..` operator to ask "how often do we have `do_mir_borrowck` on the stack and then, later, some function whose -name begins with `rustc::traits`?" (basically, code in that module). It -turns out the answer is "almost never" — only 12 samples fit that -description (if you ever see *no* samples, that often indicates your -query is messed up). +name begins with `rustc::traits`?" (basically, code in that module). +It turns out the answer is "almost never" — only 12 samples fit that +description (if you ever see *no* samples, that often indicates your query is messed up). -If you're curious, you can find out exactly which samples by using the -`--print-match` option. This will print out the full backtrace for -each sample. The `|` at the front of the line indicates the part that -the regular expression matched. +If you're curious, you can find out exactly which samples by using the `--print-match` option. +This will print out the full backtrace for each sample. +The `|` at the front of the line indicates the part that the regular expression matched. ### Example: Where does MIR borrowck spend its time? -Often we want to do more "explorational" queries. Like, we know that -MIR borrowck is 29% of the time, but where does that time get spent? -For that, the `--tree-callees` option is often the best tool. You -usually also want to give `--tree-min-percent` or -`--tree-max-depth`. The result looks like this: +Often we want to do more "explorational" queries. +Like, we know that MIR borrowck is 29% of the time, but where does that time get spent? +For that, the `--tree-callees` option is often the best tool. +You usually also want to give `--tree-min-percent` or `--tree-max-depth`. +The result looks like this: ```bash $ perf focus '{do_mir_borrowck}' --tree-callees --tree-min-percent 3 @@ -265,33 +261,30 @@ Tree What happens with `--tree-callees` is that - we find each sample matching the regular expression -- we look at the code that occurs *after* the regex match and try - to build up a call tree +- we look at the code that occurs *after* the regex match and try to build up a call tree -The `--tree-min-percent 3` option says "only show me things that take -more than 3% of the time". Without this, the tree often gets really -noisy and includes random stuff like the innards of -malloc. `--tree-max-depth` can be useful too, it just limits how many -levels we print. +The `--tree-min-percent 3` option says "only show me things that take more than 3% of the time". +Without this, +the tree often gets really noisy and includes random stuff like the innards of malloc. +`--tree-max-depth` can be useful too, it just limits how many levels we print. For each line, we display the percent of time in that function altogether ("total") and the percent of time spent in **just that -function and not some callee of that function** (self). Usually -"total" is the more interesting number, but not always. +function and not some callee of that function** (self). +Usually "total" is the more interesting number, but not always. ### Relative percentages By default, all in perf-focus are relative to the **total program execution**. This is useful to help you keep perspective — often as we drill down to find hot spots, we can lose sight of the fact that, -in terms of overall program execution, this "hot spot" is actually not -important. It also ensures that percentages between different queries -are easily compared against one another. +in terms of overall program execution, this "hot spot" is actually not important. +It also ensures that percentages between different queries are easily compared against one another. That said, sometimes it's useful to get relative percentages, so `perf -focus` offers a `--relative` option. In this case, the percentages are -listed only for samples that match (vs all samples). So for example we -could get our percentages relative to the borrowck itself +focus` offers a `--relative` option. +In this case, the percentages are listed only for samples that match (vs all samples). +So for example we could get our percentages relative to the borrowck itself like so: ```bash @@ -310,6 +303,7 @@ Tree ``` Here you see that `compute_regions` came up as "47% total" — that -means that 47% of `do_mir_borrowck` is spent in that function. Before, +means that 47% of `do_mir_borrowck` is spent in that function. +Before, we saw 20% — that's because `do_mir_borrowck` itself is only 43% of the total time (and `.47 * .43 = .20`). diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md index d18021bf278d9..951a541548b23 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md @@ -5,7 +5,9 @@ This page is about the test suite named `rustdoc-gui` used to test the "GUI" of `rustdoc` (i.e., the HTML/JS/CSS as rendered in a browser). For other rustdoc-specific test suites, see [Rustdoc test suites]. -These use a NodeJS-based tool called [`browser-UI-test`] that uses [puppeteer] to run tests in a headless browser and check rendering and interactivity. For information on how to write this form of test, see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme] as well as [the description of the `.goml` format][goml-script] +These use a NodeJS-based tool called [`browser-UI-test`] that uses [puppeteer] to run tests in a headless browser and check rendering and interactivity. +For information on how to write this form of test, +see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme] as well as [the description of the `.goml` format][goml-script]. [Rustdoc test suites]: ../tests/compiletest.md#rustdoc-test-suites [`browser-UI-test`]: https://github.com/GuillaumeGomez/browser-UI-test/ diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md index e88d2703e3a23..ac2b4da5c652e 100644 --- a/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md +++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/search.md @@ -1,16 +1,13 @@ # Rustdoc search -Rustdoc Search is two programs: `search_index.rs` -and `search.js`. The first generates a nasty JSON -file with a full list of items and function signatures +Rustdoc Search is two programs: `search_index.rs` and `search.js`. +The first generates a nasty JSON file with a full list of items and function signatures in the crates in the doc bundle, and the second reads -it, turns it into some in-memory structures, and -scans them linearly to search. +it, turns it into some in-memory structures, and scans them linearly to search. ## Search index format -`search.js` calls this Raw, because it turns it into -a more normal object tree after loading it. +`search.js` calls this Raw, because it turns it into a more normal object tree after loading it. For space savings, it's also written without newlines or spaces. ```json @@ -44,8 +41,7 @@ For space savings, it's also written without newlines or spaces. ] ``` -[`src/librustdoc/html/static/js/rustdoc.d.ts`] -defines an actual schema in a TypeScript `type`. +[`src/librustdoc/html/static/js/rustdoc.d.ts`] defines an actual schema in a TypeScript `type`. | Key | Name | Description | | --- | -------------------- | ------------ | @@ -75,10 +71,8 @@ It makes a lot of compromises: * The `rustdoc` compiler runs on one crate at a time, so each crate has an essentially separate search index. - It [merges] them by having each crate on one line - and looking at the first quoted string. -* Names in the search index are given - in their original case and with underscores. + It [merges] them by having each crate on one line and looking at the first quoted string. +* Names in the search index are given in their original case and with underscores. When the search index is loaded, `search.js` stores the original names for display, but also folds them to lowercase and strips underscores for search. @@ -156,10 +150,8 @@ for (i, entry) in search_index.iter().enumerate() { } ``` -This is valid because everything has a parent module -(even if it's just the crate itself), -and is easy to assemble because the rustdoc generator sorts by path -before serializing. +This is valid because everything has a parent module (even if it's just the crate itself), +and is easy to assemble because the rustdoc generator sorts by path before serializing. Doing this allows rustdoc to not only make the search index smaller, but reuse the same string representing the parent path across multiple in-memory items. @@ -233,21 +225,21 @@ work through a "sandwich workload" of three steps: Reducing the amount of data downloaded here will almost always increase latency, by delaying the decision of what to download behind other work and/or adding -data dependencies where something can't be downloaded without first downloading -something else. In this case, we can't start downloading descriptions until +data dependencies where something can't be downloaded without first downloading something else. +In this case, we can't start downloading descriptions until after the search is done, because that's what allows it to decide *which* descriptions to download (it needs to sort the results then truncate to 200). To do this, two columns are stored in the search index, building on both Roaring Bitmaps and on VLQ Hex. -* `e` is an index of **e**mpty descriptions. It's a [roaring bitmap] of - each item (the crate itself is item 0, the rest start at 1). +* `e` is an index of **e**mpty descriptions. + It's a [roaring bitmap] of each item (the crate itself is item 0, the rest start at 1). * `D` is a shard list, stored in [VLQ hex] as flat list of integers. Each integer gives you the number of descriptions in the shard. As the decoder walks the index, it checks if the description is empty. - if it's not, then it's in the "current" shard. When all items are - exhausted, it goes on to the next shard. + if it's not, then it's in the "current" shard. + When all items are exhausted, it goes on to the next shard. Inside each shard is a newline-delimited list of descriptions, wrapped in a JSONP-style function call. @@ -280,8 +272,7 @@ Because of zigzag encoding, `` ` `` is +0, `a` is -0 (which is not used), ## Searching by name -Searching by name works by looping through the search index -and running these functions on each: +Searching by name works by looping through the search index and running these functions on each: * [`editDistance`] is always used to determine a match (unless quotes are specified, which would use simple equality instead). @@ -295,20 +286,19 @@ and running these functions on each: If it returns anything other than -1, the result is added, even if `editDistance` exceeds its threshold, and the index is stored for ranking. -* [`checkPath`] is used if, and only if, a parent path is specified - in the query. For example, `vec` has no parent path, but `vec::vec` does. +* [`checkPath`] is used if, and only if, a parent path is specified in the query. + For example, `vec` has no parent path, but `vec::vec` does. Within checkPath, editDistance and indexOf are used, and the path query has its own heuristic threshold, too. If it's not within the threshold, the entry is rejected, even if the first two pass. - If it's within the threshold, the path distance is stored - for ranking. + If it's within the threshold, the path distance is stored for ranking. * [`checkType`] is used only if there's a type filter, - like the struct in `struct:vec`. If it fails, + like the struct in `struct:vec`. + If it fails, the entry is rejected. -If all four criteria pass -(plus the crate filter, which isn't technically part of the query), +If all four criteria pass (plus the crate filter, which isn't technically part of the query), the results are sorted by [`sortResults`]. [`editDistance`]: https://github.com/rust-lang/rust/blob/79b710c13968a1a48d94431d024d2b1677940866/src/librustdoc/html/static/js/search.js#L137 @@ -336,17 +326,18 @@ is going to match the same things `T, u32` matches (though rustdoc will detect this particular problem and warn about it). Then, when actually looping over each item, -the bloom filter will probably reject entries that don't have every -type mentioned in the query. +the bloom filter will probably reject entries that don't have every type mentioned in the query. For example, the bloom query allows a query of `i32 -> u32` to match a function with the type `i32, u32 -> bool`, but unification will reject it later. The unification filter ensures that: -* Bag semantics are respected. If you query says `i32, i32`, +* Bag semantics are respected. + If your query says `i32, i32`, then the function has to mention *two* i32s, not just one. -* Nesting semantics are respected. If your query says `vec