diff options
317 files changed, 5270 insertions, 3188 deletions
diff --git a/Cargo.lock b/Cargo.lock index 24cd5b825b2..2dc6e8ff103 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2347,11 +2347,11 @@ dependencies = [ [[package]] name = "miow" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044" +checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.60.2", ] [[package]] @@ -5984,9 +5984,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.16" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cd35d6cae91109a0ffd207b573cf3c741cab7e921dd376ea7aaf2c52a3408c" +checksum = "1c9208f87cac2332fd80dcf36d54e9163d3446e28301e0c6e424984425738984" dependencies = [ "anyhow", "clap", @@ -5994,9 +5994,9 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.237.0", + "wasmparser 0.239.0", "wat", - "windows-sys 0.59.0", + "windows-sys 0.60.2", "winsplit", "wit-component", "wit-parser", @@ -6021,24 +6021,24 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.237.0" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efe92d1321afa53ffc88a57c497bb7330c3cf84c98ffdba4a4caf6a0684fad3c" +checksum = "5be00faa2b4950c76fe618c409d2c3ea5a3c9422013e079482d78544bb2d184c" dependencies = [ "leb128fmt", - "wasmparser 0.237.0", + "wasmparser 0.239.0", ] [[package]] name = "wasm-metadata" -version = "0.237.0" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc0b0a0c4f35ca6efa7a797671372915d4e9659dba2d59edc6fafc931d19997" +checksum = "20b3ec880a9ac69ccd92fbdbcf46ee833071cf09f82bb005b2327c7ae6025ae2" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.237.0", - "wasmparser 0.237.0", + "wasm-encoder 0.239.0", + "wasmparser 0.239.0", ] [[package]] @@ -6063,9 +6063,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.237.0" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d2a40ca0d2bdf4b0bf36c13a737d0b2c58e4c8aaefe1c57f336dd75369ca250" +checksum = "8c9d90bb93e764f6beabf1d02028c70a2156a6583e63ac4218dd07ef733368b0" dependencies = [ "bitflags", "hashbrown", @@ -6076,22 +6076,22 @@ dependencies = [ [[package]] name = "wast" -version = "237.0.0" +version = "239.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf66f545acbd55082485cb9a6daab54579cb8628a027162253e8e9f5963c767" +checksum = "9139176fe8a2590e0fb174cdcaf373b224cb93c3dde08e4297c1361d2ba1ea5d" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.1", - "wasm-encoder 0.237.0", + "wasm-encoder 0.239.0", ] [[package]] name = "wat" -version = "1.237.0" +version = "1.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27975186f549e4b8d6878b627be732863883c72f7bf4dcf8f96e5f8242f73da9" +checksum = "3e1c941927d34709f255558166f8901a2005f8ab4a9650432e9281b7cc6f3b75" dependencies = [ "wast", ] @@ -6316,15 +6316,6 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" @@ -6352,21 +6343,6 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" @@ -6409,12 +6385,6 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" @@ -6427,12 +6397,6 @@ checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" @@ -6445,12 +6409,6 @@ checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" @@ -6475,12 +6433,6 @@ checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" @@ -6493,12 +6445,6 @@ checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" @@ -6511,12 +6457,6 @@ checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" @@ -6529,12 +6469,6 @@ checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" @@ -6580,9 +6514,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.237.0" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb7674f76c10e82fe00b256a9d4ffb2b8d037d42ab8e9a83ebb3be35c9d0bf6" +checksum = "88a866b19dba2c94d706ec58c92a4c62ab63e482b4c935d2a085ac94caecb136" dependencies = [ "anyhow", "bitflags", @@ -6591,17 +6525,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.237.0", + "wasm-encoder 0.239.0", "wasm-metadata", - "wasmparser 0.237.0", + "wasmparser 0.239.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.237.0" +version = "0.239.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2596a5bc7c24cc965b56ad6ff9e32394c4e401764f89620a888519c6e849ab" +checksum = "55c92c939d667b7bf0c6bf2d1f67196529758f99a2a45a3355cc56964fd5315d" dependencies = [ "anyhow", "id-arena", @@ -6612,7 +6546,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.237.0", + "wasmparser 0.239.0", ] [[package]] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 802a6fa3249..3e8fddd9954 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2284,6 +2284,54 @@ pub struct FnSig { pub span: Span, } +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() + } + + /// The span of the header's safety, or where to insert it if empty. + pub fn safety_span(&self) -> Span { + match self.header.safety { + Safety::Unsafe(span) | Safety::Safe(span) => span, + Safety::Default => { + // Insert after the `coroutine_kind` if available. + if let Some(extern_span) = self.header.ext.span() { + return extern_span.shrink_to_lo(); + } + + // Insert right at the front of the signature. + self.header_span().shrink_to_hi() + } + } + } + + /// The span of the header's extern, or where to insert it if empty. + pub fn extern_span(&self) -> Span { + self.header.ext.span().unwrap_or(self.safety_span().shrink_to_hi()) + } +} + /// A constraint on an associated item. /// /// ### Examples @@ -3526,6 +3574,13 @@ impl Extern { None => Extern::Implicit(span), } } + + pub fn span(self) -> Option<Span> { + match self { + Extern::None => None, + Extern::Implicit(span) | Extern::Explicit(_, span) => Some(span), + } + } } /// A function header. @@ -3534,12 +3589,12 @@ impl Extern { /// included in this struct (e.g., `async unsafe fn` or `const extern "C" fn`). #[derive(Clone, Copy, Encodable, Decodable, Debug, Walkable)] pub struct FnHeader { - /// Whether this is `unsafe`, or has a default safety. - pub safety: Safety, - /// Whether this is `async`, `gen`, or nothing. - pub coroutine_kind: Option<CoroutineKind>, /// The `const` keyword, if any pub constness: Const, + /// Whether this is `async`, `gen`, or nothing. + pub coroutine_kind: Option<CoroutineKind>, + /// Whether this is `unsafe`, or has a default safety. + pub safety: Safety, /// The `extern` keyword and corresponding ABI string, if any. pub ext: Extern, } @@ -3553,38 +3608,6 @@ impl FnHeader { || matches!(constness, Const::Yes(_)) || !matches!(ext, Extern::None) } - - /// Return a span encompassing the header, or none if all options are default. - pub fn span(&self) -> Option<Span> { - fn append(a: &mut Option<Span>, b: Span) { - *a = match a { - None => Some(b), - Some(x) => Some(x.to(b)), - } - } - - let mut full_span = None; - - match self.safety { - Safety::Unsafe(span) | Safety::Safe(span) => append(&mut full_span, span), - Safety::Default => {} - }; - - if let Some(coroutine_kind) = self.coroutine_kind { - append(&mut full_span, coroutine_kind.span()); - } - - if let Const::Yes(span) = self.constness { - append(&mut full_span, span); - } - - match self.ext { - Extern::Implicit(span) | Extern::Explicit(_, span) => append(&mut full_span, span), - Extern::None => {} - } - - full_span - } } impl Default for FnHeader { diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 3674814b796..bb6b25baf01 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1536,7 +1536,13 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::LangItem::Range } } - (None, Some(..), Closed) => hir::LangItem::RangeToInclusive, + (None, Some(..), Closed) => { + if self.tcx.features().new_range() { + hir::LangItem::RangeToInclusiveCopy + } else { + hir::LangItem::RangeToInclusive + } + } (Some(e1), Some(e2), Closed) => { if self.tcx.features().new_range() { hir::LangItem::RangeInclusiveCopy @@ -1560,13 +1566,26 @@ impl<'hir> LoweringContext<'_, 'hir> { }; let fields = self.arena.alloc_from_iter( - e1.iter().map(|e| (sym::start, e)).chain(e2.iter().map(|e| (sym::end, e))).map( - |(s, e)| { + e1.iter() + .map(|e| (sym::start, e)) + .chain(e2.iter().map(|e| { + ( + if matches!( + lang_item, + hir::LangItem::RangeInclusiveCopy | hir::LangItem::RangeToInclusiveCopy + ) { + sym::last + } else { + sym::end + }, + e, + ) + })) + .map(|(s, e)| { let expr = self.lower_expr(e); let ident = Ident::new(s, self.lower_span(e.span)); self.expr_field(ident, expr, e.span) - }, - ), + }), ); hir::ExprKind::Struct( diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 72f20a95ff0..4e2243e8787 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2101,17 +2101,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { { return; } - if self.tcx.features().more_maybe_bounds() { - return; - } } RelaxedBoundPolicy::Forbidden(reason) => { - if self.tcx.features().more_maybe_bounds() { - return; - } - match reason { RelaxedBoundForbiddenReason::TraitObjectTy => { + if self.tcx.features().more_maybe_bounds() { + return; + } + self.dcx().span_err( span, "relaxed bounds are not permitted in trait object types", @@ -2119,6 +2116,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { return; } RelaxedBoundForbiddenReason::SuperTrait => { + if self.tcx.features().more_maybe_bounds() { + return; + } + let mut diag = self.dcx().struct_span_err( span, "relaxed bounds are not permitted in supertrait bounds", diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index e5f1fcdc4b4..8dcf3e3aa38 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -57,8 +57,6 @@ ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetim .label = {ast_passes_auto_super_lifetime} .suggestion = remove the super traits or lifetime bounds -ast_passes_bad_c_variadic = defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block .cannot_have = cannot have a body .invalid = the invalid body @@ -66,6 +64,19 @@ ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect +ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list + +ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` functions + .label = `extern "{$abi}"` because of this + .help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +ast_passes_c_variadic_must_be_unsafe = + functions with a C variable argument list must be unsafe + .suggestion = add the `unsafe` keyword to this definition + +ast_passes_c_variadic_no_extern = `...` is not supported for non-extern functions + .help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic .const = `const` because of this .variadic = C-variadic because of this @@ -84,6 +95,10 @@ ast_passes_const_without_body = ast_passes_constraint_on_negative_bound = associated type constraints not allowed on negative bounds +ast_passes_coroutine_and_c_variadic = functions cannot be both `{$coroutine_kind}` and C-variadic + .const = `{$coroutine_kind}` because of this + .variadic = C-variadic because of this + ast_passes_equality_in_where = equality constraints are not yet supported in `where` clauses .label = not supported .suggestion = if `{$ident}` is an associated type you're trying to set, use the associated type binding syntax diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 538918a890d..a6ef89b553d 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -492,7 +492,7 @@ impl<'a> AstValidator<'a> { } if !spans.is_empty() { - let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo()); + let header_span = sig.header_span(); let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span()); let padding = if header_span.is_empty() { "" } else { " " }; @@ -685,22 +685,53 @@ impl<'a> AstValidator<'a> { }); } + if let Some(coroutine_kind) = sig.header.coroutine_kind { + self.dcx().emit_err(errors::CoroutineAndCVariadic { + spans: vec![coroutine_kind.span(), variadic_param.span], + coroutine_kind: coroutine_kind.as_str(), + coroutine_span: coroutine_kind.span(), + variadic_span: variadic_param.span, + }); + } + match fn_ctxt { FnCtxt::Foreign => return, FnCtxt::Free => match sig.header.ext { - Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _) - | Extern::Explicit(StrLit { symbol_unescaped: sym::C_dash_unwind, .. }, _) - | Extern::Implicit(_) - if matches!(sig.header.safety, Safety::Unsafe(_)) => - { - return; + Extern::Implicit(_) => { + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); + } } - _ => {} - }, - FnCtxt::Assoc(_) => {} - }; + Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { + if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { + self.dcx().emit_err(errors::CVariadicBadExtern { + span: variadic_param.span, + abi: symbol_unescaped, + extern_span: sig.extern_span(), + }); + } - self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); + } + } + Extern::None => { + let err = errors::CVariadicNoExtern { span: variadic_param.span }; + self.dcx().emit_err(err); + } + }, + FnCtxt::Assoc(_) => { + // For now, C variable argument lists are unsupported in associated functions. + let err = errors::CVariadicAssociatedFunction { span: variadic_param.span }; + self.dcx().emit_err(err); + } + } } fn check_item_named(&self, ident: Ident, kind: &str) { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 476ed27a10e..ae805042c54 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -319,13 +319,47 @@ pub(crate) struct ExternItemAscii { } #[derive(Diagnostic)] -#[diag(ast_passes_bad_c_variadic)] -pub(crate) struct BadCVariadic { +#[diag(ast_passes_c_variadic_associated_function)] +pub(crate) struct CVariadicAssociatedFunction { #[primary_span] pub span: Span, } #[derive(Diagnostic)] +#[diag(ast_passes_c_variadic_no_extern)] +#[help] +pub(crate) struct CVariadicNoExtern { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_c_variadic_must_be_unsafe)] +pub(crate) struct CVariadicMustBeUnsafe { + #[primary_span] + pub span: Span, + + #[suggestion( + ast_passes_suggestion, + applicability = "maybe-incorrect", + code = "unsafe ", + style = "verbose" + )] + pub unsafe_span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_c_variadic_bad_extern)] +#[help] +pub(crate) struct CVariadicBadExtern { + #[primary_span] + pub span: Span, + pub abi: Symbol, + #[label] + pub extern_span: Span, +} + +#[derive(Diagnostic)] #[diag(ast_passes_item_underscore)] pub(crate) struct ItemUnderscore<'a> { #[primary_span] @@ -660,6 +694,18 @@ pub(crate) struct ConstAndCVariadic { } #[derive(Diagnostic)] +#[diag(ast_passes_coroutine_and_c_variadic)] +pub(crate) struct CoroutineAndCVariadic { + #[primary_span] + pub spans: Vec<Span>, + pub coroutine_kind: &'static str, + #[label(ast_passes_const)] + pub coroutine_span: Span, + #[label(ast_passes_variadic)] + pub variadic_span: Span, +} + +#[derive(Diagnostic)] #[diag(ast_passes_pattern_in_foreign, code = E0130)] // FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::PatternsInFnsWithoutBody`) pub(crate) struct PatternInForeign { diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index ffdacff7152..d5d51f2e79a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -218,6 +218,7 @@ impl<S: Stage> AttributeParser<S> for NakedParser { sym::rustc_std_internal_symbol, // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity sym::rustc_align, + sym::rustc_align_static, // obviously compatible with self sym::naked, // documentation diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index d23a7ae72f8..0b2c05482bf 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -176,3 +176,27 @@ impl<S: Stage> SingleAttributeParser<S> for PatternComplexityLimitParser { }) } } + +pub(crate) struct NoCoreParser; + +impl<S: Stage> NoArgsAttributeParser<S> for NoCoreParser { + const PATH: &[Symbol] = &[sym::no_core]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; + // because it's a crate-level attribute, we already warn about it. + // Putting target limitations here would give duplicate warnings + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoCore; + const TYPE: AttributeType = AttributeType::CrateLevel; +} + +pub(crate) struct NoStdParser; + +impl<S: Stage> NoArgsAttributeParser<S> for NoStdParser { + const PATH: &[Symbol] = &[sym::no_std]; + const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn; + // because it's a crate-level attribute, we already warn about it. + // Putting target limitations here would give duplicate warnings + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd; + const TYPE: AttributeType = AttributeType::CrateLevel; +} diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 23aabd15597..0330e2515c7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -331,3 +331,30 @@ impl<S: Stage> AttributeParser<S> for AlignParser { Some(AttributeKind::Align { align, span }) } } + +#[derive(Default)] +pub(crate) struct AlignStaticParser(AlignParser); + +impl AlignStaticParser { + const PATH: &'static [Symbol] = &[sym::rustc_align_static]; + const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE; + + fn parse<'c, S: Stage>( + &mut self, + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) { + self.0.parse(cx, args) + } +} + +impl<S: Stage> AttributeParser<S> for AlignStaticParser { + const ATTRIBUTES: AcceptMapping<Self, S> = &[(Self::PATH, Self::TEMPLATE, Self::parse)]; + const ALLOWED_TARGETS: AllowedTargets = + AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> { + let (align, span) = self.0.0?; + Some(AttributeKind::Align { align, span }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 98e86838a3a..b3ab1d3edd6 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -25,8 +25,8 @@ use crate::attributes::codegen_attrs::{ }; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::crate_level::{ - CrateNameParser, MoveSizeLimitParser, PatternComplexityLimitParser, RecursionLimitParser, - TypeLengthLimitParser, + CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser, + RecursionLimitParser, TypeLengthLimitParser, }; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::dummy::DummyParser; @@ -50,7 +50,7 @@ use crate::attributes::proc_macro_attrs::{ ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, }; use crate::attributes::prototype::CustomMirParser; -use crate::attributes::repr::{AlignParser, ReprParser}; +use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart, RustcObjectLifetimeDefaultParser, @@ -152,6 +152,7 @@ attribute_parsers!( pub(crate) static ATTRIBUTE_PARSERS = [ // tidy-alphabetical-start AlignParser, + AlignStaticParser, BodyStabilityParser, ConfusablesParser, ConstStabilityParser, @@ -222,8 +223,10 @@ attribute_parsers!( Single<WithoutArgs<MacroEscapeParser>>, Single<WithoutArgs<MarkerParser>>, Single<WithoutArgs<MayDangleParser>>, + Single<WithoutArgs<NoCoreParser>>, Single<WithoutArgs<NoImplicitPreludeParser>>, Single<WithoutArgs<NoMangleParser>>, + Single<WithoutArgs<NoStdParser>>, Single<WithoutArgs<NonExhaustiveParser>>, Single<WithoutArgs<ParenSugarParser>>, Single<WithoutArgs<PassByValueParser>>, diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 013258a1b4e..855da5caa31 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -1,8 +1,8 @@ mod context; -use rustc_ast::token::{self, Delimiter}; +use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; -use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment}; +use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp, token}; use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; @@ -29,7 +29,7 @@ pub(crate) fn expand_assert<'cx>( // `core::panic` and `std::panic` are different macros, so we use call-site // context to pick up whichever is currently in scope. - let call_site_span = cx.with_call_site_ctxt(cond_expr.span); + let call_site_span = cx.with_call_site_ctxt(span); let panic_path = || { if use_panic_2021(span) { @@ -63,7 +63,7 @@ pub(crate) fn expand_assert<'cx>( }), })), ); - assert_cond_check(cx, call_site_span, cond_expr, then) + expr_if_not(cx, call_site_span, cond_expr, then, None) } // If `generic_assert` is enabled, generates rich captured outputs // @@ -88,33 +88,26 @@ pub(crate) fn expand_assert<'cx>( )), )], ); - assert_cond_check(cx, call_site_span, cond_expr, then) + expr_if_not(cx, call_site_span, cond_expr, then, None) }; ExpandResult::Ready(MacEager::expr(expr)) } -/// `assert!($cond_expr, $custom_message)` struct Assert { cond_expr: Box<Expr>, custom_message: Option<TokenStream>, } -/// `match <cond> { true => {} _ => <then> }` -fn assert_cond_check(cx: &ExtCtxt<'_>, span: Span, cond: Box<Expr>, then: Box<Expr>) -> Box<Expr> { - // Instead of expanding to `if !<cond> { <then> }`, we expand to - // `match <cond> { true => {} _ => <then> }`. - // This allows us to always complain about mismatched types instead of "cannot apply unary - // operator `!` to type `X`" when passing an invalid `<cond>`, while also allowing `<cond>` to - // be `&true`. - let els = cx.expr_block(cx.block(span, thin_vec![])); - let mut arms = thin_vec![]; - arms.push(cx.arm(span, cx.pat_lit(span, cx.expr_bool(span, true)), els)); - arms.push(cx.arm(span, cx.pat_wild(span), then)); - - // We wrap the `match` in a statement to limit the length of any borrows introduced in the - // condition. - cx.expr_block(cx.block(span, [cx.stmt_expr(cx.expr_match(span, cond, arms))].into())) +// if !{ ... } { ... } else { ... } +fn expr_if_not( + cx: &ExtCtxt<'_>, + span: Span, + cond: Box<Expr>, + then: Box<Expr>, + els: Option<Box<Expr>>, +) -> Box<Expr> { + cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els) } fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> { diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 6415e55e0b0..a6c8e7d29cc 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -565,6 +565,7 @@ fn make_format_args( &used, &args, &pieces, + &invalid_refs, detect_foreign_fmt, str_style, fmt_str, @@ -645,6 +646,7 @@ fn report_missing_placeholders( used: &[bool], args: &FormatArguments, pieces: &[parse::Piece<'_>], + invalid_refs: &[(usize, Option<Span>, PositionUsedAs, FormatArgPositionKind)], detect_foreign_fmt: bool, str_style: Option<usize>, fmt_str: &str, @@ -762,6 +764,31 @@ fn report_missing_placeholders( diag.span_label(fmt_span, "formatting specifier missing"); } + if !found_foreign && invalid_refs.is_empty() { + // Show example if user didn't use any format specifiers + let show_example = used.iter().all(|used| !used); + + if !show_example { + if unused.len() > 1 { + diag.note(format!("consider adding {} format specifiers", unused.len())); + } + } else { + let original_fmt_str = + if fmt_str.len() >= 1 { &fmt_str[..fmt_str.len() - 1] } else { "" }; + + let msg = if unused.len() == 1 { + "a format specifier".to_string() + } else { + format!("{} format specifiers", unused.len()) + }; + + let sugg = format!("\"{}{}\"", original_fmt_str, "{}".repeat(unused.len())); + let msg = format!("format specifiers use curly braces, consider adding {msg}"); + + diag.span_suggestion_verbose(fmt_span, msg, sugg, Applicability::MaybeIncorrect); + } + } + diag.emit(); } diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 37bab5be542..11b868f81a9 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -1,3 +1,5 @@ +//! The implementation of built-in macros which relate to the file system. + use std::path::{Path, PathBuf}; use std::rc::Rc; use std::sync::Arc; @@ -11,9 +13,11 @@ use rustc_expand::base::{ }; use rustc_expand::module::DirOwnership; use rustc_lint_defs::BuiltinLintDiag; -use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_parse::lexer::StripTokens; +use rustc_parse::parser::ForceCollect; use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; +use rustc_session::parse::ParseSess; use rustc_span::source_map::SourceMap; use rustc_span::{ByteSymbol, Pos, Span, Symbol}; use smallvec::SmallVec; @@ -23,11 +27,7 @@ use crate::util::{ check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr, }; -// These macros all relate to the file system; they either return -// the column/row/filename of the expression, or they include -// a given file into the current one. - -/// line!(): expands to the current line number +/// Expand `line!()` to the current line number. pub(crate) fn expand_line( cx: &mut ExtCtxt<'_>, sp: Span, @@ -42,7 +42,7 @@ pub(crate) fn expand_line( ExpandResult::Ready(MacEager::expr(cx.expr_u32(topmost, loc.line as u32))) } -/* column!(): expands to the current column number */ +/// Expand `column!()` to the current column number. pub(crate) fn expand_column( cx: &mut ExtCtxt<'_>, sp: Span, @@ -57,9 +57,7 @@ pub(crate) fn expand_column( ExpandResult::Ready(MacEager::expr(cx.expr_u32(topmost, loc.col.to_usize() as u32 + 1))) } -/// file!(): expands to the current filename */ -/// The source_file (`loc.file`) contains a bunch more information we could spit -/// out if we wanted. +/// Expand `file!()` to the current filename. pub(crate) fn expand_file( cx: &mut ExtCtxt<'_>, sp: Span, @@ -81,6 +79,7 @@ pub(crate) fn expand_file( ))) } +/// Expand `stringify!($input)`. pub(crate) fn expand_stringify( cx: &mut ExtCtxt<'_>, sp: Span, @@ -91,6 +90,7 @@ pub(crate) fn expand_stringify( ExpandResult::Ready(MacEager::expr(cx.expr_str(sp, Symbol::intern(&s)))) } +/// Expand `module_path!()` to (a textual representation of) the current module path. pub(crate) fn expand_mod( cx: &mut ExtCtxt<'_>, sp: Span, @@ -104,9 +104,9 @@ pub(crate) fn expand_mod( ExpandResult::Ready(MacEager::expr(cx.expr_str(sp, Symbol::intern(&string)))) } -/// include! : parse the given file as an expr -/// This is generally a bad idea because it's going to behave -/// unhygienically. +/// Expand `include!($input)`. +/// +/// This works in item and expression position. Notably, it doesn't work in pattern position. pub(crate) fn expand_include<'cx>( cx: &'cx mut ExtCtxt<'_>, sp: Span, @@ -116,39 +116,48 @@ pub(crate) fn expand_include<'cx>( let ExpandResult::Ready(mac) = get_single_str_from_tts(cx, sp, tts, "include!") else { return ExpandResult::Retry(()); }; - let file = match mac { - Ok(file) => file, + let path = match mac { + Ok(path) => path, Err(guar) => return ExpandResult::Ready(DummyResult::any(sp, guar)), }; // The file will be added to the code map by the parser - let file = match resolve_path(&cx.sess, file.as_str(), sp) { - Ok(f) => f, + let path = match resolve_path(&cx.sess, path.as_str(), sp) { + Ok(path) => path, Err(err) => { let guar = err.emit(); return ExpandResult::Ready(DummyResult::any(sp, guar)); } }; - let p = unwrap_or_emit_fatal(new_parser_from_file(cx.psess(), &file, Some(sp))); // If in the included file we have e.g., `mod bar;`, - // then the path of `bar.rs` should be relative to the directory of `file`. + // then the path of `bar.rs` should be relative to the directory of `path`. // See https://github.com/rust-lang/rust/pull/69838/files#r395217057 for a discussion. // `MacroExpander::fully_expand_fragment` later restores, so "stack discipline" is maintained. - let dir_path = file.parent().unwrap_or(&file).to_owned(); + let dir_path = path.parent().unwrap_or(&path).to_owned(); cx.current_expansion.module = Rc::new(cx.current_expansion.module.with_dir_path(dir_path)); cx.current_expansion.dir_ownership = DirOwnership::Owned { relative: None }; struct ExpandInclude<'a> { - p: Parser<'a>, + psess: &'a ParseSess, + path: PathBuf, node_id: ast::NodeId, + span: Span, } impl<'a> MacResult for ExpandInclude<'a> { - fn make_expr(mut self: Box<ExpandInclude<'a>>) -> Option<Box<ast::Expr>> { - let expr = parse_expr(&mut self.p).ok()?; - if self.p.token != token::Eof { - self.p.psess.buffer_lint( + fn make_expr(self: Box<ExpandInclude<'a>>) -> Option<Box<ast::Expr>> { + let mut p = unwrap_or_emit_fatal(new_parser_from_file( + self.psess, + &self.path, + // Don't strip frontmatter for backward compatibility, `---` may be the start of a + // manifold negation. FIXME: Ideally, we wouldn't strip shebangs here either. + StripTokens::Shebang, + Some(self.span), + )); + let expr = parse_expr(&mut p).ok()?; + if p.token != token::Eof { + p.psess.buffer_lint( INCOMPLETE_INCLUDE, - self.p.token.span, + p.token.span, self.node_id, BuiltinLintDiag::IncompleteInclude, ); @@ -156,24 +165,27 @@ pub(crate) fn expand_include<'cx>( Some(expr) } - fn make_items(mut self: Box<ExpandInclude<'a>>) -> Option<SmallVec<[Box<ast::Item>; 1]>> { + fn make_items(self: Box<ExpandInclude<'a>>) -> Option<SmallVec<[Box<ast::Item>; 1]>> { + let mut p = unwrap_or_emit_fatal(new_parser_from_file( + self.psess, + &self.path, + StripTokens::ShebangAndFrontmatter, + Some(self.span), + )); let mut ret = SmallVec::new(); loop { - match self.p.parse_item(ForceCollect::No) { + match p.parse_item(ForceCollect::No) { Err(err) => { err.emit(); break; } Ok(Some(item)) => ret.push(item), Ok(None) => { - if self.p.token != token::Eof { - self.p - .dcx() - .create_err(errors::ExpectedItem { - span: self.p.token.span, - token: &pprust::token_to_string(&self.p.token), - }) - .emit(); + if p.token != token::Eof { + p.dcx().emit_err(errors::ExpectedItem { + span: p.token.span, + token: &pprust::token_to_string(&p.token), + }); } break; @@ -184,10 +196,17 @@ pub(crate) fn expand_include<'cx>( } } - ExpandResult::Ready(Box::new(ExpandInclude { p, node_id: cx.current_expansion.lint_node_id })) + ExpandResult::Ready(Box::new(ExpandInclude { + psess: cx.psess(), + path, + node_id: cx.current_expansion.lint_node_id, + span: sp, + })) } -/// `include_str!`: read the given file, insert it as a literal string expr +/// Expand `include_str!($input)` to the content of the UTF-8-encoded file given by path `$input` as a string literal. +/// +/// This works in expression, pattern and statement position. pub(crate) fn expand_include_str( cx: &mut ExtCtxt<'_>, sp: Span, @@ -206,6 +225,7 @@ pub(crate) fn expand_include_str( Ok((bytes, bsp)) => match std::str::from_utf8(&bytes) { Ok(src) => { let interned_src = Symbol::intern(src); + // MacEager converts the expr into a pat if need be. MacEager::expr(cx.expr_str(cx.with_def_site_ctxt(bsp), interned_src)) } Err(utf8err) => { @@ -218,6 +238,9 @@ pub(crate) fn expand_include_str( }) } +/// Expand `include_bytes!($input)` to the content of the file given by path `$input`. +/// +/// This works in expression, pattern and statement position. pub(crate) fn expand_include_bytes( cx: &mut ExtCtxt<'_>, sp: Span, @@ -237,6 +260,7 @@ pub(crate) fn expand_include_bytes( // Don't care about getting the span for the raw bytes, // because the console can't really show them anyway. let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(ByteSymbol::intern(&bytes))); + // MacEager converts the expr into a pat if need be. MacEager::expr(expr) } Err(dummy) => dummy, diff --git a/compiler/rustc_codegen_gcc/src/consts.rs b/compiler/rustc_codegen_gcc/src/consts.rs index 619277eba8b..7fe8fc122b3 100644 --- a/compiler/rustc_codegen_gcc/src/consts.rs +++ b/compiler/rustc_codegen_gcc/src/consts.rs @@ -81,6 +81,8 @@ impl<'gcc, 'tcx> StaticCodegenMethods for CodegenCx<'gcc, 'tcx> { if global.to_rvalue().get_type() != val_llty { global.to_rvalue().set_type(val_llty); } + + // NOTE: Alignment from attributes has already been applied to the allocation. set_global_alignment(self, global, alloc.align); global.global_set_initializer_rvalue(value); diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index 9ec7b0f80ae..dc9bb743560 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -452,6 +452,8 @@ impl<'ll> CodegenCx<'ll, '_> { self.statics_to_rauw.borrow_mut().push((g, new_g)); new_g }; + + // NOTE: Alignment from attributes has already been applied to the allocation. set_global_alignment(self, g, alloc.align); llvm::set_initializer(g, v); diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 7eb5d302058..ab08125217f 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -908,6 +908,21 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( ) } "aarch64" => emit_aapcs_va_arg(bx, addr, target_ty), + "arm" => { + // Types wider than 16 bytes are not currently supported. Clang has special logic for + // such types, but `VaArgSafe` is not implemented for any type that is this large. + assert!(bx.cx.size_of(target_ty).bytes() <= 16); + + emit_ptr_va_arg( + bx, + addr, + target_ty, + PassMode::Direct, + SlotSize::Bytes4, + AllowHigherAlign::Yes, + ForceRightAdjust::No, + ) + } "s390x" => emit_s390x_va_arg(bx, addr, target_ty), "powerpc" => emit_powerpc_va_arg(bx, addr, target_ty), "powerpc64" | "powerpc64le" => emit_ptr_va_arg( diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 5f6976f5d00..6492ef73956 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -520,7 +520,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { LocalRef::Place(va_list) => { bx.va_end(va_list.val.llval); - // Explicitly end the lifetime of the `va_list`, this matters for LLVM. + // Explicitly end the lifetime of the `va_list`, improves LLVM codegen. bx.lifetime_end(va_list.val.llval, va_list.layout.size); } _ => bug!("C-variadic function must have a `VaList` place"), diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 06873313e2e..6b109e8b8e2 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -438,6 +438,10 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if fx.fn_abi.c_variadic && arg_index == fx.fn_abi.args.len() { let va_list = PlaceRef::alloca(bx, bx.layout_of(arg_ty)); + + // Explicitly start the lifetime of the `va_list`, improves LLVM codegen. + bx.lifetime_start(va_list.val.llval, va_list.layout.size); + bx.va_start(va_list.val.llval); return LocalRef::Place(va_list); diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 6ec85648d6d..ebcdb9461d0 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -953,6 +953,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // # Global allocations if let Some(global_alloc) = self.tcx.try_get_global_alloc(id) { + // NOTE: `static` alignment from attributes has already been applied to the allocation. let (size, align) = global_alloc.size_and_align(*self.tcx, self.typing_env); let mutbl = global_alloc.mutability(*self.tcx, self.typing_env); let kind = match global_alloc { diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 72bee345406..f667823723c 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,6 +1,6 @@ use rustc_hir::def_id::LocalDefId; use rustc_middle::mir; -use rustc_middle::mir::interpret::{AllocInit, Allocation, InterpResult, Pointer}; +use rustc_middle::mir::interpret::{AllocInit, Allocation, GlobalAlloc, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{TyCtxt, TypeVisitable, TypeVisitableExt}; use tracing::debug; @@ -38,7 +38,14 @@ pub(crate) fn create_static_alloc<'tcx>( static_def_id: LocalDefId, layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { - let alloc = Allocation::try_new(layout.size, layout.align.abi, AllocInit::Uninit, ())?; + // Inherit size and align from the `GlobalAlloc::Static` so we can avoid duplicating + // the alignment attribute logic. + let (size, align) = + GlobalAlloc::Static(static_def_id.into()).size_and_align(*ecx.tcx, ecx.typing_env); + assert_eq!(size, layout.size); + assert!(align >= layout.align.abi); + + let alloc = Allocation::try_new(size, align, AllocInit::Uninit, ())?; let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into()); assert_eq!(ecx.machine.static_root_ids, None); ecx.machine.static_root_ids = Some((alloc_id, static_def_id)); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index f3ed6042105..d00a4c35834 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -51,6 +51,7 @@ use rustc_lint::unerased_lint_store; use rustc_metadata::creader::MetadataLoader; use rustc_metadata::locator; use rustc_middle::ty::TyCtxt; +use rustc_parse::lexer::StripTokens; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::config::{ CG_OPTIONS, CrateType, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, Sysroot, @@ -1288,10 +1289,15 @@ fn warn_on_confusing_output_filename_flag( fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> { let mut parser = unwrap_or_emit_fatal(match &sess.io.input { - Input::File(file) => new_parser_from_file(&sess.psess, file, None), - Input::Str { name, input } => { - new_parser_from_source_str(&sess.psess, name.clone(), input.clone()) + Input::File(file) => { + new_parser_from_file(&sess.psess, file, StripTokens::ShebangAndFrontmatter, None) } + Input::Str { name, input } => new_parser_from_source_str( + &sess.psess, + name.clone(), + input.clone(), + StripTokens::ShebangAndFrontmatter, + ), }); parser.parse_inner_attributes() } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index e4e4866b64c..ed8aa71d59d 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -940,11 +940,27 @@ fn extract_symbol_from_pnr<'a>( { Ok(*symbol) } + ParseNtResult::Literal(expr) + if let ExprKind::Lit(lit @ Lit { kind: LitKind::Integer, symbol, suffix }) = + &expr.kind => + { + if lit.is_semantic_float() { + Err(dcx + .struct_err("floats are not supported as metavariables of `${concat(..)}`") + .with_span(span_err)) + } else if suffix.is_none() { + Ok(*symbol) + } else { + Err(dcx + .struct_err("integer metavariables of `${concat(..)}` must not be suffixed") + .with_span(span_err)) + } + } _ => Err(dcx .struct_err( "metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`", ) - .with_note("currently only string literals are supported") + .with_note("currently only string and integer literals are supported") .with_span(span_err)), } } diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 19f3cdbc549..79ab3cab22c 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -4,6 +4,7 @@ use std::path::{self, Path, PathBuf}; use rustc_ast::{AttrVec, Attribute, Inline, Item, ModSpans}; use rustc_attr_parsing::validate_attr; use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_parse::lexer::StripTokens; use rustc_parse::{exp, new_parser_from_file, unwrap_or_emit_fatal}; use rustc_session::Session; use rustc_session::parse::ParseSess; @@ -67,8 +68,12 @@ pub(crate) fn parse_external_mod( } // Actually parse the external file as a module. - let mut parser = - unwrap_or_emit_fatal(new_parser_from_file(&sess.psess, &mp.file_path, Some(span))); + let mut parser = unwrap_or_emit_fatal(new_parser_from_file( + &sess.psess, + &mp.file_path, + StripTokens::ShebangAndFrontmatter, + Some(span), + )); let (inner_attrs, items, inner_span) = parser.parse_mod(exp!(Eof)).map_err(|err| ModError::ParserError(err))?; attrs.extend(inner_attrs); diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 5b1d3d6d35b..295573f4492 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -8,7 +8,7 @@ use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult}; -use rustc_parse::lexer::nfc_normalize; +use rustc_parse::lexer::{StripTokens, nfc_normalize}; use rustc_parse::parser::Parser; use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; use rustc_proc_macro::bridge::{ @@ -485,8 +485,13 @@ impl server::FreeFunctions for Rustc<'_, '_> { fn literal_from_str(&mut self, s: &str) -> Result<Literal<Self::Span, Self::Symbol>, ()> { let name = FileName::proc_macro_source_code(s); - let mut parser = - unwrap_or_emit_fatal(new_parser_from_source_str(self.psess(), name, s.to_owned())); + + let mut parser = unwrap_or_emit_fatal(new_parser_from_source_str( + self.psess(), + name, + s.to_owned(), + StripTokens::Nothing, + )); let first_span = parser.token.span.data(); let minus_present = parser.eat(exp!(Minus)); diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index e81003b1897..129ab7eccb5 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -621,6 +621,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity gated!(rustc_align, Normal, template!(List: &["alignment"]), DuplicatesOk, EncodeCrossCrate::No, fn_align, experimental!(rustc_align)), + gated!(rustc_align_static, Normal, template!(List: &["alignment"]), DuplicatesOk, EncodeCrossCrate::No, static_align, experimental!(rustc_align_static)), ungated!( unsafe(Edition2024) export_name, Normal, template!(NameValueStr: "name", "https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute"), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 4f35bf63a1a..93e5588146e 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -632,6 +632,8 @@ declare_features! ( (unstable, simd_ffi, "1.0.0", Some(27731)), /// Allows specialization of implementations (RFC 1210). (incomplete, specialization, "1.7.0", Some(31844)), + /// Allows using `#[rustc_align_static(...)]` on static items. + (unstable, static_align, "CURRENT_RUSTC_VERSION", Some(146177)), /// Allows attributes on expressions and non-item statements. (unstable, stmt_expr_attributes, "1.6.0", Some(15701)), /// Allows lints part of the strict provenance effort. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index b4c1b61b9b5..ea11a99efbc 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -579,12 +579,18 @@ pub enum AttributeKind { /// Represents `#[naked]` Naked(Span), + /// Represents `#[no_core]` + NoCore(Span), + /// Represents `#[no_implicit_prelude]` NoImplicitPrelude(Span), /// Represents `#[no_mangle]` NoMangle(Span), + /// Represents `#[no_std]` + NoStd(Span), + /// Represents `#[non_exhaustive]` NonExhaustive(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 0e208be3497..55521c15854 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -64,8 +64,10 @@ impl AttributeKind { MoveSizeLimit { .. } => No, MustUse { .. } => Yes, Naked(..) => No, + NoCore(..) => No, NoImplicitPrelude(..) => No, - NoMangle(..) => Yes, // Needed for rustdoc + NoMangle(..) => Yes, // Needed for rustdoc + NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc Optimize(..) => No, ParenSugar(..) => No, diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 8af4740f376..95abe5c40dd 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -440,6 +440,43 @@ impl DefKind { | DefKind::ExternCrate => false, } } + + /// Returns `true` if `self` is a kind of definition that does not have its own + /// type-checking context, i.e. closure, coroutine or inline const. + #[inline] + pub fn is_typeck_child(self) -> bool { + match self { + DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody => true, + DefKind::Mod + | DefKind::Struct + | DefKind::Union + | DefKind::Enum + | DefKind::Variant + | DefKind::Trait + | DefKind::TyAlias + | DefKind::ForeignTy + | DefKind::TraitAlias + | DefKind::AssocTy + | DefKind::TyParam + | DefKind::Fn + | DefKind::Const + | DefKind::ConstParam + | DefKind::Static { .. } + | DefKind::Ctor(_, _) + | DefKind::AssocFn + | DefKind::AssocConst + | DefKind::Macro(_) + | DefKind::ExternCrate + | DefKind::Use + | DefKind::ForeignMod + | DefKind::AnonConst + | DefKind::OpaqueTy + | DefKind::Field + | DefKind::LifetimeParam + | DefKind::GlobalAsm + | DefKind::Impl { .. } => false, + } + } } /// The resolution of a path or export. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ae03121e5f7..75551fe4c19 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2616,6 +2616,18 @@ impl Expr<'_> { ) | ( ExprKind::Struct( + QPath::LangItem(LangItem::RangeToInclusiveCopy, _), + [val1], + StructTailExpr::None, + ), + ExprKind::Struct( + QPath::LangItem(LangItem::RangeToInclusiveCopy, _), + [val2], + StructTailExpr::None, + ), + ) + | ( + ExprKind::Struct( QPath::LangItem(LangItem::RangeFrom, _), [val1], StructTailExpr::None, @@ -2705,7 +2717,8 @@ pub fn is_range_literal(expr: &Expr<'_>) -> bool { | LangItem::RangeToInclusive | LangItem::RangeCopy | LangItem::RangeFromCopy - | LangItem::RangeInclusiveCopy, + | LangItem::RangeInclusiveCopy + | LangItem::RangeToInclusiveCopy, .. ) ), diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 0464665b41f..67d2f15d414 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -422,6 +422,7 @@ language_item_table! { RangeFromCopy, sym::RangeFromCopy, range_from_copy_struct, Target::Struct, GenericRequirement::None; RangeCopy, sym::RangeCopy, range_copy_struct, Target::Struct, GenericRequirement::None; RangeInclusiveCopy, sym::RangeInclusiveCopy, range_inclusive_copy_struct, Target::Struct, GenericRequirement::None; + RangeToInclusiveCopy, sym::RangeToInclusiveCopy, range_to_inclusive_copy_struct, Target::Struct, GenericRequirement::None; String, sym::String, string, Target::Struct, GenericRequirement::None; CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index b59dc4bd132..126ffabd448 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -174,12 +174,6 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen } }; - if let Node::TraitItem(item) = node { - let mut bounds = Vec::new(); - icx.lowerer().add_default_trait_item_bounds(item, &mut bounds); - predicates.extend(bounds); - } - let generics = tcx.generics_of(def_id); // Below we'll consider the bounds on the type parameters (including `Self`) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index d14aef8ace4..99dc8e6e522 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -1,12 +1,13 @@ +use std::assert_matches::assert_matches; use std::ops::ControlFlow; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::struct_span_code_err; use rustc_hir as hir; +use rustc_hir::PolyTraitRef; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; -use rustc_hir::{AmbigArg, PolyTraitRef}; use rustc_middle::bug; use rustc_middle::ty::{ self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, @@ -230,122 +231,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - /// Checks whether `Self: DefaultAutoTrait` bounds should be added on trait super bounds - /// or associated items. - /// - /// To keep backward compatibility with existing code, `experimental_default_bounds` bounds - /// should be added everywhere, including super bounds. However this causes a huge performance - /// costs. For optimization purposes instead of adding default supertraits, bounds - /// are added to the associated items: - /// - /// ```ignore(illustrative) - /// // Default bounds are generated in the following way: - /// trait Trait { - /// fn foo(&self) where Self: Leak {} - /// } - /// - /// // instead of this: - /// trait Trait: Leak { - /// fn foo(&self) {} - /// } - /// ``` - /// It is not always possible to do this because of backward compatibility: - /// - /// ```ignore(illustrative) - /// pub trait Trait<Rhs = Self> {} - /// pub trait Trait1 : Trait {} - /// //~^ ERROR: `Rhs` requires `DefaultAutoTrait`, but `Self` is not `DefaultAutoTrait` - /// ``` - /// - /// or: - /// - /// ```ignore(illustrative) - /// trait Trait { - /// type Type where Self: Sized; - /// } - /// trait Trait2<T> : Trait<Type = T> {} - /// //~^ ERROR: `DefaultAutoTrait` required for `Trait2`, by implicit `Self: DefaultAutoTrait` in `Trait::Type` - /// ``` - /// - /// Therefore, `experimental_default_bounds` are still being added to supertraits if - /// the `SelfTyParam` or `AssocItemConstraint` were found in a trait header. - fn requires_default_supertraits( - &self, - hir_bounds: &'tcx [hir::GenericBound<'tcx>], - hir_generics: &'tcx hir::Generics<'tcx>, - ) -> bool { - struct TraitInfoCollector; - - impl<'tcx> hir::intravisit::Visitor<'tcx> for TraitInfoCollector { - type Result = ControlFlow<()>; - - fn visit_assoc_item_constraint( - &mut self, - _constraint: &'tcx hir::AssocItemConstraint<'tcx>, - ) -> Self::Result { - ControlFlow::Break(()) - } - - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { - if matches!( - &t.kind, - hir::TyKind::Path(hir::QPath::Resolved( - _, - hir::Path { res: hir::def::Res::SelfTyParam { .. }, .. }, - )) - ) { - return ControlFlow::Break(()); - } - hir::intravisit::walk_ty(self, t) - } - } - - let mut found = false; - for bound in hir_bounds { - found |= hir::intravisit::walk_param_bound(&mut TraitInfoCollector, bound).is_break(); - } - found |= hir::intravisit::walk_generics(&mut TraitInfoCollector, hir_generics).is_break(); - found - } - - /// Implicitly add `Self: DefaultAutoTrait` clauses on trait associated items if - /// they are not added as super trait bounds to the trait itself. See - /// `requires_default_supertraits` for more information. - pub(crate) fn add_default_trait_item_bounds( - &self, - trait_item: &hir::TraitItem<'tcx>, - bounds: &mut Vec<(ty::Clause<'tcx>, Span)>, - ) { - let tcx = self.tcx(); - if !tcx.sess.opts.unstable_opts.experimental_default_bounds { - return; - } - - let parent = tcx.local_parent(trait_item.hir_id().owner.def_id); - let hir::Node::Item(parent_trait) = tcx.hir_node_by_def_id(parent) else { - unreachable!(); - }; - - let (trait_generics, trait_bounds) = match parent_trait.kind { - hir::ItemKind::Trait(_, _, _, _, generics, supertraits, _) => (generics, supertraits), - hir::ItemKind::TraitAlias(_, generics, supertraits) => (generics, supertraits), - _ => unreachable!(), - }; - - if !self.requires_default_supertraits(trait_bounds, trait_generics) { - let self_ty_where_predicates = (parent, trait_item.generics.predicates); - self.add_default_traits( - bounds, - tcx.types.self_param, - &[], - Some(self_ty_where_predicates), - trait_item.span, - ); - } - } - - /// Lazily sets `experimental_default_bounds` to true on trait super bounds. - /// See `requires_default_supertraits` for more information. + /// Adds `experimental_default_bounds` bounds to the supertrait bounds. pub(crate) fn add_default_super_traits( &self, trait_def_id: LocalDefId, @@ -354,21 +240,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_generics: &'tcx hir::Generics<'tcx>, span: Span, ) { - if !self.tcx().sess.opts.unstable_opts.experimental_default_bounds { + assert_matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias); + + // Supertraits for auto trait are unsound according to the unstable book: + // https://doc.rust-lang.org/beta/unstable-book/language-features/auto-traits.html#supertraits + if self.tcx().trait_is_auto(trait_def_id.to_def_id()) { return; } - assert!(matches!(self.tcx().def_kind(trait_def_id), DefKind::Trait | DefKind::TraitAlias)); - if self.requires_default_supertraits(hir_bounds, hir_generics) { - let self_ty_where_predicates = (trait_def_id, hir_generics.predicates); - self.add_default_traits( - bounds, - self.tcx().types.self_param, - hir_bounds, - Some(self_ty_where_predicates), - span, - ); - } + self.add_default_traits( + bounds, + self.tcx().types.self_param, + hir_bounds, + Some((trait_def_id, hir_generics.predicates)), + span, + ); } pub(crate) fn add_default_traits( diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 2562ab7542a..6659aff7111 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -238,7 +238,8 @@ pub fn check_crate(tcx: TyCtxt<'_>) { _ => (), } // Skip `AnonConst`s because we feed their `type_of`. - if !matches!(def_kind, DefKind::AnonConst) { + // Also skip items for which typeck forwards to parent typeck. + if !(matches!(def_kind, DefKind::AnonConst) || def_kind.is_typeck_child()) { tcx.ensure_ok().typeck(item_def_id); } // Ensure we generate the new `DefId` before finishing `check_crate`. diff --git a/compiler/rustc_index/src/idx.rs b/compiler/rustc_index/src/idx.rs index 33f406e2113..9cd7134659c 100644 --- a/compiler/rustc_index/src/idx.rs +++ b/compiler/rustc_index/src/idx.rs @@ -130,7 +130,22 @@ impl<I: Idx, T> IntoSliceIdx<I, [T]> for core::range::RangeFrom<I> { impl<I: Idx, T> IntoSliceIdx<I, [T]> for core::range::RangeInclusive<I> { type Output = core::range::RangeInclusive<usize>; #[inline] + #[cfg(bootstrap)] fn into_slice_idx(self) -> Self::Output { core::range::RangeInclusive { start: self.start.index(), end: self.end.index() } } + #[inline] + #[cfg(not(bootstrap))] + fn into_slice_idx(self) -> Self::Output { + core::range::RangeInclusive { start: self.start.index(), last: self.last.index() } + } +} + +#[cfg(all(feature = "nightly", not(bootstrap)))] +impl<I: Idx, T> IntoSliceIdx<I, [T]> for core::range::RangeToInclusive<I> { + type Output = core::range::RangeToInclusive<usize>; + #[inline] + fn into_slice_idx(self) -> Self::Output { + core::range::RangeToInclusive { last: self.last.index() } + } } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 4c820b8877b..b52c5b4cd66 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -13,7 +13,8 @@ use rustc_lint::LintStore; use rustc_middle::ty; use rustc_middle::ty::CurrentGcx; use rustc_middle::util::Providers; -use rustc_parse::new_parser_from_simple_source_str; +use rustc_parse::lexer::StripTokens; +use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::attr::AllowLeadingUnsafe; use rustc_query_impl::QueryCtxt; use rustc_query_system::query::print_query_stack; @@ -68,7 +69,8 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg { }; } - match new_parser_from_simple_source_str(&psess, filename, s.to_string()) { + match new_parser_from_source_str(&psess, filename, s.to_string(), StripTokens::Nothing) + { Ok(mut parser) => match parser.parse_meta_item(AllowLeadingUnsafe::No) { Ok(meta_item) if parser.token == token::Eof => { if meta_item.path.segments.len() != 1 { @@ -166,13 +168,15 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> Ch error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`") }; - let mut parser = match new_parser_from_simple_source_str(&psess, filename, s.to_string()) { - Ok(parser) => parser, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - expected_error(); - } - }; + let mut parser = + match new_parser_from_source_str(&psess, filename, s.to_string(), StripTokens::Nothing) + { + Ok(parser) => parser, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + expected_error(); + } + }; let meta_item = match parser.parse_meta_item(AllowLeadingUnsafe::No) { Ok(meta_item) if parser.token == token::Eof => meta_item, diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 6d9751d7d4d..d39219bfd66 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -28,6 +28,7 @@ use rustc_middle::arena::Arena; use rustc_middle::dep_graph::DepsType; use rustc_middle::ty::{self, CurrentGcx, GlobalCtxt, RegisteredTools, TyCtxt}; use rustc_middle::util::Providers; +use rustc_parse::lexer::StripTokens; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_passes::{abi_test, input_stats, layout_test}; use rustc_resolve::{Resolver, ResolverOutputs}; @@ -52,10 +53,18 @@ pub fn parse<'a>(sess: &'a Session) -> ast::Crate { let mut krate = sess .time("parse_crate", || { let mut parser = unwrap_or_emit_fatal(match &sess.io.input { - Input::File(file) => new_parser_from_file(&sess.psess, file, None), - Input::Str { input, name } => { - new_parser_from_source_str(&sess.psess, name.clone(), input.clone()) - } + Input::File(file) => new_parser_from_file( + &sess.psess, + file, + StripTokens::ShebangAndFrontmatter, + None, + ), + Input::Str { input, name } => new_parser_from_source_str( + &sess.psess, + name.clone(), + input.clone(), + StripTokens::ShebangAndFrontmatter, + ), }); parser.parse_crate_mod() }) diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs index 5513c703f1d..93f067d0983 100644 --- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs +++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs @@ -47,7 +47,8 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable { .explicit_super_predicates_of(def_id) .iter_identity_copied() .filter_map(|(pred, _)| pred.as_trait_clause()) - .filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), hir::LangItem::MetaSized)); + .filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), hir::LangItem::MetaSized)) + .filter(|pred| !cx.tcx.is_default_trait(pred.def_id())); if direct_super_traits_iter.count() > 1 { cx.emit_span_lint( MULTIPLE_SUPERTRAIT_UPCASTABLE, diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index bed99a4ff2a..9762e0f21da 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -386,7 +386,16 @@ impl<'tcx> GlobalAlloc<'tcx> { .expect("statics should not have generic parameters"); let layout = tcx.layout_of(typing_env.as_query_input(ty)).unwrap(); assert!(layout.is_sized()); - (layout.size, layout.align.abi) + + // Take over-alignment from attributes into account. + let align = match tcx.codegen_fn_attrs(def_id).alignment { + Some(align_from_attribute) => { + Ord::max(align_from_attribute, layout.align.abi) + } + None => layout.align.abi, + }; + + (layout.size, align) } } GlobalAlloc::Memory(alloc) => { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index a7d99f513a1..f9d0a5f0a3b 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -78,8 +78,9 @@ impl<'dis, 'de, 'tcx> MirDumper<'dis, 'de, 'tcx> { pub fn new(tcx: TyCtxt<'tcx>, pass_name: &'static str, body: &Body<'tcx>) -> Option<Self> { let dump_enabled = if let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir { // see notes on #41697 below - let node_path = - ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id())); + let node_path = ty::print::with_no_trimmed_paths!( + ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id())) + ); filters.split('|').any(|or_filter| { or_filter.split('&').all(|and_filter| { let and_filter_trimmed = and_filter.trim(); @@ -173,9 +174,10 @@ impl<'dis, 'de, 'tcx> MirDumper<'dis, 'de, 'tcx> { // trigger `type_of`, and this can run while we are already attempting to evaluate `type_of`. pub fn dump_mir_to_writer(&self, body: &Body<'tcx>, w: &mut dyn io::Write) -> io::Result<()> { // see notes on #41697 above - let def_path = ty::print::with_forced_impl_filename_line!( - self.tcx().def_path_str(body.source.def_id()) - ); + let def_path = + ty::print::with_no_trimmed_paths!(ty::print::with_forced_impl_filename_line!( + self.tcx().def_path_str(body.source.def_id()) + )); // ignore-tidy-odd-backticks the literal below is fine write!(w, "// MIR for `{def_path}")?; match body.source.promoted { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 218ac2cfbc1..8ea767dccd3 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -651,7 +651,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> { | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"), } - let trait_impls = tcx.trait_impls_of(trait_def_id); + #[allow(rustc::usage_of_type_ir_traits)] + self.for_each_blanket_impl(trait_def_id, f) + } + fn for_each_blanket_impl(self, trait_def_id: DefId, mut f: impl FnMut(DefId)) { + let trait_impls = self.trait_impls_of(trait_def_id); for &impl_def_id in trait_impls.blanket_impls() { f(impl_def_id); } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 96fbdf44791..029586a9c55 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -608,10 +608,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns `true` if `def_id` refers to a definition that does not have its own /// type-checking context, i.e. closure, coroutine or inline const. pub fn is_typeck_child(self, def_id: DefId) -> bool { - matches!( - self.def_kind(def_id), - DefKind::Closure | DefKind::InlineConst | DefKind::SyntheticCoroutineBody - ) + self.def_kind(def_id).is_typeck_child() } /// Returns `true` if `def_id` refers to a trait (i.e., `trait Foo { ... }`). diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index e3cf0330b14..fb777496e31 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -11,8 +11,9 @@ use rustc_type_ir::lang_items::SolverTraitLangItem; use rustc_type_ir::search_graph::CandidateHeadUsages; use rustc_type_ir::solve::SizedTraitKind; use rustc_type_ir::{ - self as ty, Interner, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitableExt as _, TypeVisitor, TypingMode, Upcast as _, elaborate, + self as ty, Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast, + elaborate, }; use tracing::{debug, instrument}; @@ -187,6 +188,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, impl_def_id: I::ImplId, + then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>, ) -> Result<Candidate<I>, NoSolution>; /// If the predicate contained an error, we want to avoid emitting unnecessary trait @@ -365,6 +367,15 @@ pub(super) enum AssembleCandidatesFrom { EnvAndBounds, } +impl AssembleCandidatesFrom { + fn should_assemble_impl_candidates(&self) -> bool { + match self { + AssembleCandidatesFrom::All => true, + AssembleCandidatesFrom::EnvAndBounds => false, + } + } +} + /// This is currently used to track the [CandidateHeadUsages] of all failed `ParamEnv` /// candidates. This is then used to ignore their head usages in case there's another /// always applicable `ParamEnv` candidate. Look at how `param_env_head_usages` is @@ -397,14 +408,15 @@ where return (candidates, failed_candidate_info); }; + let goal: Goal<I, G> = goal + .with(self.cx(), goal.predicate.with_replaced_self_ty(self.cx(), normalized_self_ty)); + if normalized_self_ty.is_ty_var() { debug!("self type has been normalized to infer"); - candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + self.try_assemble_bounds_via_registered_opaques(goal, assemble_from, &mut candidates); return (candidates, failed_candidate_info); } - let goal: Goal<I, G> = goal - .with(self.cx(), goal.predicate.with_replaced_self_ty(self.cx(), normalized_self_ty)); // Vars that show up in the rest of the goal substs may have been constrained by // normalizing the self type as well, since type variables are not uniquified. let goal = self.resolve_vars_if_possible(goal); @@ -484,8 +496,9 @@ where if cx.impl_is_default(impl_def_id) { return; } - - match G::consider_impl_candidate(self, goal, impl_def_id) { + match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + }) { Ok(candidate) => candidates.push(candidate), Err(NoSolution) => (), } @@ -943,6 +956,116 @@ where } } + /// If the self type is the hidden type of an opaque, try to assemble + /// candidates for it by consider its item bounds and by using blanket + /// impls. This is used to incompletely guide type inference when handling + /// non-defining uses in the defining scope. + /// + /// We otherwise just fail fail with ambiguity. Even if we're using an + /// opaque type item bound or a blank impls, we still force its certainty + /// to be `Maybe` so that we properly prove this goal later. + /// + /// See <https://github.com/rust-lang/trait-system-refactor-initiative/issues/182> + /// for why this is necessary. + fn try_assemble_bounds_via_registered_opaques<G: GoalKind<D>>( + &mut self, + goal: Goal<I, G>, + assemble_from: AssembleCandidatesFrom, + candidates: &mut Vec<Candidate<I>>, + ) { + let self_ty = goal.predicate.self_ty(); + // If the self type is sub unified with any opaque type, we + // also look at blanket impls for it. + let mut assemble_blanket_impls = false; + for alias_ty in self.opaques_with_sub_unified_hidden_type(self_ty) { + assemble_blanket_impls = true; + debug!("self ty is sub unified with {alias_ty:?}"); + + struct ReplaceOpaque<I: Interner> { + cx: I, + alias_ty: ty::AliasTy<I>, + self_ty: I::Ty, + } + impl<I: Interner> TypeFolder<I> for ReplaceOpaque<I> { + fn cx(&self) -> I { + self.cx + } + fn fold_ty(&mut self, ty: I::Ty) -> I::Ty { + if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() { + if alias_ty == self.alias_ty { + return self.self_ty; + } + } + ty.super_fold_with(self) + } + } + + // We look at all item-bounds of the opaque, replacing the + // opaque with the current self type before considering + // them as a candidate. Imagine e've got `?x: Trait<?y>` + // and `?x` has been sub-unified with the hidden type of + // `impl Trait<u32>`, We take the item bound `opaque: Trait<u32>` + // and replace all occurrences of `opaque` with `?x`. This results + // in a `?x: Trait<u32>` alias-bound candidate. + for item_bound in self + .cx() + .item_self_bounds(alias_ty.def_id) + .iter_instantiated(self.cx(), alias_ty.args) + { + let assumption = + item_bound.fold_with(&mut ReplaceOpaque { cx: self.cx(), alias_ty, self_ty }); + candidates.extend(G::probe_and_match_goal_against_assumption( + self, + CandidateSource::AliasBound, + goal, + assumption, + |ecx| { + // We want to reprove this goal once we've inferred the + // hidden type, so we force the certainty to `Maybe`. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + }, + )); + } + } + + // We also need to consider blanket impls for not-yet-defined opaque types. + // + // See tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs for an example. + if assemble_blanket_impls && assemble_from.should_assemble_impl_candidates() { + let cx = self.cx(); + cx.for_each_blanket_impl(goal.predicate.trait_def_id(cx), |impl_def_id| { + // For every `default impl`, there's always a non-default `impl` + // that will *also* apply. There's no reason to register a candidate + // for this impl, since it is *not* proof that the trait goal holds. + if cx.impl_is_default(impl_def_id) { + return; + } + + match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| { + if ecx.shallow_resolve(self_ty).is_ty_var() { + // We force the certainty of impl candidates to be `Maybe`. + let certainty = certainty.and(Certainty::AMBIGUOUS); + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + } else { + // We don't want to use impls if they constrain the opaque. + // + // FIXME(trait-system-refactor-initiative#229): This isn't + // perfect yet as it still allows us to incorrectly constrain + // other inference variables. + Err(NoSolution) + } + }) { + Ok(candidate) => candidates.push(candidate), + Err(NoSolution) => (), + } + }); + } + + if candidates.is_empty() { + candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity)); + } + } + /// Assemble and merge candidates for goals which are related to an underlying trait /// goal. Right now, this is normalizes-to and host effect goals. /// diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 6646857a136..cb72c1cd92b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -124,6 +124,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, impl_def_id: I::ImplId, + then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>, ) -> Result<Candidate<I>, NoSolution> { let cx = ecx.cx(); @@ -175,7 +176,7 @@ where }); ecx.add_goals(GoalSource::ImplWhereBound, const_conditions); - ecx.evaluate_added_goals_and_make_canonical_response(certainty) + then(ecx, certainty) }) } 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 5cd597d4fe6..3e3a5246f3d 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 @@ -407,20 +407,21 @@ where // If we have run this goal before, and it was stalled, check that any of the goal's // args have changed. Otherwise, we don't need to re-run the goal because it'll remain // stalled, since it'll canonicalize the same way and evaluation is pure. - if let Some(stalled_on) = stalled_on - && !stalled_on.stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) - && !self - .delegate - .opaque_types_storage_num_entries() - .needs_reevaluation(stalled_on.num_opaques) + if let Some(GoalStalledOn { num_opaques, ref stalled_vars, ref sub_roots, stalled_cause }) = + stalled_on + && !stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value)) + && !sub_roots + .iter() + .any(|&vid| self.delegate.sub_unification_table_root_var(vid) != vid) + && !self.delegate.opaque_types_storage_num_entries().needs_reevaluation(num_opaques) { return Ok(( NestedNormalizationGoals::empty(), GoalEvaluation { goal, - certainty: Certainty::Maybe(stalled_on.stalled_cause), + certainty: Certainty::Maybe(stalled_cause), has_changed: HasChanged::No, - stalled_on: Some(stalled_on), + stalled_on, }, )); } @@ -476,16 +477,6 @@ where HasChanged::No => { let mut stalled_vars = orig_values; - // Remove the canonicalized universal vars, since we only care about stalled existentials. - stalled_vars.retain(|arg| match arg.kind() { - ty::GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Infer(_)), - ty::GenericArgKind::Const(ct) => { - matches!(ct.kind(), ty::ConstKind::Infer(_)) - } - // Lifetimes can never stall goals. - ty::GenericArgKind::Lifetime(_) => false, - }); - // Remove the unconstrained RHS arg, which is expected to have changed. if let Some(normalizes_to) = goal.predicate.as_normalizes_to() { let normalizes_to = normalizes_to.skip_binder(); @@ -497,6 +488,27 @@ where stalled_vars.swap_remove(idx); } + // Remove the canonicalized universal vars, since we only care about stalled existentials. + let mut sub_roots = Vec::new(); + stalled_vars.retain(|arg| match arg.kind() { + // Lifetimes can never stall goals. + ty::GenericArgKind::Lifetime(_) => false, + ty::GenericArgKind::Type(ty) => match ty.kind() { + ty::Infer(ty::TyVar(vid)) => { + sub_roots.push(self.delegate.sub_unification_table_root_var(vid)); + true + } + ty::Infer(_) => true, + ty::Param(_) | ty::Placeholder(_) => false, + _ => unreachable!("unexpected orig_value: {ty:?}"), + }, + ty::GenericArgKind::Const(ct) => match ct.kind() { + ty::ConstKind::Infer(_) => true, + ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(_) => false, + _ => unreachable!("unexpected orig_value: {ct:?}"), + }, + }); + Some(GoalStalledOn { num_opaques: canonical_goal .canonical @@ -505,6 +517,7 @@ where .opaque_types .len(), stalled_vars, + sub_roots, stalled_cause, }) } @@ -1047,6 +1060,10 @@ where self.delegate.resolve_vars_if_possible(value) } + pub(super) fn shallow_resolve(&self, ty: I::Ty) -> I::Ty { + self.delegate.shallow_resolve(ty) + } + pub(super) fn eager_resolve_region(&self, r: I::Region) -> I::Region { if let ty::ReVar(vid) = r.kind() { self.delegate.opportunistic_resolve_lt_var(vid) @@ -1163,6 +1180,33 @@ where ) -> bool { may_use_unstable_feature(&**self.delegate, param_env, symbol) } + + pub(crate) fn opaques_with_sub_unified_hidden_type( + &self, + self_ty: I::Ty, + ) -> impl Iterator<Item = ty::AliasTy<I>> + use<'a, D, I> { + let delegate = self.delegate; + delegate + .clone_opaque_types_lookup_table() + .into_iter() + .chain(delegate.clone_duplicate_opaque_types()) + .filter_map(move |(key, hidden_ty)| { + if let ty::Infer(ty::TyVar(self_vid)) = self_ty.kind() { + if let ty::Infer(ty::TyVar(hidden_vid)) = hidden_ty.kind() { + if delegate.sub_unification_table_root_var(self_vid) + == delegate.sub_unification_table_root_var(hidden_vid) + { + return Some(ty::AliasTy::new_from_args( + delegate.cx(), + key.def_id.into(), + key.args, + )); + } + } + } + None + }) + } } /// Eagerly replace aliases with inference variables, emitting `AliasRelate` diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index db3460c4ff6..cd27c9c26c1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -24,7 +24,7 @@ mod trait_goals; use derive_where::derive_where; use rustc_type_ir::inherent::*; pub use rustc_type_ir::solve::*; -use rustc_type_ir::{self as ty, Interner, TypingMode}; +use rustc_type_ir::{self as ty, Interner, TyVid, TypingMode}; use tracing::instrument; pub use self::eval_ctxt::{ @@ -418,11 +418,6 @@ pub struct GoalEvaluation<I: Interner> { pub has_changed: HasChanged, /// If the [`Certainty`] was `Maybe`, then keep track of whether the goal has changed /// before rerunning it. - /// - /// We knowingly ignore the `sub_root` of our inference variables here. This means we - /// may not reevaluate a goal even though a change to the `sub_root` could cause a goal - /// to make progress. Tracking them adds additional complexity for an incredibly minor - /// type inference improvement. We could look into properly handling this in the future. pub stalled_on: Option<GoalStalledOn<I>>, } @@ -431,6 +426,7 @@ pub struct GoalEvaluation<I: Interner> { pub struct GoalStalledOn<I: Interner> { pub num_opaques: usize, pub stalled_vars: Vec<I::GenericArg>, + pub sub_roots: Vec<TyVid>, /// The cause that will be returned on subsequent evaluations if this goal remains stalled. pub stalled_cause: MaybeCause, } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 6e6be829f52..54b92ebac1d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -194,6 +194,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, NormalizesTo<I>>, impl_def_id: I::ImplId, + then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>, ) -> Result<Candidate<I>, NoSolution> { let cx = ecx.cx(); @@ -314,8 +315,7 @@ where // nested goal for consistency. ty::TypingMode::Coherence => { ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + return then(ecx, Certainty::Yes); } ty::TypingMode::Analysis { .. } | ty::TypingMode::Borrowck { .. } @@ -325,8 +325,7 @@ where goal, goal.predicate.alias, ); - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + return then(ecx, Certainty::Yes); } } } else { diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 3e720a47f32..a69e867289c 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -55,6 +55,7 @@ where ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, TraitPredicate<I>>, impl_def_id: I::ImplId, + then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>, ) -> Result<Candidate<I>, NoSolution> { let cx = ecx.cx(); @@ -112,7 +113,7 @@ where .map(|pred| goal.with(cx, pred)), ); - ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty) + then(ecx, maximal_certainty) }) } diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index f5f081efc49..51019db7c00 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -45,7 +45,7 @@ pub(crate) struct UnmatchedDelim { } /// Which tokens should be stripped before lexing the tokens. -pub(crate) enum StripTokens { +pub enum StripTokens { /// Strip both shebang and frontmatter. ShebangAndFrontmatter, /// Strip the shebang but not frontmatter. diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index d8792d7af4c..88b67d792de 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -54,29 +54,18 @@ pub fn unwrap_or_emit_fatal<T>(expr: Result<T, Vec<Diag<'_>>>) -> T { } } -/// Creates a new parser from a source string. On failure, the errors must be consumed via -/// `unwrap_or_emit_fatal`, `emit`, `cancel`, etc., otherwise a panic will occur when they are -/// dropped. -pub fn new_parser_from_source_str( - psess: &ParseSess, - name: FileName, - source: String, -) -> Result<Parser<'_>, Vec<Diag<'_>>> { - let source_file = psess.source_map().new_source_file(name, source); - new_parser_from_source_file(psess, source_file, StripTokens::ShebangAndFrontmatter) -} - -/// Creates a new parser from a simple (no shebang, no frontmatter) source string. +/// Creates a new parser from a source string. /// /// On failure, the errors must be consumed via `unwrap_or_emit_fatal`, `emit`, `cancel`, /// etc., otherwise a panic will occur when they are dropped. -pub fn new_parser_from_simple_source_str( +pub fn new_parser_from_source_str( psess: &ParseSess, name: FileName, source: String, + strip_tokens: StripTokens, ) -> Result<Parser<'_>, Vec<Diag<'_>>> { let source_file = psess.source_map().new_source_file(name, source); - new_parser_from_source_file(psess, source_file, StripTokens::Nothing) + new_parser_from_source_file(psess, source_file, strip_tokens) } /// Creates a new parser from a filename. On failure, the errors must be consumed via @@ -87,6 +76,7 @@ pub fn new_parser_from_simple_source_str( pub fn new_parser_from_file<'a>( psess: &'a ParseSess, path: &Path, + strip_tokens: StripTokens, sp: Option<Span>, ) -> Result<Parser<'a>, Vec<Diag<'a>>> { let sm = psess.source_map(); @@ -110,7 +100,7 @@ pub fn new_parser_from_file<'a>( } err.emit(); }); - new_parser_from_source_file(psess, source_file, StripTokens::ShebangAndFrontmatter) + new_parser_from_source_file(psess, source_file, strip_tokens) } pub fn utf8_error<E: EmissionGuarantee>( @@ -172,6 +162,9 @@ fn new_parser_from_source_file( Ok(parser) } +/// Given a source string, produces a sequence of token trees. +/// +/// NOTE: This only strips shebangs, not frontmatter! pub fn source_str_to_stream( psess: &ParseSess, name: FileName, @@ -179,13 +172,16 @@ pub fn source_str_to_stream( override_span: Option<Span>, ) -> Result<TokenStream, Vec<Diag<'_>>> { let source_file = psess.source_map().new_source_file(name, source); - // used mainly for `proc_macro` and the likes, not for our parsing purposes, so don't parse - // frontmatters as frontmatters, but for compatibility reason still strip the shebang + // FIXME(frontmatter): Consider stripping frontmatter in a future edition. We can't strip them + // in the current edition since that would be breaking. + // See also <https://github.com/rust-lang/rust/issues/145520>. + // Alternatively, stop stripping shebangs here, too, if T-lang and crater approve. source_file_to_stream(psess, source_file, override_span, StripTokens::Shebang) } -/// Given a source file, produces a sequence of token trees. Returns any buffered errors from -/// parsing the token stream. +/// Given a source file, produces a sequence of token trees. +/// +/// Returns any buffered errors from parsing the token stream. fn source_file_to_stream<'psess>( psess: &'psess ParseSess, source_file: Arc<SourceFile>, diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index a6e7266e71b..e645fb47b9e 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -22,6 +22,7 @@ use rustc_span::{ }; use termcolor::WriteColor; +use crate::lexer::StripTokens; use crate::parser::{ForceCollect, Parser}; use crate::{new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; @@ -35,6 +36,7 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> { psess, PathBuf::from("bogofile").into(), source_str, + StripTokens::Nothing, )) } @@ -2240,7 +2242,7 @@ fn parse_item_from_source_str( source: String, psess: &ParseSess, ) -> PResult<'_, Option<Box<ast::Item>>> { - unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source)) + unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source, StripTokens::Nothing)) .parse_item(ForceCollect::No) } @@ -2520,7 +2522,8 @@ fn ttdelim_span() { source: String, psess: &ParseSess, ) -> PResult<'_, Box<ast::Expr>> { - unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source)).parse_expr() + unwrap_or_emit_fatal(new_parser_from_source_str(psess, name, source, StripTokens::Nothing)) + .parse_expr() } create_default_session_globals_then(|| { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6168647183f..23aaafac934 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -92,10 +92,10 @@ fn can_continue_type_after_non_fn_ident(t: &Token) -> bool { } fn can_begin_dyn_bound_in_edition_2015(t: &Token) -> bool { - // `Not`, `Tilde` & `Const` are deliberately not part of this list to + // `!`, `const`, `[`, `async` are deliberately not part of this list to // contain the number of potential regressions esp. in MBE code. - // `Const` would regress `rfc-2632-const-trait-impl/mbe-dyn-const-2015.rs`. - // `Not` would regress `dyn!(...)` macro calls in Rust 2015. + // `const` and `[` would regress UI test `macro-dyn-const-2015.rs`. + // `!` would regress `dyn!(...)` macro calls in Rust 2015. t.is_path_start() || t.is_lifetime() || t == &TokenKind::Question @@ -1015,12 +1015,18 @@ impl<'a> Parser<'a> { || self.check(exp!(Tilde)) || self.check_keyword(exp!(For)) || self.check(exp!(OpenParen)) - || self.check(exp!(OpenBracket)) + || self.can_begin_maybe_const_bound() || self.check_keyword(exp!(Const)) || self.check_keyword(exp!(Async)) || self.check_keyword(exp!(Use)) } + fn can_begin_maybe_const_bound(&mut self) -> bool { + self.check(exp!(OpenBracket)) + && self.look_ahead(1, |t| t.is_keyword(kw::Const)) + && self.look_ahead(2, |t| *t == token::CloseBracket) + } + /// Parse a bound. /// /// ```ebnf @@ -1199,10 +1205,7 @@ impl<'a> Parser<'a> { let span = tilde.to(self.prev_token.span); self.psess.gated_spans.gate(sym::const_trait_impl, span); BoundConstness::Maybe(span) - } else if self.check(exp!(OpenBracket)) - && self.look_ahead(1, |t| t.is_keyword(kw::Const)) - && self.look_ahead(2, |t| *t == token::CloseBracket) - { + } else if self.can_begin_maybe_const_bound() { let start = self.token.span; self.bump(); self.expect_keyword(exp!(Const)).unwrap(); diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index afd08319738..ab7885905a6 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -501,6 +501,10 @@ passes_repr_align_should_be_align = `#[repr(align(...))]` is not supported on {$item} .help = use `#[rustc_align(...)]` instead +passes_repr_align_should_be_align_static = + `#[repr(align(...))]` is not supported on {$item} + .help = use `#[rustc_align_static(...)]` instead + passes_repr_conflicting = conflicting representation hints diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 487bdd2a888..2562d2e0b83 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -274,6 +274,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::MoveSizeLimit { .. } | AttributeKind::TypeLengthLimit { .. } | AttributeKind::PatternComplexityLimit { .. } + | AttributeKind::NoCore { .. } + | AttributeKind::NoStd { .. } ) => { /* do nothing */ } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); @@ -1606,12 +1608,18 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ReprAttr::ReprAlign(align) => { match target { Target::Struct | Target::Union | Target::Enum => {} - Target::Fn | Target::Method(_) => { + Target::Fn | Target::Method(_) if self.tcx.features().fn_align() => { self.dcx().emit_err(errors::ReprAlignShouldBeAlign { span: *repr_span, item: target.plural_name(), }); } + Target::Static if self.tcx.features().static_align() => { + self.dcx().emit_err(errors::ReprAlignShouldBeAlignStatic { + span: *repr_span, + item: target.plural_name(), + }); + } _ => { self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { hint_span: *repr_span, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 23dcabef1a1..2da4b6f52cf 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1610,6 +1610,15 @@ pub(crate) struct ReprAlignShouldBeAlign { } #[derive(Diagnostic)] +#[diag(passes_repr_align_should_be_align_static)] +pub(crate) struct ReprAlignShouldBeAlignStatic { + #[primary_span] + #[help] + pub span: Span, + pub item: &'static str, +} + +#[derive(Diagnostic)] #[diag(passes_custom_mir_phase_requires_dialect)] pub(crate) struct CustomMirPhaseRequiresDialect { #[primary_span] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e5108d8b7e9..cdb0b5b58da 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -7,7 +7,7 @@ use std::ops::Deref; use std::{fmt, str}; use rustc_arena::DroplessArena; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::stable_hasher::{ HashStable, StableCompare, StableHasher, ToStableHashKey, }; @@ -21,18 +21,17 @@ mod tests; // The proc macro code for this is in `compiler/rustc_macros/src/symbols.rs`. symbols! { - // This list includes things that are definitely keywords (e.g. `if`), - // a few things that are definitely not keywords (e.g. the empty symbol, - // `{{root}}`) and things where there is disagreement between people and/or - // documents (such as the Rust Reference) about whether it is a keyword - // (e.g. `_`). + // This list includes things that are definitely keywords (e.g. `if`), a + // few things that are definitely not keywords (e.g. `{{root}}`) and things + // where there is disagreement between people and/or documents (such as the + // Rust Reference) about whether it is a keyword (e.g. `_`). // // If you modify this list, adjust any relevant `Symbol::{is,can_be}_*` // predicates and `used_keywords`. Also consider adding new keywords to the // `ui/parser/raw/raw-idents.rs` test. Keywords { - // Special reserved identifiers used internally for elided lifetimes, - // unnamed method parameters, crate root module, error recovery etc. + // Special reserved identifiers used internally for unnamed method + // parameters, crate root module, etc. // Matching predicates: `is_special`/`is_reserved` // // tidy-alphabetical-start @@ -326,6 +325,7 @@ symbols! { RangeSub, RangeTo, RangeToInclusive, + RangeToInclusiveCopy, Rc, RcWeak, Ready, @@ -1280,6 +1280,7 @@ symbols! { lang, lang_items, large_assignments, + last, lateout, lazy_normalization_consts, lazy_type_alias, @@ -1846,6 +1847,7 @@ symbols! { rustc_abi, // FIXME(#82232, #143834): temporary name to mitigate `#[align]` nameres ambiguity rustc_align, + rustc_align_static, rustc_allocator, rustc_allocator_zeroed, rustc_allocator_zeroed_variant, @@ -2097,6 +2099,7 @@ symbols! { staged_api, start, state, + static_align, static_in_const, static_nobundle, static_recursion, @@ -2868,11 +2871,20 @@ impl Interner { let byte_strs = FxIndexSet::from_iter( init.iter().copied().chain(extra.iter().copied()).map(|str| str.as_bytes()), ); - assert_eq!( - byte_strs.len(), - init.len() + extra.len(), - "duplicate symbols in the rustc symbol list and the extra symbols added by the driver", - ); + + // The order in which duplicates are reported is irrelevant. + #[expect(rustc::potential_query_instability)] + if byte_strs.len() != init.len() + extra.len() { + panic!( + "duplicate symbols in the rustc symbol list and the extra symbols added by the driver: {:?}", + FxHashSet::intersection( + &init.iter().copied().collect(), + &extra.iter().copied().collect(), + ) + .collect::<Vec<_>>() + ) + } + Interner(Lock::new(InternerInner { arena: Default::default(), byte_strs })) } diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 4c1b8c99426..dc70089c385 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -849,6 +849,7 @@ const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("miscellaneous-extensions-3", Unstable(sym::s390x_target_feature), &[]), ("miscellaneous-extensions-4", Unstable(sym::s390x_target_feature), &[]), ("nnp-assist", Unstable(sym::s390x_target_feature), &["vector"]), + ("soft-float", Forbidden { reason: "currently unsupported ABI-configuration feature" }, &[]), ("transactional-execution", Unstable(sym::s390x_target_feature), &[]), ("vector", Unstable(sym::s390x_target_feature), &[]), ("vector-enhancements-1", Unstable(sym::s390x_target_feature), &["vector"]), @@ -1177,6 +1178,13 @@ impl Target { _ => unreachable!(), } } + "s390x" => { + // We don't currently support a softfloat target on this architecture. + // As usual, we have to reject swapping the `soft-float` target feature. + // The "vector" target feature does not affect the ABI for floats + // because the vector and float registers overlap. + FeatureConstraints { required: &[], incompatible: &["soft-float"] } + } _ => NOTHING, } } 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 d71110521ff..e18e294635b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1618,18 +1618,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { let e = self.tcx.erase_and_anonymize_regions(e); let f = self.tcx.erase_and_anonymize_regions(f); - let mut expected = with_forced_trimmed_paths!(e.sort_string(self.tcx)); - let mut found = with_forced_trimmed_paths!(f.sort_string(self.tcx)); - if let ObligationCauseCode::Pattern { span, .. } = cause.code() - && let Some(span) = span - && !span.from_expansion() - && cause.span.from_expansion() - { - // When the type error comes from a macro like `assert!()`, and we are pointing at - // code the user wrote the cause and effect are reversed as the expected value is - // what the macro expanded to. - (found, expected) = (expected, found); - } + let expected = with_forced_trimmed_paths!(e.sort_string(self.tcx)); + let found = with_forced_trimmed_paths!(f.sort_string(self.tcx)); if expected == found { label_or_note(span, terr.to_string(self.tcx)); } else { @@ -2152,9 +2142,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> Option<(DiagStyledString, DiagStyledString)> { match values { ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found), - ValuePairs::Terms(exp_found) => { - self.expected_found_str_term(cause, exp_found, long_ty_path) - } + ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found, long_ty_path), ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found), ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), @@ -2193,7 +2181,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn expected_found_str_term( &self, - cause: &ObligationCause<'tcx>, exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>, long_ty_path: &mut Option<PathBuf>, ) -> Option<(DiagStyledString, DiagStyledString)> { @@ -2201,27 +2188,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if exp_found.references_error() { return None; } - let (mut expected, mut found) = (exp_found.expected, exp_found.found); - - if let ObligationCauseCode::Pattern { span, .. } = cause.code() - && let Some(span) = span - && !span.from_expansion() - && cause.span.from_expansion() - { - // When the type error comes from a macro like `assert!()`, and we are pointing at - // code the user wrote, the cause and effect are reversed as the expected value is - // what the macro expanded to. So if the user provided a `Type` when the macro is - // written in such a way that a `bool` was expected, we want to print: - // = note: expected `bool` - // found `Type`" - // but as far as the compiler is concerned, after expansion what was expected was `Type` - // = note: expected `Type` - // found `bool`" - // so we reverse them here to match user expectation. - (expected, found) = (found, expected); - } - Some(match (expected.kind(), found.kind()) { + Some(match (exp_found.expected.kind(), exp_found.found.kind()) { (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => { let (mut exp, mut fnd) = self.cmp(expected, found); // Use the terminal width as the basis to determine when to compress the printed diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 3a83b1739ff..cf58aec14a6 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -347,6 +347,7 @@ pub trait Interner: self_ty: Self::Ty, f: impl FnMut(Self::ImplId), ); + fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, f: impl FnMut(Self::ImplId)); fn has_item_definition(self, def_id: Self::DefId) -> bool; diff --git a/library/core/src/any.rs b/library/core/src/any.rs index e7d9763d46e..76ea2d18a82 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -705,7 +705,8 @@ impl dyn Any + Send + Sync { /// std::mem::forget(fake_one_ring); /// } /// ``` -#[derive(Clone, Copy, Eq, PartialOrd, Ord)] +#[derive(Copy, PartialOrd, Ord)] +#[derive_const(Clone, Eq)] #[stable(feature = "rust1", since = "1.0.0")] #[lang = "type_id"] pub struct TypeId { diff --git a/library/core/src/array/equality.rs b/library/core/src/array/equality.rs index 1ad2cca64a3..c2c7ccf0daa 100644 --- a/library/core/src/array/equality.rs +++ b/library/core/src/array/equality.rs @@ -1,9 +1,10 @@ use crate::cmp::BytewiseEq; #[stable(feature = "rust1", since = "1.0.0")] -impl<T, U, const N: usize> PartialEq<[U; N]> for [T; N] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T, U, const N: usize> const PartialEq<[U; N]> for [T; N] where - T: PartialEq<U>, + T: [const] PartialEq<U>, { #[inline] fn eq(&self, other: &[U; N]) -> bool { @@ -16,9 +17,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl<T, U, const N: usize> PartialEq<[U]> for [T; N] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T, U, const N: usize> const PartialEq<[U]> for [T; N] where - T: PartialEq<U>, + T: [const] PartialEq<U>, { #[inline] fn eq(&self, other: &[U]) -> bool { @@ -37,9 +39,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl<T, U, const N: usize> PartialEq<[U; N]> for [T] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T, U, const N: usize> const PartialEq<[U; N]> for [T] where - T: PartialEq<U>, + T: [const] PartialEq<U>, { #[inline] fn eq(&self, other: &[U; N]) -> bool { @@ -58,9 +61,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl<T, U, const N: usize> PartialEq<&[U]> for [T; N] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T, U, const N: usize> const PartialEq<&[U]> for [T; N] where - T: PartialEq<U>, + T: [const] PartialEq<U>, { #[inline] fn eq(&self, other: &&[U]) -> bool { @@ -73,9 +77,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl<T, U, const N: usize> PartialEq<[U; N]> for &[T] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T, U, const N: usize> const PartialEq<[U; N]> for &[T] where - T: PartialEq<U>, + T: [const] PartialEq<U>, { #[inline] fn eq(&self, other: &[U; N]) -> bool { @@ -88,9 +93,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl<T, U, const N: usize> PartialEq<&mut [U]> for [T; N] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T, U, const N: usize> const PartialEq<&mut [U]> for [T; N] where - T: PartialEq<U>, + T: [const] PartialEq<U>, { #[inline] fn eq(&self, other: &&mut [U]) -> bool { @@ -103,9 +109,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl<T, U, const N: usize> PartialEq<[U; N]> for &mut [T] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T, U, const N: usize> const PartialEq<[U; N]> for &mut [T] where - T: PartialEq<U>, + T: [const] PartialEq<U>, { #[inline] fn eq(&self, other: &[U; N]) -> bool { @@ -122,14 +129,18 @@ where // __impl_slice_eq2! { [A; $N], &'b mut [B; $N] } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: Eq, const N: usize> Eq for [T; N] {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T: [const] Eq, const N: usize> const Eq for [T; N] {} +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] trait SpecArrayEq<Other, const N: usize>: Sized { fn spec_eq(a: &[Self; N], b: &[Other; N]) -> bool; fn spec_ne(a: &[Self; N], b: &[Other; N]) -> bool; } -impl<T: PartialEq<Other>, Other, const N: usize> SpecArrayEq<Other, N> for T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T: [const] PartialEq<Other>, Other, const N: usize> const SpecArrayEq<Other, N> for T { default fn spec_eq(a: &[Self; N], b: &[Other; N]) -> bool { a[..] == b[..] } @@ -138,7 +149,8 @@ impl<T: PartialEq<Other>, Other, const N: usize> SpecArrayEq<Other, N> for T { } } -impl<T: BytewiseEq<U>, U, const N: usize> SpecArrayEq<U, N> for T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T: [const] BytewiseEq<U>, U, const N: usize> const SpecArrayEq<U, N> for T { fn spec_eq(a: &[T; N], b: &[U; N]) -> bool { // SAFETY: Arrays are compared element-wise, and don't add any padding // between elements, so when the elements are `BytewiseEq`, we can diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs index 49d540314a2..178af2c0e3b 100644 --- a/library/core/src/ascii/ascii_char.rs +++ b/library/core/src/ascii/ascii_char.rs @@ -54,7 +54,8 @@ use crate::{assert_unsafe_precondition, fmt}; /// [chart]: https://www.unicode.org/charts/PDF/U0000.pdf /// [NIST FIPS 1-2]: https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub1-2-1977.pdf /// [NamesList]: https://www.unicode.org/Public/15.0.0/ucd/NamesList.txt -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +#[derive(Copy, Hash)] +#[derive_const(Clone, Eq, PartialEq, Ord, PartialOrd)] #[unstable(feature = "ascii_char", issue = "110998")] #[repr(u8)] pub enum AsciiChar { diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 0812322f3fb..94536f25b41 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -29,7 +29,7 @@ mod bytewise; pub(crate) use bytewise::BytewiseEq; use self::Ordering::*; -use crate::marker::PointeeSized; +use crate::marker::{Destruct, PointeeSized}; use crate::ops::ControlFlow; /// Trait for comparisons using the equality operator. @@ -334,7 +334,9 @@ pub macro PartialEq($item:item) { #[doc(alias = "!=")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Eq"] -pub trait Eq: PartialEq<Self> + PointeeSized { +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub trait Eq: [const] PartialEq<Self> + PointeeSized { // this method is used solely by `impl Eq or #[derive(Eq)]` to assert that every component of a // type implements `Eq` itself. The current deriving infrastructure means doing this assertion // without using a method on this trait is nearly impossible. @@ -380,8 +382,8 @@ pub struct AssertParamIsEq<T: Eq + PointeeSized> { /// /// assert_eq!(2.cmp(&1), Ordering::Greater); /// ``` -#[derive(Clone, Copy, Eq, PartialOrd, Ord, Debug, Hash)] -#[derive_const(PartialEq)] +#[derive(Copy, Debug, Hash)] +#[derive_const(Clone, Eq, PartialOrd, Ord, PartialEq)] #[stable(feature = "rust1", since = "1.0.0")] // This is a lang item only so that `BinOp::Cmp` in MIR can return it. // It has no special behavior, but does require that the three variants @@ -635,7 +637,11 @@ impl Ordering { #[inline] #[must_use] #[stable(feature = "ordering_chaining", since = "1.17.0")] - pub fn then_with<F: FnOnce() -> Ordering>(self, f: F) -> Ordering { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + pub const fn then_with<F>(self, f: F) -> Ordering + where + F: [const] FnOnce() -> Ordering + [const] Destruct, + { match self { Equal => f(), _ => self, @@ -659,13 +665,15 @@ impl Ordering { /// v.sort_by_key(|&num| (num > 3, Reverse(num))); /// assert_eq!(v, vec![3, 2, 1, 6, 5, 4]); /// ``` -#[derive(PartialEq, Eq, Debug, Copy, Default, Hash)] +#[derive(Copy, Debug, Hash)] +#[derive_const(PartialEq, Eq, Default)] #[stable(feature = "reverse_cmp_key", since = "1.19.0")] #[repr(transparent)] pub struct Reverse<T>(#[stable(feature = "reverse_cmp_key", since = "1.19.0")] pub T); #[stable(feature = "reverse_cmp_key", since = "1.19.0")] -impl<T: PartialOrd> PartialOrd for Reverse<T> { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T: [const] PartialOrd> const PartialOrd for Reverse<T> { #[inline] fn partial_cmp(&self, other: &Reverse<T>) -> Option<Ordering> { other.0.partial_cmp(&self.0) @@ -690,7 +698,8 @@ impl<T: PartialOrd> PartialOrd for Reverse<T> { } #[stable(feature = "reverse_cmp_key", since = "1.19.0")] -impl<T: Ord> Ord for Reverse<T> { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T: [const] Ord> const Ord for Reverse<T> { #[inline] fn cmp(&self, other: &Reverse<T>) -> Ordering { other.0.cmp(&self.0) @@ -957,7 +966,9 @@ impl<T: Clone> Clone for Reverse<T> { #[doc(alias = ">=")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Ord"] -pub trait Ord: Eq + PartialOrd<Self> + PointeeSized { +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub trait Ord: [const] Eq + [const] PartialOrd<Self> + PointeeSized { /// This method returns an [`Ordering`] between `self` and `other`. /// /// By convention, `self.cmp(&other)` returns the ordering matching the expression @@ -1011,7 +1022,7 @@ pub trait Ord: Eq + PartialOrd<Self> + PointeeSized { #[rustc_diagnostic_item = "cmp_ord_max"] fn max(self, other: Self) -> Self where - Self: Sized, + Self: Sized + [const] Destruct, { if other < self { self } else { other } } @@ -1050,7 +1061,7 @@ pub trait Ord: Eq + PartialOrd<Self> + PointeeSized { #[rustc_diagnostic_item = "cmp_ord_min"] fn min(self, other: Self) -> Self where - Self: Sized, + Self: Sized + [const] Destruct, { if other < self { other } else { self } } @@ -1076,7 +1087,7 @@ pub trait Ord: Eq + PartialOrd<Self> + PointeeSized { #[stable(feature = "clamp", since = "1.50.0")] fn clamp(self, min: Self, max: Self) -> Self where - Self: Sized, + Self: Sized + [const] Destruct, { assert!(min <= max); if self < min { @@ -1341,6 +1352,8 @@ pub macro Ord($item:item) { )] #[rustc_diagnostic_item = "PartialOrd"] #[allow(multiple_supertrait_upcastable)] // FIXME(sized_hierarchy): remove this +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] pub trait PartialOrd<Rhs: PointeeSized = Self>: PartialEq<Rhs> + PointeeSized { /// This method returns an ordering between `self` and `other` values if one exists. /// @@ -1481,13 +1494,14 @@ pub trait PartialOrd<Rhs: PointeeSized = Self>: PartialEq<Rhs> + PointeeSized { } } -fn default_chaining_impl<T, U>( +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +const fn default_chaining_impl<T, U>( lhs: &T, rhs: &U, - p: impl FnOnce(Ordering) -> bool, + p: impl [const] FnOnce(Ordering) -> bool + [const] Destruct, ) -> ControlFlow<bool> where - T: PartialOrd<U> + PointeeSized, + T: [const] PartialOrd<U> + PointeeSized, U: PointeeSized, { // It's important that this only call `partial_cmp` once, not call `eq` then @@ -1545,7 +1559,8 @@ pub macro PartialOrd($item:item) { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "cmp_min"] -pub fn min<T: Ord>(v1: T, v2: T) -> T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn min<T: [const] Ord + [const] Destruct>(v1: T, v2: T) -> T { v1.min(v2) } @@ -1575,7 +1590,12 @@ pub fn min<T: Ord>(v1: T, v2: T) -> T { #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] -pub fn min_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn min_by<T: [const] Destruct, F: [const] FnOnce(&T, &T) -> Ordering>( + v1: T, + v2: T, + compare: F, +) -> T { if compare(&v1, &v2).is_le() { v1 } else { v2 } } @@ -1600,7 +1620,13 @@ pub fn min_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T { #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] -pub fn min_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn min_by_key<T, F, K>(v1: T, v2: T, mut f: F) -> T +where + T: [const] Destruct, + F: [const] FnMut(&T) -> K + [const] Destruct, + K: [const] Ord + [const] Destruct, +{ if f(&v2) < f(&v1) { v2 } else { v1 } } @@ -1640,7 +1666,8 @@ pub fn min_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "cmp_max"] -pub fn max<T: Ord>(v1: T, v2: T) -> T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn max<T: [const] Ord + [const] Destruct>(v1: T, v2: T) -> T { v1.max(v2) } @@ -1670,7 +1697,12 @@ pub fn max<T: Ord>(v1: T, v2: T) -> T { #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] -pub fn max_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn max_by<T: [const] Destruct, F: [const] FnOnce(&T, &T) -> Ordering>( + v1: T, + v2: T, + compare: F, +) -> T { if compare(&v1, &v2).is_gt() { v1 } else { v2 } } @@ -1695,7 +1727,13 @@ pub fn max_by<T, F: FnOnce(&T, &T) -> Ordering>(v1: T, v2: T, compare: F) -> T { #[inline] #[must_use] #[stable(feature = "cmp_min_max_by", since = "1.53.0")] -pub fn max_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn max_by_key<T, F, K>(v1: T, v2: T, mut f: F) -> T +where + T: [const] Destruct, + F: [const] FnMut(&T) -> K + [const] Destruct, + K: [const] Ord + [const] Destruct, +{ if f(&v2) < f(&v1) { v1 } else { v2 } } @@ -1739,9 +1777,10 @@ pub fn max_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T { #[inline] #[must_use] #[unstable(feature = "cmp_minmax", issue = "115939")] -pub fn minmax<T>(v1: T, v2: T) -> [T; 2] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn minmax<T>(v1: T, v2: T) -> [T; 2] where - T: Ord, + T: [const] Ord, { if v2 < v1 { [v2, v1] } else { [v1, v2] } } @@ -1773,9 +1812,10 @@ where #[inline] #[must_use] #[unstable(feature = "cmp_minmax", issue = "115939")] -pub fn minmax_by<T, F>(v1: T, v2: T, compare: F) -> [T; 2] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn minmax_by<T, F>(v1: T, v2: T, compare: F) -> [T; 2] where - F: FnOnce(&T, &T) -> Ordering, + F: [const] FnOnce(&T, &T) -> Ordering, { if compare(&v1, &v2).is_le() { [v1, v2] } else { [v2, v1] } } @@ -1801,10 +1841,11 @@ where #[inline] #[must_use] #[unstable(feature = "cmp_minmax", issue = "115939")] -pub fn minmax_by_key<T, F, K>(v1: T, v2: T, mut f: F) -> [T; 2] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +pub const fn minmax_by_key<T, F, K>(v1: T, v2: T, mut f: F) -> [T; 2] where - F: FnMut(&T) -> K, - K: Ord, + F: [const] FnMut(&T) -> K + [const] Destruct, + K: [const] Ord + [const] Destruct, { if f(&v2) < f(&v1) { [v2, v1] } else { [v1, v2] } } @@ -1830,7 +1871,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl PartialEq for () { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialEq for () { #[inline] fn eq(&self, _other: &()) -> bool { true @@ -1848,7 +1890,8 @@ mod impls { macro_rules! eq_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] - impl Eq for $t {} + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Eq for $t {} )*) } @@ -1896,7 +1939,8 @@ mod impls { macro_rules! partial_ord_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] - impl PartialOrd for $t { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialOrd for $t { #[inline] fn partial_cmp(&self, other: &Self) -> Option<Ordering> { match (*self <= *other, *self >= *other) { @@ -1913,7 +1957,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl PartialOrd for () { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialOrd for () { #[inline] fn partial_cmp(&self, _: &()) -> Option<Ordering> { Some(Equal) @@ -1921,7 +1966,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl PartialOrd for bool { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialOrd for bool { #[inline] fn partial_cmp(&self, other: &bool) -> Option<Ordering> { Some(self.cmp(other)) @@ -1935,7 +1981,8 @@ mod impls { macro_rules! ord_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] - impl PartialOrd for $t { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialOrd for $t { #[inline] fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(crate::intrinsics::three_way_compare(*self, *other)) @@ -1945,7 +1992,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Ord for $t { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Ord for $t { #[inline] fn cmp(&self, other: &Self) -> Ordering { crate::intrinsics::three_way_compare(*self, *other) @@ -1955,7 +2003,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Ord for () { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Ord for () { #[inline] fn cmp(&self, _other: &()) -> Ordering { Equal @@ -1963,7 +2012,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Ord for bool { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Ord for bool { #[inline] fn cmp(&self, other: &bool) -> Ordering { // Casting to i8's and converting the difference to an Ordering generates @@ -1998,7 +2048,8 @@ mod impls { ord_impl! { char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 } #[unstable(feature = "never_type", issue = "35121")] - impl PartialEq for ! { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialEq for ! { #[inline] fn eq(&self, _: &!) -> bool { *self @@ -2006,10 +2057,12 @@ mod impls { } #[unstable(feature = "never_type", issue = "35121")] - impl Eq for ! {} + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Eq for ! {} #[unstable(feature = "never_type", issue = "35121")] - impl PartialOrd for ! { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const PartialOrd for ! { #[inline] fn partial_cmp(&self, _: &!) -> Option<Ordering> { *self @@ -2017,7 +2070,8 @@ mod impls { } #[unstable(feature = "never_type", issue = "35121")] - impl Ord for ! { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl const Ord for ! { #[inline] fn cmp(&self, _: &!) -> Ordering { *self @@ -2042,9 +2096,10 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl<A: PointeeSized, B: PointeeSized> PartialOrd<&B> for &A + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<A: PointeeSized, B: PointeeSized> const PartialOrd<&B> for &A where - A: PartialOrd<B>, + A: [const] PartialOrd<B>, { #[inline] fn partial_cmp(&self, other: &&B) -> Option<Ordering> { @@ -2084,9 +2139,10 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl<A: PointeeSized> Ord for &A + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<A: PointeeSized> const Ord for &A where - A: Ord, + A: [const] Ord, { #[inline] fn cmp(&self, other: &Self) -> Ordering { @@ -2094,7 +2150,8 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl<A: PointeeSized> Eq for &A where A: Eq {} + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<A: PointeeSized> const Eq for &A where A: [const] Eq {} // &mut pointers @@ -2114,9 +2171,10 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl<A: PointeeSized, B: PointeeSized> PartialOrd<&mut B> for &mut A + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<A: PointeeSized, B: PointeeSized> const PartialOrd<&mut B> for &mut A where - A: PartialOrd<B>, + A: [const] PartialOrd<B>, { #[inline] fn partial_cmp(&self, other: &&mut B) -> Option<Ordering> { @@ -2156,9 +2214,10 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl<A: PointeeSized> Ord for &mut A + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<A: PointeeSized> const Ord for &mut A where - A: Ord, + A: [const] Ord, { #[inline] fn cmp(&self, other: &Self) -> Ordering { @@ -2166,7 +2225,8 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl<A: PointeeSized> Eq for &mut A where A: Eq {} + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<A: PointeeSized> const Eq for &mut A where A: [const] Eq {} #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index a3cfd85974a..89cda30c030 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -963,17 +963,20 @@ impl const PartialEq for Infallible { } #[stable(feature = "convert_infallible", since = "1.34.0")] -impl Eq for Infallible {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const Eq for Infallible {} #[stable(feature = "convert_infallible", since = "1.34.0")] -impl PartialOrd for Infallible { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const PartialOrd for Infallible { fn partial_cmp(&self, _other: &Self) -> Option<crate::cmp::Ordering> { match *self {} } } #[stable(feature = "convert_infallible", since = "1.34.0")] -impl Ord for Infallible { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const Ord for Infallible { fn cmp(&self, _other: &Self) -> crate::cmp::Ordering { match *self {} } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 0f38081c355..1b7c28bb95a 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -4,7 +4,7 @@ use super::{IntErrorKind, ParseIntError}; use crate::clone::UseCloned; use crate::cmp::Ordering; use crate::hash::{Hash, Hasher}; -use crate::marker::{Freeze, StructuralPartialEq}; +use crate::marker::{Destruct, Freeze, StructuralPartialEq}; use crate::ops::{BitOr, BitOrAssign, Div, DivAssign, Neg, Rem, RemAssign}; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::str::FromStr; @@ -220,12 +220,14 @@ where impl<T> StructuralPartialEq for NonZero<T> where T: ZeroablePrimitive + StructuralPartialEq {} #[stable(feature = "nonzero", since = "1.28.0")] -impl<T> Eq for NonZero<T> where T: ZeroablePrimitive + Eq {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T> const Eq for NonZero<T> where T: ZeroablePrimitive + [const] Eq {} #[stable(feature = "nonzero", since = "1.28.0")] -impl<T> PartialOrd for NonZero<T> +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T> const PartialOrd for NonZero<T> where - T: ZeroablePrimitive + PartialOrd, + T: ZeroablePrimitive + [const] PartialOrd, { #[inline] fn partial_cmp(&self, other: &Self) -> Option<Ordering> { @@ -254,9 +256,12 @@ where } #[stable(feature = "nonzero", since = "1.28.0")] -impl<T> Ord for NonZero<T> +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T> const Ord for NonZero<T> where - T: ZeroablePrimitive + Ord, + // FIXME(const_hack): the T: ~const Destruct should be inferred from the Self: ~const Destruct. + // See https://github.com/rust-lang/rust/issues/144207 + T: ZeroablePrimitive + [const] Ord + [const] Destruct, { #[inline] fn cmp(&self, other: &Self) -> Ordering { diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs index 73b74d53323..b760a7c4e21 100644 --- a/library/core/src/ops/control_flow.rs +++ b/library/core/src/ops/control_flow.rs @@ -83,7 +83,8 @@ use crate::{convert, ops}; #[must_use] // ControlFlow should not implement PartialOrd or Ord, per RFC 3058: // https://rust-lang.github.io/rfcs/3058-try-trait-v2.html#traits-for-controlflow -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Copy, Debug, Hash)] +#[derive_const(Clone, PartialEq, Eq)] pub enum ControlFlow<B, C = ()> { /// Move on to the next phase of the operation as normal. #[stable(feature = "control_flow_enum_type", since = "1.55.0")] diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 198636c67d0..886d581b0a6 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -585,7 +585,8 @@ use crate::{cmp, convert, hint, mem, slice}; /// The `Option` type. See [the module level documentation](self) for more. #[doc(search_unbox)] -#[derive(Copy, Eq, Debug, Hash)] +#[derive(Copy, Debug, Hash)] +#[derive_const(Eq)] #[rustc_diagnostic_item = "Option"] #[lang = "Option"] #[stable(feature = "rust1", since = "1.0.0")] @@ -2363,7 +2364,8 @@ impl<T: [const] PartialEq> const PartialEq for Option<T> { // https://github.com/rust-lang/rust/issues/49892, although still // not optimal. #[stable(feature = "rust1", since = "1.0.0")] -impl<T: PartialOrd> PartialOrd for Option<T> { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T: [const] PartialOrd> const PartialOrd for Option<T> { #[inline] fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { match (self, other) { @@ -2376,7 +2378,8 @@ impl<T: PartialOrd> PartialOrd for Option<T> { } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: Ord> Ord for Option<T> { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T: [const] Ord> const Ord for Option<T> { #[inline] fn cmp(&self, other: &Self) -> cmp::Ordering { match (self, other) { diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index c5f0cb8016e..45109270944 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -146,28 +146,7 @@ impl<T: PointeeSized> *const T { self as _ } - /// Gets the "address" portion of the pointer. - /// - /// This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of - /// the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that - /// casting the returned address back to a pointer yields a [pointer without - /// provenance][without_provenance], which is undefined behavior to dereference. To properly - /// restore the lost information and obtain a dereferenceable pointer, use - /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. - /// - /// If using those APIs is not possible because there is no way to preserve a pointer with the - /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts - /// or [`expose_provenance`][pointer::expose_provenance] and [`with_exposed_provenance`][with_exposed_provenance] - /// instead. However, note that this makes your code less portable and less amenable to tools - /// that check for compliance with the Rust memory model. - /// - /// On most platforms this will produce a value with the same bytes as the original - /// pointer, because all the bytes are dedicated to describing the address. - /// Platforms which need to store additional information in the pointer may - /// perform a change of representation to produce a value containing only the address - /// portion of the pointer. What that means is up to the platform to define. - /// - /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. + #[doc = include_str!("./docs/addr.md")] #[must_use] #[inline(always)] #[stable(feature = "strict_provenance", since = "1.84.0")] @@ -254,23 +233,16 @@ impl<T: PointeeSized> *const T { (self.cast(), metadata(self)) } - /// Returns `None` if the pointer is null, or else returns a shared reference to - /// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`] - /// must be used instead. - /// - /// [`as_uninit_ref`]: #method.as_uninit_ref - /// - /// # Safety - /// - /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). + #[doc = include_str!("./docs/as_ref.md")] /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. + /// ``` + /// let ptr: *const u8 = &10u8 as *const u8; /// - /// [`is_null`]: #method.is_null + /// unsafe { + /// let val_back = &*ptr; + /// assert_eq!(val_back, &10); + /// } + /// ``` /// /// # Examples /// @@ -284,20 +256,9 @@ impl<T: PointeeSized> *const T { /// } /// ``` /// - /// # Null-unchecked version /// - /// If you are sure the pointer can never be null and are looking for some kind of - /// `as_ref_unchecked` that returns the `&T` instead of `Option<&T>`, know that you can - /// dereference the pointer directly. - /// - /// ``` - /// let ptr: *const u8 = &10u8 as *const u8; - /// - /// unsafe { - /// let val_back = &*ptr; - /// assert_eq!(val_back, &10); - /// } - /// ``` + /// [`is_null`]: #method.is_null + /// [`as_uninit_ref`]: #method.as_uninit_ref #[stable(feature = "ptr_as_ref", since = "1.9.0")] #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[inline] @@ -338,23 +299,10 @@ impl<T: PointeeSized> *const T { unsafe { &*self } } - /// Returns `None` if the pointer is null, or else returns a shared reference to - /// the value wrapped in `Some`. In contrast to [`as_ref`], this does not require - /// that the value has to be initialized. - /// - /// [`as_ref`]: #method.as_ref - /// - /// # Safety - /// - /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). - /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. + #[doc = include_str!("./docs/as_uninit_ref.md")] /// /// [`is_null`]: #method.is_null + /// [`as_ref`]: #method.as_ref /// /// # Examples /// diff --git a/library/core/src/ptr/docs/INFO.md b/library/core/src/ptr/docs/INFO.md new file mode 100644 index 00000000000..28a0da4926a --- /dev/null +++ b/library/core/src/ptr/docs/INFO.md @@ -0,0 +1,21 @@ +This directory holds method documentation that otherwise +would be duplicated across mutable and immutable pointers. + +Note that most of the docs here are not the complete docs +for their corresponding method. This is for a few reasons: + +1. Examples need to be different for mutable/immutable + pointers, in order to actually call the correct method. +2. Link reference definitions are frequently different + between mutable/immutable pointers, in order to link to + the correct method. + For example, `<*const T>::as_ref` links to + `<*const T>::is_null`, while `<*mut T>::as_ref` links to + `<*mut T>::is_null`. +3. Many methods on mutable pointers link to an alternate + version that returns a mutable reference instead of + a shared reference. + +Always review the rendered docs manually when making +changes to these files to make sure you're not accidentally +splitting up a section. diff --git a/library/core/src/ptr/docs/addr.md b/library/core/src/ptr/docs/addr.md new file mode 100644 index 00000000000..785b88a9987 --- /dev/null +++ b/library/core/src/ptr/docs/addr.md @@ -0,0 +1,22 @@ +Gets the "address" portion of the pointer. + +This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of +the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that +casting the returned address back to a pointer yields a [pointer without +provenance][without_provenance], which is undefined behavior to dereference. To properly +restore the lost information and obtain a dereferenceable pointer, use +[`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. + +If using those APIs is not possible because there is no way to preserve a pointer with the +required provenance, then Strict Provenance might not be for you. Use pointer-integer casts +or [`expose_provenance`][pointer::expose_provenance] and [`with_exposed_provenance`][with_exposed_provenance] +instead. However, note that this makes your code less portable and less amenable to tools +that check for compliance with the Rust memory model. + +On most platforms this will produce a value with the same bytes as the original +pointer, because all the bytes are dedicated to describing the address. +Platforms which need to store additional information in the pointer may +perform a change of representation to produce a value containing only the address +portion of the pointer. What that means is up to the platform to define. + +This is a [Strict Provenance][crate::ptr#strict-provenance] API. diff --git a/library/core/src/ptr/docs/as_ref.md b/library/core/src/ptr/docs/as_ref.md new file mode 100644 index 00000000000..0c0d2768c74 --- /dev/null +++ b/library/core/src/ptr/docs/as_ref.md @@ -0,0 +1,19 @@ +Returns `None` if the pointer is null, or else returns a shared reference to +the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`] +must be used instead. + +# Safety + +When calling this method, you have to ensure that *either* the pointer is null *or* +the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). + +# Panics during const evaluation + +This method will panic during const evaluation if the pointer cannot be +determined to be null or not. See [`is_null`] for more information. + +# Null-unchecked version + +If you are sure the pointer can never be null and are looking for some kind of +`as_ref_unchecked` that returns the `&T` instead of `Option<&T>`, know that you can +dereference the pointer directly. diff --git a/library/core/src/ptr/docs/as_uninit_ref.md b/library/core/src/ptr/docs/as_uninit_ref.md new file mode 100644 index 00000000000..5b9a1ecb85b --- /dev/null +++ b/library/core/src/ptr/docs/as_uninit_ref.md @@ -0,0 +1,15 @@ +Returns `None` if the pointer is null, or else returns a shared reference to +the value wrapped in `Some`. In contrast to [`as_ref`], this does not require +that the value has to be initialized. + +# Safety + +When calling this method, you have to ensure that *either* the pointer is null *or* +the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). +Note that because the created reference is to `MaybeUninit<T>`, the +source pointer can point to uninitialized memory. + +# Panics during const evaluation + +This method will panic during const evaluation if the pointer cannot be +determined to be null or not. See [`is_null`] for more information. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index ce6eee4f911..ba78afc7ea1 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -135,28 +135,9 @@ impl<T: PointeeSized> *mut T { self as _ } - /// Gets the "address" portion of the pointer. - /// - /// This is similar to `self as usize`, except that the [provenance][crate::ptr#provenance] of - /// the pointer is discarded and not [exposed][crate::ptr#exposed-provenance]. This means that - /// casting the returned address back to a pointer yields a [pointer without - /// provenance][without_provenance_mut], which is undefined behavior to dereference. To properly - /// restore the lost information and obtain a dereferenceable pointer, use - /// [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. - /// - /// If using those APIs is not possible because there is no way to preserve a pointer with the - /// required provenance, then Strict Provenance might not be for you. Use pointer-integer casts - /// or [`expose_provenance`][pointer::expose_provenance] and [`with_exposed_provenance`][with_exposed_provenance] - /// instead. However, note that this makes your code less portable and less amenable to tools - /// that check for compliance with the Rust memory model. - /// - /// On most platforms this will produce a value with the same bytes as the original - /// pointer, because all the bytes are dedicated to describing the address. - /// Platforms which need to store additional information in the pointer may - /// perform a change of representation to produce a value containing only the address - /// portion of the pointer. What that means is up to the platform to define. + #[doc = include_str!("./docs/addr.md")] /// - /// This is a [Strict Provenance][crate::ptr#strict-provenance] API. + /// [without_provenance]: without_provenance_mut #[must_use] #[inline(always)] #[stable(feature = "strict_provenance", since = "1.84.0")] @@ -243,26 +224,16 @@ impl<T: PointeeSized> *mut T { (self.cast(), super::metadata(self)) } - /// Returns `None` if the pointer is null, or else returns a shared reference to - /// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`] - /// must be used instead. - /// - /// For the mutable counterpart see [`as_mut`]. + #[doc = include_str!("./docs/as_ref.md")] /// - /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 - /// [`as_mut`]: #method.as_mut - /// - /// # Safety - /// - /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). - /// - /// # Panics during const evaluation - /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. + /// ``` + /// let ptr: *mut u8 = &mut 10u8 as *mut u8; /// - /// [`is_null`]: #method.is_null-1 + /// unsafe { + /// let val_back = &*ptr; + /// println!("We got back the value: {val_back}!"); + /// } + /// ``` /// /// # Examples /// @@ -276,20 +247,14 @@ impl<T: PointeeSized> *mut T { /// } /// ``` /// - /// # Null-unchecked version - /// - /// If you are sure the pointer can never be null and are looking for some kind of - /// `as_ref_unchecked` that returns the `&T` instead of `Option<&T>`, know that you can - /// dereference the pointer directly. + /// # See Also /// - /// ``` - /// let ptr: *mut u8 = &mut 10u8 as *mut u8; + /// For the mutable counterpart see [`as_mut`]. /// - /// unsafe { - /// let val_back = &*ptr; - /// println!("We got back the value: {val_back}!"); - /// } - /// ``` + /// [`is_null`]: #method.is_null-1 + /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 + /// [`as_mut`]: #method.as_mut + #[stable(feature = "ptr_as_ref", since = "1.9.0")] #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[inline] @@ -332,28 +297,15 @@ impl<T: PointeeSized> *mut T { unsafe { &*self } } - /// Returns `None` if the pointer is null, or else returns a shared reference to - /// the value wrapped in `Some`. In contrast to [`as_ref`], this does not require - /// that the value has to be initialized. - /// - /// For the mutable counterpart see [`as_uninit_mut`]. + #[doc = include_str!("./docs/as_uninit_ref.md")] /// + /// [`is_null`]: #method.is_null-1 /// [`as_ref`]: pointer#method.as_ref-1 - /// [`as_uninit_mut`]: #method.as_uninit_mut - /// - /// # Safety - /// - /// When calling this method, you have to ensure that *either* the pointer is null *or* - /// the pointer is [convertible to a reference](crate::ptr#pointer-to-reference-conversion). - /// Note that because the created reference is to `MaybeUninit<T>`, the - /// source pointer can point to uninitialized memory. - /// - /// # Panics during const evaluation /// - /// This method will panic during const evaluation if the pointer cannot be - /// determined to be null or not. See [`is_null`] for more information. + /// # See Also + /// For the mutable counterpart see [`as_uninit_mut`]. /// - /// [`is_null`]: #method.is_null-1 + /// [`as_uninit_mut`]: #method.as_uninit_mut /// /// # Examples /// diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 332ae51d848..a096a8ceafc 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -31,9 +31,7 @@ pub use iter::{IterRange, IterRangeFrom, IterRangeInclusive}; #[doc(inline)] pub use crate::iter::Step; #[doc(inline)] -pub use crate::ops::{ - Bound, IntoBounds, OneSidedRange, RangeBounds, RangeFull, RangeTo, RangeToInclusive, -}; +pub use crate::ops::{Bound, IntoBounds, OneSidedRange, RangeBounds, RangeFull, RangeTo}; /// A (half-open) range bounded inclusively below and exclusively above /// (`start..end` in a future edition). @@ -51,7 +49,8 @@ pub use crate::ops::{ /// assert_eq!(3 + 4 + 5, Range::from(3..6).into_iter().sum()); /// ``` #[lang = "RangeCopy"] -#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)] +#[derive(Copy, Hash)] +#[derive_const(Clone, Default, PartialEq, Eq)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct Range<Idx> { /// The lower bound of the range (inclusive). @@ -209,20 +208,20 @@ impl<T> const From<legacy::Range<T>> for Range<T> { } } -/// A range bounded inclusively below and above (`start..=end`). +/// A range bounded inclusively below and above (`start..=last`). /// -/// The `RangeInclusive` `start..=end` contains all values with `x >= start` -/// and `x <= end`. It is empty unless `start <= end`. +/// The `RangeInclusive` `start..=last` contains all values with `x >= start` +/// and `x <= last`. It is empty unless `start <= last`. /// /// # Examples /// -/// The `start..=end` syntax is a `RangeInclusive`: +/// The `start..=last` syntax is a `RangeInclusive`: /// /// ``` /// #![feature(new_range_api)] /// use core::range::RangeInclusive; /// -/// assert_eq!(RangeInclusive::from(3..=5), RangeInclusive { start: 3, end: 5 }); +/// assert_eq!(RangeInclusive::from(3..=5), RangeInclusive { start: 3, last: 5 }); /// assert_eq!(3 + 4 + 5, RangeInclusive::from(3..=5).into_iter().sum()); /// ``` #[lang = "RangeInclusiveCopy"] @@ -234,7 +233,7 @@ pub struct RangeInclusive<Idx> { pub start: Idx, /// The upper bound of the range (inclusive). #[unstable(feature = "new_range_api", issue = "125687")] - pub end: Idx, + pub last: Idx, } #[unstable(feature = "new_range_api", issue = "125687")] @@ -242,7 +241,7 @@ impl<Idx: fmt::Debug> fmt::Debug for RangeInclusive<Idx> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { self.start.fmt(fmt)?; write!(fmt, "..=")?; - self.end.fmt(fmt)?; + self.last.fmt(fmt)?; Ok(()) } } @@ -306,7 +305,7 @@ impl<Idx: PartialOrd<Idx>> RangeInclusive<Idx> { #[unstable(feature = "new_range_api", issue = "125687")] #[inline] pub fn is_empty(&self) -> bool { - !(self.start <= self.end) + !(self.start <= self.last) } } @@ -335,10 +334,10 @@ impl<Idx: Step> RangeInclusive<Idx> { impl RangeInclusive<usize> { /// Converts to an exclusive `Range` for `SliceIndex` implementations. - /// The caller is responsible for dealing with `end == usize::MAX`. + /// The caller is responsible for dealing with `last == usize::MAX`. #[inline] pub(crate) const fn into_slice_range(self) -> Range<usize> { - Range { start: self.start, end: self.end + 1 } + Range { start: self.start, end: self.last + 1 } } } @@ -348,7 +347,7 @@ impl<T> RangeBounds<T> for RangeInclusive<T> { Included(&self.start) } fn end_bound(&self) -> Bound<&T> { - Included(&self.end) + Included(&self.last) } } @@ -364,7 +363,7 @@ impl<T> RangeBounds<T> for RangeInclusive<&T> { Included(self.start) } fn end_bound(&self) -> Bound<&T> { - Included(self.end) + Included(self.last) } } @@ -372,7 +371,7 @@ impl<T> RangeBounds<T> for RangeInclusive<&T> { #[unstable(feature = "new_range_api", issue = "125687")] impl<T> IntoBounds<T> for RangeInclusive<T> { fn into_bounds(self) -> (Bound<T>, Bound<T>) { - (Included(self.start), Included(self.end)) + (Included(self.start), Included(self.last)) } } @@ -381,7 +380,7 @@ impl<T> IntoBounds<T> for RangeInclusive<T> { impl<T> const From<RangeInclusive<T>> for legacy::RangeInclusive<T> { #[inline] fn from(value: RangeInclusive<T>) -> Self { - Self::new(value.start, value.end) + Self::new(value.start, value.last) } } #[unstable(feature = "new_range_api", issue = "125687")] @@ -394,8 +393,8 @@ impl<T> const From<legacy::RangeInclusive<T>> for RangeInclusive<T> { "attempted to convert from an exhausted `legacy::RangeInclusive` (unspecified behavior)" ); - let (start, end) = value.into_inner(); - RangeInclusive { start, end } + let (start, last) = value.into_inner(); + RangeInclusive { start, last } } } @@ -426,7 +425,8 @@ impl<T> const From<legacy::RangeInclusive<T>> for RangeInclusive<T> { /// assert_eq!(2 + 3 + 4, RangeFrom::from(2..).into_iter().take(3).sum()); /// ``` #[lang = "RangeFromCopy"] -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Copy, Hash)] +#[derive_const(Clone, PartialEq, Eq)] #[unstable(feature = "new_range_api", issue = "125687")] pub struct RangeFrom<Idx> { /// The lower bound of the range (inclusive). @@ -544,3 +544,107 @@ impl<T> const From<legacy::RangeFrom<T>> for RangeFrom<T> { Self { start: value.start } } } + +/// A range only bounded inclusively above (`..=last`). +/// +/// The `RangeToInclusive` `..=last` contains all values with `x <= last`. +/// It cannot serve as an [`Iterator`] because it doesn't have a starting point. +/// +/// # Examples +/// +/// The `..=last` syntax is a `RangeToInclusive`: +/// +/// ``` +/// #![feature(new_range_api)] +/// #![feature(new_range)] +/// assert_eq!((..=5), std::range::RangeToInclusive{ last: 5 }); +/// ``` +/// +/// It does not have an [`IntoIterator`] implementation, so you can't use it in a +/// `for` loop directly. This won't compile: +/// +/// ```compile_fail,E0277 +/// // error[E0277]: the trait bound `std::range::RangeToInclusive<{integer}>: +/// // std::iter::Iterator` is not satisfied +/// for i in ..=5 { +/// // ... +/// } +/// ``` +/// +/// When used as a [slicing index], `RangeToInclusive` produces a slice of all +/// array elements up to and including the index indicated by `last`. +/// +/// ``` +/// let arr = [0, 1, 2, 3, 4]; +/// assert_eq!(arr[ .. ], [0, 1, 2, 3, 4]); +/// assert_eq!(arr[ .. 3], [0, 1, 2 ]); +/// assert_eq!(arr[ ..=3], [0, 1, 2, 3 ]); // This is a `RangeToInclusive` +/// assert_eq!(arr[1.. ], [ 1, 2, 3, 4]); +/// assert_eq!(arr[1.. 3], [ 1, 2 ]); +/// assert_eq!(arr[1..=3], [ 1, 2, 3 ]); +/// ``` +/// +/// [slicing index]: crate::slice::SliceIndex +#[lang = "RangeToInclusiveCopy"] +#[doc(alias = "..=")] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[unstable(feature = "new_range_api", issue = "125687")] +pub struct RangeToInclusive<Idx> { + /// The upper bound of the range (inclusive) + #[unstable(feature = "new_range_api", issue = "125687")] + pub last: Idx, +} + +#[unstable(feature = "new_range_api", issue = "125687")] +impl<Idx: fmt::Debug> fmt::Debug for RangeToInclusive<Idx> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "..=")?; + self.last.fmt(fmt)?; + Ok(()) + } +} + +impl<Idx: PartialOrd<Idx>> RangeToInclusive<Idx> { + /// Returns `true` if `item` is contained in the range. + /// + /// # Examples + /// + /// ``` + /// assert!( (..=5).contains(&-1_000_000_000)); + /// assert!( (..=5).contains(&5)); + /// assert!(!(..=5).contains(&6)); + /// + /// assert!( (..=1.0).contains(&1.0)); + /// assert!(!(..=1.0).contains(&f32::NAN)); + /// assert!(!(..=f32::NAN).contains(&0.5)); + /// ``` + #[inline] + #[unstable(feature = "new_range_api", issue = "125687")] + pub fn contains<U>(&self, item: &U) -> bool + where + Idx: PartialOrd<U>, + U: ?Sized + PartialOrd<Idx>, + { + <Self as RangeBounds<Idx>>::contains(self, item) + } +} + +// RangeToInclusive<Idx> cannot impl From<RangeTo<Idx>> +// because underflow would be possible with (..0).into() + +#[unstable(feature = "new_range_api", issue = "125687")] +impl<T> RangeBounds<T> for RangeToInclusive<T> { + fn start_bound(&self) -> Bound<&T> { + Unbounded + } + fn end_bound(&self) -> Bound<&T> { + Included(&self.last) + } +} + +#[unstable(feature = "range_into_bounds", issue = "136903")] +impl<T> IntoBounds<T> for RangeToInclusive<T> { + fn into_bounds(self) -> (Bound<T>, Bound<T>) { + (Unbounded, Included(self.last)) + } +} diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index 1e261d8c1d9..24efd4a204a 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -164,7 +164,7 @@ impl<A: Step> IterRangeInclusive<A> { return None; } - Some(RangeInclusive { start: self.0.start, end: self.0.end }) + Some(RangeInclusive { start: self.0.start, last: self.0.end }) } } diff --git a/library/core/src/range/legacy.rs b/library/core/src/range/legacy.rs index 6723c4903f7..aa11331382d 100644 --- a/library/core/src/range/legacy.rs +++ b/library/core/src/range/legacy.rs @@ -1,10 +1,10 @@ //! # Legacy range types //! //! The types within this module will be replaced by the types -//! [`Range`], [`RangeInclusive`], and [`RangeFrom`] in the parent +//! [`Range`], [`RangeInclusive`], [`RangeToInclusive`], and [`RangeFrom`] in the parent //! module, [`core::range`]. //! //! The types here are equivalent to those in [`core::ops`]. #[doc(inline)] -pub use crate::ops::{Range, RangeFrom, RangeInclusive}; +pub use crate::ops::{Range, RangeFrom, RangeInclusive, RangeToInclusive}; diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 7dffab9b316..5c1f64bfe14 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -542,7 +542,8 @@ use crate::{convert, fmt, hint}; /// /// See the [module documentation](self) for details. #[doc(search_unbox)] -#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +#[derive(Copy, Debug, Hash)] +#[derive_const(PartialEq, PartialOrd, Eq, Ord)] #[must_use = "this `Result` may be an `Err` variant, which should be handled"] #[rustc_diagnostic_item = "Result"] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 0d801306984..103630aba0f 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -23,7 +23,8 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl<T: Eq> Eq for [T] {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<T: [const] Eq> const Eq for [T] {} /// Implements comparison of slices [lexicographically](Ord#lexicographical-comparison). #[stable(feature = "rust1", since = "1.0.0")] @@ -34,7 +35,7 @@ impl<T: Ord> Ord for [T] { } #[inline] -fn as_underlying(x: ControlFlow<bool>) -> u8 { +const fn as_underlying(x: ControlFlow<bool>) -> u8 { // SAFETY: This will only compile if `bool` and `ControlFlow<bool>` have the same // size (which isn't guaranteed but this is libcore). Because they have the same // size, it's a niched implementation, which in one byte means there can't be @@ -154,12 +155,16 @@ where } #[doc(hidden)] +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] // intermediate trait for specialization of slice's PartialOrd trait SlicePartialOrd: Sized { fn partial_compare(left: &[Self], right: &[Self]) -> Option<Ordering>; } #[doc(hidden)] +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] // intermediate trait for specialization of slice's PartialOrd chaining methods trait SliceChain: Sized { fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow<bool>; @@ -231,14 +236,17 @@ where } */ -impl<A: AlwaysApplicableOrd> SlicePartialOrd for A { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<A: [const] AlwaysApplicableOrd> const SlicePartialOrd for A { fn partial_compare(left: &[A], right: &[A]) -> Option<Ordering> { Some(SliceOrd::compare(left, right)) } } #[rustc_specialization_trait] -trait AlwaysApplicableOrd: SliceOrd + Ord {} +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +trait AlwaysApplicableOrd: [const] SliceOrd + [const] Ord {} macro_rules! always_applicable_ord { ($([$($p:tt)*] $t:ty,)*) => { @@ -257,6 +265,8 @@ always_applicable_ord! { } #[doc(hidden)] +#[const_trait] +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] // intermediate trait for specialization of slice's Ord trait SliceOrd: Sized { fn compare(left: &[Self], right: &[Self]) -> Ordering; @@ -282,17 +292,24 @@ impl<A: Ord> SliceOrd for A { /// * For every `x` and `y` of this type, `Ord(x, y)` must return the same /// value as `Ord::cmp(transmute::<_, u8>(x), transmute::<_, u8>(y))`. #[rustc_specialization_trait] -unsafe trait UnsignedBytewiseOrd: Ord {} +#[const_trait] +unsafe trait UnsignedBytewiseOrd: [const] Ord {} -unsafe impl UnsignedBytewiseOrd for bool {} -unsafe impl UnsignedBytewiseOrd for u8 {} -unsafe impl UnsignedBytewiseOrd for NonZero<u8> {} -unsafe impl UnsignedBytewiseOrd for Option<NonZero<u8>> {} -unsafe impl UnsignedBytewiseOrd for ascii::Char {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +unsafe impl const UnsignedBytewiseOrd for bool {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +unsafe impl const UnsignedBytewiseOrd for u8 {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +unsafe impl const UnsignedBytewiseOrd for NonZero<u8> {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +unsafe impl const UnsignedBytewiseOrd for Option<NonZero<u8>> {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +unsafe impl const UnsignedBytewiseOrd for ascii::Char {} // `compare_bytes` compares a sequence of unsigned bytes lexicographically, so // use it if the requirements for `UnsignedBytewiseOrd` are fulfilled. -impl<A: Ord + UnsignedBytewiseOrd> SliceOrd for A { +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<A: [const] Ord + [const] UnsignedBytewiseOrd> const SliceOrd for A { #[inline] fn compare(left: &[Self], right: &[Self]) -> Ordering { // Since the length of a slice is always less than or equal to @@ -317,7 +334,9 @@ impl<A: Ord + UnsignedBytewiseOrd> SliceOrd for A { } // Don't generate our own chaining loops for `memcmp`-able things either. -impl<A: PartialOrd + UnsignedBytewiseOrd> SliceChain for A { + +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl<A: [const] PartialOrd + [const] UnsignedBytewiseOrd> const SliceChain for A { #[inline] fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow<bool> { match SliceOrd::compare(left, right) { diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 8e1bc0bae70..a8147d745f3 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -129,6 +129,8 @@ mod private_slice_index { #[unstable(feature = "new_range_api", issue = "125687")] impl Sealed for range::RangeInclusive<usize> {} #[unstable(feature = "new_range_api", issue = "125687")] + impl Sealed for range::RangeToInclusive<usize> {} + #[unstable(feature = "new_range_api", issue = "125687")] impl Sealed for range::RangeFrom<usize> {} impl Sealed for ops::IndexRange {} @@ -788,6 +790,45 @@ unsafe impl<T> const SliceIndex<[T]> for ops::RangeToInclusive<usize> { } } +/// The methods `index` and `index_mut` panic if the end of the range is out of bounds. +#[stable(feature = "inclusive_range", since = "1.26.0")] +#[rustc_const_unstable(feature = "const_index", issue = "143775")] +unsafe impl<T> const SliceIndex<[T]> for range::RangeToInclusive<usize> { + type Output = [T]; + + #[inline] + fn get(self, slice: &[T]) -> Option<&[T]> { + (0..=self.last).get(slice) + } + + #[inline] + fn get_mut(self, slice: &mut [T]) -> Option<&mut [T]> { + (0..=self.last).get_mut(slice) + } + + #[inline] + unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked`. + unsafe { (0..=self.last).get_unchecked(slice) } + } + + #[inline] + unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] { + // SAFETY: the caller has to uphold the safety contract for `get_unchecked_mut`. + unsafe { (0..=self.last).get_unchecked_mut(slice) } + } + + #[inline] + fn index(self, slice: &[T]) -> &[T] { + (0..=self.last).index(slice) + } + + #[inline] + fn index_mut(self, slice: &mut [T]) -> &mut [T] { + (0..=self.last).index_mut(slice) + } +} + /// Performs bounds checking of a range. /// /// This method is similar to [`Index::index`] for slices, but it returns a diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index 4f228edf78e..a7cc943994c 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -32,7 +32,8 @@ impl const PartialEq for str { } #[stable(feature = "rust1", since = "1.0.0")] -impl Eq for str {} +#[rustc_const_unstable(feature = "const_cmp", issue = "143800")] +impl const Eq for str {} /// Implements comparison operations on strings. /// @@ -677,11 +678,11 @@ unsafe impl const SliceIndex<str> for range::RangeInclusive<usize> { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { - if self.end == usize::MAX { None } else { self.into_slice_range().get(slice) } + if self.last == usize::MAX { None } else { self.into_slice_range().get(slice) } } #[inline] fn get_mut(self, slice: &mut str) -> Option<&mut Self::Output> { - if self.end == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } + if self.last == usize::MAX { None } else { self.into_slice_range().get_mut(slice) } } #[inline] unsafe fn get_unchecked(self, slice: *const str) -> *const Self::Output { @@ -695,14 +696,14 @@ unsafe impl const SliceIndex<str> for range::RangeInclusive<usize> { } #[inline] fn index(self, slice: &str) -> &Self::Output { - if self.end == usize::MAX { + if self.last == usize::MAX { str_index_overflow_fail(); } self.into_slice_range().index(slice) } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { - if self.end == usize::MAX { + if self.last == usize::MAX { str_index_overflow_fail(); } self.into_slice_range().index_mut(slice) diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 2cdee1803a9..3892f831076 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -23,7 +23,8 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T: PartialEq),+> PartialEq for ($($T,)+) { + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<$($T: [const] PartialEq),+> const PartialEq for ($($T,)+) { #[inline] fn eq(&self, other: &($($T,)+)) -> bool { $( ${ignore($T)} self.${index()} == other.${index()} )&&+ @@ -38,7 +39,8 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T: Eq),+> Eq for ($($T,)+) + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<$($T: [const] Eq),+> const Eq for ($($T,)+) {} } @@ -66,7 +68,8 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T: PartialOrd),+> PartialOrd for ($($T,)+) + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<$($T: [const] PartialOrd),+> const PartialOrd for ($($T,)+) { #[inline] fn partial_cmp(&self, other: &($($T,)+)) -> Option<Ordering> { @@ -110,7 +113,8 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T: Ord),+> Ord for ($($T,)+) + #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] + impl<$($T: [const] Ord),+> const Ord for ($($T,)+) { #[inline] fn cmp(&self, other: &($($T,)+)) -> Ordering { diff --git a/library/coretests/tests/array.rs b/library/coretests/tests/array.rs index 30ccbbc3203..c4a8fc74fec 100644 --- a/library/coretests/tests/array.rs +++ b/library/coretests/tests/array.rs @@ -717,3 +717,10 @@ fn array_map_drops_unmapped_elements_on_panic() { assert_eq!(counter.load(Ordering::SeqCst), MAX); } } + +// This covers the `PartialEq::<[T]>::eq` impl for `[T; N]` when it returns false. +#[test] +fn array_eq() { + let not_true = [0u8] == [].as_slice(); + assert!(!not_true); +} diff --git a/library/coretests/tests/cmp.rs b/library/coretests/tests/cmp.rs index 6c4e2146f91..55e35a4a725 100644 --- a/library/coretests/tests/cmp.rs +++ b/library/coretests/tests/cmp.rs @@ -215,19 +215,18 @@ fn cmp_default() { assert_eq!(Fool(false), Fool(true)); } -/* FIXME(#110395) mod const_cmp { use super::*; struct S(i32); - impl PartialEq for S { + impl const PartialEq for S { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } - impl PartialOrd for S { + impl const PartialOrd for S { fn partial_cmp(&self, other: &Self) -> Option<Ordering> { let ret = match (self.0, other.0) { (a, b) if a > b => Ordering::Greater, @@ -247,4 +246,3 @@ mod const_cmp { const _: () = assert!(S(0) < S(1)); const _: () = assert!(S(1) > S(0)); } -*/ diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs index 4267fef50f8..d31eba863f5 100644 --- a/library/coretests/tests/floats/f128.rs +++ b/library/coretests/tests/floats/f128.rs @@ -17,12 +17,6 @@ const TOL: f128 = 1e-12; #[allow(unused)] const TOL_PRECISE: f128 = 1e-28; -/// First pattern over the mantissa -const NAN_MASK1: u128 = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u128 = 0x00005555555555555555555555555555; - // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. @@ -55,28 +49,6 @@ fn test_max_recip() { } #[test] -fn test_float_bits_conv() { - assert_eq!((1f128).to_bits(), 0x3fff0000000000000000000000000000); - assert_eq!((12.5f128).to_bits(), 0x40029000000000000000000000000000); - assert_eq!((1337f128).to_bits(), 0x40094e40000000000000000000000000); - assert_eq!((-14.25f128).to_bits(), 0xc002c800000000000000000000000000); - assert_biteq!(f128::from_bits(0x3fff0000000000000000000000000000), 1.0); - assert_biteq!(f128::from_bits(0x40029000000000000000000000000000), 12.5); - assert_biteq!(f128::from_bits(0x40094e40000000000000000000000000), 1337.0); - assert_biteq!(f128::from_bits(0xc002c800000000000000000000000000), -14.25); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f128::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f128::NAN.to_bits() ^ NAN_MASK2; - assert!(f128::from_bits(masked_nan1).is_nan()); - assert!(f128::from_bits(masked_nan2).is_nan()); - - assert_eq!(f128::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f128::from_bits(masked_nan2).to_bits(), masked_nan2); -} - -#[test] fn test_from() { assert_biteq!(f128::from(false), 0.0); assert_biteq!(f128::from(true), 1.0); diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs index d2818a6d768..302fd0861d7 100644 --- a/library/coretests/tests/floats/f16.rs +++ b/library/coretests/tests/floats/f16.rs @@ -19,12 +19,6 @@ const TOL_P2: f16 = 0.5; #[allow(unused)] const TOL_P4: f16 = 10.0; -/// First pattern over the mantissa -const NAN_MASK1: u16 = 0x02aa; - -/// Second pattern over the mantissa -const NAN_MASK2: u16 = 0x0155; - // FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support // the intrinsics. @@ -53,27 +47,6 @@ fn test_max_recip() { } #[test] -fn test_float_bits_conv() { - assert_eq!((1f16).to_bits(), 0x3c00); - assert_eq!((12.5f16).to_bits(), 0x4a40); - assert_eq!((1337f16).to_bits(), 0x6539); - assert_eq!((-14.25f16).to_bits(), 0xcb20); - assert_biteq!(f16::from_bits(0x3c00), 1.0); - assert_biteq!(f16::from_bits(0x4a40), 12.5); - assert_biteq!(f16::from_bits(0x6539), 1337.0); - assert_biteq!(f16::from_bits(0xcb20), -14.25); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - let masked_nan1 = f16::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f16::NAN.to_bits() ^ NAN_MASK2; - assert!(f16::from_bits(masked_nan1).is_nan()); - assert!(f16::from_bits(masked_nan2).is_nan()); - - assert_eq!(f16::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f16::from_bits(masked_nan2).to_bits(), masked_nan2); -} - -#[test] fn test_from() { assert_biteq!(f16::from(false), 0.0); assert_biteq!(f16::from(true), 1.0); diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs index 7b25f354da4..a1fe8b07650 100644 --- a/library/coretests/tests/floats/f32.rs +++ b/library/coretests/tests/floats/f32.rs @@ -2,12 +2,6 @@ use core::f32; use super::assert_biteq; -/// First pattern over the mantissa -const NAN_MASK1: u32 = 0x002a_aaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u32 = 0x0055_5555; - // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] #[test] @@ -25,25 +19,3 @@ fn test_mul_add() { assert_biteq!(f32::math::mul_add(8.9f32, inf, 3.2), inf); assert_biteq!(f32::math::mul_add(-3.2f32, 2.4, neg_inf), neg_inf); } - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f32).to_bits(), 0x3f800000); - assert_eq!((12.5f32).to_bits(), 0x41480000); - assert_eq!((1337f32).to_bits(), 0x44a72000); - assert_eq!((-14.25f32).to_bits(), 0xc1640000); - assert_biteq!(f32::from_bits(0x3f800000), 1.0); - assert_biteq!(f32::from_bits(0x41480000), 12.5); - assert_biteq!(f32::from_bits(0x44a72000), 1337.0); - assert_biteq!(f32::from_bits(0xc1640000), -14.25); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits - let masked_nan1 = f32::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f32::NAN.to_bits() ^ NAN_MASK2; - assert!(f32::from_bits(masked_nan1).is_nan()); - assert!(f32::from_bits(masked_nan2).is_nan()); - - assert_eq!(f32::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2); -} diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs index 099d85627a3..4c5a3d68d1f 100644 --- a/library/coretests/tests/floats/f64.rs +++ b/library/coretests/tests/floats/f64.rs @@ -2,12 +2,6 @@ use core::f64; use super::assert_biteq; -/// First pattern over the mantissa -const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa; - -/// Second pattern over the mantissa -const NAN_MASK2: u64 = 0x0005_5555_5555_5555; - // FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/ #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)] #[test] @@ -25,24 +19,3 @@ fn test_mul_add() { assert_biteq!(8.9f64.mul_add(inf, 3.2), inf); assert_biteq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf); } - -#[test] -fn test_float_bits_conv() { - assert_eq!((1f64).to_bits(), 0x3ff0000000000000); - assert_eq!((12.5f64).to_bits(), 0x4029000000000000); - assert_eq!((1337f64).to_bits(), 0x4094e40000000000); - assert_eq!((-14.25f64).to_bits(), 0xc02c800000000000); - assert_biteq!(f64::from_bits(0x3ff0000000000000), 1.0); - assert_biteq!(f64::from_bits(0x4029000000000000), 12.5); - assert_biteq!(f64::from_bits(0x4094e40000000000), 1337.0); - assert_biteq!(f64::from_bits(0xc02c800000000000), -14.25); - - // Check that NaNs roundtrip their bits regardless of signaling-ness - let masked_nan1 = f64::NAN.to_bits() ^ NAN_MASK1; - let masked_nan2 = f64::NAN.to_bits() ^ NAN_MASK2; - assert!(f64::from_bits(masked_nan1).is_nan()); - assert!(f64::from_bits(masked_nan2).is_nan()); - - assert_eq!(f64::from_bits(masked_nan1).to_bits(), masked_nan1); - assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2); -} diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index c0439845a4a..31515561c63 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -30,6 +30,10 @@ trait TestableFloat: Sized { const EPS_ADD: Self; const EPS_MUL: Self; const EPS_DIV: Self; + const RAW_1: Self; + const RAW_12_DOT_5: Self; + const RAW_1337: Self; + const RAW_MINUS_14_DOT_25: Self; } impl TestableFloat for f16 { @@ -50,6 +54,10 @@ impl TestableFloat for f16 { const EPS_ADD: Self = if cfg!(miri) { 1e1 } else { 0.0 }; const EPS_MUL: Self = if cfg!(miri) { 1e3 } else { 0.0 }; const EPS_DIV: Self = if cfg!(miri) { 1e0 } else { 0.0 }; + const RAW_1: Self = Self::from_bits(0x3c00); + const RAW_12_DOT_5: Self = Self::from_bits(0x4a40); + const RAW_1337: Self = Self::from_bits(0x6539); + const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xcb20); } impl TestableFloat for f32 { @@ -72,6 +80,10 @@ impl TestableFloat for f32 { const EPS_ADD: Self = if cfg!(miri) { 1e-3 } else { 0.0 }; const EPS_MUL: Self = if cfg!(miri) { 1e-1 } else { 0.0 }; const EPS_DIV: Self = if cfg!(miri) { 1e-4 } else { 0.0 }; + const RAW_1: Self = Self::from_bits(0x3f800000); + const RAW_12_DOT_5: Self = Self::from_bits(0x41480000); + const RAW_1337: Self = Self::from_bits(0x44a72000); + const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc1640000); } impl TestableFloat for f64 { @@ -90,6 +102,10 @@ impl TestableFloat for f64 { const EPS_ADD: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; const EPS_MUL: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; const EPS_DIV: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; + const RAW_1: Self = Self::from_bits(0x3ff0000000000000); + const RAW_12_DOT_5: Self = Self::from_bits(0x4029000000000000); + const RAW_1337: Self = Self::from_bits(0x4094e40000000000); + const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc02c800000000000); } impl TestableFloat for f128 { @@ -108,6 +124,10 @@ impl TestableFloat for f128 { const EPS_ADD: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; const EPS_MUL: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; const EPS_DIV: Self = if cfg!(miri) { 1e-6 } else { 0.0 }; + const RAW_1: Self = Self::from_bits(0x3fff0000000000000000000000000000); + const RAW_12_DOT_5: Self = Self::from_bits(0x40029000000000000000000000000000); + const RAW_1337: Self = Self::from_bits(0x40094e40000000000000000000000000); + const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc002c800000000000000000000000000); } /// Determine the tolerance for values of the argument type. @@ -250,6 +270,8 @@ macro_rules! float_test { $( $( #[$f16_meta] )+ )? fn test_f16() { type $fty = f16; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } $test } @@ -257,6 +279,8 @@ macro_rules! float_test { $( $( #[$f32_meta] )+ )? fn test_f32() { type $fty = f32; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } $test } @@ -264,6 +288,8 @@ macro_rules! float_test { $( $( #[$f64_meta] )+ )? fn test_f64() { type $fty = f64; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } $test } @@ -271,6 +297,8 @@ macro_rules! float_test { $( $( #[$f128_meta] )+ )? fn test_f128() { type $fty = f128; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } $test } @@ -293,6 +321,8 @@ macro_rules! float_test { $( $( #[$f16_const_meta] )+ )? fn test_f16() { type $fty = f16; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } const { $test } } @@ -300,6 +330,8 @@ macro_rules! float_test { $( $( #[$f32_const_meta] )+ )? fn test_f32() { type $fty = f32; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } const { $test } } @@ -307,6 +339,8 @@ macro_rules! float_test { $( $( #[$f64_const_meta] )+ )? fn test_f64() { type $fty = f64; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } const { $test } } @@ -314,6 +348,8 @@ macro_rules! float_test { $( $( #[$f128_const_meta] )+ )? fn test_f128() { type $fty = f128; + #[allow(unused)] + const fn flt (x: $fty) -> $fty { x } const { $test } } } @@ -1479,3 +1515,30 @@ float_test! { assert_approx_eq!(a.algebraic_rem(b), a % b, Float::EPS_DIV); } } + +float_test! { + name: to_bits_conv, + attrs: { + f16: #[cfg(target_has_reliable_f16)], + f128: #[cfg(target_has_reliable_f128)], + }, + test<Float> { + assert_biteq!(flt(1.0), Float::RAW_1); + assert_biteq!(flt(12.5), Float::RAW_12_DOT_5); + assert_biteq!(flt(1337.0), Float::RAW_1337); + assert_biteq!(flt(-14.25), Float::RAW_MINUS_14_DOT_25); + assert_biteq!(Float::RAW_1, 1.0); + assert_biteq!(Float::RAW_12_DOT_5, 12.5); + assert_biteq!(Float::RAW_1337, 1337.0); + assert_biteq!(Float::RAW_MINUS_14_DOT_25, -14.25); + + // Check that NaNs roundtrip their bits regardless of signaling-ness + let masked_nan1 = Float::NAN.to_bits() ^ Float::NAN_MASK1; + let masked_nan2 = Float::NAN.to_bits() ^ Float::NAN_MASK2; + assert!(Float::from_bits(masked_nan1).is_nan()); + assert!(Float::from_bits(masked_nan2).is_nan()); + + assert_biteq!(Float::from_bits(masked_nan1), Float::from_bits(masked_nan1)); + assert_biteq!(Float::from_bits(masked_nan2), Float::from_bits(masked_nan2)); + } +} diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index b5658a9970f..04fa0aeba65 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -15,6 +15,7 @@ #![feature(cfg_target_has_reliable_f16_f128)] #![feature(char_max_len)] #![feature(clone_to_uninit)] +#![feature(const_cmp)] #![feature(const_convert)] #![feature(const_destruct)] #![feature(const_eval_select)] @@ -81,6 +82,7 @@ #![feature(next_index)] #![feature(non_exhaustive_omitted_patterns_lint)] #![feature(numfmt)] +#![feature(one_sided_range)] #![feature(option_reduce)] #![feature(pattern)] #![feature(peekable_next_if_map)] diff --git a/library/coretests/tests/ops.rs b/library/coretests/tests/ops.rs index 501e0f33fe4..121718f2167 100644 --- a/library/coretests/tests/ops.rs +++ b/library/coretests/tests/ops.rs @@ -2,7 +2,8 @@ mod control_flow; mod from_residual; use core::ops::{ - Bound, Deref, DerefMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, + Bound, Deref, DerefMut, OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeFrom, + RangeFull, RangeInclusive, RangeTo, RangeToInclusive, }; // Test the Range structs and syntax. @@ -71,6 +72,36 @@ fn test_range_to_inclusive() { } #[test] +fn test_range_contains() { + assert!(!(1u32..5).contains(&0u32)); + assert!((1u32..5).contains(&1u32)); + assert!((1u32..5).contains(&4u32)); + assert!(!(1u32..5).contains(&5u32)); + assert!(!(1u32..5).contains(&6u32)); +} + +#[test] +fn test_range_to_contains() { + assert!(!(1u32..=5).contains(&0)); + assert!((1u32..=5).contains(&1)); + assert!((1u32..=5).contains(&4)); + assert!((1u32..=5).contains(&5)); + assert!(!(1u32..=5).contains(&6)); +} + +// This test covers `RangeBounds::contains` when the start is excluded, +// which cannot be directly expressed by Rust's built-in range syntax. +#[test] +fn test_range_bounds_contains() { + let r = (Bound::Excluded(1u32), Bound::Included(5u32)); + assert!(!r.contains(&0)); + assert!(!r.contains(&1)); + assert!(r.contains(&3)); + assert!(r.contains(&5)); + assert!(!r.contains(&6)); +} + +#[test] fn test_range_is_empty() { assert!(!(0.0..10.0).is_empty()); assert!((-0.0..0.0).is_empty()); @@ -92,6 +123,34 @@ fn test_range_is_empty() { } #[test] +fn test_range_inclusive_end_bound() { + let mut r = 1u32..=1; + r.next().unwrap(); + assert!(!r.contains(&1)); +} + +#[test] +fn test_range_bounds() { + let r = (Bound::Included(1u32), Bound::Excluded(5u32)); + assert!(!r.contains(&0)); + assert!(r.contains(&1)); + assert!(r.contains(&3)); + assert!(!r.contains(&5)); + assert!(!r.contains(&6)); + + let r = (Bound::<u32>::Unbounded, Bound::Unbounded); + assert!(r.contains(&0)); + assert!(r.contains(&u32::MAX)); +} + +#[test] +fn test_one_sided_range_bound() { + assert!(matches!((..1u32).bound(), (OneSidedRangeBound::End, 1))); + assert!(matches!((1u32..).bound(), (OneSidedRangeBound::StartInclusive, 1))); + assert!(matches!((..=1u32).bound(), (OneSidedRangeBound::EndInclusive, 1))); +} + +#[test] fn test_bound_cloned_unbounded() { assert_eq!(Bound::<&u32>::Unbounded.cloned(), Bound::Unbounded); } @@ -240,3 +299,17 @@ fn deref_on_ref() { fn test_not_never() { if !return () {} } + +#[test] +fn test_fmt() { + let mut r = 1..=1; + assert_eq!(format!("{:?}", r), "1..=1"); + r.next().unwrap(); + assert_eq!(format!("{:?}", r), "1..=1 (exhausted)"); + + assert_eq!(format!("{:?}", 1..1), "1..1"); + assert_eq!(format!("{:?}", 1..), "1.."); + assert_eq!(format!("{:?}", ..1), "..1"); + assert_eq!(format!("{:?}", ..=1), "..=1"); + assert_eq!(format!("{:?}", ..), ".."); +} diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 57a980d6acd..21e82d43a80 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -95,6 +95,9 @@ impl Error { pub(crate) const ZERO_TIMEOUT: Self = const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout"); + + pub(crate) const NO_ADDRESSES: Self = + const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses"); } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 97db0d6ab75..5725816c600 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -350,6 +350,7 @@ #![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(fmt_internals)] +#![feature(fn_ptr_trait)] #![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] diff --git a/library/std/src/net/mod.rs b/library/std/src/net/mod.rs index ddd3b68dd2d..40f1a93e39d 100644 --- a/library/std/src/net/mod.rs +++ b/library/std/src/net/mod.rs @@ -34,7 +34,6 @@ pub use self::tcp::IntoIncoming; pub use self::tcp::{Incoming, TcpListener, TcpStream}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::udp::UdpSocket; -use crate::io::{self, ErrorKind}; mod ip_addr; mod socket_addr; @@ -67,23 +66,3 @@ pub enum Shutdown { #[stable(feature = "rust1", since = "1.0.0")] Both, } - -fn each_addr<A: ToSocketAddrs, F, T>(addr: A, mut f: F) -> io::Result<T> -where - F: FnMut(io::Result<&SocketAddr>) -> io::Result<T>, -{ - let addrs = match addr.to_socket_addrs() { - Ok(addrs) => addrs, - Err(e) => return f(Err(e)), - }; - let mut last_err = None; - for addr in addrs { - match f(Ok(&addr)) { - Ok(l) => return Ok(l), - Err(e) => last_err = Some(e), - } - } - Err(last_err.unwrap_or_else(|| { - io::const_error!(ErrorKind::InvalidInput, "could not resolve to any addresses") - })) -} diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 10685b49319..ae50f531a71 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -167,7 +167,7 @@ impl TcpStream { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> { - super::each_addr(addr, net_imp::TcpStream::connect).map(TcpStream) + net_imp::TcpStream::connect(addr).map(TcpStream) } /// Opens a TCP connection to a remote host with a timeout. @@ -782,7 +782,7 @@ impl TcpListener { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> { - super::each_addr(addr, net_imp::TcpListener::bind).map(TcpListener) + net_imp::TcpListener::bind(addr).map(TcpListener) } /// Returns the local socket address of this listener. diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 03003037b29..7c7ef7b2f70 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -1,5 +1,5 @@ use crate::io::prelude::*; -use crate::io::{BorrowedBuf, IoSlice, IoSliceMut}; +use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut}; use crate::mem::MaybeUninit; use crate::net::test::{next_test_ip4, next_test_ip6}; use crate::net::*; diff --git a/library/std/src/net/udp.rs b/library/std/src/net/udp.rs index a97b3299774..72e292e3d15 100644 --- a/library/std/src/net/udp.rs +++ b/library/std/src/net/udp.rs @@ -120,7 +120,7 @@ impl UdpSocket { /// [`Ipv4Addr::UNSPECIFIED`] or [`Ipv6Addr::UNSPECIFIED`]. #[stable(feature = "rust1", since = "1.0.0")] pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> { - super::each_addr(addr, net_imp::UdpSocket::bind).map(UdpSocket) + net_imp::UdpSocket::bind(addr).map(UdpSocket) } /// Receives a single datagram message on the socket. On success, returns the number @@ -677,7 +677,7 @@ impl UdpSocket { /// on the platform. #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> io::Result<()> { - super::each_addr(addr, |addr| self.0.connect(addr)) + self.0.connect(addr) } /// Sends data on the socket to the remote address to which it is connected. diff --git a/library/std/src/net/udp/tests.rs b/library/std/src/net/udp/tests.rs index 91da3135f97..0638b36c54f 100644 --- a/library/std/src/net/udp/tests.rs +++ b/library/std/src/net/udp/tests.rs @@ -1,3 +1,4 @@ +use crate::io::ErrorKind; use crate::net::test::{compare_ignore_zoneid, next_test_ip4, next_test_ip6}; use crate::net::*; use crate::sync::mpsc::channel; diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index 712ce03f90b..8988126bd90 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -1,4 +1,5 @@ use crate::fmt; +use crate::panic::RefUnwindSafe; use crate::sync::nonpoison::{Condvar, Mutex}; /// A barrier enables multiple threads to synchronize the beginning @@ -31,6 +32,9 @@ pub struct Barrier { num_threads: usize, } +#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] +impl RefUnwindSafe for Barrier {} + // The inner state of a double barrier struct BarrierState { count: usize, diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index 8c115015580..2dbdc8a4e02 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -31,6 +31,7 @@ pub mod process; pub mod random; pub mod stdio; pub mod sync; +pub mod thread; pub mod thread_local; // FIXME(117276): remove this, move feature implementations into individual diff --git a/library/std/src/sys/net/connection/mod.rs b/library/std/src/sys/net/connection/mod.rs new file mode 100644 index 00000000000..7f9636a8ccf --- /dev/null +++ b/library/std/src/sys/net/connection/mod.rs @@ -0,0 +1,57 @@ +cfg_select! { + any( + all(target_family = "unix", not(target_os = "l4re")), + target_os = "windows", + target_os = "hermit", + all(target_os = "wasi", target_env = "p2"), + target_os = "solid_asp3", + ) => { + mod socket; + pub use socket::*; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use sgx::*; + } + all(target_os = "wasi", target_env = "p1") => { + mod wasip1; + pub use wasip1::*; + } + target_os = "xous" => { + mod xous; + pub use xous::*; + } + target_os = "uefi" => { + mod uefi; + pub use uefi::*; + } + _ => { + mod unsupported; + pub use unsupported::*; + } +} + +#[cfg_attr( + // Make sure that this is used on some platforms at least. + not(any(target_os = "linux", target_os = "windows")), + allow(dead_code) +)] +fn each_addr<A: crate::net::ToSocketAddrs, F, T>(addr: A, mut f: F) -> crate::io::Result<T> +where + F: FnMut(&crate::net::SocketAddr) -> crate::io::Result<T>, +{ + use crate::io::Error; + + let mut last_err = None; + for addr in addr.to_socket_addrs()? { + match f(&addr) { + Ok(l) => return Ok(l), + Err(e) => last_err = Some(e), + } + } + + match last_err { + Some(err) => Err(err), + None => Err(Error::NO_ADDRESSES), + } +} diff --git a/library/std/src/sys/net/connection/sgx.rs b/library/std/src/sys/net/connection/sgx.rs index 2389fd1bcb6..9b54571997d 100644 --- a/library/std/src/sys/net/connection/sgx.rs +++ b/library/std/src/sys/net/connection/sgx.rs @@ -1,3 +1,5 @@ +use crate::error; +use crate::fmt::{self, Write}; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::Arc; @@ -5,7 +7,6 @@ use crate::sys::abi::usercalls; use crate::sys::fd::FileDesc; use crate::sys::{AsInner, FromInner, IntoInner, TryIntoInner, sgx_ineffective, unsupported}; use crate::time::Duration; -use crate::{error, fmt}; const DEFAULT_FAKE_TTL: u32 = 64; @@ -63,18 +64,52 @@ impl fmt::Debug for TcpStream { } } -fn io_err_to_addr(result: io::Result<&SocketAddr>) -> io::Result<String> { - match result { - Ok(saddr) => Ok(saddr.to_string()), - // need to downcast twice because io::Error::into_inner doesn't return the original - // value if the conversion fails - Err(e) => { - if e.get_ref().and_then(|e| e.downcast_ref::<NonIpSockAddr>()).is_some() { - Ok(e.into_inner().unwrap().downcast::<NonIpSockAddr>().unwrap().host) - } else { - Err(e) +/// Converts each address in `addr` into a hostname. +/// +/// SGX doesn't support DNS resolution but rather accepts hostnames in +/// the same place as socket addresses. So, to make e.g. +/// ```rust +/// TcpStream::connect("example.com:80")` +/// ``` +/// work, the DNS lookup returns a special error (`NonIpSockAddr`) instead, +/// which contains the hostname being looked up. When `.to_socket_addrs()` +/// fails, we inspect the error and try recover the hostname from it. If that +/// succeeds, we thus continue with the hostname. +/// +/// This is a terrible hack and leads to buggy code. For instance, when users +/// use the result of `.to_socket_addrs()` in their own `ToSocketAddrs` +/// implementation to select from a list of possible URLs, the only URL used +/// will be that of the last item tried. +// FIXME: This is a terrible, terrible hack. Fixing this requires Fortanix to +// add a method for resolving addresses. +fn each_addr<A: ToSocketAddrs, F, T>(addr: A, mut f: F) -> io::Result<T> +where + F: FnMut(&str) -> io::Result<T>, +{ + match addr.to_socket_addrs() { + Ok(addrs) => { + let mut last_err = None; + let mut encoded = String::new(); + for addr in addrs { + // Format the IP address as a string, reusing the buffer. + encoded.clear(); + write!(encoded, "{}", &addr).unwrap(); + + match f(&encoded) { + Ok(val) => return Ok(val), + Err(err) => last_err = Some(err), + } + } + + match last_err { + Some(err) => Err(err), + None => Err(io::Error::NO_ADDRESSES), } } + Err(err) => match err.get_ref().and_then(|e| e.downcast_ref::<NonIpSockAddr>()) { + Some(NonIpSockAddr { host }) => f(host), + None => Err(err), + }, } } @@ -86,17 +121,18 @@ fn addr_to_sockaddr(addr: Option<&str>) -> io::Result<SocketAddr> { } impl TcpStream { - pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> { - let addr = io_err_to_addr(addr)?; - let (fd, local_addr, peer_addr) = usercalls::connect_stream(&addr)?; - Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) }) + pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> { + each_addr(addr, |addr| { + let (fd, local_addr, peer_addr) = usercalls::connect_stream(addr)?; + Ok(TcpStream { inner: Socket::new(fd, local_addr), peer_addr: Some(peer_addr) }) + }) } pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result<TcpStream> { if dur == Duration::default() { return Err(io::Error::ZERO_TIMEOUT); } - Self::connect(Ok(addr)) // FIXME: ignoring timeout + Self::connect(addr) // FIXME: ignoring timeout } pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { @@ -247,10 +283,11 @@ impl fmt::Debug for TcpListener { } impl TcpListener { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> { - let addr = io_err_to_addr(addr)?; - let (fd, local_addr) = usercalls::bind_stream(&addr)?; - Ok(TcpListener { inner: Socket::new(fd, local_addr) }) + pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> { + each_addr(addr, |addr| { + let (fd, local_addr) = usercalls::bind_stream(addr)?; + Ok(TcpListener { inner: Socket::new(fd, local_addr) }) + }) } pub fn socket_addr(&self) -> io::Result<SocketAddr> { @@ -316,7 +353,7 @@ impl FromInner<Socket> for TcpListener { pub struct UdpSocket(!); impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { + pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<UdpSocket> { unsupported() } @@ -436,7 +473,7 @@ impl UdpSocket { self.0 } - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + pub fn connect<A: ToSocketAddrs>(&self, _: A) -> io::Result<()> { self.0 } } diff --git a/library/std/src/sys/net/connection/socket/hermit.rs b/library/std/src/sys/net/connection/socket/hermit.rs index f49821657d9..5200eaa5786 100644 --- a/library/std/src/sys/net/connection/socket/hermit.rs +++ b/library/std/src/sys/net/connection/socket/hermit.rs @@ -304,7 +304,8 @@ impl Socket { } pub fn take_error(&self) -> io::Result<Option<io::Error>> { - unimplemented!() + let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } } // This is used by sys_common code to abstract over Windows and Unix. diff --git a/library/std/src/sys/net/connection/socket.rs b/library/std/src/sys/net/connection/socket/mod.rs index aa83ed65d4c..564f2e3a01f 100644 --- a/library/std/src/sys/net/connection/socket.rs +++ b/library/std/src/sys/net/connection/socket/mod.rs @@ -3,8 +3,11 @@ mod tests; use crate::ffi::{c_int, c_void}; use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::net::{ + Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, +}; use crate::sys::common::small_c_string::run_with_cstr; +use crate::sys::net::connection::each_addr; use crate::sys_common::{AsInner, FromInner}; use crate::time::Duration; use crate::{cmp, fmt, mem, ptr}; @@ -342,14 +345,15 @@ pub struct TcpStream { } impl TcpStream { - pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> { - let addr = addr?; - + pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> { init(); + return each_addr(addr, inner); - let sock = Socket::new(addr, c::SOCK_STREAM)?; - sock.connect(addr)?; - Ok(TcpStream { inner: sock }) + fn inner(addr: &SocketAddr) -> io::Result<TcpStream> { + let sock = Socket::new(addr, c::SOCK_STREAM)?; + sock.connect(addr)?; + Ok(TcpStream { inner: sock }) + } } pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result<TcpStream> { @@ -512,48 +516,45 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> { - let addr = addr?; - + pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> { init(); - - let sock = Socket::new(addr, c::SOCK_STREAM)?; - - // On platforms with Berkeley-derived sockets, this allows to quickly - // rebind a socket, without needing to wait for the OS to clean up the - // previous one. - // - // On Windows, this allows rebinding sockets which are actively in use, - // which allows “socket hijacking”, so we explicitly don't set it here. - // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse - #[cfg(not(windows))] - setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; - - // Bind our new socket - let (addr, len) = socket_addr_to_c(addr); - cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; - - cfg_select! { - target_os = "horizon" => { + return each_addr(addr, inner); + + fn inner(addr: &SocketAddr) -> io::Result<TcpListener> { + let sock = Socket::new(addr, c::SOCK_STREAM)?; + + // On platforms with Berkeley-derived sockets, this allows to quickly + // rebind a socket, without needing to wait for the OS to clean up the + // previous one. + // + // On Windows, this allows rebinding sockets which are actively in use, + // which allows “socket hijacking”, so we explicitly don't set it here. + // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse + #[cfg(not(windows))] + setsockopt(&sock, c::SOL_SOCKET, c::SO_REUSEADDR, 1 as c_int)?; + + // Bind our new socket + let (addr, len) = socket_addr_to_c(addr); + cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; + + let backlog = if cfg!(target_os = "horizon") { // The 3DS doesn't support a big connection backlog. Sometimes // it allows up to about 37, but other times it doesn't even // accept 32. There may be a global limitation causing this. - let backlog = 20; - } - target_os = "haiku" => { + 20 + } else if cfg!(target_os = "haiku") { // Haiku does not support a queue length > 32 // https://github.com/haiku/haiku/blob/979a0bc487864675517fb2fab28f87dc8bf43041/headers/posix/sys/socket.h#L81 - let backlog = 32; - } - _ => { + 32 + } else { // The default for all other platforms - let backlog = 128; - } - } + 128 + }; - // Start listening - cvt(unsafe { c::listen(sock.as_raw(), backlog) })?; - Ok(TcpListener { inner: sock }) + // Start listening + cvt(unsafe { c::listen(sock.as_raw(), backlog) })?; + Ok(TcpListener { inner: sock }) + } } #[inline] @@ -639,15 +640,16 @@ pub struct UdpSocket { } impl UdpSocket { - pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { - let addr = addr?; - + pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> { init(); + return each_addr(addr, inner); - let sock = Socket::new(addr, c::SOCK_DGRAM)?; - let (addr, len) = socket_addr_to_c(addr); - cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; - Ok(UdpSocket { inner: sock }) + fn inner(addr: &SocketAddr) -> io::Result<UdpSocket> { + let sock = Socket::new(addr, c::SOCK_DGRAM)?; + let (addr, len) = socket_addr_to_c(addr); + cvt(unsafe { c::bind(sock.as_raw(), addr.as_ptr(), len as _) })?; + Ok(UdpSocket { inner: sock }) + } } #[inline] @@ -822,9 +824,13 @@ impl UdpSocket { Ok(ret as usize) } - pub fn connect(&self, addr: io::Result<&SocketAddr>) -> io::Result<()> { - let (addr, len) = socket_addr_to_c(addr?); - cvt_r(|| unsafe { c::connect(self.inner.as_raw(), addr.as_ptr(), len) }).map(drop) + pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> io::Result<()> { + return each_addr(addr, |addr| inner(self, addr)); + + fn inner(this: &UdpSocket, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = socket_addr_to_c(addr); + cvt_r(|| unsafe { c::connect(this.inner.as_raw(), addr.as_ptr(), len) }).map(drop) + } } } diff --git a/library/std/src/sys/net/connection/uefi/mod.rs b/library/std/src/sys/net/connection/uefi/mod.rs index 16e3487a174..00368042873 100644 --- a/library/std/src/sys/net/connection/uefi/mod.rs +++ b/library/std/src/sys/net/connection/uefi/mod.rs @@ -1,6 +1,7 @@ +use super::each_addr; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sync::{Arc, Mutex}; use crate::sys::unsupported; use crate::time::Duration; @@ -15,13 +16,17 @@ pub struct TcpStream { } impl TcpStream { - pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> { - let inner = tcp::Tcp::connect(addr?, None)?; - Ok(Self { - inner, - read_timeout: Arc::new(Mutex::new(None)), - write_timeout: Arc::new(Mutex::new(None)), - }) + pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> { + return each_addr(addr, inner); + + fn inner(addr: &SocketAddr) -> io::Result<TcpStream> { + let inner = tcp::Tcp::connect(addr, None)?; + Ok(TcpStream { + inner, + read_timeout: Arc::new(Mutex::new(None)), + write_timeout: Arc::new(Mutex::new(None)), + }) + } } pub fn connect_timeout(addr: &SocketAddr, timeout: Duration) -> io::Result<TcpStream> { @@ -145,7 +150,7 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { + pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<TcpListener> { unsupported() } @@ -195,7 +200,7 @@ impl fmt::Debug for TcpListener { pub struct UdpSocket(!); impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { + pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<UdpSocket> { unsupported() } @@ -315,7 +320,7 @@ impl UdpSocket { self.0 } - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + pub fn connect<A: ToSocketAddrs>(&self, _: A) -> io::Result<()> { self.0 } } diff --git a/library/std/src/sys/net/connection/unsupported.rs b/library/std/src/sys/net/connection/unsupported.rs index da217439626..fbc86343272 100644 --- a/library/std/src/sys/net/connection/unsupported.rs +++ b/library/std/src/sys/net/connection/unsupported.rs @@ -1,13 +1,13 @@ use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::sys::unsupported; use crate::time::Duration; pub struct TcpStream(!); impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { + pub fn connect<A: ToSocketAddrs>(_: A) -> io::Result<TcpStream> { unsupported() } @@ -121,7 +121,7 @@ impl fmt::Debug for TcpStream { pub struct TcpListener(!); impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { + pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<TcpListener> { unsupported() } @@ -171,7 +171,7 @@ impl fmt::Debug for TcpListener { pub struct UdpSocket(!); impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { + pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<UdpSocket> { unsupported() } @@ -291,7 +291,7 @@ impl UdpSocket { self.0 } - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + pub fn connect<A: ToSocketAddrs>(&self, _: A) -> io::Result<()> { self.0 } } diff --git a/library/std/src/sys/net/connection/wasip1.rs b/library/std/src/sys/net/connection/wasip1.rs index 951dc65e5b4..cdfa25c8a44 100644 --- a/library/std/src/sys/net/connection/wasip1.rs +++ b/library/std/src/sys/net/connection/wasip1.rs @@ -2,7 +2,7 @@ use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; +use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr, ToSocketAddrs}; use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::WasiFd; use crate::sys::{err2io, unsupported}; @@ -60,7 +60,7 @@ impl FromRawFd for Socket { } impl TcpStream { - pub fn connect(_: io::Result<&SocketAddr>) -> io::Result<TcpStream> { + pub fn connect<A: ToSocketAddrs>(_: A) -> io::Result<TcpStream> { unsupported() } @@ -212,7 +212,7 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<TcpListener> { + pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<TcpListener> { unsupported() } @@ -316,7 +316,7 @@ pub struct UdpSocket { } impl UdpSocket { - pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { + pub fn bind<A: ToSocketAddrs>(_: A) -> io::Result<UdpSocket> { unsupported() } @@ -436,7 +436,7 @@ impl UdpSocket { unsupported() } - pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> { + pub fn connect<A: ToSocketAddrs>(&self, _: A) -> io::Result<()> { unsupported() } diff --git a/library/std/src/sys/net/connection/xous/tcplistener.rs b/library/std/src/sys/net/connection/xous/tcplistener.rs index bdf1fcd9302..8818ef2ca9a 100644 --- a/library/std/src/sys/net/connection/xous/tcplistener.rs +++ b/library/std/src/sys/net/connection/xous/tcplistener.rs @@ -2,9 +2,10 @@ use core::convert::TryInto; use core::sync::atomic::{Atomic, AtomicBool, AtomicU16, AtomicUsize, Ordering}; use super::*; -use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; use crate::os::xous::services; use crate::sync::Arc; +use crate::sys::net::connection::each_addr; use crate::{fmt, io}; macro_rules! unimpl { @@ -25,16 +26,19 @@ pub struct TcpListener { } impl TcpListener { - pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result<TcpListener> { - let mut addr = *socketaddr?; - - let fd = TcpListener::bind_inner(&mut addr)?; - return Ok(TcpListener { - fd: Arc::new(AtomicU16::new(fd)), - local: addr, - handle_count: Arc::new(AtomicUsize::new(1)), - nonblocking: Arc::new(AtomicBool::new(false)), - }); + pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> { + return each_addr(addr, inner); + + fn inner(addr: &SocketAddr) -> io::Result<TcpListener> { + let mut addr = *addr; + let fd = TcpListener::bind_inner(&mut addr)?; + Ok(TcpListener { + fd: Arc::new(AtomicU16::new(fd)), + local: addr, + handle_count: Arc::new(AtomicUsize::new(1)), + nonblocking: Arc::new(AtomicBool::new(false)), + }) + } } /// This returns the raw fd of a Listener, so that it can also be used by the diff --git a/library/std/src/sys/net/connection/xous/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs index 54524767452..4df75453d1f 100644 --- a/library/std/src/sys/net/connection/xous/tcpstream.rs +++ b/library/std/src/sys/net/connection/xous/tcpstream.rs @@ -3,9 +3,12 @@ use core::sync::atomic::{Atomic, AtomicBool, AtomicU32, AtomicUsize, Ordering}; use super::*; use crate::fmt; use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; -use crate::net::{IpAddr, Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6}; +use crate::net::{ + IpAddr, Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs, +}; use crate::os::xous::services; use crate::sync::Arc; +use crate::sys::net::connection::each_addr; use crate::time::Duration; macro_rules! unimpl { @@ -79,8 +82,8 @@ impl TcpStream { } } - pub fn connect(socketaddr: io::Result<&SocketAddr>) -> io::Result<TcpStream> { - Self::connect_timeout(socketaddr?, Duration::ZERO) + pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> { + each_addr(addr, |addr| Self::connect_timeout(addr, Duration::ZERO)) } pub fn connect_timeout(addr: &SocketAddr, duration: Duration) -> io::Result<TcpStream> { diff --git a/library/std/src/sys/net/connection/xous/udp.rs b/library/std/src/sys/net/connection/xous/udp.rs index 2127d3267ed..ce54ea3b79e 100644 --- a/library/std/src/sys/net/connection/xous/udp.rs +++ b/library/std/src/sys/net/connection/xous/udp.rs @@ -3,9 +3,10 @@ use core::sync::atomic::{Atomic, AtomicUsize, Ordering}; use super::*; use crate::cell::Cell; -use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; use crate::os::xous::services; use crate::sync::Arc; +use crate::sys::net::connection::each_addr; use crate::time::Duration; use crate::{fmt, io}; @@ -32,40 +33,45 @@ pub struct UdpSocket { } impl UdpSocket { - pub fn bind(socketaddr: io::Result<&SocketAddr>) -> io::Result<UdpSocket> { - let addr = socketaddr?; - // Construct the request - let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; - - // Serialize the StdUdpBind structure. This is done "manually" because we don't want to - // make an auto-serdes (like bincode or rkyv) crate a dependency of Xous. - let port_bytes = addr.port().to_le_bytes(); - connect_request.raw[0] = port_bytes[0]; - connect_request.raw[1] = port_bytes[1]; - match addr.ip() { - IpAddr::V4(addr) => { - connect_request.raw[2] = 4; - for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { - *dest = src; + pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> { + return each_addr(addr, inner); + + fn inner(addr: &SocketAddr) -> io::Result<UdpSocket> { + // Construct the request + let mut connect_request = ConnectRequest { raw: [0u8; 4096] }; + + // Serialize the StdUdpBind structure. This is done "manually" because we don't want to + // make an auto-serdes (like bincode or rkyv) crate a dependency of Xous. + let port_bytes = addr.port().to_le_bytes(); + connect_request.raw[0] = port_bytes[0]; + connect_request.raw[1] = port_bytes[1]; + match addr.ip() { + IpAddr::V4(addr) => { + connect_request.raw[2] = 4; + for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } } - } - IpAddr::V6(addr) => { - connect_request.raw[2] = 6; - for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { - *dest = src; + IpAddr::V6(addr) => { + connect_request.raw[2] = 6; + for (dest, src) in connect_request.raw[3..].iter_mut().zip(addr.octets()) { + *dest = src; + } } } - } - let response = crate::os::xous::ffi::lend_mut( - services::net_server(), - services::NetLendMut::StdUdpBind.into(), - &mut connect_request.raw, - 0, - 4096, - ); + let response = crate::os::xous::ffi::lend_mut( + services::net_server(), + services::NetLendMut::StdUdpBind.into(), + &mut connect_request.raw, + 0, + 4096, + ); + + let Ok((_, valid)) = response else { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid response")); + }; - if let Ok((_, valid)) = response { // The first four bytes should be zero upon success, and will be nonzero // for an error. let response = connect_request.raw; @@ -87,8 +93,9 @@ impl UdpSocket { )); } } + let fd = response[1] as u16; - return Ok(UdpSocket { + Ok(UdpSocket { fd, local: *addr, remote: Cell::new(None), @@ -96,9 +103,8 @@ impl UdpSocket { write_timeout: Cell::new(0), handle_count: Arc::new(AtomicUsize::new(1)), nonblocking: Cell::new(false), - }); + }) } - Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid response")) } pub fn peer_addr(&self) -> io::Result<SocketAddr> { @@ -198,10 +204,11 @@ impl UdpSocket { self.peek_from(buf).map(|(len, _addr)| len) } - pub fn connect(&self, maybe_addr: io::Result<&SocketAddr>) -> io::Result<()> { - let addr = maybe_addr?; - self.remote.set(Some(*addr)); - Ok(()) + pub fn connect<A: ToSocketAddrs>(&self, addr: A) -> io::Result<()> { + each_addr(addr, |addr| { + self.remote.set(Some(*addr)); + Ok(()) + }) } pub fn send(&self, buf: &[u8]) -> io::Result<usize> { diff --git a/library/std/src/sys/net/mod.rs b/library/std/src/sys/net/mod.rs index 5df1fe138ab..dffc4ea7f81 100644 --- a/library/std/src/sys/net/mod.rs +++ b/library/std/src/sys/net/mod.rs @@ -1,46 +1,4 @@ -cfg_select! { - any( - all(target_family = "unix", not(target_os = "l4re")), - target_os = "windows", - target_os = "hermit", - all(target_os = "wasi", target_env = "p2"), - target_os = "solid_asp3", - ) => { - mod connection { - mod socket; - pub use socket::*; - } - } - all(target_vendor = "fortanix", target_env = "sgx") => { - mod connection { - mod sgx; - pub use sgx::*; - } - } - all(target_os = "wasi", target_env = "p1") => { - mod connection { - mod wasip1; - pub use wasip1::*; - } - } - target_os = "xous" => { - mod connection { - mod xous; - pub use xous::*; - } - } - target_os = "uefi" => { - mod connection { - mod uefi; - pub use uefi::*; - } - } - _ => { - mod connection { - mod unsupported; - pub use unsupported::*; - } - } -} - +/// This module contains the implementations of `TcpStream`, `TcpListener` and +/// `UdpSocket` as well as related functionality like DNS resolving. +mod connection; pub use connection::*; diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index fb8d69b7375..3ddf6e5acb0 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -25,7 +25,6 @@ pub mod futex; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; pub fn unsupported<T>() -> crate::io::Result<T> { diff --git a/library/std/src/sys/pal/hermit/os.rs b/library/std/src/sys/pal/hermit/os.rs index 0fe713a503b..9681964ed9b 100644 --- a/library/std/src/sys/pal/hermit/os.rs +++ b/library/std/src/sys/pal/hermit/os.rs @@ -3,7 +3,7 @@ use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::path::{self, PathBuf}; use crate::sys::unsupported; -use crate::{fmt, io, str}; +use crate::{fmt, io}; pub fn errno() -> i32 { unsafe { hermit_abi::get_errno() } diff --git a/library/std/src/sys/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs index 57247cffad3..b8c4d7740c4 100644 --- a/library/std/src/sys/pal/sgx/abi/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/mod.rs @@ -67,7 +67,7 @@ extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64 let tls_guard = unsafe { tls.activate() }; if secondary { - let join_notifier = super::thread::Thread::entry(); + let join_notifier = crate::sys::thread::Thread::entry(); drop(tls_guard); drop(join_notifier); diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 4a297b6823f..9a33873af58 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -13,7 +13,6 @@ mod libunwind_integration; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod thread_parking; pub mod time; pub mod waitqueue; diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs index 0011cf256df..9ca6dc58118 100644 --- a/library/std/src/sys/pal/solid/mod.rs +++ b/library/std/src/sys/pal/solid/mod.rs @@ -10,10 +10,8 @@ pub mod itron { pub mod error; pub mod spin; pub mod task; - pub mod thread; pub mod thread_parking; pub mod time; - use super::unsupported; } // `error` is `pub(crate)` so that it can be accessed by `itron/error.rs` as @@ -22,7 +20,7 @@ pub(crate) mod error; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub use self::itron::{thread, thread_parking}; +pub use self::itron::thread_parking; pub mod time; // SAFETY: must be called only once during runtime initialization. diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs index c7b17777258..dd0155265da 100644 --- a/library/std/src/sys/pal/teeos/mod.rs +++ b/library/std/src/sys/pal/teeos/mod.rs @@ -9,7 +9,6 @@ pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; #[allow(non_upper_case_globals)] #[path = "../unix/time.rs"] pub mod time; diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index 8911a2ee519..ebd311db1e1 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -17,7 +17,6 @@ pub mod helpers; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; #[cfg(test)] diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs deleted file mode 100644 index 47a48008c76..00000000000 --- a/library/std/src/sys/pal/uefi/thread.rs +++ /dev/null @@ -1,66 +0,0 @@ -use super::unsupported; -use crate::ffi::CStr; -use crate::io; -use crate::num::NonZero; -use crate::ptr::NonNull; -use crate::time::{Duration, Instant}; - -pub struct Thread(!); - -pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new( - _stack: usize, - _name: Option<&str>, - _p: Box<dyn FnOnce()>, - ) -> io::Result<Thread> { - unsupported() - } - - pub fn yield_now() { - // do nothing - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - let boot_services: NonNull<r_efi::efi::BootServices> = - crate::os::uefi::env::boot_services().expect("can't sleep").cast(); - let mut dur_ms = dur.as_micros(); - // ceil up to the nearest microsecond - if dur.subsec_nanos() % 1000 > 0 { - dur_ms += 1; - } - - while dur_ms > 0 { - let ms = crate::cmp::min(dur_ms, usize::MAX as u128); - let _ = unsafe { ((*boot_services.as_ptr()).stall)(ms as usize) }; - dur_ms -= ms; - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - - pub fn join(self) { - self.0 - } -} - -pub(crate) fn current_os_id() -> Option<u64> { - None -} - -pub fn available_parallelism() -> io::Result<NonZero<usize>> { - // UEFI is single threaded - Ok(NonZero::new(1).unwrap()) -} diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index ac5c823a1bf..dd1059fe04a 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -17,7 +17,6 @@ pub mod os; pub mod pipe; pub mod stack_overflow; pub mod sync; -pub mod thread; pub mod thread_parking; pub mod time; @@ -55,7 +54,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { // thread-id for the main thread and so renaming the main thread will rename the // process and we only want to enable this on platforms we've tested. if cfg!(target_vendor = "apple") { - thread::Thread::set_name(&c"main"); + crate::sys::thread::set_name(c"main"); } unsafe fn sanitize_standard_fds() { diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs index c8cf75b876c..a3b980a3f3d 100644 --- a/library/std/src/sys/pal/unix/weak.rs +++ b/library/std/src/sys/pal/unix/weak.rs @@ -22,11 +22,24 @@ #![allow(dead_code, unused_macros)] #![forbid(unsafe_op_in_unsafe_fn)] -use crate::ffi::CStr; -use crate::marker::PhantomData; -use crate::sync::atomic::{self, Atomic, AtomicPtr, Ordering}; +use crate::ffi::{CStr, c_char, c_void}; +use crate::marker::{FnPtr, PhantomData}; +use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::{mem, ptr}; +// We currently only test `dlsym!`, but that doesn't work on all platforms, so +// we gate the tests to only the platforms where it is actually used. +// +// FIXME(joboet): add more tests, reorganise the whole module and get rid of +// `#[allow(dead_code, unused_macros)]`. +#[cfg(any( + target_vendor = "apple", + all(target_os = "linux", target_env = "gnu"), + target_os = "freebsd", +))] +#[cfg(test)] +mod tests; + // We can use true weak linkage on ELF targets. #[cfg(all(unix, not(target_vendor = "apple")))] pub(crate) macro weak { @@ -64,7 +77,7 @@ impl<F: Copy> ExternWeak<F> { pub(crate) macro dlsym { (fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => ( - dlsym!( + dlsym!( #[link_name = stringify!($name)] fn $name($($param : $t),*) -> $ret; ); @@ -73,21 +86,39 @@ pub(crate) macro dlsym { #[link_name = $sym:expr] fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty; ) => ( - static DLSYM: DlsymWeak<unsafe extern "C" fn($($t),*) -> $ret> = - DlsymWeak::new(concat!($sym, '\0')); + static DLSYM: DlsymWeak<unsafe extern "C" fn($($t),*) -> $ret> = { + let Ok(name) = CStr::from_bytes_with_nul(concat!($sym, '\0').as_bytes()) else { + panic!("symbol name may not contain NUL") + }; + + // SAFETY: Whoever calls the function pointer returned by `get()` + // is responsible for ensuring that the signature is correct. Just + // like with extern blocks, this is syntactically enforced by making + // the function pointer be unsafe. + unsafe { DlsymWeak::new(name) } + }; + let $name = &DLSYM; ) } + pub(crate) struct DlsymWeak<F> { - name: &'static str, + /// A pointer to the nul-terminated name of the symbol. + // Use a pointer instead of `&'static CStr` to save space. + name: *const c_char, func: Atomic<*mut libc::c_void>, _marker: PhantomData<F>, } -impl<F> DlsymWeak<F> { - pub(crate) const fn new(name: &'static str) -> Self { +impl<F: FnPtr> DlsymWeak<F> { + /// # Safety + /// + /// If the signature of `F` does not match the signature of the symbol (if + /// it exists), calling the function pointer returned by `get()` is + /// undefined behaviour. + pub(crate) const unsafe fn new(name: &'static CStr) -> Self { DlsymWeak { - name, + name: name.as_ptr(), func: AtomicPtr::new(ptr::without_provenance_mut(1)), _marker: PhantomData, } @@ -95,62 +126,59 @@ impl<F> DlsymWeak<F> { #[inline] pub(crate) fn get(&self) -> Option<F> { - unsafe { - // Relaxed is fine here because we fence before reading through the - // pointer (see the comment below). - match self.func.load(Ordering::Relaxed) { - func if func.addr() == 1 => self.initialize(), - func if func.is_null() => None, - func => { - let func = mem::transmute_copy::<*mut libc::c_void, F>(&func); - // The caller is presumably going to read through this value - // (by calling the function we've dlsymed). This means we'd - // need to have loaded it with at least C11's consume - // ordering in order to be guaranteed that the data we read - // from the pointer isn't from before the pointer was - // stored. Rust has no equivalent to memory_order_consume, - // so we use an acquire fence (sorry, ARM). - // - // Now, in practice this likely isn't needed even on CPUs - // where relaxed and consume mean different things. The - // symbols we're loading are probably present (or not) at - // init, and even if they aren't the runtime dynamic loader - // is extremely likely have sufficient barriers internally - // (possibly implicitly, for example the ones provided by - // invoking `mprotect`). - // - // That said, none of that's *guaranteed*, and so we fence. - atomic::fence(Ordering::Acquire); - Some(func) - } - } + // The caller is presumably going to read through this value + // (by calling the function we've dlsymed). This means we'd + // need to have loaded it with at least C11's consume + // ordering in order to be guaranteed that the data we read + // from the pointer isn't from before the pointer was + // stored. Rust has no equivalent to memory_order_consume, + // so we use an acquire load (sorry, ARM). + // + // Now, in practice this likely isn't needed even on CPUs + // where relaxed and consume mean different things. The + // symbols we're loading are probably present (or not) at + // init, and even if they aren't the runtime dynamic loader + // is extremely likely have sufficient barriers internally + // (possibly implicitly, for example the ones provided by + // invoking `mprotect`). + // + // That said, none of that's *guaranteed*, so we use acquire. + match self.func.load(Ordering::Acquire) { + func if func.addr() == 1 => self.initialize(), + func if func.is_null() => None, + // SAFETY: + // `func` is not null and `F` implements `FnPtr`, thus this + // transmutation is well-defined. It is the responsibility of the + // creator of this `DlsymWeak` to ensure that calling the resulting + // function pointer does not result in undefined behaviour (though + // the `dlsym!` macro delegates this responsibility to the caller + // of the function by using `unsafe` function pointers). + // FIXME: use `transmute` once it stops complaining about generics. + func => Some(unsafe { mem::transmute_copy::<*mut c_void, F>(&func) }), } } // Cold because it should only happen during first-time initialization. #[cold] - unsafe fn initialize(&self) -> Option<F> { - assert_eq!(size_of::<F>(), size_of::<*mut libc::c_void>()); - - let val = unsafe { fetch(self.name) }; - // This synchronizes with the acquire fence in `get`. + fn initialize(&self) -> Option<F> { + // SAFETY: `self.name` was created from a `&'static CStr` and is + // therefore a valid C string pointer. + let val = unsafe { libc::dlsym(libc::RTLD_DEFAULT, self.name) }; + // This synchronizes with the acquire load in `get`. self.func.store(val, Ordering::Release); if val.is_null() { None } else { + // SAFETY: see the comment in `get`. + // FIXME: use `transmute` once it stops complaining about generics. Some(unsafe { mem::transmute_copy::<*mut libc::c_void, F>(&val) }) } } } -unsafe fn fetch(name: &str) -> *mut libc::c_void { - let name = match CStr::from_bytes_with_nul(name.as_bytes()) { - Ok(cstr) => cstr, - Err(..) => return ptr::null_mut(), - }; - unsafe { libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr()) } -} +unsafe impl<F> Send for DlsymWeak<F> {} +unsafe impl<F> Sync for DlsymWeak<F> {} #[cfg(not(any(target_os = "linux", target_os = "android")))] pub(crate) macro syscall { diff --git a/library/std/src/sys/pal/unix/weak/tests.rs b/library/std/src/sys/pal/unix/weak/tests.rs new file mode 100644 index 00000000000..d807ba64e35 --- /dev/null +++ b/library/std/src/sys/pal/unix/weak/tests.rs @@ -0,0 +1,32 @@ +use super::*; + +#[test] +fn dlsym_existing() { + const TEST_STRING: &'static CStr = c"Ferris!"; + + // Try to find a symbol that definitely exists. + dlsym! { + fn strlen(cs: *const c_char) -> usize; + } + + dlsym! { + #[link_name = "strlen"] + fn custom_name(cs: *const c_char) -> usize; + } + + let strlen = strlen.get().unwrap(); + assert_eq!(unsafe { strlen(TEST_STRING.as_ptr()) }, TEST_STRING.count_bytes()); + + let custom_name = custom_name.get().unwrap(); + assert_eq!(unsafe { custom_name(TEST_STRING.as_ptr()) }, TEST_STRING.count_bytes()); +} + +#[test] +fn dlsym_missing() { + // Try to find a symbol that definitely does not exist. + dlsym! { + fn test_symbol_that_does_not_exist() -> i32; + } + + assert!(test_symbol_that_does_not_exist.get().is_none()); +} diff --git a/library/std/src/sys/pal/wasip1/mod.rs b/library/std/src/sys/pal/wasip1/mod.rs index 61dd1c3f98b..ae5da3c1f77 100644 --- a/library/std/src/sys/pal/wasip1/mod.rs +++ b/library/std/src/sys/pal/wasip1/mod.rs @@ -20,7 +20,6 @@ pub mod futex; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; #[path = "../unsupported/common.rs"] diff --git a/library/std/src/sys/pal/wasip1/thread.rs b/library/std/src/sys/pal/wasip1/thread.rs deleted file mode 100644 index e062b49bd7a..00000000000 --- a/library/std/src/sys/pal/wasip1/thread.rs +++ /dev/null @@ -1,214 +0,0 @@ -#![forbid(unsafe_op_in_unsafe_fn)] - -use crate::ffi::CStr; -use crate::num::NonZero; -use crate::time::{Duration, Instant}; -use crate::{io, mem}; - -cfg_select! { - target_feature = "atomics" => { - use crate::cmp; - use crate::ptr; - use crate::sys::os; - // Add a few symbols not in upstream `libc` just yet. - mod libc { - pub use crate::ffi; - pub use libc::*; - - // defined in wasi-libc - // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108 - #[repr(C)] - union pthread_attr_union { - __i: [ffi::c_int; if size_of::<ffi::c_long>() == 8 { 14 } else { 9 }], - __vi: [ffi::c_int; if size_of::<ffi::c_long>() == 8 { 14 } else { 9 }], - __s: [ffi::c_ulong; if size_of::<ffi::c_long>() == 8 { 7 } else { 9 }], - } - - #[repr(C)] - pub struct pthread_attr_t { - __u: pthread_attr_union, - } - - #[allow(non_camel_case_types)] - pub type pthread_t = *mut ffi::c_void; - - pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84; - - unsafe extern "C" { - pub fn pthread_create( - native: *mut pthread_t, - attr: *const pthread_attr_t, - f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void, - value: *mut ffi::c_void, - ) -> ffi::c_int; - pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int; - pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int; - pub fn pthread_attr_setstacksize( - attr: *mut pthread_attr_t, - stack_size: libc::size_t, - ) -> ffi::c_int; - pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int; - pub fn pthread_detach(thread: pthread_t) -> ffi::c_int; - } - } - - pub struct Thread { - id: libc::pthread_t, - } - - impl Drop for Thread { - fn drop(&mut self) { - let ret = unsafe { libc::pthread_detach(self.id) }; - debug_assert_eq!(ret, 0); - } - } - } - _ => { - pub struct Thread(!); - } -} - -pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - cfg_select! { - target_feature = "atomics" => { - pub unsafe fn new(stack: usize, _name: Option<&str>, p: Box<dyn FnOnce()>) -> io::Result<Thread> { - let p = Box::into_raw(Box::new(p)); - let mut native: libc::pthread_t = unsafe { mem::zeroed() }; - let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; - assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0); - - let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); - - match unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) } { - 0 => {} - n => { - assert_eq!(n, libc::EINVAL); - // EINVAL means |stack_size| is either too small or not a - // multiple of the system page size. Because it's definitely - // >= PTHREAD_STACK_MIN, it must be an alignment issue. - // Round up to the nearest page and try again. - let page_size = os::page_size(); - let stack_size = - (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); - assert_eq!(unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) }, 0); - } - }; - - let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) }; - // Note: if the thread creation fails and this assert fails, then p will - // be leaked. However, an alternative design could cause double-free - // which is clearly worse. - assert_eq!(unsafe {libc::pthread_attr_destroy(&mut attr) }, 0); - - return if ret != 0 { - // The thread failed to start and as a result p was not consumed. Therefore, it is - // safe to reconstruct the box so that it gets deallocated. - unsafe { drop(Box::from_raw(p)); } - Err(io::Error::from_raw_os_error(ret)) - } else { - Ok(Thread { id: native }) - }; - - extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { - unsafe { - // Finally, let's run some code. - Box::from_raw(main as *mut Box<dyn FnOnce()>)(); - } - ptr::null_mut() - } - } - } - _ => { - pub unsafe fn new(_stack: usize, _name: Option<&str>, _p: Box<dyn FnOnce()>) -> io::Result<Thread> { - crate::sys::unsupported() - } - } - } - - pub fn yield_now() { - let ret = unsafe { wasi::sched_yield() }; - debug_assert_eq!(ret, Ok(())); - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - let mut nanos = dur.as_nanos(); - while nanos > 0 { - const USERDATA: wasi::Userdata = 0x0123_45678; - - let clock = wasi::SubscriptionClock { - id: wasi::CLOCKID_MONOTONIC, - timeout: u64::try_from(nanos).unwrap_or(u64::MAX), - precision: 0, - flags: 0, - }; - nanos -= u128::from(clock.timeout); - - let in_ = wasi::Subscription { - userdata: USERDATA, - u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, - }; - unsafe { - let mut event: wasi::Event = mem::zeroed(); - let res = wasi::poll_oneoff(&in_, &mut event, 1); - match (res, event) { - ( - Ok(1), - wasi::Event { - userdata: USERDATA, - error: wasi::ERRNO_SUCCESS, - type_: wasi::EVENTTYPE_CLOCK, - .. - }, - ) => {} - _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), - } - } - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - - pub fn join(self) { - cfg_select! { - target_feature = "atomics" => { - let id = mem::ManuallyDrop::new(self).id; - let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; - if ret != 0 { - rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); - } - } - _ => { - self.0 - } - } - } -} - -pub(crate) fn current_os_id() -> Option<u64> { - None -} - -pub fn available_parallelism() -> io::Result<NonZero<usize>> { - cfg_select! { - target_feature = "atomics" => { - match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { - -1 => Err(io::Error::last_os_error()), - cpus => NonZero::new(cpus as usize).ok_or(io::Error::UNKNOWN_THREAD_COUNT), - } - } - _ => crate::sys::unsupported(), - } -} diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 5f3fb6d6ddf..c1d89da2677 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -14,7 +14,6 @@ pub mod futex; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; #[path = "../unsupported/common.rs"] diff --git a/library/std/src/sys/pal/wasip2/thread.rs b/library/std/src/sys/pal/wasip2/thread.rs deleted file mode 100644 index ad52918f15a..00000000000 --- a/library/std/src/sys/pal/wasip2/thread.rs +++ /dev/null @@ -1,73 +0,0 @@ -use crate::ffi::CStr; -use crate::io; -use crate::num::NonZero; -use crate::time::{Duration, Instant}; - -pub struct Thread(!); - -pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; - -impl Thread { - pub unsafe fn new( - _stack: usize, - _name: Option<&str>, - _p: Box<dyn FnOnce()>, - ) -> io::Result<Thread> { - // Note that unlike WASIp1 even if the wasm `atomics` feature is enabled - // there is no support for threads, not even experimentally, not even in - // wasi-libc. Thus this is unconditionally unsupported. - crate::sys::unsupported() - } - - pub fn yield_now() { - // no API for this in WASIp2, but there's also no threads, so that's - // sort of expected. - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - // Sleep in increments of `u64::MAX` nanoseconds until the `dur` is - // entirely drained. - let mut remaining = dur.as_nanos(); - while remaining > 0 { - let amt = u64::try_from(remaining).unwrap_or(u64::MAX); - wasip2::clocks::monotonic_clock::subscribe_duration(amt).block(); - remaining -= u128::from(amt); - } - } - - pub fn sleep_until(deadline: Instant) { - match u64::try_from(deadline.into_inner().as_duration().as_nanos()) { - // If the point in time we're sleeping to fits within a 64-bit - // number of nanoseconds then directly use `subscribe_instant`. - Ok(deadline) => { - wasip2::clocks::monotonic_clock::subscribe_instant(deadline).block(); - } - // ... otherwise we're sleeping for 500+ years relative to the - // "start" of what the system is using as a clock so speed/accuracy - // is not so much of a concern. Use `sleep` instead. - Err(_) => { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - } - } - - pub fn join(self) { - self.0 - } -} - -pub(crate) fn current_os_id() -> Option<u64> { - None -} - -pub fn available_parallelism() -> io::Result<NonZero<usize>> { - crate::sys::unsupported() -} diff --git a/library/std/src/sys/pal/wasip2/time.rs b/library/std/src/sys/pal/wasip2/time.rs index f1f6839774b..980070e7b85 100644 --- a/library/std/src/sys/pal/wasip2/time.rs +++ b/library/std/src/sys/pal/wasip2/time.rs @@ -25,7 +25,7 @@ impl Instant { Some(Instant(self.0.checked_sub(*other)?)) } - pub(super) fn as_duration(&self) -> &Duration { + pub(crate) fn as_duration(&self) -> &Duration { &self.0 } } diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs deleted file mode 100644 index 42a7dbdf8b8..00000000000 --- a/library/std/src/sys/pal/wasm/atomics/thread.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::ffi::CStr; -use crate::io; -use crate::num::NonZero; -use crate::sys::unsupported; -use crate::time::{Duration, Instant}; - -pub struct Thread(!); - -pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; - -impl Thread { - // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new( - _stack: usize, - _name: Option<&str>, - _p: Box<dyn FnOnce()>, - ) -> io::Result<Thread> { - unsupported() - } - - pub fn yield_now() {} - - pub fn set_name(_name: &CStr) {} - - pub fn sleep(dur: Duration) { - #[cfg(target_arch = "wasm32")] - use core::arch::wasm32 as wasm; - #[cfg(target_arch = "wasm64")] - use core::arch::wasm64 as wasm; - - use crate::cmp; - - // Use an atomic wait to block the current thread artificially with a - // timeout listed. Note that we should never be notified (return value - // of 0) or our comparison should never fail (return value of 1) so we - // should always only resume execution through a timeout (return value - // 2). - let mut nanos = dur.as_nanos(); - while nanos > 0 { - let amt = cmp::min(i64::MAX as u128, nanos); - let mut x = 0; - let val = unsafe { wasm::memory_atomic_wait32(&mut x, 0, amt as i64) }; - debug_assert_eq!(val, 2); - nanos -= amt; - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - - pub fn join(self) {} -} - -pub(crate) fn current_os_id() -> Option<u64> { - None -} - -pub fn available_parallelism() -> io::Result<NonZero<usize>> { - unsupported() -} - -pub mod guard { - pub type Guard = !; - pub unsafe fn current() -> Option<Guard> { - None - } - pub unsafe fn init() -> Option<Guard> { - None - } -} diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs index 346c9ff88c9..a20cd0e9ac7 100644 --- a/library/std/src/sys/pal/wasm/mod.rs +++ b/library/std/src/sys/pal/wasm/mod.rs @@ -23,18 +23,9 @@ pub mod pipe; #[path = "../unsupported/time.rs"] pub mod time; -cfg_select! { - target_feature = "atomics" => { - #[path = "atomics/futex.rs"] - pub mod futex; - #[path = "atomics/thread.rs"] - pub mod thread; - } - _ => { - #[path = "../unsupported/thread.rs"] - pub mod thread; - } -} +#[cfg(target_feature = "atomics")] +#[path = "atomics/futex.rs"] +pub mod futex; #[path = "../unsupported/common.rs"] #[deny(unsafe_op_in_unsafe_fn)] diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 3b6a86cbc8f..3357946b8f7 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -20,7 +20,6 @@ pub mod futex; pub mod handle; pub mod os; pub mod pipe; -pub mod thread; pub mod time; cfg_select! { not(target_vendor = "uwp") => { @@ -48,9 +47,9 @@ pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) { unsafe { stack_overflow::init(); - // Normally, `thread::spawn` will call `Thread::set_name` but since this thread already + // Normally, `thread::spawn` will call `set_name` but since this thread already // exists, we have to call it ourselves. - thread::Thread::set_name_wide(wide_str!("main")); + crate::sys::thread::set_name_wide(wide_str!("main")); } } diff --git a/library/std/src/sys/pal/windows/time.rs b/library/std/src/sys/pal/windows/time.rs index a948c07e0a3..f8f9a9fd818 100644 --- a/library/std/src/sys/pal/windows/time.rs +++ b/library/std/src/sys/pal/windows/time.rs @@ -232,7 +232,7 @@ mod perf_counter { } /// A timer you can wait on. -pub(super) struct WaitableTimer { +pub(crate) struct WaitableTimer { handle: c::HANDLE, } impl WaitableTimer { diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs index 042c4ff862f..e673157e0eb 100644 --- a/library/std/src/sys/pal/xous/mod.rs +++ b/library/std/src/sys/pal/xous/mod.rs @@ -5,7 +5,6 @@ use crate::os::xous::ffi::exit; pub mod os; #[path = "../unsupported/pipe.rs"] pub mod pipe; -pub mod thread; pub mod time; #[path = "../unsupported/common.rs"] diff --git a/library/std/src/sys/platform_version/darwin/tests.rs b/library/std/src/sys/platform_version/darwin/tests.rs index eecd58ec79e..17b2cc18ec0 100644 --- a/library/std/src/sys/platform_version/darwin/tests.rs +++ b/library/std/src/sys/platform_version/darwin/tests.rs @@ -35,9 +35,9 @@ fn compare_against_sw_vers() { assert_eq!(__isOSVersionAtLeast(major, minor, subminor), 1); // One lower is available - assert_eq!(__isOSVersionAtLeast(major, minor, subminor.saturating_sub(1)), 1); - assert_eq!(__isOSVersionAtLeast(major, minor.saturating_sub(1), subminor), 1); - assert_eq!(__isOSVersionAtLeast(major.saturating_sub(1), minor, subminor), 1); + assert_eq!(__isOSVersionAtLeast(major, minor, (subminor as u32).saturating_sub(1) as i32), 1); + assert_eq!(__isOSVersionAtLeast(major, (minor as u32).saturating_sub(1) as i32, subminor), 1); + assert_eq!(__isOSVersionAtLeast((major as u32).saturating_sub(1) as i32, minor, subminor), 1); // One higher isn't available assert_eq!(__isOSVersionAtLeast(major, minor, subminor + 1), 0); diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index 49e15d65f25..17d99cdb385 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -276,7 +276,9 @@ fn wait( // If the managing thread happens to signal and unpark us before we // can park ourselves, the result could be this thread never gets // unparked. Luckily `park` comes with the guarantee that if it got - // an `unpark` just before on an unparked thread it does not park. + // an `unpark` just before on an unparked thread it does not park. Crucially, we know + // the `unpark` must have happened between the `compare_exchange_weak` above and here, + // and there's no other `park` in that code that could steal our token. // SAFETY: we retrieved this handle on the current thread above. unsafe { node.thread.park() } } diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/thread/hermit.rs index cc4734b6819..4d9f3b114c2 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/thread/hermit.rs @@ -1,10 +1,5 @@ -#![allow(dead_code)] - -use super::hermit_abi; -use crate::ffi::CStr; -use crate::mem::ManuallyDrop; use crate::num::NonZero; -use crate::time::{Duration, Instant}; +use crate::time::Duration; use crate::{io, ptr}; pub type Tid = hermit_abi::Tid; @@ -68,57 +63,30 @@ impl Thread { } } - #[inline] - pub fn yield_now() { - unsafe { - hermit_abi::yield_now(); - } - } - - #[inline] - pub fn set_name(_name: &CStr) { - // nope - } - - #[inline] - pub fn sleep(dur: Duration) { - let micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; - let micros = u64::try_from(micros).unwrap_or(u64::MAX); - - unsafe { - hermit_abi::usleep(micros); - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { unsafe { let _ = hermit_abi::join(self.tid); } } +} - #[inline] - pub fn id(&self) -> Tid { - self.tid - } - - #[inline] - pub fn into_id(self) -> Tid { - ManuallyDrop::new(self).tid - } +pub fn available_parallelism() -> io::Result<NonZero<usize>> { + unsafe { Ok(NonZero::new_unchecked(hermit_abi::available_parallelism())) } } -pub(crate) fn current_os_id() -> Option<u64> { - None +#[inline] +pub fn sleep(dur: Duration) { + let micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; + let micros = u64::try_from(micros).unwrap_or(u64::MAX); + + unsafe { + hermit_abi::usleep(micros); + } } -pub fn available_parallelism() -> io::Result<NonZero<usize>> { - unsafe { Ok(NonZero::new_unchecked(hermit_abi::available_parallelism())) } +#[inline] +pub fn yield_now() { + unsafe { + hermit_abi::yield_now(); + } } diff --git a/library/std/src/sys/thread/mod.rs b/library/std/src/sys/thread/mod.rs new file mode 100644 index 00000000000..6bb7fc1a20e --- /dev/null +++ b/library/std/src/sys/thread/mod.rs @@ -0,0 +1,152 @@ +cfg_select! { + target_os = "hermit" => { + mod hermit; + pub use hermit::{Thread, available_parallelism, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{current_os_id, set_name}; + } + all(target_vendor = "fortanix", target_env = "sgx") => { + mod sgx; + pub use sgx::{Thread, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + + // SGX should protect in-enclave data from outside attackers, so there + // must not be any data leakage to the OS, particularly no 1-1 mapping + // between SGX thread names and OS thread names. Hence `set_name` is + // intentionally a no-op. + // + // Note that the internally visible SGX thread name is already provided + // by the platform-agnostic Rust thread code. This can be observed in + // the [`std::thread::tests::test_named_thread`] test, which succeeds + // as-is with the SGX target. + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{available_parallelism, set_name}; + } + target_os = "solid_asp3" => { + mod solid; + pub use solid::{Thread, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{available_parallelism, current_os_id, set_name}; + } + target_os = "teeos" => { + mod teeos; + pub use teeos::{Thread, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{available_parallelism, current_os_id, set_name}; + } + target_os = "uefi" => { + mod uefi; + pub use uefi::{available_parallelism, sleep}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{Thread, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + target_family = "unix" => { + mod unix; + pub use unix::{Thread, available_parallelism, current_os_id, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + #[cfg(not(any( + target_env = "newlib", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox", + target_os = "hurd", + target_os = "aix", + )))] + pub use unix::set_name; + #[cfg(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "fuchsia", + target_os = "vxworks", + ))] + pub use unix::sleep_until; + #[expect(dead_code)] + mod unsupported; + #[cfg(any( + target_env = "newlib", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox", + target_os = "hurd", + target_os = "aix", + ))] + pub use unsupported::set_name; + } + all(target_os = "wasi", target_env = "p1") => { + mod wasip1; + pub use wasip1::{DEFAULT_MIN_STACK_SIZE, sleep, yield_now}; + #[cfg(target_feature = "atomics")] + pub use wasip1::{Thread, available_parallelism}; + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{current_os_id, set_name}; + #[cfg(not(target_feature = "atomics"))] + pub use unsupported::{Thread, available_parallelism}; + } + all(target_os = "wasi", target_env = "p2") => { + mod wasip2; + pub use wasip2::{sleep, sleep_until}; + #[expect(dead_code)] + mod unsupported; + // Note that unlike WASIp1 even if the wasm `atomics` feature is enabled + // there is no support for threads, not even experimentally, not even in + // wasi-libc. Thus this is unconditionally unsupported. + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + all(target_family = "wasm", target_feature = "atomics") => { + mod wasm; + pub use wasm::sleep; + + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + target_os = "windows" => { + mod windows; + pub use windows::{Thread, available_parallelism, current_os_id, set_name, set_name_wide, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + } + target_os = "xous" => { + mod xous; + pub use xous::{Thread, available_parallelism, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + + #[expect(dead_code)] + mod unsupported; + pub use unsupported::{current_os_id, set_name}; + } + _ => { + mod unsupported; + pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, sleep, yield_now, DEFAULT_MIN_STACK_SIZE}; + } +} + +#[cfg(not(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "fuchsia", + target_os = "vxworks", + all(target_os = "wasi", target_env = "p2"), +)))] +pub fn sleep_until(deadline: crate::time::Instant) { + use crate::time::Instant; + + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + sleep(delay); + } +} diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/thread/sgx.rs index 1f613badcd7..f20ef7d86b9 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/thread/sgx.rs @@ -1,11 +1,8 @@ #![cfg_attr(test, allow(dead_code))] // why is this necessary? -use super::abi::{thread, usercalls}; -use super::unsupported; -use crate::ffi::CStr; use crate::io; -use crate::num::NonZero; -use crate::time::{Duration, Instant}; +use crate::sys::pal::abi::{thread, usercalls}; +use crate::time::Duration; pub struct Thread(task_queue::JoinHandle); @@ -108,51 +105,27 @@ impl Thread { Ok(Thread(handle)) } - pub(super) fn entry() -> JoinNotifier { + pub(crate) fn entry() -> JoinNotifier { let mut pending_tasks = task_queue::lock(); let task = rtunwrap!(Some, pending_tasks.pop()); drop(pending_tasks); // make sure to not hold the task queue lock longer than necessary task.run() } - pub fn yield_now() { - let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO)); - rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); - } - - /// SGX should protect in-enclave data from the outside (attacker), - /// so there should be no data leakage to the OS, - /// and therefore also no 1-1 mapping between SGX thread names and OS thread names. - /// - /// This is why the method is intentionally No-Op. - pub fn set_name(_name: &CStr) { - // Note that the internally visible SGX thread name is already provided - // by the platform-agnostic (target-agnostic) Rust thread code. - // This can be observed in the [`std::thread::tests::test_named_thread`] test, - // which succeeds as-is with the SGX target. - } - - pub fn sleep(dur: Duration) { - usercalls::wait_timeout(0, dur, || true); - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { self.0.wait(); } } -pub(crate) fn current_os_id() -> Option<u64> { +pub fn current_os_id() -> Option<u64> { Some(thread::current().addr().get() as u64) } -pub fn available_parallelism() -> io::Result<NonZero<usize>> { - unsupported() +pub fn sleep(dur: Duration) { + usercalls::wait_timeout(0, dur, || true); +} + +pub fn yield_now() { + let wait_error = rtunwrap!(Err, usercalls::wait(0, usercalls::raw::WAIT_NO)); + rtassert!(wait_error.kind() == io::ErrorKind::WouldBlock); } diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/thread/solid.rs index 4e14cb3cbca..46a84faa802 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/thread/solid.rs @@ -1,16 +1,14 @@ //! Thread implementation backed by μITRON tasks. Assumes `acre_tsk` and //! `exd_tsk` are available. -use super::error::{ItronError, expect_success, expect_success_aborting}; -use super::time::dur2reltims; -use super::{abi, task}; use crate::cell::UnsafeCell; -use crate::ffi::CStr; use crate::mem::ManuallyDrop; -use crate::num::NonZero; use crate::ptr::NonNull; use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; -use crate::time::{Duration, Instant}; +use crate::sys::pal::itron::error::{ItronError, expect_success, expect_success_aborting}; +use crate::sys::pal::itron::time::dur2reltims; +use crate::sys::pal::itron::{abi, task}; +use crate::time::Duration; use crate::{hint, io}; pub struct Thread { @@ -195,28 +193,6 @@ impl Thread { Ok(Self { p_inner, task: new_task }) } - pub fn yield_now() { - expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq"); - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - for timeout in dur2reltims(dur) { - expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk"); - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { // Safety: `ThreadInner` is alive at this point let inner = unsafe { self.p_inner.as_ref() }; @@ -361,10 +337,12 @@ unsafe fn terminate_and_delete_current_task() -> ! { unsafe { crate::hint::unreachable_unchecked() }; } -pub(crate) fn current_os_id() -> Option<u64> { - None +pub fn yield_now() { + expect_success(unsafe { abi::rot_rdq(abi::TPRI_SELF) }, &"rot_rdq"); } -pub fn available_parallelism() -> io::Result<NonZero<usize>> { - super::unsupported() +pub fn sleep(dur: Duration) { + for timeout in dur2reltims(dur) { + expect_success(unsafe { abi::dly_tsk(timeout) }, &"dly_tsk"); + } } diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/thread/teeos.rs index 1812d11e692..cad100395c9 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/thread/teeos.rs @@ -1,12 +1,18 @@ -use crate::ffi::CStr; use crate::mem::{self, ManuallyDrop}; -use crate::num::NonZero; use crate::sys::os; -use crate::time::{Duration, Instant}; +use crate::time::Duration; use crate::{cmp, io, ptr}; pub const DEFAULT_MIN_STACK_SIZE: usize = 8 * 1024; +unsafe extern "C" { + safe fn TEE_Wait(timeout: u32) -> u32; +} + +fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { + libc::PTHREAD_STACK_MIN.try_into().expect("Infallible") +} + pub struct Thread { id: libc::pthread_t, } @@ -16,10 +22,6 @@ pub struct Thread { unsafe impl Send for Thread {} unsafe impl Sync for Thread {} -unsafe extern "C" { - pub fn TEE_Wait(timeout: u32) -> u32; -} - impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements pub unsafe fn new( @@ -74,7 +76,7 @@ impl Thread { } else { // The new thread will start running earliest after the next yield. // We add a yield here, so that the user does not have to. - Thread::yield_now(); + yield_now(); Ok(Thread { id: native }) }; @@ -91,36 +93,6 @@ impl Thread { } } - pub fn yield_now() { - let ret = unsafe { libc::sched_yield() }; - debug_assert_eq!(ret, 0); - } - - /// This does not do anything on teeos - pub fn set_name(_name: &CStr) { - // Both pthread_setname_np and prctl are not available to the TA, - // so we can't implement this currently. If the need arises please - // contact the teeos rustzone team. - } - - /// only main thread could wait for sometime in teeos - pub fn sleep(dur: Duration) { - let sleep_millis = dur.as_millis(); - let final_sleep: u32 = - if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 }; - unsafe { - let _ = TEE_Wait(final_sleep); - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - /// must join, because no pthread_detach supported pub fn join(self) { let id = self.into_id(); @@ -128,10 +100,6 @@ impl Thread { assert!(ret == 0, "failed to join thread: {}", io::Error::from_raw_os_error(ret)); } - pub fn id(&self) -> libc::pthread_t { - self.id - } - pub fn into_id(self) -> libc::pthread_t { ManuallyDrop::new(self).id } @@ -144,16 +112,15 @@ impl Drop for Thread { } } -pub(crate) fn current_os_id() -> Option<u64> { - None -} - -// Note: Both `sched_getaffinity` and `sysconf` are available but not functional on -// teeos, so this function always returns an Error! -pub fn available_parallelism() -> io::Result<NonZero<usize>> { - Err(io::Error::UNKNOWN_THREAD_COUNT) +pub fn yield_now() { + let ret = unsafe { libc::sched_yield() }; + debug_assert_eq!(ret, 0); } -fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { - libc::PTHREAD_STACK_MIN.try_into().expect("Infallible") +/// only main thread could wait for sometime in teeos +pub fn sleep(dur: Duration) { + let sleep_millis = dur.as_millis(); + let final_sleep: u32 = + if sleep_millis >= u32::MAX as u128 { u32::MAX } else { sleep_millis as u32 }; + TEE_Wait(final_sleep); } diff --git a/library/std/src/sys/thread/uefi.rs b/library/std/src/sys/thread/uefi.rs new file mode 100644 index 00000000000..94f67d7ace2 --- /dev/null +++ b/library/std/src/sys/thread/uefi.rs @@ -0,0 +1,25 @@ +use crate::io; +use crate::num::NonZero; +use crate::ptr::NonNull; +use crate::time::Duration; + +pub fn available_parallelism() -> io::Result<NonZero<usize>> { + // UEFI is single threaded + Ok(NonZero::new(1).unwrap()) +} + +pub fn sleep(dur: Duration) { + let boot_services: NonNull<r_efi::efi::BootServices> = + crate::os::uefi::env::boot_services().expect("can't sleep").cast(); + let mut dur_ms = dur.as_micros(); + // ceil up to the nearest microsecond + if dur.subsec_nanos() % 1000 > 0 { + dur_ms += 1; + } + + while dur_ms > 0 { + let ms = crate::cmp::min(dur_ms, usize::MAX as u128); + let _ = unsafe { ((*boot_services.as_ptr()).stall)(ms as usize) }; + dur_ms -= ms; + } +} diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/thread/unix.rs index 3389b8c0c8a..2d2c4f90212 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/thread/unix.rs @@ -1,3 +1,11 @@ +#[cfg(not(any( + target_env = "newlib", + target_os = "l4re", + target_os = "emscripten", + target_os = "redox", + target_os = "hurd", + target_os = "aix", +)))] use crate::ffi::CStr; use crate::mem::{self, ManuallyDrop}; use crate::num::NonZero; @@ -6,7 +14,7 @@ use crate::sys::weak::dlsym; #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto",))] use crate::sys::weak::weak; use crate::sys::{os, stack_overflow}; -use crate::time::{Duration, Instant}; +use crate::time::Duration; use crate::{cmp, io, ptr}; #[cfg(not(any( target_os = "l4re", @@ -121,273 +129,6 @@ impl Thread { } } - pub fn yield_now() { - let ret = unsafe { libc::sched_yield() }; - debug_assert_eq!(ret, 0); - } - - #[cfg(target_os = "android")] - pub fn set_name(name: &CStr) { - const PR_SET_NAME: libc::c_int = 15; - unsafe { - let res = libc::prctl( - PR_SET_NAME, - name.as_ptr(), - 0 as libc::c_ulong, - 0 as libc::c_ulong, - 0 as libc::c_ulong, - ); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, 0); - } - } - - #[cfg(any( - target_os = "linux", - target_os = "freebsd", - target_os = "dragonfly", - target_os = "nuttx", - target_os = "cygwin" - ))] - pub fn set_name(name: &CStr) { - unsafe { - cfg_select! { - any(target_os = "linux", target_os = "cygwin") => { - // Linux and Cygwin limits the allowed length of the name. - const TASK_COMM_LEN: usize = 16; - let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); - } - _ => { - // FreeBSD, DragonFly BSD and NuttX do not enforce length limits. - } - }; - // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux, - // FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0. - let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, 0); - } - } - - #[cfg(target_os = "openbsd")] - pub fn set_name(name: &CStr) { - unsafe { - libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); - } - } - - #[cfg(target_vendor = "apple")] - pub fn set_name(name: &CStr) { - unsafe { - let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name); - let res = libc::pthread_setname_np(name.as_ptr()); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, 0); - } - } - - #[cfg(target_os = "netbsd")] - pub fn set_name(name: &CStr) { - unsafe { - let res = libc::pthread_setname_np( - libc::pthread_self(), - c"%s".as_ptr(), - name.as_ptr() as *mut libc::c_void, - ); - debug_assert_eq!(res, 0); - } - } - - #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] - pub fn set_name(name: &CStr) { - weak!( - fn pthread_setname_np( - thread: libc::pthread_t, - name: *const libc::c_char, - ) -> libc::c_int; - ); - - if let Some(f) = pthread_setname_np.get() { - #[cfg(target_os = "nto")] - const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize; - #[cfg(any(target_os = "solaris", target_os = "illumos"))] - const THREAD_NAME_MAX: usize = 32; - - let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name); - let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; - debug_assert_eq!(res, 0); - } - } - - #[cfg(target_os = "fuchsia")] - pub fn set_name(name: &CStr) { - use super::fuchsia::*; - unsafe { - zx_object_set_property( - zx_thread_self(), - ZX_PROP_NAME, - name.as_ptr() as *const libc::c_void, - name.to_bytes().len(), - ); - } - } - - #[cfg(target_os = "haiku")] - pub fn set_name(name: &CStr) { - unsafe { - let thread_self = libc::find_thread(ptr::null_mut()); - let res = libc::rename_thread(thread_self, name.as_ptr()); - // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. - debug_assert_eq!(res, libc::B_OK); - } - } - - #[cfg(target_os = "vxworks")] - pub fn set_name(name: &CStr) { - let mut name = truncate_cstr::<{ (libc::VX_TASK_RENAME_LENGTH - 1) as usize }>(name); - let res = unsafe { libc::taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; - debug_assert_eq!(res, libc::OK); - } - - #[cfg(any( - target_env = "newlib", - target_os = "l4re", - target_os = "emscripten", - target_os = "redox", - target_os = "hurd", - target_os = "aix", - ))] - pub fn set_name(_name: &CStr) { - // Newlib and Emscripten have no way to set a thread name. - } - - #[cfg(not(target_os = "espidf"))] - pub fn sleep(dur: Duration) { - let mut secs = dur.as_secs(); - let mut nsecs = dur.subsec_nanos() as _; - - // If we're awoken with a signal then the return value will be -1 and - // nanosleep will fill in `ts` with the remaining time. - unsafe { - while secs > 0 || nsecs > 0 { - let mut ts = libc::timespec { - tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t, - tv_nsec: nsecs, - }; - secs -= ts.tv_sec as u64; - let ts_ptr = &raw mut ts; - if libc::nanosleep(ts_ptr, ts_ptr) == -1 { - assert_eq!(os::errno(), libc::EINTR); - secs += ts.tv_sec as u64; - nsecs = ts.tv_nsec; - } else { - nsecs = 0; - } - } - } - } - - #[cfg(target_os = "espidf")] - pub fn sleep(dur: Duration) { - // ESP-IDF does not have `nanosleep`, so we use `usleep` instead. - // As per the documentation of `usleep`, it is expected to support - // sleep times as big as at least up to 1 second. - // - // ESP-IDF does support almost up to `u32::MAX`, but due to a potential integer overflow in its - // `usleep` implementation - // (https://github.com/espressif/esp-idf/blob/d7ca8b94c852052e3bc33292287ef4dd62c9eeb1/components/newlib/time.c#L210), - // we limit the sleep time to the maximum one that would not cause the underlying `usleep` implementation to overflow - // (`portTICK_PERIOD_MS` can be anything between 1 to 1000, and is 10 by default). - const MAX_MICROS: u32 = u32::MAX - 1_000_000 - 1; - - // Add any nanoseconds smaller than a microsecond as an extra microsecond - // so as to comply with the `std::thread::sleep` contract which mandates - // implementations to sleep for _at least_ the provided `dur`. - // We can't overflow `micros` as it is a `u128`, while `Duration` is a pair of - // (`u64` secs, `u32` nanos), where the nanos are strictly smaller than 1 second - // (i.e. < 1_000_000_000) - let mut micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; - - while micros > 0 { - let st = if micros > MAX_MICROS as u128 { MAX_MICROS } else { micros as u32 }; - unsafe { - libc::usleep(st); - } - - micros -= st as u128; - } - } - - // Any unix that has clock_nanosleep - // If this list changes update the MIRI chock_nanosleep shim - #[cfg(any( - target_os = "freebsd", - target_os = "netbsd", - target_os = "linux", - target_os = "android", - target_os = "solaris", - target_os = "illumos", - target_os = "dragonfly", - target_os = "hurd", - target_os = "fuchsia", - target_os = "vxworks", - ))] - pub fn sleep_until(deadline: Instant) { - let Some(ts) = deadline.into_inner().into_timespec().to_timespec() else { - // The deadline is further in the future then can be passed to - // clock_nanosleep. We have to use Self::sleep instead. This might - // happen on 32 bit platforms, especially closer to 2038. - let now = Instant::now(); - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - return; - }; - - unsafe { - // When we get interrupted (res = EINTR) call clock_nanosleep again - loop { - let res = libc::clock_nanosleep( - super::time::Instant::CLOCK_ID, - libc::TIMER_ABSTIME, - &ts, - core::ptr::null_mut(), // not required with TIMER_ABSTIME - ); - - if res == 0 { - break; - } else { - assert_eq!( - res, - libc::EINTR, - "timespec is in range, - clockid is valid and kernel should support it" - ); - } - } - } - } - - // Any unix that does not have clock_nanosleep - #[cfg(not(any( - target_os = "freebsd", - target_os = "netbsd", - target_os = "linux", - target_os = "android", - target_os = "solaris", - target_os = "illumos", - target_os = "dragonfly", - target_os = "hurd", - target_os = "fuchsia", - target_os = "vxworks", - )))] - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { let id = self.into_id(); let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; @@ -410,84 +151,6 @@ impl Drop for Thread { } } -pub(crate) fn current_os_id() -> Option<u64> { - // Most Unix platforms have a way to query an integer ID of the current thread, all with - // slightly different spellings. - // - // The OS thread ID is used rather than `pthread_self` so as to match what will be displayed - // for process inspection (debuggers, trace, `top`, etc.). - cfg_select! { - // Most platforms have a function returning a `pid_t` or int, which is an `i32`. - any(target_os = "android", target_os = "linux") => { - use crate::sys::weak::syscall; - - // `libc::gettid` is only available on glibc 2.30+, but the syscall is available - // since Linux 2.4.11. - syscall!(fn gettid() -> libc::pid_t;); - - // SAFETY: FFI call with no preconditions. - let id: libc::pid_t = unsafe { gettid() }; - Some(id as u64) - } - target_os = "nto" => { - // SAFETY: FFI call with no preconditions. - let id: libc::pid_t = unsafe { libc::gettid() }; - Some(id as u64) - } - target_os = "openbsd" => { - // SAFETY: FFI call with no preconditions. - let id: libc::pid_t = unsafe { libc::getthrid() }; - Some(id as u64) - } - target_os = "freebsd" => { - // SAFETY: FFI call with no preconditions. - let id: libc::c_int = unsafe { libc::pthread_getthreadid_np() }; - Some(id as u64) - } - target_os = "netbsd" => { - // SAFETY: FFI call with no preconditions. - let id: libc::lwpid_t = unsafe { libc::_lwp_self() }; - Some(id as u64) - } - any(target_os = "illumos", target_os = "solaris") => { - // On Illumos and Solaris, the `pthread_t` is the same as the OS thread ID. - // SAFETY: FFI call with no preconditions. - let id: libc::pthread_t = unsafe { libc::pthread_self() }; - Some(id as u64) - } - target_vendor = "apple" => { - // Apple allows querying arbitrary thread IDs, `thread=NULL` queries the current thread. - let mut id = 0u64; - // SAFETY: `thread_id` is a valid pointer, no other preconditions. - let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) }; - if status == 0 { - Some(id) - } else { - None - } - } - // Other platforms don't have an OS thread ID or don't have a way to access it. - _ => None, - } -} - -#[cfg(any( - target_os = "linux", - target_os = "nto", - target_os = "solaris", - target_os = "illumos", - target_os = "vxworks", - target_os = "cygwin", - target_vendor = "apple", -))] -fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { - let mut result = [0; MAX_WITH_NUL]; - for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) { - *dst = *src as libc::c_char; - } - result -} - pub fn available_parallelism() -> io::Result<NonZero<usize>> { cfg_select! { any( @@ -668,6 +331,318 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { } } +pub fn current_os_id() -> Option<u64> { + // Most Unix platforms have a way to query an integer ID of the current thread, all with + // slightly different spellings. + // + // The OS thread ID is used rather than `pthread_self` so as to match what will be displayed + // for process inspection (debuggers, trace, `top`, etc.). + cfg_select! { + // Most platforms have a function returning a `pid_t` or int, which is an `i32`. + any(target_os = "android", target_os = "linux") => { + use crate::sys::pal::weak::syscall; + + // `libc::gettid` is only available on glibc 2.30+, but the syscall is available + // since Linux 2.4.11. + syscall!(fn gettid() -> libc::pid_t;); + + // SAFETY: FFI call with no preconditions. + let id: libc::pid_t = unsafe { gettid() }; + Some(id as u64) + } + target_os = "nto" => { + // SAFETY: FFI call with no preconditions. + let id: libc::pid_t = unsafe { libc::gettid() }; + Some(id as u64) + } + target_os = "openbsd" => { + // SAFETY: FFI call with no preconditions. + let id: libc::pid_t = unsafe { libc::getthrid() }; + Some(id as u64) + } + target_os = "freebsd" => { + // SAFETY: FFI call with no preconditions. + let id: libc::c_int = unsafe { libc::pthread_getthreadid_np() }; + Some(id as u64) + } + target_os = "netbsd" => { + // SAFETY: FFI call with no preconditions. + let id: libc::lwpid_t = unsafe { libc::_lwp_self() }; + Some(id as u64) + } + any(target_os = "illumos", target_os = "solaris") => { + // On Illumos and Solaris, the `pthread_t` is the same as the OS thread ID. + // SAFETY: FFI call with no preconditions. + let id: libc::pthread_t = unsafe { libc::pthread_self() }; + Some(id as u64) + } + target_vendor = "apple" => { + // Apple allows querying arbitrary thread IDs, `thread=NULL` queries the current thread. + let mut id = 0u64; + // SAFETY: `thread_id` is a valid pointer, no other preconditions. + let status: libc::c_int = unsafe { libc::pthread_threadid_np(0, &mut id) }; + if status == 0 { + Some(id) + } else { + None + } + } + // Other platforms don't have an OS thread ID or don't have a way to access it. + _ => None, + } +} + +#[cfg(any( + target_os = "linux", + target_os = "nto", + target_os = "solaris", + target_os = "illumos", + target_os = "vxworks", + target_os = "cygwin", + target_vendor = "apple", +))] +fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] { + let mut result = [0; MAX_WITH_NUL]; + for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) { + *dst = *src as libc::c_char; + } + result +} + +#[cfg(target_os = "android")] +pub fn set_name(name: &CStr) { + const PR_SET_NAME: libc::c_int = 15; + unsafe { + let res = libc::prctl( + PR_SET_NAME, + name.as_ptr(), + 0 as libc::c_ulong, + 0 as libc::c_ulong, + 0 as libc::c_ulong, + ); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); + } +} + +#[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "nuttx", + target_os = "cygwin" +))] +pub fn set_name(name: &CStr) { + unsafe { + cfg_select! { + any(target_os = "linux", target_os = "cygwin") => { + // Linux and Cygwin limits the allowed length of the name. + const TASK_COMM_LEN: usize = 16; + let name = truncate_cstr::<{ TASK_COMM_LEN }>(name); + } + _ => { + // FreeBSD, DragonFly BSD and NuttX do not enforce length limits. + } + }; + // Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20 for Linux, + // FreeBSD 12.2 and 13.0, and DragonFly BSD 6.0. + let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); + } +} + +#[cfg(target_os = "openbsd")] +pub fn set_name(name: &CStr) { + unsafe { + libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); + } +} + +#[cfg(target_vendor = "apple")] +pub fn set_name(name: &CStr) { + unsafe { + let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name); + let res = libc::pthread_setname_np(name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); + } +} + +#[cfg(target_os = "netbsd")] +pub fn set_name(name: &CStr) { + unsafe { + let res = libc::pthread_setname_np( + libc::pthread_self(), + c"%s".as_ptr(), + name.as_ptr() as *mut libc::c_void, + ); + debug_assert_eq!(res, 0); + } +} + +#[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "nto"))] +pub fn set_name(name: &CStr) { + weak!( + fn pthread_setname_np(thread: libc::pthread_t, name: *const libc::c_char) -> libc::c_int; + ); + + if let Some(f) = pthread_setname_np.get() { + #[cfg(target_os = "nto")] + const THREAD_NAME_MAX: usize = libc::_NTO_THREAD_NAME_MAX as usize; + #[cfg(any(target_os = "solaris", target_os = "illumos"))] + const THREAD_NAME_MAX: usize = 32; + + let name = truncate_cstr::<{ THREAD_NAME_MAX }>(name); + let res = unsafe { f(libc::pthread_self(), name.as_ptr()) }; + debug_assert_eq!(res, 0); + } +} + +#[cfg(target_os = "fuchsia")] +pub fn set_name(name: &CStr) { + use crate::sys::pal::fuchsia::*; + unsafe { + zx_object_set_property( + zx_thread_self(), + ZX_PROP_NAME, + name.as_ptr() as *const libc::c_void, + name.to_bytes().len(), + ); + } +} + +#[cfg(target_os = "haiku")] +pub fn set_name(name: &CStr) { + unsafe { + let thread_self = libc::find_thread(ptr::null_mut()); + let res = libc::rename_thread(thread_self, name.as_ptr()); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, libc::B_OK); + } +} + +#[cfg(target_os = "vxworks")] +pub fn set_name(name: &CStr) { + let mut name = truncate_cstr::<{ (libc::VX_TASK_RENAME_LENGTH - 1) as usize }>(name); + let res = unsafe { libc::taskNameSet(libc::taskIdSelf(), name.as_mut_ptr()) }; + debug_assert_eq!(res, libc::OK); +} + +#[cfg(not(target_os = "espidf"))] +pub fn sleep(dur: Duration) { + let mut secs = dur.as_secs(); + let mut nsecs = dur.subsec_nanos() as _; + + // If we're awoken with a signal then the return value will be -1 and + // nanosleep will fill in `ts` with the remaining time. + unsafe { + while secs > 0 || nsecs > 0 { + let mut ts = libc::timespec { + tv_sec: cmp::min(libc::time_t::MAX as u64, secs) as libc::time_t, + tv_nsec: nsecs, + }; + secs -= ts.tv_sec as u64; + let ts_ptr = &raw mut ts; + if libc::nanosleep(ts_ptr, ts_ptr) == -1 { + assert_eq!(os::errno(), libc::EINTR); + secs += ts.tv_sec as u64; + nsecs = ts.tv_nsec; + } else { + nsecs = 0; + } + } + } +} + +#[cfg(target_os = "espidf")] +pub fn sleep(dur: Duration) { + // ESP-IDF does not have `nanosleep`, so we use `usleep` instead. + // As per the documentation of `usleep`, it is expected to support + // sleep times as big as at least up to 1 second. + // + // ESP-IDF does support almost up to `u32::MAX`, but due to a potential integer overflow in its + // `usleep` implementation + // (https://github.com/espressif/esp-idf/blob/d7ca8b94c852052e3bc33292287ef4dd62c9eeb1/components/newlib/time.c#L210), + // we limit the sleep time to the maximum one that would not cause the underlying `usleep` implementation to overflow + // (`portTICK_PERIOD_MS` can be anything between 1 to 1000, and is 10 by default). + const MAX_MICROS: u32 = u32::MAX - 1_000_000 - 1; + + // Add any nanoseconds smaller than a microsecond as an extra microsecond + // so as to comply with the `std::thread::sleep` contract which mandates + // implementations to sleep for _at least_ the provided `dur`. + // We can't overflow `micros` as it is a `u128`, while `Duration` is a pair of + // (`u64` secs, `u32` nanos), where the nanos are strictly smaller than 1 second + // (i.e. < 1_000_000_000) + let mut micros = dur.as_micros() + if dur.subsec_nanos() % 1_000 > 0 { 1 } else { 0 }; + + while micros > 0 { + let st = if micros > MAX_MICROS as u128 { MAX_MICROS } else { micros as u32 }; + unsafe { + libc::usleep(st); + } + + micros -= st as u128; + } +} + +// Any unix that has clock_nanosleep +// If this list changes update the MIRI chock_nanosleep shim +#[cfg(any( + target_os = "freebsd", + target_os = "netbsd", + target_os = "linux", + target_os = "android", + target_os = "solaris", + target_os = "illumos", + target_os = "dragonfly", + target_os = "hurd", + target_os = "fuchsia", + target_os = "vxworks", +))] +pub fn sleep_until(deadline: crate::time::Instant) { + use crate::time::Instant; + + let Some(ts) = deadline.into_inner().into_timespec().to_timespec() else { + // The deadline is further in the future then can be passed to + // clock_nanosleep. We have to use Self::sleep instead. This might + // happen on 32 bit platforms, especially closer to 2038. + let now = Instant::now(); + if let Some(delay) = deadline.checked_duration_since(now) { + sleep(delay); + } + return; + }; + + unsafe { + // When we get interrupted (res = EINTR) call clock_nanosleep again + loop { + let res = libc::clock_nanosleep( + crate::sys::time::Instant::CLOCK_ID, + libc::TIMER_ABSTIME, + &ts, + core::ptr::null_mut(), // not required with TIMER_ABSTIME + ); + + if res == 0 { + break; + } else { + assert_eq!( + res, + libc::EINTR, + "timespec is in range, + clockid is valid and kernel should support it" + ); + } + } + } +} + +pub fn yield_now() { + let ret = unsafe { libc::sched_yield() }; + debug_assert_eq!(ret, 0); +} + #[cfg(any(target_os = "android", target_os = "linux"))] mod cgroups { //! Currently not covered diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/thread/unsupported.rs index 34d9b5ec70c..a5001efa3b4 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/thread/unsupported.rs @@ -1,8 +1,7 @@ -use super::unsupported; use crate::ffi::CStr; use crate::io; use crate::num::NonZero; -use crate::time::{Duration, Instant}; +use crate::time::Duration; pub struct Thread(!); @@ -15,23 +14,7 @@ impl Thread { _name: Option<&str>, _p: Box<dyn FnOnce()>, ) -> io::Result<Thread> { - unsupported() - } - - pub fn yield_now() { - // do nothing - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(_dur: Duration) { - panic!("can't sleep"); - } - - pub fn sleep_until(_deadline: Instant) { - panic!("can't sleep"); + Err(io::Error::UNSUPPORTED_PLATFORM) } pub fn join(self) { @@ -39,10 +22,22 @@ impl Thread { } } -pub(crate) fn current_os_id() -> Option<u64> { +pub fn available_parallelism() -> io::Result<NonZero<usize>> { + Err(io::Error::UNKNOWN_THREAD_COUNT) +} + +pub fn current_os_id() -> Option<u64> { None } -pub fn available_parallelism() -> io::Result<NonZero<usize>> { - unsupported() +pub fn yield_now() { + // do nothing +} + +pub fn set_name(_name: &CStr) { + // nope +} + +pub fn sleep(_dur: Duration) { + panic!("can't sleep"); } diff --git a/library/std/src/sys/thread/wasip1.rs b/library/std/src/sys/thread/wasip1.rs new file mode 100644 index 00000000000..83001fad49c --- /dev/null +++ b/library/std/src/sys/thread/wasip1.rs @@ -0,0 +1,185 @@ +#![forbid(unsafe_op_in_unsafe_fn)] + +#[cfg(target_feature = "atomics")] +use crate::io; +use crate::mem; +#[cfg(target_feature = "atomics")] +use crate::num::NonZero; +#[cfg(target_feature = "atomics")] +use crate::sys::os; +use crate::time::Duration; +#[cfg(target_feature = "atomics")] +use crate::{cmp, ptr}; + +// Add a few symbols not in upstream `libc` just yet. +#[cfg(target_feature = "atomics")] +mod libc { + pub use libc::*; + + pub use crate::ffi; + + // defined in wasi-libc + // https://github.com/WebAssembly/wasi-libc/blob/a6f871343313220b76009827ed0153586361c0d5/libc-top-half/musl/include/alltypes.h.in#L108 + #[repr(C)] + union pthread_attr_union { + __i: [ffi::c_int; if size_of::<ffi::c_long>() == 8 { 14 } else { 9 }], + __vi: [ffi::c_int; if size_of::<ffi::c_long>() == 8 { 14 } else { 9 }], + __s: [ffi::c_ulong; if size_of::<ffi::c_long>() == 8 { 7 } else { 9 }], + } + + #[repr(C)] + pub struct pthread_attr_t { + __u: pthread_attr_union, + } + + #[allow(non_camel_case_types)] + pub type pthread_t = *mut ffi::c_void; + + pub const _SC_NPROCESSORS_ONLN: ffi::c_int = 84; + + unsafe extern "C" { + pub fn pthread_create( + native: *mut pthread_t, + attr: *const pthread_attr_t, + f: extern "C" fn(*mut ffi::c_void) -> *mut ffi::c_void, + value: *mut ffi::c_void, + ) -> ffi::c_int; + pub fn pthread_join(native: pthread_t, value: *mut *mut ffi::c_void) -> ffi::c_int; + pub fn pthread_attr_init(attrp: *mut pthread_attr_t) -> ffi::c_int; + pub fn pthread_attr_setstacksize( + attr: *mut pthread_attr_t, + stack_size: libc::size_t, + ) -> ffi::c_int; + pub fn pthread_attr_destroy(attr: *mut pthread_attr_t) -> ffi::c_int; + pub fn pthread_detach(thread: pthread_t) -> ffi::c_int; + } +} + +#[cfg(target_feature = "atomics")] +pub struct Thread { + id: libc::pthread_t, +} + +#[cfg(target_feature = "atomics")] +impl Drop for Thread { + fn drop(&mut self) { + let ret = unsafe { libc::pthread_detach(self.id) }; + debug_assert_eq!(ret, 0); + } +} + +pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; + +#[cfg(target_feature = "atomics")] +impl Thread { + // unsafe: see thread::Builder::spawn_unchecked for safety requirements + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box<dyn FnOnce()>, + ) -> io::Result<Thread> { + let p = Box::into_raw(Box::new(p)); + let mut native: libc::pthread_t = unsafe { mem::zeroed() }; + let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; + assert_eq!(unsafe { libc::pthread_attr_init(&mut attr) }, 0); + + let stack_size = cmp::max(stack, DEFAULT_MIN_STACK_SIZE); + + match unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) } { + 0 => {} + n => { + assert_eq!(n, libc::EINVAL); + // EINVAL means |stack_size| is either too small or not a + // multiple of the system page size. Because it's definitely + // >= PTHREAD_STACK_MIN, it must be an alignment issue. + // Round up to the nearest page and try again. + let page_size = os::page_size(); + let stack_size = + (stack_size + page_size - 1) & (-(page_size as isize - 1) as usize - 1); + assert_eq!(unsafe { libc::pthread_attr_setstacksize(&mut attr, stack_size) }, 0); + } + }; + + let ret = unsafe { libc::pthread_create(&mut native, &attr, thread_start, p as *mut _) }; + // Note: if the thread creation fails and this assert fails, then p will + // be leaked. However, an alternative design could cause double-free + // which is clearly worse. + assert_eq!(unsafe { libc::pthread_attr_destroy(&mut attr) }, 0); + + return if ret != 0 { + // The thread failed to start and as a result p was not consumed. Therefore, it is + // safe to reconstruct the box so that it gets deallocated. + unsafe { + drop(Box::from_raw(p)); + } + Err(io::Error::from_raw_os_error(ret)) + } else { + Ok(Thread { id: native }) + }; + + extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { + unsafe { + // Finally, let's run some code. + Box::from_raw(main as *mut Box<dyn FnOnce()>)(); + } + ptr::null_mut() + } + } + + pub fn join(self) { + let id = mem::ManuallyDrop::new(self).id; + let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; + if ret != 0 { + rtabort!("failed to join thread: {}", io::Error::from_raw_os_error(ret)); + } + } +} + +#[cfg(target_feature = "atomics")] +pub fn available_parallelism() -> io::Result<NonZero<usize>> { + match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } { + -1 => Err(io::Error::last_os_error()), + cpus => NonZero::new(cpus as usize).ok_or(io::Error::UNKNOWN_THREAD_COUNT), + } +} + +pub fn yield_now() { + let ret = unsafe { wasi::sched_yield() }; + debug_assert_eq!(ret, Ok(())); +} + +pub fn sleep(dur: Duration) { + let mut nanos = dur.as_nanos(); + while nanos > 0 { + const USERDATA: wasi::Userdata = 0x0123_45678; + + let clock = wasi::SubscriptionClock { + id: wasi::CLOCKID_MONOTONIC, + timeout: u64::try_from(nanos).unwrap_or(u64::MAX), + precision: 0, + flags: 0, + }; + nanos -= u128::from(clock.timeout); + + let in_ = wasi::Subscription { + userdata: USERDATA, + u: wasi::SubscriptionU { tag: 0, u: wasi::SubscriptionUU { clock } }, + }; + unsafe { + let mut event: wasi::Event = mem::zeroed(); + let res = wasi::poll_oneoff(&in_, &mut event, 1); + match (res, event) { + ( + Ok(1), + wasi::Event { + userdata: USERDATA, + error: wasi::ERRNO_SUCCESS, + type_: wasi::EVENTTYPE_CLOCK, + .. + }, + ) => {} + _ => panic!("thread::sleep(): unexpected result of poll_oneoff"), + } + } + } +} diff --git a/library/std/src/sys/thread/wasip2.rs b/library/std/src/sys/thread/wasip2.rs new file mode 100644 index 00000000000..420cad2a5e4 --- /dev/null +++ b/library/std/src/sys/thread/wasip2.rs @@ -0,0 +1,32 @@ +use crate::time::{Duration, Instant}; + +pub fn sleep(dur: Duration) { + // Sleep in increments of `u64::MAX` nanoseconds until the `dur` is + // entirely drained. + let mut remaining = dur.as_nanos(); + while remaining > 0 { + let amt = u64::try_from(remaining).unwrap_or(u64::MAX); + wasip2::clocks::monotonic_clock::subscribe_duration(amt).block(); + remaining -= u128::from(amt); + } +} + +pub fn sleep_until(deadline: Instant) { + match u64::try_from(deadline.into_inner().as_duration().as_nanos()) { + // If the point in time we're sleeping to fits within a 64-bit + // number of nanoseconds then directly use `subscribe_instant`. + Ok(deadline) => { + wasip2::clocks::monotonic_clock::subscribe_instant(deadline).block(); + } + // ... otherwise we're sleeping for 500+ years relative to the + // "start" of what the system is using as a clock so speed/accuracy + // is not so much of a concern. Use `sleep` instead. + Err(_) => { + let now = Instant::now(); + + if let Some(delay) = deadline.checked_duration_since(now) { + sleep(delay); + } + } + } +} diff --git a/library/std/src/sys/thread/wasm.rs b/library/std/src/sys/thread/wasm.rs new file mode 100644 index 00000000000..e843bc992ba --- /dev/null +++ b/library/std/src/sys/thread/wasm.rs @@ -0,0 +1,23 @@ +use crate::cmp; +use crate::time::Duration; + +pub fn sleep(dur: Duration) { + #[cfg(target_arch = "wasm32")] + use core::arch::wasm32 as wasm; + #[cfg(target_arch = "wasm64")] + use core::arch::wasm64 as wasm; + + // Use an atomic wait to block the current thread artificially with a + // timeout listed. Note that we should never be notified (return value + // of 0) or our comparison should never fail (return value of 1) so we + // should always only resume execution through a timeout (return value + // 2). + let mut nanos = dur.as_nanos(); + while nanos > 0 { + let amt = cmp::min(i64::MAX as u128, nanos); + let mut x = 0; + let val = unsafe { wasm::memory_atomic_wait32(&mut x, 0, amt as i64) }; + debug_assert_eq!(val, 2); + nanos -= amt; + } +} diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/thread/windows.rs index b0e38220a2d..a5640c51c4a 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/thread/windows.rs @@ -1,14 +1,14 @@ use core::ffi::c_void; -use super::time::WaitableTimer; -use super::to_u16s; use crate::ffi::CStr; use crate::num::NonZero; use crate::os::windows::io::{AsRawHandle, HandleOrNull}; use crate::sys::handle::Handle; +use crate::sys::pal::time::WaitableTimer; +use crate::sys::pal::{dur2timeout, to_u16s}; use crate::sys::{c, stack_overflow}; use crate::sys_common::FromInner; -use crate::time::{Duration, Instant}; +use crate::time::Duration; use crate::{io, ptr}; pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024; @@ -62,24 +62,6 @@ impl Thread { } } - pub fn set_name(name: &CStr) { - if let Ok(utf8) = name.to_str() { - if let Ok(utf16) = to_u16s(utf8) { - unsafe { - // SAFETY: the vec returned by `to_u16s` ends with a zero value - Self::set_name_wide(&utf16) - } - }; - }; - } - - /// # Safety - /// - /// `name` must end with a zero value - pub unsafe fn set_name_wide(name: &[u16]) { - unsafe { c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()) }; - } - pub fn join(self) { let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; if rc == c::WAIT_FAILED { @@ -87,37 +69,6 @@ impl Thread { } } - pub fn yield_now() { - // This function will return 0 if there are no other threads to execute, - // but this also means that the yield was useless so this isn't really a - // case that needs to be worried about. - unsafe { - c::SwitchToThread(); - } - } - - pub fn sleep(dur: Duration) { - fn high_precision_sleep(dur: Duration) -> Result<(), ()> { - let timer = WaitableTimer::high_resolution()?; - timer.set(dur)?; - timer.wait() - } - // Attempt to use high-precision sleep (Windows 10, version 1803+). - // On error fallback to the standard `Sleep` function. - // Also preserves the zero duration behavior of `Sleep`. - if dur.is_zero() || high_precision_sleep(dur).is_err() { - unsafe { c::Sleep(super::dur2timeout(dur)) } - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn handle(&self) -> &Handle { &self.handle } @@ -127,14 +78,6 @@ impl Thread { } } -pub(crate) fn current_os_id() -> Option<u64> { - // SAFETY: FFI call with no preconditions. - let id: u32 = unsafe { c::GetCurrentThreadId() }; - - // A return value of 0 indicates failed lookup. - if id == 0 { None } else { Some(id.into()) } -} - pub fn available_parallelism() -> io::Result<NonZero<usize>> { let res = unsafe { let mut sysinfo: c::SYSTEM_INFO = crate::mem::zeroed(); @@ -146,3 +89,52 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { cpus => Ok(unsafe { NonZero::new_unchecked(cpus) }), } } + +pub fn current_os_id() -> Option<u64> { + // SAFETY: FFI call with no preconditions. + let id: u32 = unsafe { c::GetCurrentThreadId() }; + + // A return value of 0 indicates failed lookup. + if id == 0 { None } else { Some(id.into()) } +} + +pub fn set_name(name: &CStr) { + if let Ok(utf8) = name.to_str() { + if let Ok(utf16) = to_u16s(utf8) { + unsafe { + // SAFETY: the vec returned by `to_u16s` ends with a zero value + set_name_wide(&utf16) + } + }; + }; +} + +/// # Safety +/// +/// `name` must end with a zero value +pub unsafe fn set_name_wide(name: &[u16]) { + unsafe { c::SetThreadDescription(c::GetCurrentThread(), name.as_ptr()) }; +} + +pub fn sleep(dur: Duration) { + fn high_precision_sleep(dur: Duration) -> Result<(), ()> { + let timer = WaitableTimer::high_resolution()?; + timer.set(dur)?; + timer.wait() + } + // Attempt to use high-precision sleep (Windows 10, version 1803+). + // On error fallback to the standard `Sleep` function. + // Also preserves the zero duration behavior of `Sleep`. + if dur.is_zero() || high_precision_sleep(dur).is_err() { + unsafe { c::Sleep(dur2timeout(dur)) } + } +} + +pub fn yield_now() { + // This function will return 0 if there are no other threads to execute, + // but this also means that the yield was useless so this isn't really a + // case that needs to be worried about. + unsafe { + c::SwitchToThread(); + } +} diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/thread/xous.rs index 92803c94c6e..133e15a0928 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/thread/xous.rs @@ -1,6 +1,5 @@ use core::arch::asm; -use crate::ffi::CStr; use crate::io; use crate::num::NonZero; use crate::os::xous::ffi::{ @@ -8,7 +7,7 @@ use crate::os::xous::ffi::{ map_memory, update_memory_flags, }; use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; -use crate::time::{Duration, Instant}; +use crate::time::Duration; pub struct Thread { tid: ThreadId, @@ -110,46 +109,29 @@ impl Thread { Ok(Thread { tid }) } - pub fn yield_now() { - do_yield(); - } - - pub fn set_name(_name: &CStr) { - // nope - } - - pub fn sleep(dur: Duration) { - // Because the sleep server works on units of `usized milliseconds`, split - // the messages up into these chunks. This means we may run into issues - // if you try to sleep a thread for more than 49 days on a 32-bit system. - let mut millis = dur.as_millis(); - while millis > 0 { - let sleep_duration = - if millis > (usize::MAX as _) { usize::MAX } else { millis as usize }; - blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into()) - .expect("failed to send message to ticktimer server"); - millis -= sleep_duration as u128; - } - } - - pub fn sleep_until(deadline: Instant) { - let now = Instant::now(); - - if let Some(delay) = deadline.checked_duration_since(now) { - Self::sleep(delay); - } - } - pub fn join(self) { join_thread(self.tid).unwrap(); } } -pub(crate) fn current_os_id() -> Option<u64> { - None -} - pub fn available_parallelism() -> io::Result<NonZero<usize>> { // We're unicore right now. Ok(unsafe { NonZero::new_unchecked(1) }) } + +pub fn yield_now() { + do_yield(); +} + +pub fn sleep(dur: Duration) { + // Because the sleep server works on units of `usized milliseconds`, split + // the messages up into these chunks. This means we may run into issues + // if you try to sleep a thread for more than 49 days on a 32-bit system. + let mut millis = dur.as_millis(); + while millis > 0 { + let sleep_duration = if millis > (usize::MAX as _) { usize::MAX } else { millis as usize }; + blocking_scalar(ticktimer_server(), TicktimerScalar::SleepMs(sleep_duration).into()) + .expect("failed to send message to ticktimer server"); + millis -= sleep_duration as u128; + } +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index b6059c28cec..4d09b2b4e9d 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -550,7 +550,7 @@ impl Builder { } if let Some(name) = their_thread.cname() { - imp::Thread::set_name(name); + imp::set_name(name); } let f = f.into_inner(); @@ -763,7 +763,7 @@ where /// [`Mutex`]: crate::sync::Mutex #[stable(feature = "rust1", since = "1.0.0")] pub fn yield_now() { - imp::Thread::yield_now() + imp::yield_now() } /// Determines whether the current thread is unwinding because of panic. @@ -884,7 +884,7 @@ pub fn sleep_ms(ms: u32) { /// ``` #[stable(feature = "thread_sleep", since = "1.4.0")] pub fn sleep(dur: Duration) { - imp::Thread::sleep(dur) + imp::sleep(dur) } /// Puts the current thread to sleep until the specified deadline has passed. @@ -983,7 +983,7 @@ pub fn sleep(dur: Duration) { /// ``` #[unstable(feature = "thread_sleep_until", issue = "113752")] pub fn sleep_until(deadline: Instant) { - imp::Thread::sleep_until(deadline) + imp::sleep_until(deadline) } /// Used to ensure that `park` and `park_timeout` do not unwind, as that can @@ -1021,13 +1021,23 @@ impl Drop for PanicGuard { /// specifying a maximum time to block the thread for. /// /// * The [`unpark`] method on a [`Thread`] atomically makes the token available -/// if it wasn't already. Because the token is initially absent, [`unpark`] -/// followed by [`park`] will result in the second call returning immediately. -/// -/// The API is typically used by acquiring a handle to the current thread, -/// placing that handle in a shared data structure so that other threads can -/// find it, and then `park`ing in a loop. When some desired condition is met, another -/// thread calls [`unpark`] on the handle. +/// if it wasn't already. Because the token can be held by a thread even if it is currently not +/// parked, [`unpark`] followed by [`park`] will result in the second call returning immediately. +/// However, note that to rely on this guarantee, you need to make sure that your `unpark` happens +/// after all `park` that may be done by other data structures! +/// +/// The API is typically used by acquiring a handle to the current thread, placing that handle in a +/// shared data structure so that other threads can find it, and then `park`ing in a loop. When some +/// desired condition is met, another thread calls [`unpark`] on the handle. The last bullet point +/// above guarantees that even if the `unpark` occurs before the thread is finished `park`ing, it +/// will be woken up properly. +/// +/// Note that the coordination via the shared data structure is crucial: If you `unpark` a thread +/// without first establishing that it is about to be `park`ing within your code, that `unpark` may +/// get consumed by a *different* `park` in the same thread, leading to a deadlock. This also means +/// you must not call unknown code between setting up for parking and calling `park`; for instance, +/// if you invoke `println!`, that may itself call `park` and thus consume your `unpark` and cause a +/// deadlock. /// /// The motivation for this design is twofold: /// @@ -1058,21 +1068,24 @@ impl Drop for PanicGuard { /// /// ``` /// use std::thread; -/// use std::sync::{Arc, atomic::{Ordering, AtomicBool}}; +/// use std::sync::atomic::{Ordering, AtomicBool}; /// use std::time::Duration; /// -/// let flag = Arc::new(AtomicBool::new(false)); -/// let flag2 = Arc::clone(&flag); +/// static QUEUED: AtomicBool = AtomicBool::new(false); +/// static FLAG: AtomicBool = AtomicBool::new(false); /// /// let parked_thread = thread::spawn(move || { +/// println!("Thread spawned"); +/// // Signal that we are going to `park`. Between this store and our `park`, there may +/// // be no other `park`, or else that `park` could consume our `unpark` token! +/// QUEUED.store(true, Ordering::Release); /// // We want to wait until the flag is set. We *could* just spin, but using /// // park/unpark is more efficient. -/// while !flag2.load(Ordering::Relaxed) { -/// println!("Parking thread"); +/// while !FLAG.load(Ordering::Acquire) { +/// // We can *not* use `println!` here since that could use thread parking internally. /// thread::park(); /// // We *could* get here spuriously, i.e., way before the 10ms below are over! /// // But that is no problem, we are in a loop until the flag is set anyway. -/// println!("Thread unparked"); /// } /// println!("Flag received"); /// }); @@ -1080,11 +1093,22 @@ impl Drop for PanicGuard { /// // Let some time pass for the thread to be spawned. /// thread::sleep(Duration::from_millis(10)); /// +/// // Ensure the thread is about to park. +/// // This is crucial! It guarantees that the `unpark` below is not consumed +/// // by some other code in the parked thread (e.g. inside `println!`). +/// while !QUEUED.load(Ordering::Acquire) { +/// // Spinning is of course inefficient; in practice, this would more likely be +/// // a dequeue where we have no work to do if there's nobody queued. +/// std::hint::spin_loop(); +/// } +/// /// // Set the flag, and let the thread wake up. -/// // There is no race condition here, if `unpark` +/// // There is no race condition here: if `unpark` /// // happens first, `park` will return immediately. +/// // There is also no other `park` that could consume this token, +/// // since we waited until the other thread got queued. /// // Hence there is no risk of a deadlock. -/// flag.store(true, Ordering::Relaxed); +/// FLAG.store(true, Ordering::Release); /// println!("Unpark the thread"); /// parked_thread.thread().unpark(); /// @@ -1494,10 +1518,14 @@ impl Thread { /// ``` /// use std::thread; /// use std::time::Duration; + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// static QUEUED: AtomicBool = AtomicBool::new(false); /// /// let parked_thread = thread::Builder::new() /// .spawn(|| { /// println!("Parking thread"); + /// QUEUED.store(true, Ordering::Release); /// thread::park(); /// println!("Thread unparked"); /// }) @@ -1506,6 +1534,15 @@ impl Thread { /// // Let some time pass for the thread to be spawned. /// thread::sleep(Duration::from_millis(10)); /// + /// // Wait until the other thread is queued. + /// // This is crucial! It guarantees that the `unpark` below is not consumed + /// // by some other code in the parked thread (e.g. inside `println!`). + /// while !QUEUED.load(Ordering::Acquire) { + /// // Spinning is of course inefficient; in practice, this would more likely be + /// // a dequeue where we have no work to do if there's nobody queued. + /// std::hint::spin_loop(); + /// } + /// /// println!("Unpark the thread"); /// parked_thread.thread().unpark(); /// diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index ae889f1e778..2117f5f93ce 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -287,6 +287,8 @@ fn test_park_unpark_called_other_thread() { for _ in 0..10 { let th = thread::current(); + // Here we rely on `thread::spawn` (specifically the part that runs after spawning + // the thread) to not consume the parking token. let _guard = thread::spawn(move || { super::sleep(Duration::from_millis(50)); th.unpark(); @@ -316,6 +318,8 @@ fn test_park_timeout_unpark_called_other_thread() { for _ in 0..10 { let th = thread::current(); + // Here we rely on `thread::spawn` (specifically the part that runs after spawning + // the thread) to not consume the parking token. let _guard = thread::spawn(move || { super::sleep(Duration::from_millis(50)); th.unpark(); diff --git a/library/std/tests/sync/barrier.rs b/library/std/tests/sync/barrier.rs index 8aefff9d507..a66bd629699 100644 --- a/library/std/tests/sync/barrier.rs +++ b/library/std/tests/sync/barrier.rs @@ -1,3 +1,4 @@ +use std::panic::RefUnwindSafe; use std::sync::mpsc::{TryRecvError, channel}; use std::sync::{Arc, Barrier}; use std::thread; @@ -33,3 +34,11 @@ fn test_barrier() { } assert!(leader_found); } + +/// Asserts that `Barrier` is ref unwind safe. +/// +/// See <https://github.com/rust-lang/rust/issues/146087>. +const _: () = { + const fn check_ref_unwind_safe<T: RefUnwindSafe>() {} + check_ref_unwind_safe::<Barrier>(); +}; diff --git a/library/std/tests/sync/condvar.rs b/library/std/tests/sync/condvar.rs index 1d712a64300..1b1c33efad5 100644 --- a/library/std/tests/sync/condvar.rs +++ b/library/std/tests/sync/condvar.rs @@ -17,7 +17,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: notify_one, test_body: { @@ -38,7 +38,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: notify_all, test_body: { @@ -79,7 +79,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: test_mutex_arc_condvar, test_body: { @@ -116,7 +116,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: wait_while, test_body: { @@ -141,7 +141,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: wait_timeout_wait, test_body: { @@ -164,7 +164,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: wait_timeout_while_wait, test_body: { @@ -180,7 +180,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: wait_timeout_while_instant_satisfy, test_body: { @@ -197,7 +197,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: wait_timeout_while_wake, test_body: { @@ -226,7 +226,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads +#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads. nonpoison_and_poison_unwrap_test!( name: wait_timeout_wake, test_body: { diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs index ac1dbebcc5c..23112e10284 100644 --- a/library/std/tests/sync/lib.rs +++ b/library/std/tests/sync/lib.rs @@ -58,6 +58,9 @@ fn result_unwrap<T, E: std::fmt::Debug>(x: Result<T, E>) -> T { /// a no-op (the identity function). /// /// The test names will be prefiex with `poison_` or `nonpoison_`. +/// +/// Important: most attributes (except `cfg`) will not work properly! (They are only applied to the first test.) +/// See <https://github.com/rust-lang/rust/pull/146433> for more information. macro_rules! nonpoison_and_poison_unwrap_test { ( name: $name:ident, diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index 612c75c7aef..2445764001b 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -266,7 +266,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +#[cfg(panic = "unwind")] // Requires unwinding support. nonpoison_and_poison_unwrap_test!( name: test_panics, test_body: { @@ -297,7 +297,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +#[cfg(panic = "unwind")] // Requires unwinding support. nonpoison_and_poison_unwrap_test!( name: test_mutex_arc_access_in_unwind, test_body: { diff --git a/library/std/tests/sync/rwlock.rs b/library/std/tests/sync/rwlock.rs index eca15d2a4ad..65d8bac7194 100644 --- a/library/std/tests/sync/rwlock.rs +++ b/library/std/tests/sync/rwlock.rs @@ -50,7 +50,7 @@ nonpoison_and_poison_unwrap_test!( // FIXME: On macOS we use a provenance-incorrect implementation and Miri // catches that issue with a chance of around 1/1000. // See <https://github.com/rust-lang/rust/issues/121950> for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] +#[cfg(not(all(miri, target_os = "macos")))] nonpoison_and_poison_unwrap_test!( name: frob, test_body: { @@ -124,7 +124,7 @@ nonpoison_and_poison_unwrap_test!( } ); -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +#[cfg(panic = "unwind")] // Requires unwinding support. nonpoison_and_poison_unwrap_test!( name: test_rw_arc_access_in_unwind, test_body: { @@ -315,7 +315,7 @@ nonpoison_and_poison_unwrap_test!( // FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue. // See <https://github.com/rust-lang/rust/issues/121950> for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] +#[cfg(not(all(miri, target_os = "macos")))] nonpoison_and_poison_unwrap_test!( name: test_downgrade_observe, test_body: { @@ -362,7 +362,7 @@ nonpoison_and_poison_unwrap_test!( // FIXME: On macOS we use a provenance-incorrect implementation and Miri catches that issue. // See <https://github.com/rust-lang/rust/issues/121950> for details. -#[cfg_attr(all(miri, target_os = "macos"), ignore)] +#[cfg(not(all(miri, target_os = "macos")))] nonpoison_and_poison_unwrap_test!( name: test_downgrade_atomic, test_body: { diff --git a/library/std_detect/src/detect/os/linux/aarch64.rs b/library/std_detect/src/detect/os/linux/aarch64.rs index 87a9d6ebb88..b733b8a9eb2 100644 --- a/library/std_detect/src/detect/os/linux/aarch64.rs +++ b/library/std_detect/src/detect/os/linux/aarch64.rs @@ -140,7 +140,7 @@ struct AtHwcap { impl From<auxvec::AuxVec> for AtHwcap { /// Reads AtHwcap from the auxiliary vector. fn from(auxv: auxvec::AuxVec) -> Self { - AtHwcap { + let mut cap = AtHwcap { fp: bit::test(auxv.hwcap, 0), asimd: bit::test(auxv.hwcap, 1), // evtstrm: bit::test(auxv.hwcap, 2), @@ -207,39 +207,50 @@ impl From<auxvec::AuxVec> for AtHwcap { // smef32f32: bit::test(auxv.hwcap2, 29), smefa64: bit::test(auxv.hwcap2, 30), wfxt: bit::test(auxv.hwcap2, 31), - // ebf16: bit::test(auxv.hwcap2, 32), - // sveebf16: bit::test(auxv.hwcap2, 33), - cssc: bit::test(auxv.hwcap2, 34), - // rprfm: bit::test(auxv.hwcap2, 35), - sve2p1: bit::test(auxv.hwcap2, 36), - sme2: bit::test(auxv.hwcap2, 37), - sme2p1: bit::test(auxv.hwcap2, 38), - // smei16i32: bit::test(auxv.hwcap2, 39), - // smebi32i32: bit::test(auxv.hwcap2, 40), - smeb16b16: bit::test(auxv.hwcap2, 41), - smef16f16: bit::test(auxv.hwcap2, 42), - mops: bit::test(auxv.hwcap2, 43), - hbc: bit::test(auxv.hwcap2, 44), - sveb16b16: bit::test(auxv.hwcap2, 45), - lrcpc3: bit::test(auxv.hwcap2, 46), - lse128: bit::test(auxv.hwcap2, 47), - fpmr: bit::test(auxv.hwcap2, 48), - lut: bit::test(auxv.hwcap2, 49), - faminmax: bit::test(auxv.hwcap2, 50), - f8cvt: bit::test(auxv.hwcap2, 51), - f8fma: bit::test(auxv.hwcap2, 52), - f8dp4: bit::test(auxv.hwcap2, 53), - f8dp2: bit::test(auxv.hwcap2, 54), - f8e4m3: bit::test(auxv.hwcap2, 55), - f8e5m2: bit::test(auxv.hwcap2, 56), - smelutv2: bit::test(auxv.hwcap2, 57), - smef8f16: bit::test(auxv.hwcap2, 58), - smef8f32: bit::test(auxv.hwcap2, 59), - smesf8fma: bit::test(auxv.hwcap2, 60), - smesf8dp4: bit::test(auxv.hwcap2, 61), - smesf8dp2: bit::test(auxv.hwcap2, 62), - // pauthlr: bit::test(auxv.hwcap2, ??), + ..Default::default() + }; + + // Hardware capabilities from bits 32 to 63 should only + // be tested on LP64 targets with 64 bits `usize`. + // On ILP32 targets like `aarch64-unknown-linux-gnu_ilp32`, + // these hardware capabilities will default to `false`. + // https://github.com/rust-lang/rust/issues/146230 + #[cfg(target_pointer_width = "64")] + { + // cap.ebf16: bit::test(auxv.hwcap2, 32); + // cap.sveebf16: bit::test(auxv.hwcap2, 33); + cap.cssc = bit::test(auxv.hwcap2, 34); + // cap.rprfm: bit::test(auxv.hwcap2, 35); + cap.sve2p1 = bit::test(auxv.hwcap2, 36); + cap.sme2 = bit::test(auxv.hwcap2, 37); + cap.sme2p1 = bit::test(auxv.hwcap2, 38); + // cap.smei16i32 = bit::test(auxv.hwcap2, 39); + // cap.smebi32i32 = bit::test(auxv.hwcap2, 40); + cap.smeb16b16 = bit::test(auxv.hwcap2, 41); + cap.smef16f16 = bit::test(auxv.hwcap2, 42); + cap.mops = bit::test(auxv.hwcap2, 43); + cap.hbc = bit::test(auxv.hwcap2, 44); + cap.sveb16b16 = bit::test(auxv.hwcap2, 45); + cap.lrcpc3 = bit::test(auxv.hwcap2, 46); + cap.lse128 = bit::test(auxv.hwcap2, 47); + cap.fpmr = bit::test(auxv.hwcap2, 48); + cap.lut = bit::test(auxv.hwcap2, 49); + cap.faminmax = bit::test(auxv.hwcap2, 50); + cap.f8cvt = bit::test(auxv.hwcap2, 51); + cap.f8fma = bit::test(auxv.hwcap2, 52); + cap.f8dp4 = bit::test(auxv.hwcap2, 53); + cap.f8dp2 = bit::test(auxv.hwcap2, 54); + cap.f8e4m3 = bit::test(auxv.hwcap2, 55); + cap.f8e5m2 = bit::test(auxv.hwcap2, 56); + cap.smelutv2 = bit::test(auxv.hwcap2, 57); + cap.smef8f16 = bit::test(auxv.hwcap2, 58); + cap.smef8f32 = bit::test(auxv.hwcap2, 59); + cap.smesf8fma = bit::test(auxv.hwcap2, 60); + cap.smesf8dp4 = bit::test(auxv.hwcap2, 61); + cap.smesf8dp2 = bit::test(auxv.hwcap2, 62); + // cap.pauthlr = bit::test(auxv.hwcap2, ??); } + cap } } diff --git a/package-lock.json b/package-lock.json index def0cfa86a5..d0297bf70b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "dependencies": { - "browser-ui-test": "^0.22.0", + "browser-ui-test": "^0.22.2", "es-check": "^6.2.1", "eslint": "^8.57.1", "eslint-js": "github:eslint/js", @@ -485,9 +485,9 @@ } }, "node_modules/browser-ui-test": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/browser-ui-test/-/browser-ui-test-0.22.0.tgz", - "integrity": "sha512-p/C02TMybTDKsAjpGOdnyNC0Q25KDae/fKMnvHaqcJ0tXRqNKwndW2Ltq7HTmin5xqg8GGOmysEgWTZkXu6pfA==", + "version": "0.22.2", + "resolved": "https://registry.npmjs.org/browser-ui-test/-/browser-ui-test-0.22.2.tgz", + "integrity": "sha512-eNB/PN2yDGe5n5IwE3ld/N6A39jM1oRzJmT5nOVQqrvoZEtcd9JSggDQPNVUnMEyuGcD4OEOWMsEa4oJppAmDQ==", "license": "MIT", "dependencies": { "css-unit-converter": "^1.1.2", diff --git a/package.json b/package.json index 976d6303634..04e0f6af19a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "browser-ui-test": "^0.22.0", + "browser-ui-test": "^0.22.2", "es-check": "^6.2.1", "eslint": "^8.57.1", "eslint-js": "github:eslint/js", diff --git a/src/bootstrap/defaults/bootstrap.dist.toml b/src/bootstrap/defaults/bootstrap.dist.toml index 9daf9faac14..b111a20f8d8 100644 --- a/src/bootstrap/defaults/bootstrap.dist.toml +++ b/src/bootstrap/defaults/bootstrap.dist.toml @@ -14,6 +14,10 @@ compiletest-use-stage0-libtest = false [llvm] download-ci-llvm = false +# Most users installing from source want to build all parts of the project from source. +[gcc] +download-ci-gcc = false + [rust] # We have several defaults in bootstrap that depend on whether the channel is `dev` (e.g. `omit-git-hash` and `download-ci-llvm`). # Make sure they don't get set when installing from source. diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 0b75e85772f..1458b0beefa 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1383,14 +1383,17 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS } } - // Build jemalloc on AArch64 with support for page sizes up to 64K - // See: https://github.com/rust-lang/rust/pull/135081 // See also the "JEMALLOC_SYS_WITH_LG_PAGE" setting in the tool build step. - if builder.config.jemalloc(target) - && target.starts_with("aarch64") - && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() - { - cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + if builder.config.jemalloc(target) && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() { + // Build jemalloc on AArch64 with support for page sizes up to 64K + // See: https://github.com/rust-lang/rust/pull/135081 + if target.starts_with("aarch64") { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + } + // Build jemalloc on LoongArch with support for page sizes up to 16K + else if target.starts_with("loongarch") { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "14"); + } } } diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index 77c9622a9bf..717dea37e9e 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -32,6 +32,10 @@ pub struct GccOutput { impl GccOutput { /// Install the required libgccjit library file(s) to the specified `path`. pub fn install_to(&self, builder: &Builder<'_>, directory: &Path) { + if builder.config.dry_run() { + return; + } + // At build time, cg_gcc has to link to libgccjit.so (the unversioned symbol). // However, at runtime, it will by default look for libgccjit.so.0. // So when we install the built libgccjit.so file to the target `directory`, we add it there @@ -39,8 +43,16 @@ impl GccOutput { let mut target_filename = self.libgccjit.file_name().unwrap().to_str().unwrap().to_string(); target_filename.push_str(".0"); + // If we build libgccjit ourselves, then `self.libgccjit` can actually be a symlink. + // In that case, we have to resolve it first, otherwise we'd create a symlink to a symlink, + // which wouldn't work. + let actual_libgccjit_path = t!( + self.libgccjit.canonicalize(), + format!("Cannot find libgccjit at {}", self.libgccjit.display()) + ); + let dst = directory.join(target_filename); - builder.copy_link(&self.libgccjit, &dst, FileType::NativeLibrary); + builder.copy_link(&actual_libgccjit_path, &dst, FileType::NativeLibrary); } } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 6870bf3eddc..dcc4898cae1 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -228,12 +228,18 @@ pub fn prepare_tool_cargo( // own copy cargo.env("LZMA_API_STATIC", "1"); - // Build jemalloc on AArch64 with support for page sizes up to 64K - // See: https://github.com/rust-lang/rust/pull/135081 // Note that `miri` always uses jemalloc. As such, there is no checking of the jemalloc build flag. // See also the "JEMALLOC_SYS_WITH_LG_PAGE" setting in the compile build step. - if target.starts_with("aarch64") && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() { - cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + if env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() { + // Build jemalloc on AArch64 with support for page sizes up to 64K + // See: https://github.com/rust-lang/rust/pull/135081 + if target.starts_with("aarch64") { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + } + // Build jemalloc on LoongArch with support for page sizes up to 16K + else if target.starts_with("loongarch") { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "14"); + } } // CFG_RELEASE is needed by rustfmt (and possibly other tools) which diff --git a/src/bootstrap/src/core/config/mod.rs b/src/bootstrap/src/core/config/mod.rs index 5999348a7fe..05a5dfc0bc5 100644 --- a/src/bootstrap/src/core/config/mod.rs +++ b/src/bootstrap/src/core/config/mod.rs @@ -422,10 +422,10 @@ impl std::str::FromStr for RustcLto { #[derive(Default, Clone)] pub enum GccCiMode { /// Build GCC from the local `src/gcc` submodule. - #[default] BuildLocally, /// Try to download GCC from CI. /// If it is not available on CI, it will be built locally instead. + #[default] DownloadFromCi, } diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 01309072927..03b39882e30 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -541,4 +541,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "Added a new option `rust.break-on-ice` to control if internal compiler errors cause a debug break on Windows.", }, + ChangeInfo { + change_id: 146435, + severity: ChangeSeverity::Info, + summary: "The default value of the `gcc.download-ci-gcc` option has been changed to `true`.", + }, ]; diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index 7e0fb5794f4..b7d7151b171 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -2,9 +2,7 @@ set -euo pipefail -# https://github.com/rust-lang/rust/pull/144443 -# https://github.com/rust-lang/rust/pull/145928 -LINUX_VERSION=8851e27d2cb947ea8bbbe8e812068f7bf5cbd00b +LINUX_VERSION=v6.17-rc5 # Build rustc, rustdoc, cargo, clippy-driver and rustfmt ../x.py build --stage 2 library rustdoc clippy rustfmt diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 6c5b48d8c8f..9ad9cb5b526 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -95,8 +95,8 @@ target | notes `arm-unknown-linux-gnueabihf` | Armv6 Linux, hardfloat (kernel 3.2+, glibc 2.17) `armv7-unknown-linux-gnueabihf` | Armv7-A Linux, hardfloat (kernel 3.2+, glibc 2.17) [`armv7-unknown-linux-ohos`](platform-support/openharmony.md) | Armv7-A OpenHarmony -[`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, glibc 2.36) -[`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, musl 1.2.5) +[`loongarch64-unknown-linux-gnu`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, glibc 2.36), LSX required +[`loongarch64-unknown-linux-musl`](platform-support/loongarch-linux.md) | LoongArch64 Linux, LP64D ABI (kernel 5.19+, musl 1.2.5), LSX required [`i686-pc-windows-gnu`](platform-support/windows-gnu.md) | 32-bit MinGW (Windows 10+, Windows Server 2016+, Pentium 4) [^x86_32-floats-return-ABI] [^win32-msvc-alignment] `powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2+, glibc 2.17) `powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2+, glibc 2.17) diff --git a/src/doc/rustc/src/platform-support/loongarch-linux.md b/src/doc/rustc/src/platform-support/loongarch-linux.md index 817d3a89230..a923218282c 100644 --- a/src/doc/rustc/src/platform-support/loongarch-linux.md +++ b/src/doc/rustc/src/platform-support/loongarch-linux.md @@ -7,8 +7,8 @@ LoongArch is a RISC ISA developed by Loongson Technology Corporation Limited. | Target | Description | |--------|-------------| -| `loongarch64-unknown-linux-gnu` | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36) | -| `loongarch64-unknown-linux-musl` | LoongArch64 Linux, LP64D ABI (kernel 5.19, musl 1.2.5) | +| `loongarch64-unknown-linux-gnu` | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36), LSX required | +| `loongarch64-unknown-linux-musl` | LoongArch64 Linux, LP64D ABI (kernel 5.19, musl 1.2.5), LSX required | These support both native and cross builds, and have full support for `std`. @@ -23,8 +23,6 @@ Reference material: ## Target maintainers [@heiher](https://github.com/heiher) -[@xiangzhai](https://github.com/xiangzhai) -[@zhaixiaojuan](https://github.com/zhaixiaojuan) [@xen0n](https://github.com/xen0n) ## Requirements @@ -46,8 +44,8 @@ The targets require a reasonably up-to-date LoongArch toolchain on the host. Currently the following components are used by the Rust CI to build the target, and the versions can be seen as the minimum requirement: -* GNU Binutils 2.40 -* GCC 13.x +* GNU Binutils 2.42 +* GCC 14.x * glibc 2.36 * linux-headers 5.19 @@ -59,6 +57,11 @@ for newer LoongArch ELF relocation types, among other features. Recent LLVM/Clang toolchains may be able to build the targets, but are not currently being actively tested. +### CPU features + +These targets require the double-precision floating-point and LSX (LoongArch +SIMD Extension) features. + ## Building These targets are distributed through `rustup`, and otherwise require no diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs index d684e6f8650..b5a8d64ff4f 100644 --- a/src/librustdoc/clean/render_macro_matchers.rs +++ b/src/librustdoc/clean/render_macro_matchers.rs @@ -3,6 +3,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast_pretty::pprust::PrintState; use rustc_ast_pretty::pprust::state::State as Printer; use rustc_middle::ty::TyCtxt; +use rustc_parse::lexer::StripTokens; use rustc_session::parse::ParseSess; use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_span::{FileName, Span}; @@ -64,14 +65,18 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option<String // Create a Parser. let psess = ParseSess::new(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec()); let file_name = FileName::macro_expansion_source_code(&snippet); - let mut parser = - match rustc_parse::new_parser_from_source_str(&psess, file_name, snippet.clone()) { - Ok(parser) => parser, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - return None; - } - }; + let mut parser = match rustc_parse::new_parser_from_source_str( + &psess, + file_name, + snippet.clone(), + StripTokens::Nothing, + ) { + Ok(parser) => parser, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + return None; + } + }; // Reparse a single token tree. if parser.token == token::Eof { diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index f229f77c978..5eaadc9eb45 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -10,6 +10,7 @@ use rustc_ast::tokenstream::TokenTree; use rustc_ast::{self as ast, AttrStyle, HasAttrs, StmtKind}; use rustc_errors::emitter::stderr_destination; use rustc_errors::{ColorConfig, DiagCtxtHandle}; +use rustc_parse::lexer::StripTokens; use rustc_parse::new_parser_from_source_str; use rustc_session::parse::ParseSess; use rustc_span::edition::{DEFAULT_EDITION, Edition}; @@ -468,14 +469,16 @@ fn parse_source( let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); let psess = ParseSess::with_dcx(dcx, sm); - let mut parser = match new_parser_from_source_str(&psess, filename, wrapped_source) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(|err| err.cancel()); - reset_error_count(&psess); - return Err(()); - } - }; + // Don't strip any tokens; it wouldn't matter anyway because the source is wrapped in a function. + let mut parser = + match new_parser_from_source_str(&psess, filename, wrapped_source, StripTokens::Nothing) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(|err| err.cancel()); + reset_error_count(&psess); + return Err(()); + } + }; fn push_to_s(s: &mut String, source: &str, span: rustc_span::Span, prev_span_hi: &mut usize) { let extra_len = DOCTEST_CODE_WRAPPER.len(); diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index b01b596da68..3b84ae2bed0 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -3639,7 +3639,7 @@ class DocSearch { if (contains.length === 0) { return 0; } - const maxPathEditDistance = Math.floor( + const maxPathEditDistance = parsedQuery.literalSearch ? 0 : Math.floor( contains.reduce((acc, next) => acc + next.length, 0) / 3, ); let ret_dist = maxPathEditDistance + 1; @@ -3650,7 +3650,9 @@ class DocSearch { let dist_total = 0; for (let x = 0; x < clength; ++x) { const [p, c] = [path[i + x], contains[x]]; - if (Math.floor((p.length - c.length) / 3) <= maxPathEditDistance && + if (parsedQuery.literalSearch && p !== c) { + continue pathiter; + } else if (Math.floor((p.length - c.length) / 3) <= maxPathEditDistance && p.indexOf(c) !== -1 ) { // discount distance on substring match diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs index f70bdf4e4fe..06256d61151 100644 --- a/src/librustdoc/passes/lint/bare_urls.rs +++ b/src/librustdoc/passes/lint/bare_urls.rs @@ -17,7 +17,10 @@ use crate::core::DocContext; use crate::html::markdown::main_body_opts; pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &str) { - let report_diag = |cx: &DocContext<'_>, msg: &'static str, range: Range<usize>| { + let report_diag = |cx: &DocContext<'_>, + msg: &'static str, + range: Range<usize>, + without_brackets: Option<&str>| { let maybe_sp = source_span_for_markdown_range(cx.tcx, dox, &range, &item.attrs.doc_strings) .map(|(sp, _)| sp); let sp = maybe_sp.unwrap_or_else(|| item.attr_span(cx.tcx)); @@ -27,14 +30,22 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & // The fallback of using the attribute span is suitable for // highlighting where the error is, but not for placing the < and > if let Some(sp) = maybe_sp { - lint.multipart_suggestion( - "use an automatic link instead", - vec![ - (sp.shrink_to_lo(), "<".to_string()), - (sp.shrink_to_hi(), ">".to_string()), - ], - Applicability::MachineApplicable, - ); + if let Some(without_brackets) = without_brackets { + lint.multipart_suggestion( + "use an automatic link instead", + vec![(sp, format!("<{without_brackets}>"))], + Applicability::MachineApplicable, + ); + } else { + lint.multipart_suggestion( + "use an automatic link instead", + vec![ + (sp.shrink_to_lo(), "<".to_string()), + (sp.shrink_to_hi(), ">".to_string()), + ], + Applicability::MachineApplicable, + ); + } } }); }; @@ -43,7 +54,7 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & while let Some((event, range)) = p.next() { match event { - Event::Text(s) => find_raw_urls(cx, &s, range, &report_diag), + Event::Text(s) => find_raw_urls(cx, dox, &s, range, &report_diag), // We don't want to check the text inside code blocks or links. Event::Start(tag @ (Tag::CodeBlock(_) | Tag::Link { .. })) => { for (event, _) in p.by_ref() { @@ -67,25 +78,35 @@ static URL_REGEX: LazyLock<Regex> = LazyLock::new(|| { r"https?://", // url scheme r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains r"[a-zA-Z]{2,63}", // root domain - r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)" // optional query or url fragments + r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)", // optional query or url fragments )) .expect("failed to build regex") }); fn find_raw_urls( cx: &DocContext<'_>, + dox: &str, text: &str, range: Range<usize>, - f: &impl Fn(&DocContext<'_>, &'static str, Range<usize>), + f: &impl Fn(&DocContext<'_>, &'static str, Range<usize>, Option<&str>), ) { trace!("looking for raw urls in {text}"); // For now, we only check "full" URLs (meaning, starting with "http://" or "https://"). for match_ in URL_REGEX.find_iter(text) { - let url_range = match_.range(); - f( - cx, - "this URL is not a hyperlink", - Range { start: range.start + url_range.start, end: range.start + url_range.end }, - ); + let mut url_range = match_.range(); + url_range.start += range.start; + url_range.end += range.start; + let mut without_brackets = None; + // If the link is contained inside `[]`, then we need to replace the brackets and + // not just add `<>`. + if dox[..url_range.start].ends_with('[') + && url_range.end <= dox.len() + && dox[url_range.end..].starts_with(']') + { + url_range.start -= 1; + url_range.end += 1; + without_brackets = Some(match_.as_str()); + } + f(cx, "this URL is not a hyperlink", url_range, without_brackets); } } diff --git a/src/llvm-project b/src/llvm-project -Subproject 19f0a49c5c3f4ba88b5e7ac620b9a0d8574c09c +Subproject 333793696b0a08002acac6af983adc7a5233fc5 diff --git a/src/tools/cargo b/src/tools/cargo -Subproject 761c4658d0079d607e6d33cf0c060e61a617cad +Subproject 24bb93c388fb8c211a37986539f24a819dc669d diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs index 74283d7ba86..43bb9723555 100644 --- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs +++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs @@ -8,6 +8,7 @@ use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind}; use rustc_errors::emitter::HumanEmitter; use rustc_errors::{Diag, DiagCtxt}; use rustc_lint::LateContext; +use rustc_parse::lexer::StripTokens; use rustc_parse::new_parser_from_source_str; use rustc_parse::parser::ForceCollect; use rustc_session::parse::ParseSess; @@ -49,13 +50,14 @@ pub fn check( let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let psess = ParseSess::with_dcx(dcx, sm); - let mut parser = match new_parser_from_source_str(&psess, filename, code) { - Ok(p) => p, - Err(errs) => { - errs.into_iter().for_each(Diag::cancel); - return (false, test_attr_spans); - }, - }; + let mut parser = + match new_parser_from_source_str(&psess, filename, code, StripTokens::ShebangAndFrontmatter) { + Ok(p) => p, + Err(errs) => { + errs.into_iter().for_each(Diag::cancel); + return (false, test_attr_spans); + }, + }; let mut relevant_main_found = false; let mut eligible = true; diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index 788a04357b1..cf0c85990b1 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -11,7 +11,7 @@ use rustc_ast::{BinOpKind, LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{Body, Expr, ExprKind}; +use rustc_hir::{Block, Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; @@ -135,12 +135,12 @@ fn assert_len_expr<'hir>( cx: &LateContext<'_>, expr: &'hir Expr<'hir>, ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { - let (cmp, asserted_len, slice_len) = if let Some( - higher::IfLetOrMatch::Match(cond, [_, then], _) - ) = higher::IfLetOrMatch::parse(cx, expr) - && let ExprKind::Binary(bin_op, left, right) = &cond.kind + let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) + && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind + && let ExprKind::Binary(bin_op, left, right) = &condition.kind // check if `then` block has a never type expression - && cx.typeck_results().expr_ty(then.body).is_never() + && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind + && cx.typeck_results().expr_ty(then_expr).is_never() { len_comparison(bin_op.node, left, right)? } else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 14b64eb4d54..e1077bdf4b1 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -2133,17 +2133,11 @@ pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> { } pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool { - cx.tcx - .hir_attrs(hir::CRATE_HIR_ID) - .iter() - .any(|attr| attr.has_name(sym::no_std)) + find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoStd(..)) } pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool { - cx.tcx - .hir_attrs(hir::CRATE_HIR_ID) - .iter() - .any(|attr| attr.has_name(sym::no_core)) + find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoCore(..)) } /// Check if parent of a hir node is a trait implementation block. diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 278101ac27f..8033b74a8d2 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -194,7 +194,6 @@ generate! { itertools, join, kw, - last, lazy_static, lint_vec, ln, diff --git a/src/tools/clippy/tests/ui/const_is_empty.rs b/src/tools/clippy/tests/ui/const_is_empty.rs index 63c6342a323..8bb4f0e5d97 100644 --- a/src/tools/clippy/tests/ui/const_is_empty.rs +++ b/src/tools/clippy/tests/ui/const_is_empty.rs @@ -196,7 +196,6 @@ fn issue_13106() { const { assert!(EMPTY_STR.is_empty()); - //~^ const_is_empty } const { diff --git a/src/tools/clippy/tests/ui/const_is_empty.stderr b/src/tools/clippy/tests/ui/const_is_empty.stderr index 9a42518698e..2ba189058e8 100644 --- a/src/tools/clippy/tests/ui/const_is_empty.stderr +++ b/src/tools/clippy/tests/ui/const_is_empty.stderr @@ -158,16 +158,10 @@ LL | let _ = val.is_empty(); | ^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:198:17 - | -LL | assert!(EMPTY_STR.is_empty()); - | ^^^^^^^^^^^^^^^^^^^^ - -error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:203:9 + --> tests/ui/const_is_empty.rs:202:9 | LL | EMPTY_STR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 28 previous errors +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.rs b/src/tools/clippy/tests/ui/incompatible_msrv.rs index 882f909e30c..f7f21e1850d 100644 --- a/src/tools/clippy/tests/ui/incompatible_msrv.rs +++ b/src/tools/clippy/tests/ui/incompatible_msrv.rs @@ -1,6 +1,6 @@ #![warn(clippy::incompatible_msrv)] #![feature(custom_inner_attributes)] -#![allow(stable_features, clippy::diverging_sub_expression)] +#![allow(stable_features)] #![feature(strict_provenance)] // For use in test #![clippy::msrv = "1.3.0"] diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 517aa343a6d..d433ee8daaa 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -606,6 +606,7 @@ Definite bugs found: * [A bug in the new `RwLock::downgrade` implementation](https://rust-lang.zulipchat.com/#narrow/channel/269128-miri/topic/Miri.20error.20library.20test) (caught by Miri before it landed in the Rust repo) * [Mockall reading uninitialized memory when mocking `std::io::Read::read`, even if all expectations are satisfied](https://github.com/asomers/mockall/issues/647) (caught by Miri running Tokio's test suite) * [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248) +* [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816) Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment): diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr index bcec5557a3f..c7c1a769cda 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr @@ -8,13 +8,13 @@ LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _ = note: inside closure at tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr index 51533cd17a8..ae8b2b936a7 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr @@ -8,13 +8,13 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu = note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr index 63fc7ce346e..dfa9a6e2583 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr @@ -8,13 +8,13 @@ LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu = note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr index d8a3e058da9..47a0ebdcfef 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr @@ -1,5 +1,5 @@ error: Undefined Behavior: trying to join a detached thread - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here @@ -7,7 +7,7 @@ LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle( = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr index 079f01c0e54..3b1181c92fd 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr @@ -9,13 +9,13 @@ LL | assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJ = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr index 70de94f56ba..6eefa2da1d8 100644 --- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr +++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr @@ -8,13 +8,13 @@ LL | assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0 = note: inside closure at tests/fail-dep/concurrency/windows_join_self.rs:LL:CC error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr index 4f3a56fef82..6dd6c36ab65 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr index 5045badaa87..3154197b95e 100644 --- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr index 3713c8da392..1af44c107ff 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr @@ -5,13 +5,13 @@ error: the evaluated program deadlocked = note: BACKTRACE on thread `unnamed-ID`: error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr index 47fc889b001..b85470225c6 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr index 99d242ec7da..bbd2ab1c4d8 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr index f766500d331..c3cc2068173 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr @@ -1,11 +1,11 @@ error: the evaluated program deadlocked - --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC | LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) }; | ^ this thread got stuck here | = note: BACKTRACE: - = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC + = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC note: inside `main` diff --git a/src/tools/miri/tests/pass/static_align.rs b/src/tools/miri/tests/pass/static_align.rs new file mode 100644 index 00000000000..f292f028568 --- /dev/null +++ b/src/tools/miri/tests/pass/static_align.rs @@ -0,0 +1,14 @@ +#![feature(static_align)] + +// When a static uses `align(N)`, its address should be a multiple of `N`. + +#[rustc_align_static(256)] +static FOO: u64 = 0; + +#[rustc_align_static(512)] +static BAR: u64 = 0; + +fn main() { + assert!(core::ptr::from_ref(&FOO).addr().is_multiple_of(256)); + assert!(core::ptr::from_ref(&BAR).addr().is_multiple_of(512)); +} diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs index 2ec8769c45f..63c6c8c99d0 100644 --- a/src/tools/rustfmt/src/parse/parser.rs +++ b/src/tools/rustfmt/src/parse/parser.rs @@ -3,6 +3,7 @@ use std::path::{Path, PathBuf}; use rustc_ast::{ast, attr}; use rustc_errors::Diag; +use rustc_parse::lexer::StripTokens; use rustc_parse::parser::Parser as RawParser; use rustc_parse::{exp, new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_span::{Span, sym}; @@ -64,11 +65,14 @@ impl<'a> ParserBuilder<'a> { input: Input, ) -> Result<RawParser<'a>, Vec<Diag<'a>>> { match input { - Input::File(ref file) => new_parser_from_file(psess, file, None), + Input::File(ref file) => { + new_parser_from_file(psess, file, StripTokens::ShebangAndFrontmatter, None) + } Input::Text(text) => new_parser_from_source_str( psess, rustc_span::FileName::Custom("stdin".to_owned()), text, + StripTokens::ShebangAndFrontmatter, ), } } @@ -104,8 +108,12 @@ impl<'a> Parser<'a> { span: Span, ) -> Result<(ast::AttrVec, ThinVec<Box<ast::Item>>, Span), ParserError> { let result = catch_unwind(AssertUnwindSafe(|| { - let mut parser = - unwrap_or_emit_fatal(new_parser_from_file(psess.inner(), path, Some(span))); + let mut parser = unwrap_or_emit_fatal(new_parser_from_file( + psess.inner(), + path, + StripTokens::ShebangAndFrontmatter, + Some(span), + )); match parser.parse_mod(exp!(Eof)) { Ok((a, i, spans)) => Some((a, i, spans.inner_span)), Err(e) => { diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index 321ef65117e..a6b50b5ca47 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -124,6 +124,12 @@ fn check_impl( }; } + let rerun_with_bless = |mode: &str, action: &str| { + if !bless { + eprintln!("rerun tidy with `--extra-checks={mode} --bless` to {action}"); + } + }; + let python_lint = extra_check!(Py, Lint); let python_fmt = extra_check!(Py, Fmt); let shell_lint = extra_check!(Shell, Lint); @@ -147,14 +153,21 @@ fn check_impl( } if python_lint { - eprintln!("linting python files"); let py_path = py_path.as_ref().unwrap(); - let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &["check".as_ref()]); + let args: &[&OsStr] = if bless { + eprintln!("linting python files and applying suggestions"); + &["check".as_ref(), "--fix".as_ref()] + } else { + eprintln!("linting python files"); + &["check".as_ref()] + }; + + let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, args); - if res.is_err() && show_diff { + if res.is_err() && show_diff && !bless { eprintln!("\npython linting failed! Printing diff suggestions:"); - let _ = run_ruff( + let diff_res = run_ruff( root_path, outdir, py_path, @@ -162,6 +175,10 @@ fn check_impl( &file_args, &["check".as_ref(), "--diff".as_ref()], ); + // `ruff check --diff` will return status 0 if there are no suggestions. + if diff_res.is_err() { + rerun_with_bless("py:lint", "apply ruff suggestions"); + } } // Rethrow error res?; @@ -192,7 +209,7 @@ fn check_impl( &["format".as_ref(), "--diff".as_ref()], ); } - eprintln!("rerun tidy with `--extra-checks=py:fmt --bless` to reformat Python code"); + rerun_with_bless("py:fmt", "reformat Python code"); } // Rethrow error @@ -225,7 +242,7 @@ fn check_impl( let args = merge_args(&cfg_args_clang_format, &file_args_clang_format); let res = py_runner(py_path.as_ref().unwrap(), false, None, "clang-format", &args); - if res.is_err() && show_diff { + if res.is_err() && show_diff && !bless { eprintln!("\nclang-format linting failed! Printing diff suggestions:"); let mut cfg_args_clang_format_diff = cfg_args.clone(); @@ -265,6 +282,7 @@ fn check_impl( ); } } + rerun_with_bless("cpp:fmt", "reformat C++ code"); } // Rethrow error res?; @@ -290,12 +308,16 @@ fn check_impl( args.extend_from_slice(SPELLCHECK_DIRS); if bless { - eprintln!("spellcheck files and fix"); + eprintln!("spellchecking files and fixing typos"); args.push("--write-changes"); } else { - eprintln!("spellcheck files"); + eprintln!("spellchecking files"); } - spellcheck_runner(root_path, &outdir, &cargo, &args)?; + let res = spellcheck_runner(root_path, &outdir, &cargo, &args); + if res.is_err() { + rerun_with_bless("spellcheck", "fix typos"); + } + res?; } if js_lint || js_typecheck { @@ -303,11 +325,21 @@ fn check_impl( } if js_lint { - rustdoc_js::lint(outdir, librustdoc_path, tools_path, bless)?; + if bless { + eprintln!("linting javascript files"); + } else { + eprintln!("linting javascript files and applying suggestions"); + } + let res = rustdoc_js::lint(outdir, librustdoc_path, tools_path, bless); + if res.is_err() { + rerun_with_bless("js:lint", "apply eslint suggestions"); + } + res?; rustdoc_js::es_check(outdir, librustdoc_path)?; } if js_typecheck { + eprintln!("typechecking javascript files"); rustdoc_js::typecheck(outdir, librustdoc_path)?; } diff --git a/src/tools/tidy/src/extra_checks/rustdoc_js.rs b/src/tools/tidy/src/extra_checks/rustdoc_js.rs index a6c66b8be80..5137e618367 100644 --- a/src/tools/tidy/src/extra_checks/rustdoc_js.rs +++ b/src/tools/tidy/src/extra_checks/rustdoc_js.rs @@ -57,7 +57,7 @@ fn run_eslint( if exit_status.success() { return Ok(()); } - Err(super::Error::FailedCheck("eslint command failed")) + Err(super::Error::FailedCheck("eslint")) } Err(error) => Err(super::Error::Generic(format!("eslint command failed: {error:?}"))), } @@ -94,7 +94,7 @@ pub(super) fn typecheck(outdir: &Path, librustdoc_path: &Path) -> Result<(), sup if exit_status.success() { return Ok(()); } - Err(super::Error::FailedCheck("tsc command failed")) + Err(super::Error::FailedCheck("tsc")) } Err(error) => Err(super::Error::Generic(format!("tsc command failed: {error:?}"))), } @@ -112,7 +112,7 @@ pub(super) fn es_check(outdir: &Path, librustdoc_path: &Path) -> Result<(), supe if exit_status.success() { return Ok(()); } - Err(super::Error::FailedCheck("es-check command failed")) + Err(super::Error::FailedCheck("es-check")) } Err(error) => Err(super::Error::Generic(format!("es-check command failed: {error:?}"))), } diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index 23dc86998e8..3def2391a13 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.16" +wasm-component-ld = "0.5.17" diff --git a/tests/assembly-llvm/c-variadic-arm.rs b/tests/assembly-llvm/c-variadic-arm.rs new file mode 100644 index 00000000000..2ef307405e1 --- /dev/null +++ b/tests/assembly-llvm/c-variadic-arm.rs @@ -0,0 +1,26 @@ +//@ assembly-output: emit-asm +//@ compile-flags: -Copt-level=3 +//@ only-arm +//@ ignore-thumb +//@ ignore-android +#![no_std] +#![crate_type = "lib"] +#![feature(c_variadic)] + +// Check that the assembly that rustc generates matches what clang emits. + +#[unsafe(no_mangle)] +unsafe extern "C" fn variadic(a: f64, mut args: ...) -> f64 { + // CHECK-LABEL: variadic + // CHECK: sub sp, sp + + // CHECK: vldr + // CHECK: vadd.f64 + // CHECK: vldr + // CHECK: vadd.f64 + let b = args.arg::<f64>(); + let c = args.arg::<f64>(); + a + b + c + + // CHECK: add sp, sp +} diff --git a/tests/codegen-llvm/align-static.rs b/tests/codegen-llvm/align-static.rs new file mode 100644 index 00000000000..53db998919a --- /dev/null +++ b/tests/codegen-llvm/align-static.rs @@ -0,0 +1,31 @@ +//@ compile-flags: -C no-prepopulate-passes -Z mir-opt-level=0 + +#![crate_type = "lib"] +#![feature(static_align)] + +// CHECK: @STATIC_ALIGN = +// CHECK-SAME: align 16 +#[no_mangle] +#[rustc_align_static(16)] +pub static STATIC_ALIGN: u64 = 0; + +// CHECK: @ALIGN_SPECIFIED_TWICE_1 = +// CHECK-SAME: align 64 +#[no_mangle] +#[rustc_align_static(32)] +#[rustc_align_static(64)] +pub static ALIGN_SPECIFIED_TWICE_1: u64 = 0; + +// CHECK: @ALIGN_SPECIFIED_TWICE_2 = +// CHECK-SAME: align 128 +#[no_mangle] +#[rustc_align_static(128)] +#[rustc_align_static(32)] +pub static ALIGN_SPECIFIED_TWICE_2: u64 = 0; + +// CHECK: @ALIGN_SPECIFIED_TWICE_3 = +// CHECK-SAME: align 256 +#[no_mangle] +#[rustc_align_static(32)] +#[rustc_align_static(256)] +pub static ALIGN_SPECIFIED_TWICE_3: u64 = 0; diff --git a/tests/codegen-llvm/c-variadic-lifetime.rs b/tests/codegen-llvm/c-variadic-lifetime.rs new file mode 100644 index 00000000000..5b2f8af18c8 --- /dev/null +++ b/tests/codegen-llvm/c-variadic-lifetime.rs @@ -0,0 +1,21 @@ +//@ add-core-stubs +//@ compile-flags: -Copt-level=3 +#![feature(c_variadic)] +#![crate_type = "lib"] + +// Check that `%args` explicitly has its lifetime start and end. Being explicit can improve +// instruction and register selection, see e.g. https://github.com/rust-lang/rust/pull/144549 + +#[unsafe(no_mangle)] +unsafe extern "C" fn variadic(a: f64, mut args: ...) -> f64 { + // CHECK: call void @llvm.lifetime.start.p0(i64 {{[0-9]+}}, ptr nonnull %args) + // CHECK: call void @llvm.va_start.p0(ptr nonnull %args) + + let b = args.arg::<f64>(); + let c = args.arg::<f64>(); + + a + b + c + + // CHECK: call void @llvm.va_end.p0(ptr nonnull %args) + // CHECK: call void @llvm.lifetime.end.p0(i64 {{[0-9]+}}, ptr nonnull %args) +} diff --git a/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-abort.mir b/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-abort.mir index 7be3ab8cbae..8e47aabb9b9 100644 --- a/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-abort.mir +++ b/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-abort.mir @@ -1,4 +1,4 @@ -// MIR for `drop_in_place` after SimplifyCfg-make_shim +// MIR for `std::ptr::drop_in_place` after SimplifyCfg-make_shim fn drop_in_place(_1: *mut Test) -> () { let mut _0: (); diff --git a/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-unwind.mir b/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-unwind.mir index 6c3c1aaa2bd..2457405d996 100644 --- a/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-unwind.mir +++ b/tests/mir-opt/retag.core.ptr-drop_in_place.Test.SimplifyCfg-make_shim.after.panic-unwind.mir @@ -1,4 +1,4 @@ -// MIR for `drop_in_place` after SimplifyCfg-make_shim +// MIR for `std::ptr::drop_in_place` after SimplifyCfg-make_shim fn drop_in_place(_1: *mut Test) -> () { let mut _0: (); diff --git a/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String;42].AddMovesForPackedDrops.before.mir b/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String;42].AddMovesForPackedDrops.before.mir index 9d5af8e84e4..ed3f4788cea 100644 --- a/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String;42].AddMovesForPackedDrops.before.mir +++ b/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String;42].AddMovesForPackedDrops.before.mir @@ -1,4 +1,4 @@ -// MIR for `drop_in_place` before AddMovesForPackedDrops +// MIR for `std::ptr::drop_in_place` before AddMovesForPackedDrops fn drop_in_place(_1: *mut [String; 42]) -> () { let mut _0: (); diff --git a/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir b/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir index 144880d1598..bee671af6df 100644 --- a/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir +++ b/tests/mir-opt/slice_drop_shim.core.ptr-drop_in_place.[String].AddMovesForPackedDrops.before.mir @@ -1,4 +1,4 @@ -// MIR for `drop_in_place` before AddMovesForPackedDrops +// MIR for `std::ptr::drop_in_place` before AddMovesForPackedDrops fn drop_in_place(_1: *mut [String]) -> () { let mut _0: (); diff --git a/tests/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir b/tests/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir index 51ef9f7c068..1bdb1c1debd 100644 --- a/tests/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir +++ b/tests/mir-opt/unusual_item_types.core.ptr-drop_in_place.Vec_i32_.AddMovesForPackedDrops.before.mir @@ -1,4 +1,4 @@ -// MIR for `drop_in_place` before AddMovesForPackedDrops +// MIR for `std::ptr::drop_in_place` before AddMovesForPackedDrops fn drop_in_place(_1: *mut Vec<i32>) -> () { let mut _0: (); diff --git a/tests/rustdoc-js/literal-path.js b/tests/rustdoc-js/literal-path.js new file mode 100644 index 00000000000..ea999ea1a6f --- /dev/null +++ b/tests/rustdoc-js/literal-path.js @@ -0,0 +1,23 @@ +// exact-check + +// This test ensures that literal search is always applied on elements of the path. + +const EXPECTED = [ + { + 'query': '"some::path"', + 'others': [ + { 'path': 'literal_path::some', 'name': 'Path' }, + ], + }, + { + 'query': '"somea::path"', + 'others': [ + { 'path': 'literal_path::somea', 'name': 'Path' }, + ], + }, + { + 'query': '"soma::path"', + 'others': [ + ], + }, +]; diff --git a/tests/rustdoc-js/literal-path.rs b/tests/rustdoc-js/literal-path.rs new file mode 100644 index 00000000000..fa7685fd966 --- /dev/null +++ b/tests/rustdoc-js/literal-path.rs @@ -0,0 +1,7 @@ +pub mod some { + pub struct Path; +} + +pub mod somea { + pub struct Path; +} diff --git a/tests/rustdoc-ui/lints/bare-urls.fixed b/tests/rustdoc-ui/lints/bare-urls.fixed index a91573146b8..ac63e291c5b 100644 --- a/tests/rustdoc-ui/lints/bare-urls.fixed +++ b/tests/rustdoc-ui/lints/bare-urls.fixed @@ -68,3 +68,17 @@ pub mod foo { /// https://somewhere.com/a?hello=12&bye=11#xyz pub fn bar() {} } + +/// <https://bloob.blob> +//~^ ERROR this URL is not a hyperlink +/// [ <https://bloob.blob> ] +//~^ ERROR this URL is not a hyperlink +/// [ <https://bloob.blob>] +//~^ ERROR this URL is not a hyperlink +/// [<https://bloob.blob> ] +//~^ ERROR this URL is not a hyperlink +/// [<https://bloob.blob> +//~^ ERROR this URL is not a hyperlink +/// <https://bloob.blob>] +//~^ ERROR this URL is not a hyperlink +pub fn lint_with_brackets() {} diff --git a/tests/rustdoc-ui/lints/bare-urls.rs b/tests/rustdoc-ui/lints/bare-urls.rs index 5b008cdafa2..a70a3ec822e 100644 --- a/tests/rustdoc-ui/lints/bare-urls.rs +++ b/tests/rustdoc-ui/lints/bare-urls.rs @@ -68,3 +68,17 @@ pub mod foo { /// https://somewhere.com/a?hello=12&bye=11#xyz pub fn bar() {} } + +/// [https://bloob.blob] +//~^ ERROR this URL is not a hyperlink +/// [ https://bloob.blob ] +//~^ ERROR this URL is not a hyperlink +/// [ https://bloob.blob] +//~^ ERROR this URL is not a hyperlink +/// [https://bloob.blob ] +//~^ ERROR this URL is not a hyperlink +/// [https://bloob.blob +//~^ ERROR this URL is not a hyperlink +/// https://bloob.blob] +//~^ ERROR this URL is not a hyperlink +pub fn lint_with_brackets() {} diff --git a/tests/rustdoc-ui/lints/bare-urls.stderr b/tests/rustdoc-ui/lints/bare-urls.stderr index e1108c7e7f8..fc3c642f5aa 100644 --- a/tests/rustdoc-ui/lints/bare-urls.stderr +++ b/tests/rustdoc-ui/lints/bare-urls.stderr @@ -243,5 +243,73 @@ help: use an automatic link instead LL | #[doc = "<https://example.com/raw>"] | + + -error: aborting due to 20 previous errors +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:72:5 + | +LL | /// [https://bloob.blob] + | ^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `<https://bloob.blob>` + | + = note: bare URLs are not automatically turned into clickable links + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:74:7 + | +LL | /// [ https://bloob.blob ] + | ^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | /// [ <https://bloob.blob> ] + | + + + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:76:7 + | +LL | /// [ https://bloob.blob] + | ^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | /// [ <https://bloob.blob>] + | + + + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:78:6 + | +LL | /// [https://bloob.blob ] + | ^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | /// [<https://bloob.blob> ] + | + + + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:80:6 + | +LL | /// [https://bloob.blob + | ^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | /// [<https://bloob.blob> + | + + + +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:82:5 + | +LL | /// https://bloob.blob] + | ^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | /// <https://bloob.blob>] + | + + + +error: aborting due to 26 previous errors diff --git a/tests/ui-fulldeps/auxiliary/parser.rs b/tests/ui-fulldeps/auxiliary/parser.rs index 6726969350d..6ee39e5130f 100644 --- a/tests/ui-fulldeps/auxiliary/parser.rs +++ b/tests/ui-fulldeps/auxiliary/parser.rs @@ -10,7 +10,7 @@ extern crate rustc_span; use rustc_ast::ast::{AttrKind, Attribute, DUMMY_NODE_ID, Expr}; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::node_id::NodeId; -use rustc_ast::token::{self, Token}; +use rustc_ast::token; use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree, LazyAttrTokenStream}; use rustc_errors::Diag; use rustc_parse::parser::Recovery; @@ -23,6 +23,7 @@ pub fn parse_expr(psess: &ParseSess, source_code: &str) -> Option<Box<Expr>> { psess, FileName::anon_source_code(source_code), source_code.to_owned(), + rustc_parse::lexer::StripTokens::Nothing, )); let mut parser = parser.recovery(Recovery::Forbidden); diff --git a/tests/ui-fulldeps/mod_dir_path_canonicalized.rs b/tests/ui-fulldeps/mod_dir_path_canonicalized.rs index 99cb5fc5aa1..df5f29e35fe 100644 --- a/tests/ui-fulldeps/mod_dir_path_canonicalized.rs +++ b/tests/ui-fulldeps/mod_dir_path_canonicalized.rs @@ -16,7 +16,7 @@ extern crate rustc_span; #[allow(unused_extern_crates)] extern crate rustc_driver; -use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal}; +use rustc_parse::{lexer::StripTokens, new_parser_from_file, unwrap_or_emit_fatal}; use rustc_session::parse::ParseSess; use std::path::Path; @@ -34,6 +34,11 @@ fn parse() { let path = Path::new(file!()); let path = path.canonicalize().unwrap(); - let mut parser = unwrap_or_emit_fatal(new_parser_from_file(&psess, &path, None)); + let mut parser = unwrap_or_emit_fatal(new_parser_from_file( + &psess, + &path, + StripTokens::ShebangAndFrontmatter, + None, + )); let _ = parser.parse_crate_mod(); } diff --git a/tests/ui/abi/simd-abi-checks-s390x.rs b/tests/ui/abi/simd-abi-checks-s390x.rs index 877a25e8b08..75232a66ab0 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.rs +++ b/tests/ui/abi/simd-abi-checks-s390x.rs @@ -8,6 +8,7 @@ // FIXME: +soft-float itself doesn't set -vector //@[z13_soft_float] compile-flags: --target s390x-unknown-linux-gnu -C target-cpu=z13 -C target-feature=-vector,+soft-float //@[z13_soft_float] needs-llvm-components: systemz +//[z13_soft_float]~? WARN must be disabled to ensure that the ABI of the current target can be implemented correctly #![feature(no_core, repr_simd, s390x_target_feature)] #![no_core] diff --git a/tests/ui/abi/simd-abi-checks-s390x.z10.stderr b/tests/ui/abi/simd-abi-checks-s390x.z10.stderr index c1c4e90f3cf..e1cfa373c63 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.z10.stderr +++ b/tests/ui/abi/simd-abi-checks-s390x.z10.stderr @@ -1,5 +1,5 @@ error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:38:1 + --> $DIR/simd-abi-checks-s390x.rs:39:1 | LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -7,7 +7,7 @@ LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:43:1 + --> $DIR/simd-abi-checks-s390x.rs:44:1 | LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -15,7 +15,7 @@ LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper<i8x8>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:89:1 + --> $DIR/simd-abi-checks-s390x.rs:90:1 | LL | / extern "C" fn vector_transparent_wrapper_ret_small( LL | | x: &TransparentWrapper<i8x8>, @@ -25,7 +25,7 @@ LL | | ) -> TransparentWrapper<i8x8> { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper<i8x16>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:96:1 + --> $DIR/simd-abi-checks-s390x.rs:97:1 | LL | / extern "C" fn vector_transparent_wrapper_ret( LL | | x: &TransparentWrapper<i8x16>, @@ -35,7 +35,7 @@ LL | | ) -> TransparentWrapper<i8x16> { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:111:1 + --> $DIR/simd-abi-checks-s390x.rs:112:1 | LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -43,7 +43,7 @@ LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 + --> $DIR/simd-abi-checks-s390x.rs:117:1 | LL | extern "C" fn vector_arg(x: i8x16) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -51,7 +51,7 @@ LL | extern "C" fn vector_arg(x: i8x16) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper<i8x8>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:127:1 + --> $DIR/simd-abi-checks-s390x.rs:128:1 | LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -59,7 +59,7 @@ LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper<i8x16>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:132:1 + --> $DIR/simd-abi-checks-s390x.rs:133:1 | LL | extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -67,7 +67,7 @@ LL | extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper<i8x8>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:143:1 + --> $DIR/simd-abi-checks-s390x.rs:144:1 | LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -75,7 +75,7 @@ LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper<i8x16>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:148:1 + --> $DIR/simd-abi-checks-s390x.rs:149:1 | LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here diff --git a/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr b/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr index c1c4e90f3cf..e1cfa373c63 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr +++ b/tests/ui/abi/simd-abi-checks-s390x.z13_no_vector.stderr @@ -1,5 +1,5 @@ error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:38:1 + --> $DIR/simd-abi-checks-s390x.rs:39:1 | LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -7,7 +7,7 @@ LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:43:1 + --> $DIR/simd-abi-checks-s390x.rs:44:1 | LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -15,7 +15,7 @@ LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper<i8x8>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:89:1 + --> $DIR/simd-abi-checks-s390x.rs:90:1 | LL | / extern "C" fn vector_transparent_wrapper_ret_small( LL | | x: &TransparentWrapper<i8x8>, @@ -25,7 +25,7 @@ LL | | ) -> TransparentWrapper<i8x8> { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper<i8x16>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:96:1 + --> $DIR/simd-abi-checks-s390x.rs:97:1 | LL | / extern "C" fn vector_transparent_wrapper_ret( LL | | x: &TransparentWrapper<i8x16>, @@ -35,7 +35,7 @@ LL | | ) -> TransparentWrapper<i8x16> { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:111:1 + --> $DIR/simd-abi-checks-s390x.rs:112:1 | LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -43,7 +43,7 @@ LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 + --> $DIR/simd-abi-checks-s390x.rs:117:1 | LL | extern "C" fn vector_arg(x: i8x16) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -51,7 +51,7 @@ LL | extern "C" fn vector_arg(x: i8x16) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper<i8x8>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:127:1 + --> $DIR/simd-abi-checks-s390x.rs:128:1 | LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -59,7 +59,7 @@ LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper<i8x16>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:132:1 + --> $DIR/simd-abi-checks-s390x.rs:133:1 | LL | extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -67,7 +67,7 @@ LL | extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper<i8x8>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:143:1 + --> $DIR/simd-abi-checks-s390x.rs:144:1 | LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -75,7 +75,7 @@ LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper<i8x16>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:148:1 + --> $DIR/simd-abi-checks-s390x.rs:149:1 | LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here diff --git a/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr b/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr index c1c4e90f3cf..577fcc23bf1 100644 --- a/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr +++ b/tests/ui/abi/simd-abi-checks-s390x.z13_soft_float.stderr @@ -1,5 +1,10 @@ +warning: target feature `soft-float` must be disabled to ensure that the ABI of the current target can be implemented correctly + | + = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344> + error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:38:1 + --> $DIR/simd-abi-checks-s390x.rs:39:1 | LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -7,7 +12,7 @@ LL | extern "C" fn vector_ret_small(x: &i8x8) -> i8x8 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:43:1 + --> $DIR/simd-abi-checks-s390x.rs:44:1 | LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -15,7 +20,7 @@ LL | extern "C" fn vector_ret(x: &i8x16) -> i8x16 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper<i8x8>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:89:1 + --> $DIR/simd-abi-checks-s390x.rs:90:1 | LL | / extern "C" fn vector_transparent_wrapper_ret_small( LL | | x: &TransparentWrapper<i8x8>, @@ -25,7 +30,7 @@ LL | | ) -> TransparentWrapper<i8x8> { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper<i8x16>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:96:1 + --> $DIR/simd-abi-checks-s390x.rs:97:1 | LL | / extern "C" fn vector_transparent_wrapper_ret( LL | | x: &TransparentWrapper<i8x16>, @@ -35,7 +40,7 @@ LL | | ) -> TransparentWrapper<i8x16> { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x8` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:111:1 + --> $DIR/simd-abi-checks-s390x.rs:112:1 | LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -43,7 +48,7 @@ LL | extern "C" fn vector_arg_small(x: i8x8) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `i8x16` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:116:1 + --> $DIR/simd-abi-checks-s390x.rs:117:1 | LL | extern "C" fn vector_arg(x: i8x16) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -51,7 +56,7 @@ LL | extern "C" fn vector_arg(x: i8x16) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper<i8x8>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:127:1 + --> $DIR/simd-abi-checks-s390x.rs:128:1 | LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -59,7 +64,7 @@ LL | extern "C" fn vector_wrapper_arg_small(x: Wrapper<i8x8>) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `Wrapper<i8x16>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:132:1 + --> $DIR/simd-abi-checks-s390x.rs:133:1 | LL | extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -67,7 +72,7 @@ LL | extern "C" fn vector_wrapper_arg(x: Wrapper<i8x16>) -> i64 { = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper<i8x8>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:143:1 + --> $DIR/simd-abi-checks-s390x.rs:144:1 | LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8x8>) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here @@ -75,12 +80,12 @@ LL | extern "C" fn vector_transparent_wrapper_arg_small(x: TransparentWrapper<i8 = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) error: this function definition uses SIMD vector type `TransparentWrapper<i8x16>` which (with the chosen ABI) requires the `vector` target feature, which is not enabled - --> $DIR/simd-abi-checks-s390x.rs:148:1 + --> $DIR/simd-abi-checks-s390x.rs:149:1 | LL | extern "C" fn vector_transparent_wrapper_arg(x: TransparentWrapper<i8x16>) -> i64 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here | = help: consider enabling it globally (`-C target-feature=+vector`) or locally (`#[target_feature(enable="vector")]`) -error: aborting due to 10 previous errors +error: aborting due to 10 previous errors; 1 warning emitted diff --git a/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.rs b/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.rs index ea67831b68e..a13a255d536 100644 --- a/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.rs +++ b/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.rs @@ -1,21 +1,22 @@ // Demonstrates and records a theoretical regressions / breaking changes caused by the // introduction of async trait bounds. -// Setting the edition to 2018 since we don't regress `demo! { dyn async }` in Rust <2018. +// Setting the edition to >2015 since we didn't regress `demo! { dyn async }` in Rust 2015. //@ edition:2018 macro_rules! demo { - ($ty:ty) => { compile_error!("ty"); }; + ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! //~^ ERROR ty //~| ERROR ty - (impl $c:ident Trait) => {}; - (dyn $c:ident Trait) => {}; + + // DON'T MODIFY THE MATCHERS BELOW UNLESS THE ASYNC TRAIT MODIFIER SYNTAX CHANGES! + + (impl $c:ident Trait) => { /* KEEP THIS EMPTY! */ }; + (dyn $c:ident Trait) => { /* KEEP THIS EMPTY! */ }; } -demo! { impl async Trait } -//~^ ERROR `async` trait bounds are unstable +demo! { impl async Trait } //~ ERROR `async` trait bounds are unstable -demo! { dyn async Trait } -//~^ ERROR `async` trait bounds are unstable +demo! { dyn async Trait } //~ ERROR `async` trait bounds are unstable fn main() {} diff --git a/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.stderr b/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.stderr index 6c3044e64d2..0292c53fb1c 100644 --- a/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.stderr +++ b/tests/ui/async-await/async-fn/macro-async-trait-bound-theoretical-regression.stderr @@ -1,7 +1,7 @@ error: ty --> $DIR/macro-async-trait-bound-theoretical-regression.rs:8:19 | -LL | ($ty:ty) => { compile_error!("ty"); }; +LL | ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! | ^^^^^^^^^^^^^^^^^^^^ ... LL | demo! { impl async Trait } @@ -12,7 +12,7 @@ LL | demo! { impl async Trait } error: ty --> $DIR/macro-async-trait-bound-theoretical-regression.rs:8:19 | -LL | ($ty:ty) => { compile_error!("ty"); }; +LL | ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! | ^^^^^^^^^^^^^^^^^^^^ ... LL | demo! { dyn async Trait } @@ -21,7 +21,7 @@ LL | demo! { dyn async Trait } = note: this error originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: `async` trait bounds are unstable - --> $DIR/macro-async-trait-bound-theoretical-regression.rs:15:14 + --> $DIR/macro-async-trait-bound-theoretical-regression.rs:18:14 | LL | demo! { impl async Trait } | ^^^^^ @@ -32,7 +32,7 @@ LL | demo! { impl async Trait } = help: use the desugared name of the async trait, such as `AsyncFn` error[E0658]: `async` trait bounds are unstable - --> $DIR/macro-async-trait-bound-theoretical-regression.rs:18:13 + --> $DIR/macro-async-trait-bound-theoretical-regression.rs:20:13 | LL | demo! { dyn async Trait } | ^^^^^ diff --git a/tests/ui/attributes/malformed-no-std.rs b/tests/ui/attributes/malformed-no-std.rs new file mode 100644 index 00000000000..528ecb549b0 --- /dev/null +++ b/tests/ui/attributes/malformed-no-std.rs @@ -0,0 +1,25 @@ +#![feature(no_core)] +// these all still apply no_std and then later error +#![no_std = "foo"] +//~^ ERROR malformed `no_std` attribute input +#![no_std("bar")] +//~^ ERROR malformed `no_std` attribute input +#![no_std(foo = "bar")] +//~^ ERROR malformed `no_std` attribute input +#![no_core = "foo"] +//~^ ERROR malformed `no_core` attribute input +#![no_core("bar")] +//~^ ERROR malformed `no_core` attribute input +#![no_core(foo = "bar")] +//~^ ERROR malformed `no_core` attribute input + +#[deny(unused_attributes)] +#[no_std] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: +#[no_core] +//~^ ERROR crate-level attribute should be an inner attribute: add an exclamation mark: +// to fix compilation +extern crate core; +extern crate std; + +fn main() {} diff --git a/tests/ui/attributes/malformed-no-std.stderr b/tests/ui/attributes/malformed-no-std.stderr new file mode 100644 index 00000000000..322d5f03e41 --- /dev/null +++ b/tests/ui/attributes/malformed-no-std.stderr @@ -0,0 +1,86 @@ +error[E0565]: malformed `no_std` attribute input + --> $DIR/malformed-no-std.rs:3:1 + | +LL | #![no_std = "foo"] + | ^^^^^^^^^^-------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![no_std]` + +error[E0565]: malformed `no_std` attribute input + --> $DIR/malformed-no-std.rs:5:1 + | +LL | #![no_std("bar")] + | ^^^^^^^^^-------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![no_std]` + +error[E0565]: malformed `no_std` attribute input + --> $DIR/malformed-no-std.rs:7:1 + | +LL | #![no_std(foo = "bar")] + | ^^^^^^^^^-------------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![no_std]` + +error[E0565]: malformed `no_core` attribute input + --> $DIR/malformed-no-std.rs:9:1 + | +LL | #![no_core = "foo"] + | ^^^^^^^^^^^-------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![no_core]` + +error[E0565]: malformed `no_core` attribute input + --> $DIR/malformed-no-std.rs:11:1 + | +LL | #![no_core("bar")] + | ^^^^^^^^^^-------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![no_core]` + +error[E0565]: malformed `no_core` attribute input + --> $DIR/malformed-no-std.rs:13:1 + | +LL | #![no_core(foo = "bar")] + | ^^^^^^^^^^-------------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#![no_core]` + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` + --> $DIR/malformed-no-std.rs:17:1 + | +LL | #[no_std] + | ^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this extern crate + --> $DIR/malformed-no-std.rs:22:1 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/malformed-no-std.rs:16:8 + | +LL | #[deny(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + +error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_core]` + --> $DIR/malformed-no-std.rs:19:1 + | +LL | #[no_core] + | ^^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this extern crate + --> $DIR/malformed-no-std.rs:22:1 + | +LL | extern crate core; + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0565`. diff --git a/tests/ui/attributes/malformed-static-align.rs b/tests/ui/attributes/malformed-static-align.rs new file mode 100644 index 00000000000..305d8acf8af --- /dev/null +++ b/tests/ui/attributes/malformed-static-align.rs @@ -0,0 +1,17 @@ +#![feature(static_align)] +#![crate_type = "lib"] + +#[rustc_align_static = 16] //~ ERROR malformed `rustc_align_static` attribute input +static S1: () = (); + +#[rustc_align_static("hello")] //~ ERROR invalid alignment value: not an unsuffixed integer +static S2: () = (); + +#[rustc_align_static(0)] //~ ERROR invalid alignment value: not a power of two +static S3: () = (); + +#[repr(align(16))] //~ ERROR `#[repr(align(...))]` is not supported on static +static S4: () = (); + +#[rustc_align_static(16)] //~ ERROR `#[rustc_align_static]` attribute cannot be used on structs +struct Struct1; diff --git a/tests/ui/attributes/malformed-static-align.stderr b/tests/ui/attributes/malformed-static-align.stderr new file mode 100644 index 00000000000..35f654d3990 --- /dev/null +++ b/tests/ui/attributes/malformed-static-align.stderr @@ -0,0 +1,45 @@ +error[E0539]: malformed `rustc_align_static` attribute input + --> $DIR/malformed-static-align.rs:4:1 + | +LL | #[rustc_align_static = 16] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[rustc_align_static(<alignment in bytes>)]` + +error[E0589]: invalid alignment value: not an unsuffixed integer + --> $DIR/malformed-static-align.rs:7:22 + | +LL | #[rustc_align_static("hello")] + | ^^^^^^^ + +error[E0589]: invalid alignment value: not a power of two + --> $DIR/malformed-static-align.rs:10:22 + | +LL | #[rustc_align_static(0)] + | ^ + +error: `#[rustc_align_static]` attribute cannot be used on structs + --> $DIR/malformed-static-align.rs:16:1 + | +LL | #[rustc_align_static(16)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_align_static]` can be applied to statics and foreign statics + +error: `#[repr(align(...))]` is not supported on statics + --> $DIR/malformed-static-align.rs:13:8 + | +LL | #[repr(align(16))] + | ^^^^^^^^^ + | +help: use `#[rustc_align_static(...)]` instead + --> $DIR/malformed-static-align.rs:13:8 + | +LL | #[repr(align(16))] + | ^^^^^^^^^ + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0539, E0589. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/c-variadic/issue-86053-1.rs b/tests/ui/c-variadic/issue-86053-1.rs index 537d0263adf..58dfee55cf8 100644 --- a/tests/ui/c-variadic/issue-86053-1.rs +++ b/tests/ui/c-variadic/issue-86053-1.rs @@ -13,6 +13,6 @@ fn ordering4 < 'a , 'b > ( a : , self , self , self , //~| ERROR unexpected `self` parameter in function //~| ERROR unexpected `self` parameter in function //~| ERROR `...` must be the last argument of a C-variadic function - //~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~| ERROR `...` is not supported for non-extern functions //~| ERROR cannot find type `F` in this scope } diff --git a/tests/ui/c-variadic/issue-86053-1.stderr b/tests/ui/c-variadic/issue-86053-1.stderr index b58016b5a81..adbc04d3a65 100644 --- a/tests/ui/c-variadic/issue-86053-1.stderr +++ b/tests/ui/c-variadic/issue-86053-1.stderr @@ -46,11 +46,13 @@ error: `...` must be the last argument of a C-variadic function LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: `...` is not supported for non-extern functions --> $DIR/issue-86053-1.rs:11:36 | LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error[E0412]: cannot find type `F` in this scope --> $DIR/issue-86053-1.rs:11:48 diff --git a/tests/ui/c-variadic/not-async.rs b/tests/ui/c-variadic/not-async.rs new file mode 100644 index 00000000000..45a7e1f8972 --- /dev/null +++ b/tests/ui/c-variadic/not-async.rs @@ -0,0 +1,7 @@ +//@ edition: 2021 +#![feature(c_variadic)] +#![crate_type = "lib"] + +async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} +//~^ ERROR functions cannot be both `async` and C-variadic +//~| ERROR hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds diff --git a/tests/ui/c-variadic/not-async.stderr b/tests/ui/c-variadic/not-async.stderr new file mode 100644 index 00000000000..b8caf0d8bd8 --- /dev/null +++ b/tests/ui/c-variadic/not-async.stderr @@ -0,0 +1,19 @@ +error: functions cannot be both `async` and C-variadic + --> $DIR/not-async.rs:5:1 + | +LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} + | ^^^^^ `async` because of this ^^^ C-variadic because of this + +error[E0700]: hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds + --> $DIR/not-async.rs:5:59 + | +LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} + | --------------------------------------------------------- ^^ + | | + | opaque type defined here + | + = note: hidden type `{async fn body of cannot_be_async()}` captures lifetime `'_` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index ad4fca8252d..eb6f4a32365 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -53,9 +53,7 @@ extern "cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait { x } -extern "cmse-nonsecure-entry" fn static_trait_object( - x: &'static dyn Trait, -) -> &'static dyn Trait { +extern "cmse-nonsecure-entry" fn static_trait_object(x: &'static dyn Trait) -> &'static dyn Trait { //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798] x } @@ -63,14 +61,12 @@ extern "cmse-nonsecure-entry" fn static_trait_object( #[repr(transparent)] struct WrapperTransparent<'a>(&'a dyn Trait); -extern "cmse-nonsecure-entry" fn wrapped_trait_object( - x: WrapperTransparent, -) -> WrapperTransparent { +extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> WrapperTransparent { //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798] x } -extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + //~^ ERROR `...` is not supported for `extern "cmse-nonsecure-entry"` functions //~| ERROR requires `va_list` lang_item } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index 7aeb6969feb..8937def9428 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -1,8 +1,12 @@ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/generics.rs:73:53 +error: `...` is not supported for `extern "cmse-nonsecure-entry"` functions + --> $DIR/generics.rs:69:60 | -LL | extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - | ^^^^^^ +LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + | ----------------------------- ^^^^^^ + | | + | `extern "cmse-nonsecure-entry"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type --> $DIR/generics.rs:30:1 @@ -50,28 +54,28 @@ LL | extern "cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/generics.rs:58:6 + --> $DIR/generics.rs:56:80 | -LL | ) -> &'static dyn Trait { - | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers +LL | extern "cmse-nonsecure-entry" fn static_trait_object(x: &'static dyn Trait) -> &'static dyn Trait { + | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/generics.rs:68:6 + --> $DIR/generics.rs:64:81 | -LL | ) -> WrapperTransparent { - | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers +LL | extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> WrapperTransparent { + | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error: requires `va_list` lang_item - --> $DIR/generics.rs:73:53 + --> $DIR/generics.rs:69:60 | -LL | extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - | ^^^^^^ +LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + | ^^^^^^ error: aborting due to 9 previous errors diff --git a/tests/ui/codemap_tests/issue-28308.rs b/tests/ui/codemap_tests/issue-28308.rs index b0e04d0f1f6..81493f8c453 100644 --- a/tests/ui/codemap_tests/issue-28308.rs +++ b/tests/ui/codemap_tests/issue-28308.rs @@ -1,16 +1,4 @@ fn main() { - assert!("foo"); //~ ERROR mismatched types - //~^ NOTE expected `bool`, found `str` - //~| NOTE in this expansion of assert! - let x = Some(&1); - assert!(x); //~ ERROR mismatched types - //~^ NOTE expected `bool`, found `Option<&{integer}>` - //~| NOTE expected enum `bool` - //~| NOTE in this expansion of assert! - //~| NOTE in this expansion of assert! - assert!(x, ""); //~ ERROR mismatched types - //~^ NOTE expected `bool`, found `Option<&{integer}>` - //~| NOTE expected enum `bool` - //~| NOTE in this expansion of assert! - //~| NOTE in this expansion of assert! + assert!("foo"); + //~^ ERROR cannot apply unary operator `!` } diff --git a/tests/ui/codemap_tests/issue-28308.stderr b/tests/ui/codemap_tests/issue-28308.stderr index e84ceb44aac..7bc9e05dfc0 100644 --- a/tests/ui/codemap_tests/issue-28308.stderr +++ b/tests/ui/codemap_tests/issue-28308.stderr @@ -1,27 +1,9 @@ -error[E0308]: mismatched types - --> $DIR/issue-28308.rs:2:13 +error[E0600]: cannot apply unary operator `!` to type `&'static str` + --> $DIR/issue-28308.rs:2:5 | LL | assert!("foo"); - | ^^^^^ expected `bool`, found `str` + | ^^^^^^^^^^^^^^ cannot apply unary operator `!` -error[E0308]: mismatched types - --> $DIR/issue-28308.rs:6:13 - | -LL | assert!(x); - | ^ expected `bool`, found `Option<&{integer}>` - | - = note: expected enum `bool` - found type `Option<&{integer}>` - -error[E0308]: mismatched types - --> $DIR/issue-28308.rs:11:13 - | -LL | assert!(x, ""); - | ^ expected `bool`, found `Option<&{integer}>` - | - = note: expected enum `bool` - found type `Option<&{integer}>` - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0600`. diff --git a/tests/ui/const-generics/std/const-generics-range.full.stderr b/tests/ui/const-generics/std/const-generics-range.full.stderr index 2b5c63e6643..ccede2af9e5 100644 --- a/tests/ui/const-generics/std/const-generics-range.full.stderr +++ b/tests/ui/const-generics/std/const-generics-range.full.stderr @@ -28,7 +28,7 @@ error[E0741]: `RangeTo<usize>` must implement `ConstParamTy` to be used as the t LL | struct _RangeTo<const R: std::ops::RangeTo<usize>>; | ^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0741]: `RangeToInclusive<usize>` must implement `ConstParamTy` to be used as the type of a const generic parameter +error[E0741]: `std::ops::RangeToInclusive<usize>` must implement `ConstParamTy` to be used as the type of a const generic parameter --> $DIR/const-generics-range.rs:34:35 | LL | struct _RangeToInclusive<const R: std::ops::RangeToInclusive<usize>>; diff --git a/tests/ui/const-generics/std/const-generics-range.min.stderr b/tests/ui/const-generics/std/const-generics-range.min.stderr index 04e3fe74453..43a57c880d5 100644 --- a/tests/ui/const-generics/std/const-generics-range.min.stderr +++ b/tests/ui/const-generics/std/const-generics-range.min.stderr @@ -58,7 +58,7 @@ help: add `#![feature(adt_const_params)]` to the crate attributes to enable more LL + #![feature(adt_const_params)] | -error: `RangeToInclusive<usize>` is forbidden as the type of a const generic parameter +error: `std::ops::RangeToInclusive<usize>` is forbidden as the type of a const generic parameter --> $DIR/const-generics-range.rs:34:35 | LL | struct _RangeToInclusive<const R: std::ops::RangeToInclusive<usize>>; diff --git a/tests/ui/const-generics/std/const-generics-range.rs b/tests/ui/const-generics/std/const-generics-range.rs index 3a238ed177e..391366dadba 100644 --- a/tests/ui/const-generics/std/const-generics-range.rs +++ b/tests/ui/const-generics/std/const-generics-range.rs @@ -7,32 +7,32 @@ // `Range` should be usable within const generics: struct _Range<const R: std::ops::Range<usize>>; //[min]~^ ERROR `std::ops::Range<usize>` is forbidden -const RANGE : _Range<{ 0 .. 1000 }> = _Range; +const RANGE: _Range<{ 0..1000 }> = _Range; // `RangeFrom` should be usable within const generics: struct _RangeFrom<const R: std::ops::RangeFrom<usize>>; //[min]~^ ERROR `std::ops::RangeFrom<usize>` is forbidden -const RANGE_FROM : _RangeFrom<{ 0 .. }> = _RangeFrom; +const RANGE_FROM: _RangeFrom<{ 0.. }> = _RangeFrom; // `RangeFull` should be usable within const generics: struct _RangeFull<const R: std::ops::RangeFull>; //[min]~^ ERROR `RangeFull` is forbidden -const RANGE_FULL : _RangeFull<{ .. }> = _RangeFull; +const RANGE_FULL: _RangeFull<{ .. }> = _RangeFull; // Regression test for #70155 // `RangeInclusive` should be usable within const generics: struct _RangeInclusive<const R: std::ops::RangeInclusive<usize>>; //[min]~^ ERROR `std::ops::RangeInclusive<usize>` is forbidden -const RANGE_INCLUSIVE : _RangeInclusive<{ 0 ..= 999 }> = _RangeInclusive; +const RANGE_INCLUSIVE: _RangeInclusive<{ 0..=999 }> = _RangeInclusive; // `RangeTo` should be usable within const generics: struct _RangeTo<const R: std::ops::RangeTo<usize>>; //[min]~^ ERROR `RangeTo<usize>` is forbidden -const RANGE_TO : _RangeTo<{ .. 1000 }> = _RangeTo; +const RANGE_TO: _RangeTo<{ ..1000 }> = _RangeTo; // `RangeToInclusive` should be usable within const generics: struct _RangeToInclusive<const R: std::ops::RangeToInclusive<usize>>; -//[min]~^ ERROR `RangeToInclusive<usize>` is forbidden -const RANGE_TO_INCLUSIVE : _RangeToInclusive<{ ..= 999 }> = _RangeToInclusive; +//[min]~^ ERROR `std::ops::RangeToInclusive<usize>` is forbidden +const RANGE_TO_INCLUSIVE: _RangeToInclusive<{ ..=999 }> = _RangeToInclusive; pub fn main() {} diff --git a/tests/ui/consts/const_cmp_type_id.rs b/tests/ui/consts/const_cmp_type_id.rs index ff44876c5c4..dc0701d3b1d 100644 --- a/tests/ui/consts/const_cmp_type_id.rs +++ b/tests/ui/consts/const_cmp_type_id.rs @@ -9,8 +9,7 @@ fn main() { assert!(TypeId::of::<u8>() == TypeId::of::<u8>()); assert!(TypeId::of::<()>() != TypeId::of::<u8>()); let _a = TypeId::of::<u8>() < TypeId::of::<u16>(); - //~^ ERROR: cannot call non-const operator in constants - // can't assert `_a` because it is not deterministic - // FIXME(const_trait_impl) make it pass + //~^ ERROR: the trait bound `TypeId: const PartialOrd` is not satisfied + // FIXME(const_trait_impl) make it pass; requires const comparison of pointers (#53020) } } diff --git a/tests/ui/consts/const_cmp_type_id.stderr b/tests/ui/consts/const_cmp_type_id.stderr index 05b94caef79..62a1677c0d9 100644 --- a/tests/ui/consts/const_cmp_type_id.stderr +++ b/tests/ui/consts/const_cmp_type_id.stderr @@ -1,13 +1,9 @@ -error[E0015]: cannot call non-const operator in constants +error[E0277]: the trait bound `TypeId: const PartialOrd` is not satisfied --> $DIR/const_cmp_type_id.rs:11:18 | LL | let _a = TypeId::of::<u8>() < TypeId::of::<u16>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: impl defined here, but it is not `const` - --> $SRC_DIR/core/src/any.rs:LL:COL - = note: calls in constants are limited to constant functions, tuple structs and tuple variants error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0015`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/consts/control-flow/assert.stderr b/tests/ui/consts/control-flow/assert.stderr index deaad6abbcc..026097a6ba0 100644 --- a/tests/ui/consts/control-flow/assert.stderr +++ b/tests/ui/consts/control-flow/assert.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/assert.rs:5:23 + --> $DIR/assert.rs:5:15 | LL | const _: () = assert!(false); - | ^^^^^ evaluation of `_` failed here + | ^^^^^^^^^^^^^^ evaluation of `_` failed here error: aborting due to 1 previous error diff --git a/tests/ui/consts/fn_trait_refs.rs b/tests/ui/consts/fn_trait_refs.rs index e475c0a1b6f..e4a62e18c7b 100644 --- a/tests/ui/consts/fn_trait_refs.rs +++ b/tests/ui/consts/fn_trait_refs.rs @@ -1,6 +1,5 @@ -//@ known-bug: #110395 +//@ check-pass -#![feature(const_fn_trait_ref_impls)] #![feature(fn_traits)] #![feature(unboxed_closures)] #![feature(const_trait_impl)] diff --git a/tests/ui/consts/fn_trait_refs.stderr b/tests/ui/consts/fn_trait_refs.stderr deleted file mode 100644 index bbe0714801c..00000000000 --- a/tests/ui/consts/fn_trait_refs.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0635]: unknown feature `const_fn_trait_ref_impls` - --> $DIR/fn_trait_refs.rs:3:12 - | -LL | #![feature(const_fn_trait_ref_impls)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: the trait bound `(i32, i32, i32): const PartialEq` is not satisfied - --> $DIR/fn_trait_refs.rs:71:17 - | -LL | assert!(test_one == (1, 1, 1)); - | ^^^^^^^^^^^^^^^^^^^^^ - -error[E0277]: the trait bound `(i32, i32): const PartialEq` is not satisfied - --> $DIR/fn_trait_refs.rs:74:17 - | -LL | assert!(test_two == (2, 2)); - | ^^^^^^^^^^^^^^^^^^ - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0277, E0635. -For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/deref/pin-deref-const.rs b/tests/ui/deref/pin-deref-const.rs new file mode 100644 index 00000000000..6ed00177b23 --- /dev/null +++ b/tests/ui/deref/pin-deref-const.rs @@ -0,0 +1,79 @@ +// The purpose of this file is to track the error messages from Pin and DerefMut interacting. +// +// Identical to `pin-deref.rs` except for using unstable `const fn`. + +//@ check-fail + +#![feature(const_convert)] +#![feature(const_trait_impl)] + +use std::ops::DerefMut; +use std::pin::Pin; + +struct MyUnpinType {} + +impl MyUnpinType { + const fn at_self(&self) {} + const fn at_mut_self(&mut self) {} +} + +struct MyPinType(core::marker::PhantomPinned); + +impl MyPinType { + const fn at_self(&self) {} + const fn at_mut_self(&mut self) {} +} + +const fn call_mut_ref_unpin(mut r_unpin: Pin<&mut MyUnpinType>) { + r_unpin.at_self(); + r_unpin.at_mut_self(); +} + +const fn call_ref_unpin(mut r_unpin: Pin<&MyUnpinType>) { + r_unpin.at_self(); + r_unpin.at_mut_self(); //~ ERROR: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable +} + +const fn call_mut_ref_pin(mut r_pin: Pin<&mut MyPinType>) { + r_pin.at_self(); + r_pin.at_mut_self(); //~ ERROR: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable +} + +const fn call_ref_pin(mut r_pin: Pin<&MyPinType>) { + r_pin.at_self(); + r_pin.at_mut_self(); //~ ERROR: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable +} + +const fn coerce_unpin_rr<'a>(mut r_unpin: &'a mut Pin<&MyUnpinType>) -> &'a MyUnpinType { + r_unpin +} + +const fn coerce_unpin_rm<'a>(mut r_unpin: &'a mut Pin<&MyUnpinType>) -> &'a mut MyUnpinType { + r_unpin //~ ERROR: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable +} + +const fn coerce_unpin_mr<'a>(mut r_unpin: &'a mut Pin<&mut MyUnpinType>) -> &'a MyUnpinType { + r_unpin +} + +const fn coerce_unpin_mm<'a>(mut r_unpin: &'a mut Pin<&mut MyUnpinType>) -> &'a mut MyUnpinType { + r_unpin +} + +const fn coerce_pin_rr<'a>(mut r_pin: &'a mut Pin<&MyPinType>) -> &'a MyPinType { + r_pin +} + +const fn coerce_pin_rm<'a>(mut r_pin: &'a mut Pin<&MyPinType>) -> &'a mut MyPinType { + r_pin //~ ERROR: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable +} + +const fn coerce_pin_mr<'a>(mut r_pin: &'a mut Pin<&mut MyPinType>) -> &'a MyPinType { + r_pin +} + +const fn coerce_pin_mm<'a>(mut r_pin: &'a mut Pin<&mut MyPinType>) -> &'a mut MyPinType { + r_pin //~ ERROR: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable +} + +fn main() {} diff --git a/tests/ui/deref/pin-deref-const.stderr b/tests/ui/deref/pin-deref-const.stderr new file mode 100644 index 00000000000..caa270e64ff --- /dev/null +++ b/tests/ui/deref/pin-deref-const.stderr @@ -0,0 +1,51 @@ +error[E0596]: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable + --> $DIR/pin-deref-const.rs:34:5 + | +LL | r_unpin.at_mut_self(); + | ^^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyUnpinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable + --> $DIR/pin-deref-const.rs:39:5 + | +LL | r_pin.at_mut_self(); + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&mut MyPinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable + --> $DIR/pin-deref-const.rs:44:5 + | +LL | r_pin.at_mut_self(); + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyPinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable + --> $DIR/pin-deref-const.rs:52:5 + | +LL | r_unpin + | ^^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyUnpinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable + --> $DIR/pin-deref-const.rs:68:5 + | +LL | r_pin + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyPinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable + --> $DIR/pin-deref-const.rs:76:5 + | +LL | r_pin + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&mut MyPinType>` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/deref/pin-deref.rs b/tests/ui/deref/pin-deref.rs new file mode 100644 index 00000000000..180831c9bc6 --- /dev/null +++ b/tests/ui/deref/pin-deref.rs @@ -0,0 +1,76 @@ +// The purpose of this file is to track the error messages from Pin and DerefMut interacting. +// +// Identical to `pin-deref-const.rs` except for being stable and not using `const fn`. + +//@ check-fail + +use std::ops::DerefMut; +use std::pin::Pin; + +struct MyUnpinType {} + +impl MyUnpinType { + fn at_self(&self) {} + fn at_mut_self(&mut self) {} +} + +struct MyPinType(core::marker::PhantomPinned); + +impl MyPinType { + fn at_self(&self) {} + fn at_mut_self(&mut self) {} +} + +fn call_mut_ref_unpin(mut r_unpin: Pin<&mut MyUnpinType>) { + r_unpin.at_self(); + r_unpin.at_mut_self(); +} + +fn call_ref_unpin(mut r_unpin: Pin<&MyUnpinType>) { + r_unpin.at_self(); + r_unpin.at_mut_self(); //~ ERROR: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable +} + +fn call_mut_ref_pin(mut r_pin: Pin<&mut MyPinType>) { + r_pin.at_self(); + r_pin.at_mut_self(); //~ ERROR: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable +} + +fn call_ref_pin(mut r_pin: Pin<&MyPinType>) { + r_pin.at_self(); + r_pin.at_mut_self(); //~ ERROR: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable +} + +fn coerce_unpin_rr<'a>(mut r_unpin: &'a mut Pin<&MyUnpinType>) -> &'a MyUnpinType { + r_unpin +} + +fn coerce_unpin_rm<'a>(mut r_unpin: &'a mut Pin<&MyUnpinType>) -> &'a mut MyUnpinType { + r_unpin //~ ERROR: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable +} + +fn coerce_unpin_mr<'a>(mut r_unpin: &'a mut Pin<&mut MyUnpinType>) -> &'a MyUnpinType { + r_unpin +} + +fn coerce_unpin_mm<'a>(mut r_unpin: &'a mut Pin<&mut MyUnpinType>) -> &'a mut MyUnpinType { + r_unpin +} + +fn coerce_pin_rr<'a>(mut r_pin: &'a mut Pin<&MyPinType>) -> &'a MyPinType { + r_pin +} + +fn coerce_pin_rm<'a>(mut r_pin: &'a mut Pin<&MyPinType>) -> &'a mut MyPinType { + r_pin //~ ERROR: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable +} + +fn coerce_pin_mr<'a>(mut r_pin: &'a mut Pin<&mut MyPinType>) -> &'a MyPinType { + r_pin +} + +fn coerce_pin_mm<'a>(mut r_pin: &'a mut Pin<&mut MyPinType>) -> &'a mut MyPinType { + r_pin //~ ERROR: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable +} + +fn main() {} diff --git a/tests/ui/deref/pin-deref.stderr b/tests/ui/deref/pin-deref.stderr new file mode 100644 index 00000000000..71277a10cdf --- /dev/null +++ b/tests/ui/deref/pin-deref.stderr @@ -0,0 +1,51 @@ +error[E0596]: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable + --> $DIR/pin-deref.rs:31:5 + | +LL | r_unpin.at_mut_self(); + | ^^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyUnpinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable + --> $DIR/pin-deref.rs:36:5 + | +LL | r_pin.at_mut_self(); + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&mut MyPinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable + --> $DIR/pin-deref.rs:41:5 + | +LL | r_pin.at_mut_self(); + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyPinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&MyUnpinType>` as mutable + --> $DIR/pin-deref.rs:49:5 + | +LL | r_unpin + | ^^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyUnpinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&MyPinType>` as mutable + --> $DIR/pin-deref.rs:65:5 + | +LL | r_pin + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&MyPinType>` + +error[E0596]: cannot borrow data in dereference of `Pin<&mut MyPinType>` as mutable + --> $DIR/pin-deref.rs:73:5 + | +LL | r_pin + | ^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin<&mut MyPinType>` + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0596`. diff --git a/tests/ui/deref/pin-impl-deref.rs b/tests/ui/deref/pin-impl-deref.rs new file mode 100644 index 00000000000..b1dc8dea3f2 --- /dev/null +++ b/tests/ui/deref/pin-impl-deref.rs @@ -0,0 +1,40 @@ +// The purpose of this file is to track the error messages from Pin and DerefMut interacting. + +//@ check-fail + +use std::ops::DerefMut; +use std::pin::Pin; + +struct MyUnpinType {} + +impl MyUnpinType { + fn at_self(&self) {} + fn at_mut_self(&mut self) {} +} + +struct MyPinType(core::marker::PhantomPinned); + +impl MyPinType { + fn at_self(&self) {} + fn at_mut_self(&mut self) {} +} + +fn impl_deref_mut(_: impl DerefMut) {} +fn unpin_impl_ref(r_unpin: Pin<&MyUnpinType>) { + impl_deref_mut(r_unpin) + //~^ ERROR: the trait bound `Pin<&MyUnpinType>: DerefMut` is not satisfied +} +fn unpin_impl_mut(r_unpin: Pin<&mut MyUnpinType>) { + impl_deref_mut(r_unpin) +} +fn pin_impl_ref(r_pin: Pin<&MyPinType>) { + impl_deref_mut(r_pin) + //~^ ERROR: `PhantomPinned` cannot be unpinned + //~| ERROR: the trait bound `Pin<&MyPinType>: DerefMut` is not satisfied +} +fn pin_impl_mut(r_pin: Pin<&mut MyPinType>) { + impl_deref_mut(r_pin) + //~^ ERROR: `PhantomPinned` cannot be unpinned +} + +fn main() {} diff --git a/tests/ui/deref/pin-impl-deref.stderr b/tests/ui/deref/pin-impl-deref.stderr new file mode 100644 index 00000000000..106654641a1 --- /dev/null +++ b/tests/ui/deref/pin-impl-deref.stderr @@ -0,0 +1,85 @@ +error[E0277]: the trait bound `Pin<&MyUnpinType>: DerefMut` is not satisfied + --> $DIR/pin-impl-deref.rs:24:20 + | +LL | impl_deref_mut(r_unpin) + | -------------- ^^^^^^^ the trait `DerefMut` is not implemented for `Pin<&MyUnpinType>` + | | + | required by a bound introduced by this call + | + = note: required for `Pin<&MyUnpinType>` to implement `DerefMut` +note: required by a bound in `impl_deref_mut` + --> $DIR/pin-impl-deref.rs:22:27 + | +LL | fn impl_deref_mut(_: impl DerefMut) {} + | ^^^^^^^^ required by this bound in `impl_deref_mut` +help: consider mutably borrowing here + | +LL | impl_deref_mut(&mut r_unpin) + | ++++ + +error[E0277]: the trait bound `Pin<&MyPinType>: DerefMut` is not satisfied + --> $DIR/pin-impl-deref.rs:31:20 + | +LL | impl_deref_mut(r_pin) + | -------------- ^^^^^ the trait `DerefMut` is not implemented for `Pin<&MyPinType>` + | | + | required by a bound introduced by this call + | + = note: required for `Pin<&MyPinType>` to implement `DerefMut` +note: required by a bound in `impl_deref_mut` + --> $DIR/pin-impl-deref.rs:22:27 + | +LL | fn impl_deref_mut(_: impl DerefMut) {} + | ^^^^^^^^ required by this bound in `impl_deref_mut` +help: consider mutably borrowing here + | +LL | impl_deref_mut(&mut r_pin) + | ++++ + +error[E0277]: `PhantomPinned` cannot be unpinned + --> $DIR/pin-impl-deref.rs:31:20 + | +LL | impl_deref_mut(r_pin) + | -------------- ^^^^^ within `MyPinType`, the trait `Unpin` is not implemented for `PhantomPinned` + | | + | required by a bound introduced by this call + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope +note: required because it appears within the type `MyPinType` + --> $DIR/pin-impl-deref.rs:15:8 + | +LL | struct MyPinType(core::marker::PhantomPinned); + | ^^^^^^^^^ + = note: required for `Pin<&MyPinType>` to implement `DerefMut` +note: required by a bound in `impl_deref_mut` + --> $DIR/pin-impl-deref.rs:22:27 + | +LL | fn impl_deref_mut(_: impl DerefMut) {} + | ^^^^^^^^ required by this bound in `impl_deref_mut` + +error[E0277]: `PhantomPinned` cannot be unpinned + --> $DIR/pin-impl-deref.rs:36:20 + | +LL | impl_deref_mut(r_pin) + | -------------- ^^^^^ within `MyPinType`, the trait `Unpin` is not implemented for `PhantomPinned` + | | + | required by a bound introduced by this call + | + = note: consider using the `pin!` macro + consider using `Box::pin` if you need to access the pinned value outside of the current scope +note: required because it appears within the type `MyPinType` + --> $DIR/pin-impl-deref.rs:15:8 + | +LL | struct MyPinType(core::marker::PhantomPinned); + | ^^^^^^^^^ + = note: required for `Pin<&mut MyPinType>` to implement `DerefMut` +note: required by a bound in `impl_deref_mut` + --> $DIR/pin-impl-deref.rs:22:27 + | +LL | fn impl_deref_mut(_: impl DerefMut) {} + | ^^^^^^^^ required by this bound in `impl_deref_mut` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.rs b/tests/ui/feature-gates/feature-gate-c_variadic.rs index 45c68842093..88d91dbd081 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.rs +++ b/tests/ui/feature-gates/feature-gate-c_variadic.rs @@ -1,10 +1,10 @@ -#![crate_type="lib"] +#![crate_type = "lib"] -pub unsafe extern "C" fn test(_: i32, ap: ...) { } +pub unsafe extern "C" fn test(_: i32, ap: ...) {} //~^ ERROR C-variadic functions are unstable trait Trait { - unsafe extern "C" fn trait_test(_: i32, ap: ...) { } + unsafe extern "C" fn trait_test(_: i32, ap: ...) {} //~^ ERROR C-variadic functions are unstable - //~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~| ERROR associated functions cannot have a C variable argument list } diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.stderr b/tests/ui/feature-gates/feature-gate-c_variadic.stderr index e30a2f1ede3..808aa20948d 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.stderr +++ b/tests/ui/feature-gates/feature-gate-c_variadic.stderr @@ -1,14 +1,14 @@ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: associated functions cannot have a C variable argument list --> $DIR/feature-gate-c_variadic.rs:7:45 | -LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) { } +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} | ^^^^^^^ error[E0658]: C-variadic functions are unstable --> $DIR/feature-gate-c_variadic.rs:3:1 | -LL | pub unsafe extern "C" fn test(_: i32, ap: ...) { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub unsafe extern "C" fn test(_: i32, ap: ...) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #44930 <https://github.com/rust-lang/rust/issues/44930> for more information = help: add `#![feature(c_variadic)]` to the crate attributes to enable @@ -17,8 +17,8 @@ LL | pub unsafe extern "C" fn test(_: i32, ap: ...) { } error[E0658]: C-variadic functions are unstable --> $DIR/feature-gate-c_variadic.rs:7:5 | -LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #44930 <https://github.com/rust-lang/rust/issues/44930> for more information = help: add `#![feature(c_variadic)]` to the crate attributes to enable diff --git a/tests/ui/feature-gates/feature-gate-sanitize.rs b/tests/ui/feature-gates/feature-gate-sanitize.rs index 40098d93272..768417cfae8 100644 --- a/tests/ui/feature-gates/feature-gate-sanitize.rs +++ b/tests/ui/feature-gates/feature-gate-sanitize.rs @@ -1,4 +1,3 @@ -//@ normalize-stderr: "you are using [0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9]+)?( \([^)]*\))?" -> "you are using $$RUSTC_VERSION" #![feature(no_sanitize)] //~ ERROR feature has been removed #[sanitize(address = "on")] diff --git a/tests/ui/feature-gates/feature-gate-sanitize.stderr b/tests/ui/feature-gates/feature-gate-sanitize.stderr index 7c38b351916..513999636a9 100644 --- a/tests/ui/feature-gates/feature-gate-sanitize.stderr +++ b/tests/ui/feature-gates/feature-gate-sanitize.stderr @@ -1,5 +1,5 @@ error[E0557]: feature has been removed - --> $DIR/feature-gate-sanitize.rs:2:12 + --> $DIR/feature-gate-sanitize.rs:1:12 | LL | #![feature(no_sanitize)] | ^^^^^^^^^^^ feature has been removed @@ -8,7 +8,7 @@ LL | #![feature(no_sanitize)] = note: renamed to sanitize(xyz = "on|off") error[E0658]: the `#[sanitize]` attribute is an experimental feature - --> $DIR/feature-gate-sanitize.rs:4:1 + --> $DIR/feature-gate-sanitize.rs:3:1 | LL | #[sanitize(address = "on")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/feature-gates/feature-gate-static_align.rs b/tests/ui/feature-gates/feature-gate-static_align.rs new file mode 100644 index 00000000000..4d8f0e18d94 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-static_align.rs @@ -0,0 +1,11 @@ +#![crate_type = "lib"] + +#[rustc_align_static(16)] +//~^ ERROR the `#[rustc_align_static]` attribute is an experimental feature +static REQUIRES_ALIGNMENT: u64 = 0; + +extern "C" { + #[rustc_align_static(16)] + //~^ ERROR the `#[rustc_align_static]` attribute is an experimental feature + static FOREIGN_STATIC: u32; +} diff --git a/tests/ui/feature-gates/feature-gate-static_align.stderr b/tests/ui/feature-gates/feature-gate-static_align.stderr new file mode 100644 index 00000000000..b45fcdefc9c --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-static_align.stderr @@ -0,0 +1,23 @@ +error[E0658]: the `#[rustc_align_static]` attribute is an experimental feature + --> $DIR/feature-gate-static_align.rs:3:1 + | +LL | #[rustc_align_static(16)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #146177 <https://github.com/rust-lang/rust/issues/146177> for more information + = help: add `#![feature(static_align)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the `#[rustc_align_static]` attribute is an experimental feature + --> $DIR/feature-gate-static_align.rs:8:5 + | +LL | #[rustc_align_static(16)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #146177 <https://github.com/rust-lang/rust/issues/146177> for more information + = help: add `#![feature(static_align)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index c2653dd82a9..546aa4052d3 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -539,26 +539,26 @@ mod macro_escape { #[no_std] //~^ WARN crate-level attribute should be an inner attribute -//~| HELP add a `!` mod no_std { + //~^ NOTE This attribute does not have an `!`, which means it is applied to this module mod inner { #![no_std] } -//~^ WARN crate-level attribute should be in the root module +//~^ WARN the `#![no_std]` attribute can only be used at the crate root #[no_std] fn f() { } //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this function #[no_std] struct S; //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this struct #[no_std] type T = S; //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this type alias #[no_std] impl S { } //~^ WARN crate-level attribute should be an inner attribute - //~| HELP add a `!` + //~| NOTE This attribute does not have an `!`, which means it is applied to this implementation block } // At time of authorship, #[proc_macro_derive = "2500"] signals error diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 4a3520972bf..3c835be5cff 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -209,17 +209,6 @@ help: add a `!` LL | #![reexport_test_harness_main = "2900"] | + -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:540:1 - | -LL | #[no_std] - | ^^^^^^^^^ - | -help: add a `!` - | -LL | #![no_std] - | + - warning: attribute should be applied to an `extern` block with non-Rust ABI --> $DIR/issue-43106-gating-of-builtin-attrs.rs:695:1 | @@ -387,56 +376,6 @@ help: add a `!` LL | #![reexport_test_harness_main = "2900"] impl S { } | + -warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:544:17 - | -LL | mod inner { #![no_std] } - | ^^^^^^^^^^ - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:547:5 - | -LL | #[no_std] fn f() { } - | ^^^^^^^^^ - | -help: add a `!` - | -LL | #![no_std] fn f() { } - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:5 - | -LL | #[no_std] struct S; - | ^^^^^^^^^ - | -help: add a `!` - | -LL | #![no_std] struct S; - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:555:5 - | -LL | #[no_std] type T = S; - | ^^^^^^^^^ - | -help: add a `!` - | -LL | #![no_std] type T = S; - | + - -warning: crate-level attribute should be an inner attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:5 - | -LL | #[no_std] impl S { } - | ^^^^^^^^^ - | -help: add a `!` - | -LL | #![no_std] impl S { } - | + - warning: attribute should be applied to an `extern` block with non-Rust ABI --> $DIR/issue-43106-gating-of-builtin-attrs.rs:701:17 | @@ -1095,6 +1034,76 @@ LL | #[macro_escape] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[macro_escape]` can be applied to modules, extern crates, and crates +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:540:1 + | +LL | #[no_std] + | ^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this module + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:542:1 + | +LL | / mod no_std { +LL | | +LL | | mod inner { #![no_std] } +... | +LL | | } + | |_^ + +warning: the `#![no_std]` attribute can only be used at the crate root + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:544:17 + | +LL | mod inner { #![no_std] } + | ^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:547:5 + | +LL | #[no_std] fn f() { } + | ^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this function + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:547:15 + | +LL | #[no_std] fn f() { } + | ^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:5 + | +LL | #[no_std] struct S; + | ^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this struct + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:551:15 + | +LL | #[no_std] struct S; + | ^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:555:5 + | +LL | #[no_std] type T = S; + | ^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this type alias + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:555:15 + | +LL | #[no_std] type T = S; + | ^^^^^^^^^^^ + +warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![no_std]` + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:5 + | +LL | #[no_std] impl S { } + | ^^^^^^^^^ + | +note: This attribute does not have an `!`, which means it is applied to this implementation block + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:15 + | +LL | #[no_std] impl S { } + | ^^^^^^^^^^ + warning: `#[cold]` attribute cannot be used on modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:581:1 | diff --git a/tests/ui/fmt/ifmt-bad-arg.stderr b/tests/ui/fmt/ifmt-bad-arg.stderr index 4344aee83c2..b565b836f21 100644 --- a/tests/ui/fmt/ifmt-bad-arg.stderr +++ b/tests/ui/fmt/ifmt-bad-arg.stderr @@ -62,6 +62,11 @@ LL | format!("", 1, 2); | | | | | argument never used | multiple missing formatting specifiers + | +help: format specifiers use curly braces, consider adding 2 format specifiers + | +LL | format!("{}{}", 1, 2); + | ++++ error: argument never used --> $DIR/ifmt-bad-arg.rs:33:22 @@ -102,6 +107,11 @@ LL | format!("", foo=2); | -- ^ named argument never used | | | formatting specifier missing + | +help: format specifiers use curly braces, consider adding a format specifier + | +LL | format!("{}", foo=2); + | ++ error: multiple unused formatting arguments --> $DIR/ifmt-bad-arg.rs:38:32 @@ -111,6 +121,8 @@ LL | format!("{} {}", 1, 2, foo=1, bar=2); | | | | | named argument never used | multiple missing formatting specifiers + | + = note: consider adding 2 format specifiers error: duplicate argument named `foo` --> $DIR/ifmt-bad-arg.rs:40:29 diff --git a/tests/ui/frontmatter/auxiliary/expr.rs b/tests/ui/frontmatter/auxiliary/expr.rs new file mode 100644 index 00000000000..5f694110666 --- /dev/null +++ b/tests/ui/frontmatter/auxiliary/expr.rs @@ -0,0 +1,4 @@ +--- +- +--- +1 diff --git a/tests/ui/frontmatter/auxiliary/makro.rs b/tests/ui/frontmatter/auxiliary/makro.rs index 70707b27bff..1d64fa44bd3 100644 --- a/tests/ui/frontmatter/auxiliary/makro.rs +++ b/tests/ui/frontmatter/auxiliary/makro.rs @@ -1,8 +1,20 @@ extern crate proc_macro; -use proc_macro::TokenStream; +use proc_macro::{Literal, TokenStream}; #[proc_macro] pub fn check(_: TokenStream) -> TokenStream { + // In the following test cases, the `---` may look like the start of frontmatter but it is not! + // That's because it would be backward incompatible to interpret them as such in the latest + // stable edition. That's not only the case due to the feature gate error but also due to the + // fact that we "eagerly" emit errors on malformed frontmatter. + + // issue: <https://github.com/rust-lang/rust/issues/145520> + _ = "---".parse::<TokenStream>(); + // Just a sequence of regular Rust punctuation tokens. assert_eq!(6, "---\n---".parse::<TokenStream>().unwrap().into_iter().count()); + + // issue: <https://github.com/rust-lang/rust/issues/146132> + assert!("---".parse::<Literal>().is_err()); + Default::default() } diff --git a/tests/ui/frontmatter/include-in-expr-ctxt.rs b/tests/ui/frontmatter/include-in-expr-ctxt.rs new file mode 100644 index 00000000000..7b02c9cb8a5 --- /dev/null +++ b/tests/ui/frontmatter/include-in-expr-ctxt.rs @@ -0,0 +1,9 @@ +// Check that an expr-ctxt `include` doesn't try to parse frontmatter and instead +// treats it as a regular Rust token sequence. +//@ check-pass +#![expect(double_negations)] + +fn main() { + // issue: <https://github.com/rust-lang/rust/issues/145945> + const _: () = assert!(-1 == include!("auxiliary/expr.rs")); +} diff --git a/tests/ui/frontmatter/include-in-item-ctxt.rs b/tests/ui/frontmatter/include-in-item-ctxt.rs new file mode 100644 index 00000000000..c8455bc49ab --- /dev/null +++ b/tests/ui/frontmatter/include-in-item-ctxt.rs @@ -0,0 +1,10 @@ +// Ensure that in item ctxts we can `include` files that contain frontmatter. +//@ check-pass + +#![feature(frontmatter)] + +include!("auxiliary/lib.rs"); + +fn main() { + foo(1); +} diff --git a/tests/ui/frontmatter/included-frontmatter.rs b/tests/ui/frontmatter/included-frontmatter.rs deleted file mode 100644 index 57616cd1228..00000000000 --- a/tests/ui/frontmatter/included-frontmatter.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![feature(frontmatter)] - -//@ check-pass - -include!("auxiliary/lib.rs"); - -// auxiliary/lib.rs contains a frontmatter. Ensure that we can use them in an -// `include!` macro. - -fn main() { - foo(1); -} diff --git a/tests/ui/frontmatter/proc-macro-observer.rs b/tests/ui/frontmatter/proc-macro-observer.rs index b1cc1460933..6c4c8c57289 100644 --- a/tests/ui/frontmatter/proc-macro-observer.rs +++ b/tests/ui/frontmatter/proc-macro-observer.rs @@ -2,10 +2,9 @@ //@ proc-macro: makro.rs //@ edition: 2021 -makro::check!(); +// Check that a proc-macro doesn't try to parse frontmatter and instead treats +// it as a regular Rust token sequence. See `auxiliary/makro.rs` for details. -// checks that a proc-macro doesn't know or parse frontmatters at all and instead treats -// it as normal Rust code. -// see auxiliary/makro.rs for how it is tested. +makro::check!(); fn main() {} diff --git a/tests/ui/generics/post_monomorphization_error_backtrace.stderr b/tests/ui/generics/post_monomorphization_error_backtrace.stderr index 92c7df73638..6953414f0c2 100644 --- a/tests/ui/generics/post_monomorphization_error_backtrace.stderr +++ b/tests/ui/generics/post_monomorphization_error_backtrace.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::<T>() == 0 - --> $DIR/post_monomorphization_error_backtrace.rs:6:31 + --> $DIR/post_monomorphization_error_backtrace.rs:6:23 | LL | const V: () = assert!(std::mem::size_of::<T>() == 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<u32>::V` failed here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<u32>::V` failed here note: erroneous constant encountered --> $DIR/post_monomorphization_error_backtrace.rs:14:5 @@ -17,10 +17,10 @@ LL | assert_zst::<U>() | ^^^^^^^^^^^^^^^^^ error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::<T>() == 0 - --> $DIR/post_monomorphization_error_backtrace.rs:6:31 + --> $DIR/post_monomorphization_error_backtrace.rs:6:23 | LL | const V: () = assert!(std::mem::size_of::<T>() == 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<i32>::V` failed here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<i32>::V` failed here note: erroneous constant encountered --> $DIR/post_monomorphization_error_backtrace.rs:14:5 diff --git a/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.next.stderr b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.next.stderr new file mode 100644 index 00000000000..86ac1bdad04 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.next.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/avoid-inference-constraints-from-blanket-2.rs:27:18 + | +LL | let _: u32 = x; + | --- ^ expected `u32`, found `u64` + | | + | expected due to this + | +help: you can convert a `u64` to a `u32` and panic if the converted value doesn't fit + | +LL | let _: u32 = x.try_into().unwrap(); + | ++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.rs b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.rs new file mode 100644 index 00000000000..b4f853de4aa --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-2.rs @@ -0,0 +1,31 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] check-pass + +// Regression test for trait-system-refactor-initiative#205. Avoid +// constraining other impl arguments when applying blanket impls. + +// FIXME(-Znext-solver): This currently incompletely constrains the +// argument of `opaque: Trait<?x>` using the blanket impl of trait. +// Ideally we don't do that. + +trait Trait<T> {} + +impl<T> Trait<u64> for T {} +impl Trait<u32> for u64 {} + +fn impls_trait<T: Trait<U>, U>(_: U) -> T { + todo!() +} + +fn foo() -> impl Sized { + let x = Default::default(); + if false { + return impls_trait::<_, _>(x); + } + let _: u32 = x; + //[next]~^ ERROR mismatched types + 1u64 +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.rs b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.rs new file mode 100644 index 00000000000..2f29cb4ee6b --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.rs @@ -0,0 +1,25 @@ +//@ compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +#![allow(unconditional_recursion)] + +// Regression test for trait-system-refactor-initiative#205. Avoid +// constraining other impl arguments when applying blanket impls, +// especially if the nested where-bounds of the blanket impl don't +// actually apply for the opaque. + +// FIXME(-Znext-solver): This currently incompletely constrains the +// argument of `opaque: Trait<?x>` using the blanket impl of trait. +// Ideally we don't do that. + +trait Trait<T> {} + +impl<T: Copy> Trait<u32> for T {} +impl Trait<u64> for String {} +fn impls_trait<T: Trait<U>, U>(_: T) {} + +fn test() -> impl Sized { + let x = test(); + impls_trait(x); //~ ERROR the trait bound `String: Trait<u32>` is not satisfied + String::new() +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.stderr b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.stderr new file mode 100644 index 00000000000..a5d19b48481 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket-3.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `String: Trait<u32>` is not satisfied + --> $DIR/avoid-inference-constraints-from-blanket-3.rs:22:5 + | +LL | impls_trait(x); + | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | + = help: the trait `Trait<u32>` is not implemented for `String` + but trait `Trait<u64>` is implemented for it + = help: for that trait implementation, expected `u64`, found `u32` +note: required for `String` to implement `Trait<u32>` + --> $DIR/avoid-inference-constraints-from-blanket-3.rs:16:15 + | +LL | impl<T: Copy> Trait<u32> for T {} + | ---- ^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here +note: required by a bound in `impls_trait` + --> $DIR/avoid-inference-constraints-from-blanket-3.rs:18:19 + | +LL | fn impls_trait<T: Trait<U>, U>(_: T) {} + | ^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket.rs b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket.rs new file mode 100644 index 00000000000..bb3acfde5bc --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/avoid-inference-constraints-from-blanket.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#205. Avoid constraining +// the opaque type when applying blanket impls. + +trait Trait<T> {} + +impl<T> Trait<T> for T {} +impl Trait<u32> for u64 {} + +fn impls_trait<T: Trait<U>, U>() -> T { + todo!() +} + +fn foo() -> impl Sized { + if false { + // `opaque: Trait<u32>` shouldn't constrain `opaque` to `u32` via the blanket impl + return impls_trait::<_, u32>(); + } + 1u64 +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/multiple-opaques-ambig.rs b/tests/ui/impl-trait/non-defining-uses/multiple-opaques-ambig.rs new file mode 100644 index 00000000000..e7aaf6fa135 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/multiple-opaques-ambig.rs @@ -0,0 +1,23 @@ +//@ compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +#![allow(unconditional_recursion)] + +// Regression test for trait-system-refactor-initiative#182. If multiple +// opaque types result in different item bounds, do not apply them. + +trait Trait<T> {} +impl<T, U> Trait<T> for U {} + +fn impls_trait<T: Trait<U>, U>(_: T) -> U { + todo!() +} + +fn overlap<T, U>() -> (impl Trait<T>, impl Trait<U>) { + let mut x = overlap::<T, U>().0; + x = overlap::<T, U>().1; + let u = impls_trait(x); + let _: u32 = u; + ((), ()) +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/multiple-opaques-ok.rs b/tests/ui/impl-trait/non-defining-uses/multiple-opaques-ok.rs new file mode 100644 index 00000000000..d91efe181e3 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/multiple-opaques-ok.rs @@ -0,0 +1,37 @@ +//@ compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +#![allow(unconditional_recursion)] + +// Regression test for trait-system-refactor-initiative#18, making sure +// we support being sub unified with more than 1 opaque type. + +trait Id { + type This; +} +impl Id for &'static str { + type This = &'static str; +} +fn to_assoc<T: Id>(x: T) -> <T as Id>::This { + todo!() +} + +fn mirror1() -> (impl Id<This = &'static str>, impl Sized) { + let mut opaque = mirror1().0; + opaque = mirror1().1; + let x = to_assoc(opaque); + // `?x` equals both opaques, make sure we still use the applicable + // item bound. + x.len(); + (x, x) +} +fn mirror2() -> (impl Sized, impl Id<This = &'static str>) { + let mut opaque = mirror2().0; + opaque = mirror2().1; + let x = to_assoc(opaque); + // `?x` equals both opaques, make sure we still use the applicable + // item bound. + x.len(); + (x, x) +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/no-rigid-alias.rs b/tests/ui/impl-trait/non-defining-uses/no-rigid-alias.rs new file mode 100644 index 00000000000..fca5db3e20f --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/no-rigid-alias.rs @@ -0,0 +1,27 @@ +//@ compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +#![allow(unconditional_recursion)] + +// Test for trait-system-refactor-initiative#182 making sure +// that we don't incorrectly normalize to rigid aliases if the +// opaque type only has a trait bound. + +trait Id { + type This; +} +impl<T> Id for Vec<T> { + type This = Vec<T>; +} +fn to_assoc<T: Id>(x: T) -> <T as Id>::This { + todo!() +} + +fn mirror<T>(x: Vec<T>) -> impl Id { + let x = to_assoc(mirror(x)); + // `?x` equals `<opaque::<T> as Id>::This`. We should not infer `?x` + // to be a rigid alias here. + let _: Vec<u32> = x; + x +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs b/tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs new file mode 100644 index 00000000000..50bb3995b94 --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs @@ -0,0 +1,19 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass + +// Regression test for trait-system-refactor-initiative#196. +fn iterator(b: bool) -> impl Iterator<Item = String> { + if b { + // We need to eagerly figure out the type of `i` here by using + // the `<opaque as IntoIterator>::Item` obligation. This means + // we not only have to consider item bounds, but also blanket impls. + for i in iterator(false) { + i.len(); + } + } + + vec![].into_iter() +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/use-item-bound-over-blanket-impl.rs b/tests/ui/impl-trait/non-defining-uses/use-item-bound-over-blanket-impl.rs new file mode 100644 index 00000000000..7c2766ade3f --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/use-item-bound-over-blanket-impl.rs @@ -0,0 +1,30 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +#![allow(unconditional_recursion)] + +// Regression test for trait-system-refactor-initiative#182. + +trait Id { + type This; +} +impl<T> Id for T { + type This = T; +} +fn to_assoc<T>(x: T) -> <T as Id>::This { + x +} + +fn mirror<T>(x: Vec<T>) -> impl Id<This = Vec<T>> { + let x = to_assoc(mirror(x)); + // `?x` equals `<opaque::<T> as Id>::This`. We need to eagerly infer the + // type of `?x` to prevent this method call from resulting in an error. + // + // We could use both the item bound to normalize to `Vec<T>`, or the + // blanket impl to normalize to `opaque::<T>`. We have to go with the + // item bound. + x.len(); + x +} +fn main() {} diff --git a/tests/ui/impl-trait/non-defining-uses/use-item-bound.rs b/tests/ui/impl-trait/non-defining-uses/use-item-bound.rs new file mode 100644 index 00000000000..36dcbacbe6f --- /dev/null +++ b/tests/ui/impl-trait/non-defining-uses/use-item-bound.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +#![allow(unconditional_recursion)] +// Regression test for trait-system-refactor-initiative#182. + +trait Id { + type This; +} +impl<T> Id for Vec<T> { + type This = Vec<T>; +} +fn to_assoc<T: Id>(x: T) -> <T as Id>::This { + todo!() +} + +fn mirror<T>(x: Vec<T>) -> impl Id<This = Vec<T>> { + let x = to_assoc(mirror(x)); + // `?x` equals `<opaque::<T> as Id>::This`. We need to eagerly infer the + // type of `?x` to prevent this method call from resulting in an error. + x.len(); + x +} +fn main() {} diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.rs b/tests/ui/inference/note-and-explain-ReVar-124973.rs index f1e24645636..aa4b909fa76 100644 --- a/tests/ui/inference/note-and-explain-ReVar-124973.rs +++ b/tests/ui/inference/note-and-explain-ReVar-124973.rs @@ -3,6 +3,7 @@ #![feature(c_variadic)] async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} -//~^ ERROR hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds +//~^ ERROR functions cannot be both `async` and C-variadic +//~| ERROR hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds fn main() {} diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.stderr b/tests/ui/inference/note-and-explain-ReVar-124973.stderr index 574f6508e4c..964fbc4a4fb 100644 --- a/tests/ui/inference/note-and-explain-ReVar-124973.stderr +++ b/tests/ui/inference/note-and-explain-ReVar-124973.stderr @@ -1,3 +1,9 @@ +error: functions cannot be both `async` and C-variadic + --> $DIR/note-and-explain-ReVar-124973.rs:5:1 + | +LL | async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} + | ^^^^^ `async` because of this ^^^ C-variadic because of this + error[E0700]: hidden type for `impl Future<Output = ()>` captures lifetime that does not appear in bounds --> $DIR/note-and-explain-ReVar-124973.rs:5:73 | @@ -8,6 +14,6 @@ LL | async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} | = note: hidden type `{async fn body of multiple_named_lifetimes<'a, 'b>()}` captures lifetime `'_` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/inline-const/const-expr-generic-err.stderr b/tests/ui/inline-const/const-expr-generic-err.stderr index e053e88db17..26039ba6d44 100644 --- a/tests/ui/inline-const/const-expr-generic-err.stderr +++ b/tests/ui/inline-const/const-expr-generic-err.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::<T>() == 0 - --> $DIR/const-expr-generic-err.rs:4:21 + --> $DIR/const-expr-generic-err.rs:4:13 | LL | const { assert!(std::mem::size_of::<T>() == 0); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `foo::<i32>::{constant#0}` failed here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `foo::<i32>::{constant#0}` failed here note: erroneous constant encountered --> $DIR/const-expr-generic-err.rs:4:5 diff --git a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs index cdf8aa85482..db2b4a6ad20 100644 --- a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs +++ b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs @@ -5,9 +5,10 @@ //@ [strict]compile-flags: -Zstrict-init-checks //@ needs-subprocess //@ ignore-backends: gcc +//@ edition:2024 #![allow(deprecated, invalid_value)] -#![feature(never_type)] +#![feature(never_type, rustc_private)] use std::{ mem::{self, MaybeUninit, ManuallyDrop}, @@ -15,6 +16,9 @@ use std::{ num, }; +#[cfg(target_os = "linux")] +extern crate libc; + #[allow(dead_code)] struct Foo { x: u8, @@ -108,6 +112,17 @@ fn test_panic_msg_only_if_strict<T>(op: impl (FnOnce() -> T) + 'static, msg: &st fn main() { unsafe { + #[cfg(target_os = "linux")] + { + // This test causes a large amount of crashes. If a system + // has a /proc/sys/kernel/core_pattern that uploads core dumps enabled, + // it will take a long time to complete. Set dumpable to 0 to avoid that. + if libc::prctl(libc::PR_SET_DUMPABLE, 0) < 0 { + let err = std::io::Error::last_os_error(); + panic!("failed to disable core dumps {err:?}"); + } + } + // Uninhabited types test_panic_msg( || mem::uninitialized::<!>(), diff --git a/tests/ui/iterators/ranges.stderr b/tests/ui/iterators/ranges.stderr index b9fbcd5304b..f85ce644073 100644 --- a/tests/ui/iterators/ranges.stderr +++ b/tests/ui/iterators/ranges.stderr @@ -8,15 +8,15 @@ LL | for _ in ..10 {} = note: `..end` is a `RangeTo`, which cannot be iterated on; you might have meant to have a bounded `Range`: `0..end` = note: required for `RangeTo<{integer}>` to implement `IntoIterator` -error[E0277]: `RangeToInclusive<{integer}>` is not an iterator +error[E0277]: `std::ops::RangeToInclusive<{integer}>` is not an iterator --> $DIR/ranges.rs:4:14 | LL | for _ in ..=10 {} | ^^^^^ if you meant to iterate until a value (including it), add a starting value | - = help: the trait `Iterator` is not implemented for `RangeToInclusive<{integer}>` + = help: the trait `Iterator` is not implemented for `std::ops::RangeToInclusive<{integer}>` = note: `..=end` is a `RangeToInclusive`, which cannot be iterated on; you might have meant to have a bounded `RangeInclusive`: `0..=end` - = note: required for `RangeToInclusive<{integer}>` to implement `IntoIterator` + = note: required for `std::ops::RangeToInclusive<{integer}>` to implement `IntoIterator` error: aborting due to 2 previous errors diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index 076a08ac6f2..3a3b450f3c5 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -29,18 +29,6 @@ LL | #[no_link] | ^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:27:1 - | -LL | #![no_std] - | ^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:26:1 - | -LL | #![no_std] - | ^^^^^^^^^^ - -error: unused attribute --> $DIR/unused-attr-duplicate.rs:31:1 | LL | #![windows_subsystem = "windows"] @@ -305,6 +293,18 @@ LL | #![type_length_limit = "1048576"] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute + --> $DIR/unused-attr-duplicate.rs:27:1 + | +LL | #![no_std] + | ^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:26:1 + | +LL | #![no_std] + | ^^^^^^^^^^ + +error: unused attribute --> $DIR/unused-attr-duplicate.rs:29:1 | LL | #![no_implicit_prelude] diff --git a/tests/ui/macros/assert-desugaring-145770.rs b/tests/ui/macros/assert-desugaring-145770.rs new file mode 100644 index 00000000000..d56e771ecfb --- /dev/null +++ b/tests/ui/macros/assert-desugaring-145770.rs @@ -0,0 +1,23 @@ +//! Regression test for #145770. +//! +//! Changing the `assert!` desugaring from an `if !cond {}` to `match` expression is +//! backwards-incompatible, and may need to be done over an edition boundary or limit editions for +//! which the desguaring change impacts. + +//@ check-pass + +#[derive(Debug)] +struct F { + data: bool +} + +impl std::ops::Not for F { + type Output = bool; + fn not(self) -> Self::Output { !self.data } +} + +fn main() { + let f = F { data: true }; + + assert!(f); +} diff --git a/tests/ui/macros/assert-macro-lifetimes.rs b/tests/ui/macros/assert-macro-lifetimes.rs deleted file mode 100644 index cc259283204..00000000000 --- a/tests/ui/macros/assert-macro-lifetimes.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ check-pass -#[derive(PartialEq, Eq, Hash)] -struct S; -fn main() { - let foo = std::rc::Rc::new(std::cell::RefCell::new(std::collections::HashMap::<S, S>::new())); - // Ensure that the lifetimes of the borrow do not leak past the end of `main`. - assert!(matches!(foo.borrow().get(&S).unwrap(), S)) -} diff --git a/tests/ui/macros/format-unused-lables.stderr b/tests/ui/macros/format-unused-lables.stderr index fad87fa2aee..90eed8dd86b 100644 --- a/tests/ui/macros/format-unused-lables.stderr +++ b/tests/ui/macros/format-unused-lables.stderr @@ -7,6 +7,11 @@ LL | println!("Test", 123, 456, 789); | | | argument never used | | argument never used | multiple missing formatting specifiers + | +help: format specifiers use curly braces, consider adding 3 format specifiers + | +LL | println!("Test{}{}{}", 123, 456, 789); + | ++++++ error: multiple unused formatting arguments --> $DIR/format-unused-lables.rs:6:9 @@ -19,6 +24,11 @@ LL | 456, | ^^^ argument never used LL | 789 | ^^^ argument never used + | +help: format specifiers use curly braces, consider adding 3 format specifiers + | +LL | println!("Test2{}{}{}", + | ++++++ error: named argument never used --> $DIR/format-unused-lables.rs:11:35 @@ -27,6 +37,11 @@ LL | println!("Some stuff", UNUSED="args"); | ------------ ^^^^^^ named argument never used | | | formatting specifier missing + | +help: format specifiers use curly braces, consider adding a format specifier + | +LL | println!("Some stuff{}", UNUSED="args"); + | ++ error: multiple unused formatting arguments --> $DIR/format-unused-lables.rs:14:9 diff --git a/tests/ui/macros/metavar-expressions/concat-allowed-operations.rs b/tests/ui/macros/metavar-expressions/concat-allowed-operations.rs index 695a752fe17..5ac50c943d0 100644 --- a/tests/ui/macros/metavar-expressions/concat-allowed-operations.rs +++ b/tests/ui/macros/metavar-expressions/concat-allowed-operations.rs @@ -92,6 +92,12 @@ macro_rules! combinations { }}; } +macro_rules! int_struct { + ($n: literal) => { + struct ${concat(E, $n)}; + } +} + fn main() { create_things!(behold); behold_separated_idents_in_a_fn(); @@ -112,4 +118,16 @@ fn main() { assert_eq!(VAR_123, 2); combinations!(_hello, "a", b, "b"); + + int_struct!(1_0); + int_struct!(2); + int_struct!(3___0); + int_struct!(7_); + int_struct!(08); + + let _ = E1_0; + let _ = E2; + let _ = E3___0; + let _ = E7_; + let _ = E08; } diff --git a/tests/ui/macros/metavar-expressions/concat-usage-errors.rs b/tests/ui/macros/metavar-expressions/concat-usage-errors.rs index 7d8756de9e2..277ad240b1b 100644 --- a/tests/ui/macros/metavar-expressions/concat-usage-errors.rs +++ b/tests/ui/macros/metavar-expressions/concat-usage-errors.rs @@ -140,7 +140,9 @@ macro_rules! bad_literal_non_string { //~| ERROR metavariables of `${concat(..)}` must be of type //~| ERROR metavariables of `${concat(..)}` must be of type //~| ERROR metavariables of `${concat(..)}` must be of type - //~| ERROR metavariables of `${concat(..)}` must be of type + //~| ERROR floats are not supported as metavariables of `${concat(..)}` + //~| ERROR integer metavariables of `${concat(..)}` must not be suffixed + //~| ERROR integer metavariables of `${concat(..)}` must not be suffixed } } @@ -149,7 +151,6 @@ macro_rules! bad_tt_literal { const ${concat(_foo, $tt)}: () = (); //~^ ERROR metavariables of `${concat(..)}` must be of type //~| ERROR metavariables of `${concat(..)}` must be of type - //~| ERROR metavariables of `${concat(..)}` must be of type } } @@ -178,13 +179,14 @@ fn main() { bad_literal_string!("1.0"); bad_literal_string!("'1'"); - bad_literal_non_string!(1); bad_literal_non_string!(-1); bad_literal_non_string!(1.0); bad_literal_non_string!('1'); bad_literal_non_string!(false); + bad_literal_non_string!(4f64); + bad_literal_non_string!(5u8); + bad_literal_non_string!(6_u8); - bad_tt_literal!(1); bad_tt_literal!(1.0); bad_tt_literal!('1'); } diff --git a/tests/ui/macros/metavar-expressions/concat-usage-errors.stderr b/tests/ui/macros/metavar-expressions/concat-usage-errors.stderr index 8be3e792ec3..c124b76cb78 100644 --- a/tests/ui/macros/metavar-expressions/concat-usage-errors.stderr +++ b/tests/ui/macros/metavar-expressions/concat-usage-errors.stderr @@ -130,7 +130,7 @@ error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `t LL | ${concat($ex, aaaa)} | ^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported error: variable `foo` is not recognized in meta-variable expression --> $DIR/concat-usage-errors.rs:37:30 @@ -276,7 +276,7 @@ error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `t LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` --> $DIR/concat-usage-errors.rs:138:31 @@ -284,7 +284,7 @@ error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `t LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` @@ -293,7 +293,7 @@ error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `t LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` @@ -302,43 +302,45 @@ error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `t LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` +error: floats are not supported as metavariables of `${concat(..)}` --> $DIR/concat-usage-errors.rs:138:31 | LL | const ${concat(_foo, $literal)}: () = (); | ^^^^^^^ + +error: integer metavariables of `${concat(..)}` must not be suffixed + --> $DIR/concat-usage-errors.rs:138:31 | - = note: currently only string literals are supported - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +LL | const ${concat(_foo, $literal)}: () = (); + | ^^^^^^^ -error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/concat-usage-errors.rs:149:31 +error: integer metavariables of `${concat(..)}` must not be suffixed + --> $DIR/concat-usage-errors.rs:138:31 | -LL | const ${concat(_foo, $tt)}: () = (); - | ^^ +LL | const ${concat(_foo, $literal)}: () = (); + | ^^^^^^^ | - = note: currently only string literals are supported + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/concat-usage-errors.rs:149:31 + --> $DIR/concat-usage-errors.rs:151:31 | LL | const ${concat(_foo, $tt)}: () = (); | ^^ | - = note: currently only string literals are supported - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: currently only string and integer literals are supported error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` - --> $DIR/concat-usage-errors.rs:149:31 + --> $DIR/concat-usage-errors.rs:151:31 | LL | const ${concat(_foo, $tt)}: () = (); | ^^ | - = note: currently only string literals are supported + = note: currently only string and integer literals are supported = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 43 previous errors +error: aborting due to 44 previous errors diff --git a/tests/ui/mir/alignment/packed.rs b/tests/ui/mir/alignment/packed.rs index aa79880a21a..cf908365e1a 100644 --- a/tests/ui/mir/alignment/packed.rs +++ b/tests/ui/mir/alignment/packed.rs @@ -12,7 +12,7 @@ fn main() { // Test that we can use addr_of! to get the address of a packed member which according to its // type is not aligned, but because it is a projection from a packed type is a valid place. let ptr0 = std::ptr::addr_of!(memory[0].tail); - let ptr1 = std::ptr::addr_of!(memory[1].tail); + let ptr1 = std::ptr::addr_of!(memory[0].tail); // Even if ptr0 happens to be aligned by chance, ptr1 is not. assert!(!ptr0.is_aligned() || !ptr1.is_aligned()); diff --git a/tests/ui/mir/issue-83499-input-output-iteration-ice.rs b/tests/ui/mir/issue-83499-input-output-iteration-ice.rs index 9277994d9b3..dc0d14bf9d6 100644 --- a/tests/ui/mir/issue-83499-input-output-iteration-ice.rs +++ b/tests/ui/mir/issue-83499-input-output-iteration-ice.rs @@ -4,7 +4,6 @@ fn main() {} -fn foo(_: Bar, ...) -> impl {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention -//~| ERROR cannot find type `Bar` in this scope +unsafe extern "C" fn foo(_: Bar, ...) -> impl {} +//~^ ERROR cannot find type `Bar` in this scope //~| ERROR at least one trait must be specified diff --git a/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr b/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr index 4a1aa49eb6e..31a393e7367 100644 --- a/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr +++ b/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr @@ -1,21 +1,15 @@ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/issue-83499-input-output-iteration-ice.rs:7:16 - | -LL | fn foo(_: Bar, ...) -> impl {} - | ^^^ - error: at least one trait must be specified - --> $DIR/issue-83499-input-output-iteration-ice.rs:7:24 + --> $DIR/issue-83499-input-output-iteration-ice.rs:7:42 | -LL | fn foo(_: Bar, ...) -> impl {} - | ^^^^ +LL | unsafe extern "C" fn foo(_: Bar, ...) -> impl {} + | ^^^^ error[E0412]: cannot find type `Bar` in this scope - --> $DIR/issue-83499-input-output-iteration-ice.rs:7:11 + --> $DIR/issue-83499-input-output-iteration-ice.rs:7:29 | -LL | fn foo(_: Bar, ...) -> impl {} - | ^^^ not found in this scope +LL | unsafe extern "C" fn foo(_: Bar, ...) -> impl {} + | ^^^ not found in this scope -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/mir/unsized-extern-static.stderr b/tests/ui/mir/unsized-extern-static.stderr index 93aed3549d7..c0810e650ef 100644 --- a/tests/ui/mir/unsized-extern-static.stderr +++ b/tests/ui/mir/unsized-extern-static.stderr @@ -5,6 +5,11 @@ LL | println!("C", unsafe { &symbol }); | --- ^^^^^^^^^^^^^^^^^^ argument never used | | | formatting specifier missing + | +help: format specifiers use curly braces, consider adding a format specifier + | +LL | println!("C{}", unsafe { &symbol }); + | ++ error[E0277]: the size for values of type `[i8]` cannot be known at compilation time --> $DIR/unsized-extern-static.rs:6:5 diff --git a/tests/ui/new-range/disabled.rs b/tests/ui/new-range/disabled.rs index 1a5fe3f9743..6ba29f5ca9a 100644 --- a/tests/ui/new-range/disabled.rs +++ b/tests/ui/new-range/disabled.rs @@ -6,20 +6,20 @@ fn main() { // Unchanged let a: core::range::RangeFull = ..; let b: core::range::RangeTo<u8> = ..2; - let c: core::range::RangeToInclusive<u8> = ..=3; let _: core::ops::RangeFull = a; let _: core::ops::RangeTo<u8> = b; - let _: core::ops::RangeToInclusive<u8> = c; // Changed let a: core::range::legacy::RangeFrom<u8> = 1..; let b: core::range::legacy::Range<u8> = 2..3; let c: core::range::legacy::RangeInclusive<u8> = 4..=5; + let d: core::range::legacy::RangeToInclusive<u8> = ..=3; let a: core::ops::RangeFrom<u8> = a; let b: core::ops::Range<u8> = b; let c: core::ops::RangeInclusive<u8> = c; + let d: core::ops::RangeToInclusive<u8> = d; let _: core::ops::RangeFrom<u8> = a.into_iter(); let _: core::ops::Range<u8> = b.into_iter(); diff --git a/tests/ui/new-range/enabled.rs b/tests/ui/new-range/enabled.rs index a5fb76ad52b..5ddbba492e7 100644 --- a/tests/ui/new-range/enabled.rs +++ b/tests/ui/new-range/enabled.rs @@ -7,18 +7,18 @@ fn main() { // Unchanged let a: core::range::RangeFull = ..; let b: core::range::RangeTo<u8> = ..2; - let c: core::range::RangeToInclusive<u8> = ..=3; let _: core::ops::RangeFull = a; let _: core::ops::RangeTo<u8> = b; - let _: core::ops::RangeToInclusive<u8> = c; // Changed let a: core::range::RangeFrom<u8> = 1..; let b: core::range::Range<u8> = 2..3; let c: core::range::RangeInclusive<u8> = 4..=5; + let d: core::range::RangeToInclusive<u8> = ..=3; let _: core::range::IterRangeFrom<u8> = a.into_iter(); let _: core::range::IterRange<u8> = b.into_iter(); let _: core::range::IterRangeInclusive<u8> = c.into_iter(); + // RangeToInclusive has no Iterator implementation } diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 243924e6c53..4db056f15a5 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -4,25 +4,28 @@ fn main() {} fn f1_1(x: isize, ...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR `...` is not supported for non-extern functions fn f1_2(...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR `...` is not supported for non-extern functions + +unsafe extern "Rust" fn f1_3(...) {} +//~^ ERROR `...` is not supported for `extern "Rust"` functions extern "C" fn f2_1(x: isize, ...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR functions with a C variable argument list must be unsafe extern "C" fn f2_2(...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR functions with a C variable argument list must be unsafe extern "C" fn f2_3(..., x: isize) {} //~^ ERROR `...` must be the last argument of a C-variadic function extern "C" fn f3_1(x: isize, ...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR functions with a C variable argument list must be unsafe extern "C" fn f3_2(...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR functions with a C variable argument list must be unsafe extern "C" fn f3_3(..., x: isize) {} //~^ ERROR `...` must be the last argument of a C-variadic function @@ -33,12 +36,12 @@ const unsafe extern "C" fn f4_1(x: isize, ...) {} const extern "C" fn f4_2(x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~| ERROR functions with a C variable argument list must be unsafe //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time const extern "C" fn f4_3(..., x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~| ERROR functions with a C variable argument list must be unsafe //~| ERROR `...` must be the last argument of a C-variadic function extern "C" { @@ -50,30 +53,30 @@ struct X; impl X { fn i_f1(x: isize, ...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn i_f2(...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn i_f3(..., x: isize, ...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list //~| ERROR `...` must be the last argument of a C-variadic function fn i_f4(..., x: isize, ...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list //~| ERROR `...` must be the last argument of a C-variadic function const fn i_f5(x: isize, ...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list //~| ERROR functions cannot be both `const` and C-variadic //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time } trait T { fn t_f1(x: isize, ...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn t_f2(x: isize, ...); - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn t_f3(...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn t_f4(...); - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn t_f5(..., x: isize) {} //~^ ERROR `...` must be the last argument of a C-variadic function fn t_f6(..., x: isize); diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 5c55cc38b56..0cd78318de6 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -1,181 +1,225 @@ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:6:19 | LL | fn f1_1(x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9 | LL | fn f1_2(...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: `...` is not supported for `extern "Rust"` functions --> $DIR/variadic-ffi-semantic-restrictions.rs:12:30 | +LL | unsafe extern "Rust" fn f1_3(...) {} + | ------------- ^^^ + | | + | `extern "Rust"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: functions with a C variable argument list must be unsafe + --> $DIR/variadic-ffi-semantic-restrictions.rs:15:30 + | LL | extern "C" fn f2_1(x: isize, ...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | unsafe extern "C" fn f2_1(x: isize, ...) {} + | ++++++ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:15:20 +error: functions with a C variable argument list must be unsafe + --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 | LL | extern "C" fn f2_2(...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | unsafe extern "C" fn f2_2(...) {} + | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:21:20 | LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:21:30 +error: functions with a C variable argument list must be unsafe + --> $DIR/variadic-ffi-semantic-restrictions.rs:24:30 | LL | extern "C" fn f3_1(x: isize, ...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | unsafe extern "C" fn f3_1(x: isize, ...) {} + | ++++++ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:24:20 +error: functions with a C variable argument list must be unsafe + --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 | LL | extern "C" fn f3_2(...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | unsafe extern "C" fn f3_2(...) {} + | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:30:20 | LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:30:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:33:1 | LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:34:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:37:1 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:34:36 +error: functions with a C variable argument list must be unsafe + --> $DIR/variadic-ffi-semantic-restrictions.rs:37:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | const unsafe extern "C" fn f4_2(x: isize, ...) {} + | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:39:26 + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:26 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:39:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:1 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:39:41 +error: functions with a C variable argument list must be unsafe + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:41 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | const unsafe extern "C" fn f4_3(..., x: isize, ...) {} + | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:45:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:48:13 | LL | fn e_f2(..., x: isize); | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:52:23 +error: associated functions cannot have a C variable argument list + --> $DIR/variadic-ffi-semantic-restrictions.rs:55:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 +error: associated functions cannot have a C variable argument list + --> $DIR/variadic-ffi-semantic-restrictions.rs:57:13 | LL | fn i_f2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:56:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:56:28 +error: associated functions cannot have a C variable argument list + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:28 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:59:28 +error: associated functions cannot have a C variable argument list + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:28 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:5 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:5 | LL | const fn i_f5(x: isize, ...) {} | ^^^^^ ^^^ C-variadic because of this | | | `const` because of this -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 +error: associated functions cannot have a C variable argument list + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:69:23 +error: associated functions cannot have a C variable argument list + --> $DIR/variadic-ffi-semantic-restrictions.rs:72:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:71:23 +error: associated functions cannot have a C variable argument list + --> $DIR/variadic-ffi-semantic-restrictions.rs:74:23 | LL | fn t_f2(x: isize, ...); | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:73:13 +error: associated functions cannot have a C variable argument list + --> $DIR/variadic-ffi-semantic-restrictions.rs:76:13 | LL | fn t_f3(...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:75:13 +error: associated functions cannot have a C variable argument list + --> $DIR/variadic-ffi-semantic-restrictions.rs:78:13 | LL | fn t_f4(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:77:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:80:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13 | LL | fn t_f6(..., x: isize); | ^^^ error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:30:43 + --> $DIR/variadic-ffi-semantic-restrictions.rs:33:43 | LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | ^^^ - value is dropped here @@ -183,7 +227,7 @@ LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | the destructor for this type cannot be evaluated in constant functions error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:34:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:37:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ - value is dropped here @@ -191,13 +235,13 @@ LL | const extern "C" fn f4_2(x: isize, ...) {} | the destructor for this type cannot be evaluated in constant functions error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions -error: aborting due to 32 previous errors +error: aborting due to 33 previous errors For more information about this error, try `rustc --explain E0493`. diff --git a/tests/ui/range/issue-54505-no-literals.stderr b/tests/ui/range/issue-54505-no-literals.stderr index c6d4384bcd3..62e2fe4a838 100644 --- a/tests/ui/range/issue-54505-no-literals.stderr +++ b/tests/ui/range/issue-54505-no-literals.stderr @@ -207,7 +207,7 @@ LL | take_range(std::ops::RangeToInclusive { end: 5 }); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeToInclusive<{integer}>` + found struct `std::ops::RangeToInclusive<{integer}>` note: function defined here --> $DIR/issue-54505-no-literals.rs:12:4 | @@ -227,7 +227,7 @@ LL | take_range(::std::ops::RangeToInclusive { end: 5 }); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeToInclusive<{integer}>` + found struct `std::ops::RangeToInclusive<{integer}>` note: function defined here --> $DIR/issue-54505-no-literals.rs:12:4 | diff --git a/tests/ui/range/issue-54505-no-std.stderr b/tests/ui/range/issue-54505-no-std.stderr index 2aa1d584046..866a82afb7e 100644 --- a/tests/ui/range/issue-54505-no-std.stderr +++ b/tests/ui/range/issue-54505-no-std.stderr @@ -112,7 +112,7 @@ LL | take_range(..=42); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeToInclusive<{integer}>` + found struct `core::ops::RangeToInclusive<{integer}>` note: function defined here --> $DIR/issue-54505-no-std.rs:25:4 | diff --git a/tests/ui/range/issue-54505.stderr b/tests/ui/range/issue-54505.stderr index 8b669b2910f..4d94c6c2d09 100644 --- a/tests/ui/range/issue-54505.stderr +++ b/tests/ui/range/issue-54505.stderr @@ -112,7 +112,7 @@ LL | take_range(..=42); | arguments to this function are incorrect | = note: expected reference `&_` - found struct `RangeToInclusive<{integer}>` + found struct `std::ops::RangeToInclusive<{integer}>` note: function defined here --> $DIR/issue-54505.rs:10:4 | diff --git a/tests/ui/simd/const-err-trumps-simd-err.stderr b/tests/ui/simd/const-err-trumps-simd-err.stderr index 6d25a28c92c..93d1fce637f 100644 --- a/tests/ui/simd/const-err-trumps-simd-err.stderr +++ b/tests/ui/simd/const-err-trumps-simd-err.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: LANE < 4 - --> $DIR/const-err-trumps-simd-err.rs:17:21 + --> $DIR/const-err-trumps-simd-err.rs:17:13 | LL | const { assert!(LANE < 4); } // the error should be here... - | ^^^^^^^^ evaluation of `get_elem::<4>::{constant#0}` failed here + | ^^^^^^^^^^^^^^^^^ evaluation of `get_elem::<4>::{constant#0}` failed here note: erroneous constant encountered --> $DIR/const-err-trumps-simd-err.rs:17:5 diff --git a/tests/ui/static/static-align.rs b/tests/ui/static/static-align.rs new file mode 100644 index 00000000000..93241db09f9 --- /dev/null +++ b/tests/ui/static/static-align.rs @@ -0,0 +1,26 @@ +//@ run-pass +#![feature(static_align)] + +#[rustc_align_static(64)] +static A: u8 = 0; + +#[rustc_align_static(64)] +static B: u8 = 0; + +#[rustc_align_static(128)] +#[no_mangle] +static EXPORTED: u64 = 0; + +unsafe extern "C" { + #[rustc_align_static(128)] + #[link_name = "EXPORTED"] + static C: u64; +} + +fn main() { + assert!(core::ptr::from_ref(&A).addr().is_multiple_of(64)); + assert!(core::ptr::from_ref(&B).addr().is_multiple_of(64)); + + assert!(core::ptr::from_ref(&EXPORTED).addr().is_multiple_of(128)); + unsafe { assert!(core::ptr::from_ref(&C).addr().is_multiple_of(128)) }; +} diff --git a/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs b/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs new file mode 100644 index 00000000000..29799624d78 --- /dev/null +++ b/tests/ui/suggestions/missing-format-specifiers-issue-68293.rs @@ -0,0 +1,35 @@ +fn no_format_specifier_two_unused_args() { + println!("Hello", "World"); + //~^ ERROR argument never used + //~| NOTE formatting specifier missing + //~| NOTE argument never used + //~| HELP format specifiers use curly braces, consider adding a format specifier +} + +fn no_format_specifier_multiple_unused_args() { + println!("list: ", 1, 2, 3); + //~^ ERROR multiple unused formatting arguments + //~| NOTE multiple missing formatting specifiers + //~| NOTE argument never used + //~| NOTE argument never used + //~| NOTE argument never used + //~| HELP format specifiers use curly braces, consider adding 3 format specifiers +} + +fn missing_format_specifiers_one_unused_arg() { + println!("list: {}, {}", 1, 2, 3); + //~^ ERROR argument never used + //~| NOTE formatting specifier missing + //~| NOTE argument never used +} + +fn missing_format_specifiers_multiple_unused_args() { + println!("list: {}", 1, 2, 3); + //~^ ERROR multiple unused formatting arguments + //~| NOTE multiple missing formatting specifiers + //~| NOTE argument never used + //~| NOTE argument never used + //~| NOTE consider adding 2 format specifiers +} + +fn main() { } diff --git a/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr b/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr new file mode 100644 index 00000000000..081409789f5 --- /dev/null +++ b/tests/ui/suggestions/missing-format-specifiers-issue-68293.stderr @@ -0,0 +1,49 @@ +error: argument never used + --> $DIR/missing-format-specifiers-issue-68293.rs:2:23 + | +LL | println!("Hello", "World"); + | ------- ^^^^^^^ argument never used + | | + | formatting specifier missing + | +help: format specifiers use curly braces, consider adding a format specifier + | +LL | println!("Hello{}", "World"); + | ++ + +error: multiple unused formatting arguments + --> $DIR/missing-format-specifiers-issue-68293.rs:10:24 + | +LL | println!("list: ", 1, 2, 3); + | -------- ^ ^ ^ argument never used + | | | | + | | | argument never used + | | argument never used + | multiple missing formatting specifiers + | +help: format specifiers use curly braces, consider adding 3 format specifiers + | +LL | println!("list: {}{}{}", 1, 2, 3); + | ++++++ + +error: argument never used + --> $DIR/missing-format-specifiers-issue-68293.rs:20:36 + | +LL | println!("list: {}, {}", 1, 2, 3); + | -------------- ^ argument never used + | | + | formatting specifier missing + +error: multiple unused formatting arguments + --> $DIR/missing-format-specifiers-issue-68293.rs:27:29 + | +LL | println!("list: {}", 1, 2, 3); + | ---------- ^ ^ argument never used + | | | + | | argument never used + | multiple missing formatting specifiers + | + = note: consider adding 2 format specifiers + +error: aborting due to 4 previous errors + diff --git a/tests/ui/trait-bounds/more_maybe_bounds.rs b/tests/ui/trait-bounds/more_maybe_bounds.rs index d367dd5b299..ddd4313bd5e 100644 --- a/tests/ui/trait-bounds/more_maybe_bounds.rs +++ b/tests/ui/trait-bounds/more_maybe_bounds.rs @@ -21,6 +21,20 @@ fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {} // FIXME: `?Trait1` should be rejected, `Trait1` isn't marked `#[lang = "default_traitN"]`. fn baz<T>() where T: Iterator<Item: ?Trait1> {} +//~^ ERROR this relaxed bound is not permitted here + +struct S1<T>(T); + +impl<T> S1<T> { + fn f() where T: ?Trait1 {} + //~^ ERROR this relaxed bound is not permitted here +} + +trait Trait5<'a> {} + +struct S2<T>(T) where for<'a> T: ?Trait5<'a>; +//~^ ERROR this relaxed bound is not permitted here +//~| ERROR bound modifier `?` can only be applied to default traits like `Sized` struct S; impl !Trait2 for S {} diff --git a/tests/ui/trait-bounds/more_maybe_bounds.stderr b/tests/ui/trait-bounds/more_maybe_bounds.stderr index 8dd83fc7728..0d78cfd5820 100644 --- a/tests/ui/trait-bounds/more_maybe_bounds.stderr +++ b/tests/ui/trait-bounds/more_maybe_bounds.stderr @@ -1,3 +1,27 @@ +error: this relaxed bound is not permitted here + --> $DIR/more_maybe_bounds.rs:23:37 + | +LL | fn baz<T>() where T: Iterator<Item: ?Trait1> {} + | ^^^^^^^ + | + = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item + +error: this relaxed bound is not permitted here + --> $DIR/more_maybe_bounds.rs:29:21 + | +LL | fn f() where T: ?Trait1 {} + | ^^^^^^^ + | + = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item + +error: this relaxed bound is not permitted here + --> $DIR/more_maybe_bounds.rs:35:34 + | +LL | struct S2<T>(T) where for<'a> T: ?Trait5<'a>; + | ^^^^^^^^^^^ + | + = note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item + error: bound modifier `?` can only be applied to default traits like `Sized` --> $DIR/more_maybe_bounds.rs:17:20 | @@ -16,5 +40,11 @@ error: bound modifier `?` can only be applied to default traits like `Sized` LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {} | ^^^^^^^ -error: aborting due to 3 previous errors +error: bound modifier `?` can only be applied to default traits like `Sized` + --> $DIR/more_maybe_bounds.rs:35:34 + | +LL | struct S2<T>(T) where for<'a> T: ?Trait5<'a>; + | ^^^^^^^^^^^ + +error: aborting due to 7 previous errors diff --git a/tests/ui/traits/const-traits/const-impl-trait.rs b/tests/ui/traits/const-traits/const-impl-trait.rs index da28d9a47c3..c89aaa62d99 100644 --- a/tests/ui/traits/const-traits/const-impl-trait.rs +++ b/tests/ui/traits/const-traits/const-impl-trait.rs @@ -1,7 +1,5 @@ +//@ check-pass //@ compile-flags: -Znext-solver -//@ known-bug: #110395 - -// Broken until `(): const PartialEq` #![allow(incomplete_features)] #![feature(const_trait_impl, const_cmp, const_destruct)] diff --git a/tests/ui/traits/const-traits/const-impl-trait.stderr b/tests/ui/traits/const-traits/const-impl-trait.stderr deleted file mode 100644 index cbb68d8c983..00000000000 --- a/tests/ui/traits/const-traits/const-impl-trait.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0277]: the trait bound `(): const PartialEq` is not satisfied - --> $DIR/const-impl-trait.rs:34:17 - | -LL | assert!(cmp(&())); - | --- ^^^ - | | - | required by a bound introduced by this call - | -note: required by a bound in `cmp` - --> $DIR/const-impl-trait.rs:11:23 - | -LL | const fn cmp(a: &impl [const] PartialEq) -> bool { - | ^^^^^^^^^^^^^^^^^ required by this bound in `cmp` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-use.rs b/tests/ui/traits/const-traits/const_derives/derive-const-use.rs index 1a3012de06f..78c25ccd6e5 100644 --- a/tests/ui/traits/const-traits/const_derives/derive-const-use.rs +++ b/tests/ui/traits/const-traits/const_derives/derive-const-use.rs @@ -1,5 +1,4 @@ -//@ known-bug: #110395 - +//@ check-pass #![feature(const_trait_impl, const_default, const_cmp, derive_const)] pub struct A; diff --git a/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr b/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr deleted file mode 100644 index 4ea11a0c7ed..00000000000 --- a/tests/ui/traits/const-traits/const_derives/derive-const-use.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0277]: the trait bound `(): [const] PartialEq` is not satisfied - --> $DIR/derive-const-use.rs:16:14 - | -LL | #[derive_const(Default, PartialEq)] - | --------- in this derive macro expansion -LL | pub struct S((), A); - | ^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.rs b/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.rs index ee04f74c8a6..9b03ec7553d 100644 --- a/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.rs +++ b/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.rs @@ -1,24 +1,23 @@ -// Ensure that we don't consider `const Trait` to -// match the macro fragment specifier `ty` as that would be a breaking -// change theoretically speaking. Syntactically trait object types can -// be "bare", i.e., lack the prefix `dyn`. -// By contrast, `?Trait` *does* match `ty` and therefore an arm like -// `?$Trait:path` would never be reached. -// See `parser/macro/mbe-bare-trait-object-maybe-trait-bound.rs`. -// `[const] Trait` is already an error for a `ty` fragment, -// so we do not need to prevent that. +// Ensure that we don't consider `const Trait` to match the macro fragment specifier `ty` +// as that would be a breaking change theoretically speaking. +// +// Syntactically trait object types can be "bare", i.e., lack the prefix `dyn`. +// By contrast, `?Trait` *does* match `ty` and therefore an arm like `?$Trait:path` +// would never be reached. See `parser/macro/macro-bare-trait-object-maybe-trait-bound.rs`. + +//@ check-pass (KEEP THIS AS A PASSING TEST!) macro_rules! check { - ($Type:ty) => { - compile_error!("ty"); - }; - (const $Trait:path) => {}; - ([const] $Trait:path) => { [const] Trait }; + ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + + // DON'T MODIFY THE MATCHERS BELOW UNLESS THE CONST TRAIT MODIFIER SYNTAX CHANGES! + + (const $Trait:path) => { /* KEEP THIS EMPTY! */ }; + // We don't need to check `[const] Trait` here since that matches the `ty` fragment + // already anyway since `[` may begin a slice or array type. However, it'll then + // subsequently fail due to #146122 (section 3). } check! { const Trait } -check! { [const] Trait } -//~^ ERROR: expected identifier, found `]` -//~| ERROR: const trait impls are experimental fn main() {} diff --git a/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.stderr b/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.stderr deleted file mode 100644 index a4e77154b17..00000000000 --- a/tests/ui/traits/const-traits/macro-bare-trait-objects-const-trait-bounds.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error: expected identifier, found `]` - --> $DIR/macro-bare-trait-objects-const-trait-bounds.rs:20:16 - | -LL | ($Type:ty) => { - | -------- while parsing argument for this `ty` macro fragment -... -LL | check! { [const] Trait } - | ^ expected identifier - -error[E0658]: const trait impls are experimental - --> $DIR/macro-bare-trait-objects-const-trait-bounds.rs:20:11 - | -LL | check! { [const] Trait } - | ^^^^^ - | - = note: see issue #143874 <https://github.com/rust-lang/rust/issues/143874> for more information - = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.rs b/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.rs index 35e964eacec..3f70ffa7d10 100644 --- a/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.rs +++ b/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.rs @@ -1,22 +1,32 @@ // Demonstrates and records a theoretical regressions / breaking changes caused by the -// introduction of const trait bounds. +// introduction of `const` and `[const]` trait bounds. -// Setting the edition to 2018 since we don't regress `demo! { dyn const }` in Rust <2018. +// Setting the edition to >2015 since we didn't regress `demo! { dyn const }` in Rust 2015. +// See also test `traits/const-traits/macro-dyn-const-2015.rs`. //@ edition:2018 trait Trait {} macro_rules! demo { - (impl $c:ident Trait) => { impl $c Trait {} }; - //~^ ERROR inherent - //~| WARN trait objects without an explicit `dyn` are deprecated - //~| WARN this is accepted in the current edition - (dyn $c:ident Trait) => { dyn $c Trait {} }; //~ ERROR macro expansion + ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + //~^ ERROR ty + //~| ERROR ty + //~| ERROR ty + //~| ERROR ty + + // DON'T MODIFY THE MATCHERS BELOW UNLESS THE CONST TRAIT MODIFIER SYNTAX CHANGES! + + (impl $c:ident Trait) => { /* KEEP THIS EMPTY! */ }; + (dyn $c:ident Trait) => { /* KEEP THIS EMPTY! */ }; + + (impl [const] Trait) => { /* KEEP THIS EMPTY! */ }; + (dyn [const] Trait) => { /* KEEP THIS EMPTY! */ }; } -demo! { impl const Trait } -//~^ ERROR const trait impls are experimental +demo! { impl const Trait } //~ ERROR const trait impls are experimental +demo! { dyn const Trait } //~ ERROR const trait impls are experimental -demo! { dyn const Trait } +demo! { impl [const] Trait } //~ ERROR const trait impls are experimental +demo! { dyn [const] Trait } //~ ERROR const trait impls are experimental fn main() {} diff --git a/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr b/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr index 7a4061d9c18..383936b9df1 100644 --- a/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr +++ b/tests/ui/traits/const-traits/macro-const-trait-bound-theoretical-regression.stderr @@ -1,31 +1,49 @@ -error: inherent impls cannot be const - --> $DIR/macro-const-trait-bound-theoretical-regression.rs:10:40 +error: ty + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:11:19 | -LL | (impl $c:ident Trait) => { impl $c Trait {} }; - | ^^^^^ inherent impl for this type +LL | ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + | ^^^^^^^^^^^^^^^^^^^^ ... LL | demo! { impl const Trait } - | -------------------------- - | | | - | | const because of this - | in this macro invocation + | -------------------------- in this macro invocation | - = note: only trait implementations may be annotated with `const` = note: this error originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info) -error: macro expansion ignores keyword `dyn` and any tokens following - --> $DIR/macro-const-trait-bound-theoretical-regression.rs:14:31 +error: ty + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:11:19 | -LL | (dyn $c:ident Trait) => { dyn $c Trait {} }; - | ^^^ +LL | ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + | ^^^^^^^^^^^^^^^^^^^^ ... LL | demo! { dyn const Trait } - | ------------------------- caused by the macro expansion here + | ------------------------- in this macro invocation | - = note: the usage of `demo!` is likely invalid in item context + = note: this error originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: ty + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:11:19 + | +LL | ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + | ^^^^^^^^^^^^^^^^^^^^ +... +LL | demo! { impl [const] Trait } + | ---------------------------- in this macro invocation + | + = note: this error originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: ty + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:11:19 + | +LL | ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + | ^^^^^^^^^^^^^^^^^^^^ +... +LL | demo! { dyn [const] Trait } + | --------------------------- in this macro invocation + | + = note: this error originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0658]: const trait impls are experimental - --> $DIR/macro-const-trait-bound-theoretical-regression.rs:17:14 + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:26:14 | LL | demo! { impl const Trait } | ^^^^^ @@ -34,24 +52,36 @@ LL | demo! { impl const Trait } = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/macro-const-trait-bound-theoretical-regression.rs:10:40 +error[E0658]: const trait impls are experimental + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:27:13 | -LL | (impl $c:ident Trait) => { impl $c Trait {} }; - | ^^^^^ -... -LL | demo! { impl const Trait } - | -------------------------- in this macro invocation +LL | demo! { dyn const Trait } + | ^^^^^ | - = warning: this is accepted in the current edition (Rust 2018) but is a hard error in Rust 2021! - = note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2021/warnings-promoted-to-error.html> - = note: `#[warn(bare_trait_objects)]` (part of `#[warn(rust_2021_compatibility)]`) on by default - = note: this warning originates in the macro `demo` (in Nightly builds, run with -Z macro-backtrace for more info) -help: you might have intended to implement this trait for a given type + = note: see issue #143874 <https://github.com/rust-lang/rust/issues/143874> for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: const trait impls are experimental + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:29:14 + | +LL | demo! { impl [const] Trait } + | ^^^^^^^ + | + = note: see issue #143874 <https://github.com/rust-lang/rust/issues/143874> for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: const trait impls are experimental + --> $DIR/macro-const-trait-bound-theoretical-regression.rs:30:13 | -LL | (impl $c:ident Trait) => { impl $c Trait for /* Type */ {} }; - | ++++++++++++++ +LL | demo! { dyn [const] Trait } + | ^^^^^^^ + | + = note: see issue #143874 <https://github.com/rust-lang/rust/issues/143874> for more information + = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 3 previous errors; 1 warning emitted +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/macro-dyn-const-2015.rs b/tests/ui/traits/const-traits/macro-dyn-const-2015.rs index fadfbe66788..0dfa482b986 100644 --- a/tests/ui/traits/const-traits/macro-dyn-const-2015.rs +++ b/tests/ui/traits/const-traits/macro-dyn-const-2015.rs @@ -1,14 +1,19 @@ -// Ensure that the introduction of const trait bound didn't regress this code in Rust 2015. -// See also `mbe-const-trait-bound-theoretical-regression.rs`. +// Ensure that the introduction of `const` and `[const]` trait bounds didn't regress this +// Rust 2015 code. See also test `macro-const-trait-bound-theoretical-regression.rs`. //@ edition: 2015 -//@ check-pass +//@ check-pass (KEEP THIS AS A PASSING TEST!) macro_rules! check { - ($ty:ty) => { compile_error!("ty"); }; - (dyn $c:ident) => {}; + ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + + // DON'T MODIFY THE MATCHERS BELOW UNLESS THE CONST TRAIT MODIFIER SYNTAX CHANGES! + + (dyn $c:ident) => { /* KEEP THIS EMPTY! */ }; + (dyn [$c:ident]) => { /* KEEP THIS EMPTY! */ }; } check! { dyn const } +check! { dyn [const] } fn main() {} diff --git a/tests/ui/traits/const-traits/macro-maybe-const-trait-bounds.rs b/tests/ui/traits/const-traits/macro-maybe-const-trait-bounds.rs new file mode 100644 index 00000000000..75077577b8c --- /dev/null +++ b/tests/ui/traits/const-traits/macro-maybe-const-trait-bounds.rs @@ -0,0 +1,25 @@ +// Ensure that we don't consider `[` to begin trait bounds to contain breakages. +// Only `[const]` in its entirety begins a trait bound. +// See also test `macro-const-trait-bound-theoretical-regression.rs`. + +//@ check-pass (KEEP THIS AS A PASSING TEST!) +// Setting the edition to >2015 since we didn't regress `check! { dyn [const] Trait }` in Rust 2015. +// See also test `traits/const-traits/macro-dyn-const-2015.rs`. +//@ edition:2018 + +macro_rules! check { + ($ty:ty) => { compile_error!("ty"); }; // KEEP THIS RULE FIRST AND AS IS! + + // DON'T MODIFY THE MATCHERS BELOW UNLESS THE CONST TRAIT MODIFIER SYNTAX CHANGES! + + (dyn [$($any:tt)*] Trait) => { /* KEEP THIS EMPTY! */ }; + (impl [$($any:tt)*] Trait) => { /* KEEP THIS EMPTY! */ }; +} + +check!(dyn [T] Trait); + +// issue: <https://github.com/rust-lang/rust/issues/146417> +check!(impl [T] Trait); +check!(impl [T: Bound] Trait); + +fn main() {} diff --git a/tests/ui/traits/default_auto_traits/backward-compatible-lazy-bounds-pass.rs b/tests/ui/traits/default_auto_traits/backward-compatible-lazy-bounds-pass.rs deleted file mode 100644 index 745b6ee9bc5..00000000000 --- a/tests/ui/traits/default_auto_traits/backward-compatible-lazy-bounds-pass.rs +++ /dev/null @@ -1,31 +0,0 @@ -//@ check-pass -//@ compile-flags: -Zexperimental-default-bounds - -#![feature(auto_traits, lang_items, no_core, rustc_attrs, trait_alias)] -#![no_std] -#![no_core] - -#[lang = "pointee_sized"] -trait PointeeSized {} - -#[lang = "meta_sized"] -trait MetaSized: PointeeSized {} - -#[lang = "sized"] -trait Sized: MetaSized {} - -#[lang = "default_trait1"] -auto trait DefaultTrait1 {} - -#[lang = "default_trait2"] -auto trait DefaultTrait2 {} - -trait Trait<Rhs: ?Sized = Self> {} -trait Trait1 : Trait {} - -trait Trait2 { - type Type; -} -trait Trait3<T> = Trait2<Type = T>; - -fn main() {} diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.rs b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.rs index e7cca41a47e..2e1a5d2424b 100644 --- a/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.rs +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.rs @@ -13,32 +13,37 @@ #![no_core] #[lang = "pointee_sized"] -trait PointeeSized {} +trait PointeeSized: ?Leak {} #[lang = "meta_sized"] -trait MetaSized: PointeeSized {} +trait MetaSized: PointeeSized + ?Leak {} #[lang = "sized"] -trait Sized: MetaSized {} +trait Sized: MetaSized + ?Leak {} #[lang = "copy"] -pub trait Copy {} +pub trait Copy: ?Leak {} impl<'a, T: ?Sized> Copy for &'a T {} #[lang = "legacy_receiver"] -trait Receiver {} +trait Receiver: ?Leak {} impl<T: ?Sized + ?Leak> Receiver for &T {} +impl<T: ?Sized + ?Leak> Receiver for &mut T {} #[lang = "unsize"] -trait Unsize<T: ?Sized + ?Leak> {} +trait Unsize<T: ?Sized + ?Leak>: ?Leak {} #[lang = "coerce_unsized"] -trait CoerceUnsized<T: ?Leak + ?Sized> {} +trait CoerceUnsized<T: ?Leak + ?Sized>: ?Leak {} impl<'a, 'b: 'a, T: ?Sized + ?Leak + Unsize<U>, U: ?Sized + ?Leak> CoerceUnsized<&'a U> for &'b T {} +// Omit `T: ?Leak` and `U: ?Leak`. +impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'b mut T {} #[lang = "dispatch_from_dyn"] -trait DispatchFromDyn<T: ?Leak> {} +trait DispatchFromDyn<T: ?Leak>: ?Leak {} impl<'a, T: ?Sized + ?Leak + Unsize<U>, U: ?Sized + ?Leak> DispatchFromDyn<&'a U> for &'a T {} +// Omit `T: ?Leak` and `U: ?Leak`. +impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} #[lang = "default_trait1"] auto trait Leak {} @@ -47,25 +52,52 @@ struct NonLeakS; impl !Leak for NonLeakS {} struct LeakS; -trait Trait { - fn leak_foo(&self) {} - fn maybe_leak_foo(&self) where Self: ?Leak {} +fn bounds_check() { + trait LeakTr {} + + trait MaybeLeakTr: ?Leak {} + + impl MaybeLeakTr for NonLeakS {} + + impl LeakTr for LeakS {} + impl MaybeLeakTr for LeakS {} + + let _: &dyn LeakTr = &NonLeakS; + //~^ ERROR the trait bound `NonLeakS: bounds_check::LeakTr` is not satisfied + let _: &dyn LeakTr = &LeakS; + + let _: &(dyn LeakTr + ?Leak) = &NonLeakS; + let _: &(dyn LeakTr + ?Leak) = &LeakS; + + let _: &dyn MaybeLeakTr = &NonLeakS; + let _: &dyn MaybeLeakTr = &LeakS; } -impl Trait for NonLeakS {} -impl Trait for LeakS {} - -fn main() { - let _: &dyn Trait = &NonLeakS; - //~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied - let _: &dyn Trait = &LeakS; - let _: &(dyn Trait + ?Leak) = &LeakS; - let x: &(dyn Trait + ?Leak) = &NonLeakS; - x.leak_foo(); - //~^ ERROR the trait bound `dyn Trait: Leak` is not satisfied - x.maybe_leak_foo(); +fn dyn_compat_check() { + trait DynCompatCheck1: ?Leak { + fn foo(&self) {} + } + + trait DynCompatCheck2: ?Leak { + fn mut_foo(&mut self) {} + } + + impl DynCompatCheck1 for NonLeakS {} + impl DynCompatCheck2 for NonLeakS {} + + let _: &(dyn DynCompatCheck1 + ?Leak) = &NonLeakS; + // There is no `?Leak` bound on corresponding `DispatchFromDyn` impl. + let _: &dyn DynCompatCheck2 = &NonLeakS; + //~^ ERROR the trait `DynCompatCheck2` is not dyn compatible +} + +fn args_check() { + trait LeakTr {} + // Ensure that we validate the generic args of relaxed bounds in trait object types. - let _: dyn Trait + ?Leak<(), Undefined = ()>; + let _: dyn LeakTr + ?Leak<(), Undefined = ()>; //~^ ERROR trait takes 0 generic arguments but 1 generic argument was supplied //~| ERROR associated type `Undefined` not found for `Leak` } + +fn main() {} diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr index b19c082a1b8..b96a2915c33 100644 --- a/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr @@ -1,49 +1,57 @@ -error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied - --> $DIR/maybe-bounds-in-dyn-traits.rs:59:25 +error[E0277]: the trait bound `NonLeakS: bounds_check::LeakTr` is not satisfied + --> $DIR/maybe-bounds-in-dyn-traits.rs:65:26 | -LL | let _: &dyn Trait = &NonLeakS; - | ^^^^^^^^^ unsatisfied trait bound +LL | let _: &dyn LeakTr = &NonLeakS; + | ^^^^^^^^^ unsatisfied trait bound | -help: the trait `Leak` is not implemented for `NonLeakS` - --> $DIR/maybe-bounds-in-dyn-traits.rs:46:1 +help: the trait `bounds_check::LeakTr` is not implemented for `NonLeakS` + --> $DIR/maybe-bounds-in-dyn-traits.rs:51:1 | LL | struct NonLeakS; | ^^^^^^^^^^^^^^^ - = note: required for the cast from `&NonLeakS` to `&dyn Trait + Leak` + = help: the trait `bounds_check::LeakTr` is implemented for `LeakS` + = note: required for the cast from `&NonLeakS` to `&dyn bounds_check::LeakTr + Leak` -error[E0277]: the trait bound `dyn Trait: Leak` is not satisfied - --> $DIR/maybe-bounds-in-dyn-traits.rs:64:7 - | -LL | x.leak_foo(); - | ^^^^^^^^ the trait `Leak` is not implemented for `dyn Trait` - | -note: required by a bound in `Trait::leak_foo` - --> $DIR/maybe-bounds-in-dyn-traits.rs:51:5 - | -LL | fn leak_foo(&self) {} - | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Trait::leak_foo` +error[E0038]: the trait `DynCompatCheck2` is not dyn compatible + --> $DIR/maybe-bounds-in-dyn-traits.rs:90:17 + | +LL | fn mut_foo(&mut self) {} + | --------- help: consider changing method `mut_foo`'s `self` parameter to be `&self`: `&Self` +... +LL | let _: &dyn DynCompatCheck2 = &NonLeakS; + | ^^^^^^^^^^^^^^^ `DynCompatCheck2` is not dyn compatible + | +note: for a trait to be dyn compatible it needs to allow building a vtable + for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility> + --> $DIR/maybe-bounds-in-dyn-traits.rs:82:20 + | +LL | trait DynCompatCheck2: ?Leak { + | --------------- this trait is not dyn compatible... +LL | fn mut_foo(&mut self) {} + | ^^^^^^^^^ ...because method `mut_foo`'s `self` parameter cannot be dispatched on + = help: only type `NonLeakS` implements `DynCompatCheck2`; consider using it directly instead. error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/maybe-bounds-in-dyn-traits.rs:68:25 + --> $DIR/maybe-bounds-in-dyn-traits.rs:98:26 | -LL | let _: dyn Trait + ?Leak<(), Undefined = ()>; - | ^^^^-------------------- help: remove the unnecessary generics - | | - | expected 0 generic arguments +LL | let _: dyn LeakTr + ?Leak<(), Undefined = ()>; + | ^^^^-------------------- help: remove the unnecessary generics + | | + | expected 0 generic arguments | note: trait defined here, with 0 generic parameters - --> $DIR/maybe-bounds-in-dyn-traits.rs:44:12 + --> $DIR/maybe-bounds-in-dyn-traits.rs:49:12 | LL | auto trait Leak {} | ^^^^ error[E0220]: associated type `Undefined` not found for `Leak` - --> $DIR/maybe-bounds-in-dyn-traits.rs:68:34 + --> $DIR/maybe-bounds-in-dyn-traits.rs:98:35 | -LL | let _: dyn Trait + ?Leak<(), Undefined = ()>; - | ^^^^^^^^^ associated type `Undefined` not found +LL | let _: dyn LeakTr + ?Leak<(), Undefined = ()>; + | ^^^^^^^^^ associated type `Undefined` not found error: aborting due to 4 previous errors -Some errors have detailed explanations: E0107, E0220, E0277. -For more information about an error, try `rustc --explain E0107`. +Some errors have detailed explanations: E0038, E0107, E0220, E0277. +For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs index b3801baaf70..ac4c4aca2ef 100644 --- a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.rs @@ -14,18 +14,22 @@ #![no_std] #![no_core] +#[lang = "copy"] +pub trait Copy: ?Leak {} + #[lang = "pointee_sized"] -trait PointeeSized {} +trait PointeeSized: ?Leak {} #[lang = "meta_sized"] -trait MetaSized: PointeeSized {} +trait MetaSized: PointeeSized + ?Leak {} #[lang = "sized"] -trait Sized: MetaSized {} +trait Sized: MetaSized + ?Leak {} #[lang = "legacy_receiver"] -trait LegacyReceiver {} +trait LegacyReceiver: ?Leak {} impl<T: ?Sized + ?Leak> LegacyReceiver for &T {} +// Omit `T: ?Leak`. impl<T: ?Sized> LegacyReceiver for &mut T {} #[lang = "default_trait1"] @@ -38,83 +42,40 @@ struct LeakS; mod supertraits { use crate::*; - trait MaybeLeakT1: ?Leak {} - trait MaybeLeakT2 where Self: ?Leak {} + trait MaybeLeak: ?Leak {} + impl MaybeLeak for NonLeakS {} - impl MaybeLeakT1 for NonLeakS {} - impl MaybeLeakT2 for NonLeakS {} + trait LeakT {} + impl LeakT for NonLeakS {} + //~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied } -mod maybe_self_assoc_type { +mod assoc_type_maybe_bounds { use crate::*; - trait TestBase1<T: ?Sized> {} - trait TestBase2<T: ?Leak + ?Sized> {} - - trait Test1<T> { - type MaybeLeakSelf: TestBase1<Self> where Self: ?Leak; - //~^ ERROR the trait bound `Self: Leak` is not satisfied - type LeakSelf: TestBase1<Self>; - } - - trait Test2<T> { - type MaybeLeakSelf: TestBase2<Self> where Self: ?Leak; - type LeakSelf: TestBase2<Self>; - } - - trait Test3 { + trait Test1 { type Leak1 = LeakS; type Leak2 = NonLeakS; //~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied } - trait Test4 { + trait Test2 { type MaybeLeak1: ?Leak = LeakS; type MaybeLeak2: ?Leak = NonLeakS; } - - trait Test5: ?Leak { - // ok, because assoc types have implicit where Self: Leak - type MaybeLeakSelf1: TestBase1<Self>; - type MaybeLeakSelf2: TestBase2<Self>; - } -} - -mod maybe_self_assoc_const { - use crate::*; - - const fn size_of<T: ?Sized>() -> usize { - 0 - } - - trait Trait { - const CLeak: usize = size_of::<Self>(); - const CNonLeak: usize = size_of::<Self>() where Self: ?Leak; - //~^ ERROR the trait bound `Self: Leak` is not satisfied - } } mod methods { use crate::*; - trait Trait { - fn leak_foo(&self) {} - fn maybe_leak_foo(&self) where Self: ?Leak {} - fn mut_leak_foo(&mut self) {} - // there is no relax bound on corresponding Receiver impl - fn mut_maybe_leak_foo(&mut self) where Self: ?Leak {} - //~^ ERROR `&mut Self` cannot be used as the type of `self` without the `arbitrary_self_types` + trait ReceiveCheck1: ?Leak { + fn foo(&self) {} } - impl Trait for NonLeakS {} - impl Trait for LeakS {} - - fn foo() { - LeakS.leak_foo(); - LeakS.maybe_leak_foo(); - NonLeakS.leak_foo(); - //~^ ERROR the trait bound `NonLeakS: Leak` is not satisfied - NonLeakS.maybe_leak_foo(); + trait ReceiveCheck2: ?Leak { + // There is no `?Leak` bound on corresponding `LegacyReceiver` impl. + fn mut_foo(&mut self) {} + //~^ ERROR `&mut Self` cannot be used as the type of `self` without the `arbitrary_self_types` } } diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr index 372bf817600..ab62ab81b21 100644 --- a/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-traits.stderr @@ -1,81 +1,49 @@ error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied - --> $DIR/maybe-bounds-in-traits.rs:67:22 + --> $DIR/maybe-bounds-in-traits.rs:49:20 | -LL | type Leak2 = NonLeakS; - | ^^^^^^^^ unsatisfied trait bound +LL | impl LeakT for NonLeakS {} + | ^^^^^^^^ unsatisfied trait bound | help: the trait `Leak` is not implemented for `NonLeakS` - --> $DIR/maybe-bounds-in-traits.rs:34:1 + --> $DIR/maybe-bounds-in-traits.rs:38:1 | LL | struct NonLeakS; | ^^^^^^^^^^^^^^^ -note: required by a bound in `Test3::Leak2` - --> $DIR/maybe-bounds-in-traits.rs:67:9 +note: required by a bound in `LeakT` + --> $DIR/maybe-bounds-in-traits.rs:48:5 | -LL | type Leak2 = NonLeakS; - | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Test3::Leak2` +LL | trait LeakT {} + | ^^^^^^^^^^^^^^ required by this bound in `LeakT` -error[E0277]: the trait bound `Self: Leak` is not satisfied - --> $DIR/maybe-bounds-in-traits.rs:55:29 +error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied + --> $DIR/maybe-bounds-in-traits.rs:58:22 | -LL | type MaybeLeakSelf: TestBase1<Self> where Self: ?Leak; - | ^^^^^^^^^^^^^^^ the trait `Leak` is not implemented for `Self` +LL | type Leak2 = NonLeakS; + | ^^^^^^^^ unsatisfied trait bound | -note: required by a bound in `TestBase1` - --> $DIR/maybe-bounds-in-traits.rs:51:21 +help: the trait `Leak` is not implemented for `NonLeakS` + --> $DIR/maybe-bounds-in-traits.rs:38:1 | -LL | trait TestBase1<T: ?Sized> {} - | ^ required by this bound in `TestBase1` -help: consider further restricting `Self` +LL | struct NonLeakS; + | ^^^^^^^^^^^^^^^ +note: required by a bound in `Test1::Leak2` + --> $DIR/maybe-bounds-in-traits.rs:58:9 | -LL | trait Test1<T>: Leak { - | ++++++ +LL | type Leak2 = NonLeakS; + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Test1::Leak2` error[E0658]: `&mut Self` cannot be used as the type of `self` without the `arbitrary_self_types` feature - --> $DIR/maybe-bounds-in-traits.rs:105:31 + --> $DIR/maybe-bounds-in-traits.rs:77:20 | -LL | fn mut_maybe_leak_foo(&mut self) where Self: ?Leak {} - | ^^^^^^^^^ +LL | fn mut_foo(&mut self) {} + | ^^^^^^^^^ | = note: see issue #44874 <https://github.com/rust-lang/rust/issues/44874> for more information = help: add `#![feature(arbitrary_self_types)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = help: consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box<Self>`, `self: Rc<Self>`, or `self: Arc<Self>` -error[E0277]: the trait bound `Self: Leak` is not satisfied - --> $DIR/maybe-bounds-in-traits.rs:92:43 - | -LL | const CNonLeak: usize = size_of::<Self>() where Self: ?Leak; - | ^^^^ the trait `Leak` is not implemented for `Self` - | -note: required by a bound in `size_of` - --> $DIR/maybe-bounds-in-traits.rs:86:22 - | -LL | const fn size_of<T: ?Sized>() -> usize { - | ^ required by this bound in `size_of` -help: consider further restricting `Self` - | -LL | trait Trait: Leak { - | ++++++ - -error[E0277]: the trait bound `NonLeakS: Leak` is not satisfied - --> $DIR/maybe-bounds-in-traits.rs:115:18 - | -LL | NonLeakS.leak_foo(); - | ^^^^^^^^ unsatisfied trait bound - | -help: the trait `Leak` is not implemented for `NonLeakS` - --> $DIR/maybe-bounds-in-traits.rs:34:1 - | -LL | struct NonLeakS; - | ^^^^^^^^^^^^^^^ -note: required by a bound in `methods::Trait::leak_foo` - --> $DIR/maybe-bounds-in-traits.rs:101:9 - | -LL | fn leak_foo(&self) {} - | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Trait::leak_foo` - -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors Some errors have detailed explanations: E0277, E0658. For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/transmutability/uninhabited.stderr b/tests/ui/transmutability/uninhabited.stderr index 9f289852809..4757daec997 100644 --- a/tests/ui/transmutability/uninhabited.stderr +++ b/tests/ui/transmutability/uninhabited.stderr @@ -41,10 +41,10 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/uninhabited.rs:41:17 + --> $DIR/uninhabited.rs:41:9 | LL | assert!(false); - | ^^^^^ evaluation of `yawning_void_struct::_` failed here + | ^^^^^^^^^^^^^^ evaluation of `yawning_void_struct::_` failed here error[E0277]: `()` cannot be safely transmuted into `yawning_void_enum::Void` --> $DIR/uninhabited.rs:71:41 @@ -68,10 +68,10 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/uninhabited.rs:63:17 + --> $DIR/uninhabited.rs:63:9 | LL | assert!(false); - | ^^^^^ evaluation of `yawning_void_enum::_` failed here + | ^^^^^^^^^^^^^^ evaluation of `yawning_void_enum::_` failed here error[E0277]: `u128` cannot be safely transmuted into `DistantVoid` --> $DIR/uninhabited.rs:92:43 @@ -95,10 +95,10 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/uninhabited.rs:87:17 + --> $DIR/uninhabited.rs:87:9 | LL | assert!(false); - | ^^^^^ evaluation of `distant_void::_` failed here + | ^^^^^^^^^^^^^^ evaluation of `distant_void::_` failed here error[E0277]: `Src` cannot be safely transmuted into `issue_126267::Error` --> $DIR/uninhabited.rs:108:42 diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 924fb98ae18..96d85d1e7c1 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -55,7 +55,7 @@ use self::prelude::*; */ #[doc = "inner doc attribute"] #[allow(dead_code, unused_variables)] -#[no_std] +#[attr = NoStd] mod attributes { /// outer single-line doc comment |
