diff options
230 files changed, 3258 insertions, 2510 deletions
diff --git a/Cargo.lock b/Cargo.lock index 8be13e84596..4677d34d2a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1192,6 +1192,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8975ffdaa0ef3661bfe02dbdcc06c9f829dfafe6a3c474de366a8d5e44276921" [[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + +[[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2347,11 +2353,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]] @@ -3123,6 +3129,26 @@ dependencies = [ ] [[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4577,6 +4603,7 @@ dependencies = [ "rustc_macros", "rustc_serialize", "rustc_span", + "schemars", "serde", "serde_derive", "serde_json", @@ -4901,6 +4928,31 @@ dependencies = [ ] [[package]] +name = "schemars" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +dependencies = [ + "dyn-clone", + "ref-cast", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.106", +] + +[[package]] name = "scoped-tls" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4975,6 +5027,17 @@ dependencies = [ ] [[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] name = "serde_json" version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6316,15 +6379,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 +6406,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 +6448,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 +6460,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 +6472,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 +6496,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 +6508,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 +6520,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 +6532,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" 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/context.rs b/compiler/rustc_attr_parsing/src/context.rs index d2b35090135..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; @@ -223,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_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/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs index 05351bd6ca3..7420f18aacb 100644 --- a/compiler/rustc_codegen_ssa/src/back/command.rs +++ b/compiler/rustc_codegen_ssa/src/back/command.rs @@ -109,7 +109,7 @@ impl Command { } Program::Lld(ref p, flavor) => { let mut c = process::Command::new(p); - c.arg("-flavor").arg(flavor.as_str()); + c.arg("-flavor").arg(flavor.desc()); c } }; 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_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index d00a4c35834..4f875cf99ec 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -668,6 +668,10 @@ fn print_crate_info( TargetSpecJson => { println_info!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap()); } + TargetSpecJsonSchema => { + let schema = rustc_target::spec::json_schema(); + println_info!("{}", serde_json::to_string_pretty(&schema).unwrap()); + } AllTargetSpecsJson => { let mut targets = BTreeMap::new(); for name in rustc_target::spec::TARGETS { 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_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_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_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_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 38a6b4b16c9..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); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 9793d8091e2..297df7c2c97 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -70,6 +70,7 @@ pub const PRINT_KINDS: &[(&str, PrintKind)] = &[ ("target-libdir", PrintKind::TargetLibdir), ("target-list", PrintKind::TargetList), ("target-spec-json", PrintKind::TargetSpecJson), + ("target-spec-json-schema", PrintKind::TargetSpecJsonSchema), ("tls-models", PrintKind::TlsModels), // tidy-alphabetical-end ]; @@ -1043,6 +1044,7 @@ pub enum PrintKind { TargetLibdir, TargetList, TargetSpecJson, + TargetSpecJsonSchema, TlsModels, // tidy-alphabetical-end } @@ -2323,7 +2325,8 @@ fn is_print_request_stable(print_kind: PrintKind) -> bool { | PrintKind::CheckCfg | PrintKind::CrateRootLintLevels | PrintKind::SupportedCrateTypes - | PrintKind::TargetSpecJson => false, + | PrintKind::TargetSpecJson + | PrintKind::TargetSpecJsonSchema => false, _ => true, } } diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml index 7fabb227d9a..ecdb6ab5a57 100644 --- a/compiler/rustc_target/Cargo.toml +++ b/compiler/rustc_target/Cargo.toml @@ -14,6 +14,7 @@ rustc_fs_util = { path = "../rustc_fs_util" } rustc_macros = { path = "../rustc_macros" } rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } +schemars = "1.0.4" serde = "1.0.219" serde_derive = "1.0.219" serde_json = "1.0.59" diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs index 91657fef803..b3fe1fffcce 100644 --- a/compiler/rustc_target/src/lib.rs +++ b/compiler/rustc_target/src/lib.rs @@ -72,3 +72,65 @@ fn find_relative_libdir(sysroot: &Path) -> std::borrow::Cow<'static, str> { Some(libdir) => libdir.into(), } } + +macro_rules! target_spec_enum { + ( + $( #[$attr:meta] )* + pub enum $name:ident { + $( + $( #[$variant_attr:meta] )* + $variant:ident = $string:literal, + )* + } + parse_error_type = $parse_error_type:literal; + ) => { + $( #[$attr] )* + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)] + #[derive(schemars::JsonSchema)] + pub enum $name { + $( + $( #[$variant_attr] )* + #[serde(rename = $string)] // for JSON schema generation only + $variant, + )* + } + + impl FromStr for $name { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + Ok(match s { + $( $string => Self::$variant, )* + _ => { + let all = [$( concat!("'", $string, "'") ),*].join(", "); + return Err(format!("invalid {}: '{s}'. allowed values: {all}", $parse_error_type)); + } + }) + } + } + + impl $name { + pub fn desc(&self) -> &'static str { + match self { + $( Self::$variant => $string, )* + } + } + } + + impl crate::json::ToJson for $name { + fn to_json(&self) -> crate::json::Json { + self.desc().to_json() + } + } + + crate::json::serde_deserialize_from_str!($name); + + + impl std::fmt::Display for $name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.desc()) + } + } + }; +} +use target_spec_enum; diff --git a/compiler/rustc_target/src/spec/json.rs b/compiler/rustc_target/src/spec/json.rs index e9ae5734d5b..f236be92b3b 100644 --- a/compiler/rustc_target/src/spec/json.rs +++ b/compiler/rustc_target/src/spec/json.rs @@ -408,12 +408,12 @@ impl ToJson for Target { } } -#[derive(serde_derive::Deserialize)] +#[derive(serde_derive::Deserialize, schemars::JsonSchema)] struct LinkSelfContainedComponentsWrapper { components: Vec<LinkSelfContainedComponents>, } -#[derive(serde_derive::Deserialize)] +#[derive(serde_derive::Deserialize, schemars::JsonSchema)] #[serde(untagged)] enum TargetFamiliesJson { Array(StaticCow<[StaticCow<str>]>), @@ -429,6 +429,18 @@ impl FromStr for EndianWrapper { } } crate::json::serde_deserialize_from_str!(EndianWrapper); +impl schemars::JsonSchema for EndianWrapper { + fn schema_name() -> std::borrow::Cow<'static, str> { + "Endian".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + schemars::json_schema! ({ + "type": "string", + "enum": ["big", "little"] + }) + .into() + } +} /// `ExternAbi` is in `rustc_abi`, which doesn't have access to the macro and serde. struct ExternAbiWrapper(rustc_abi::ExternAbi); @@ -441,8 +453,22 @@ impl FromStr for ExternAbiWrapper { } } crate::json::serde_deserialize_from_str!(ExternAbiWrapper); +impl schemars::JsonSchema for ExternAbiWrapper { + fn schema_name() -> std::borrow::Cow<'static, str> { + "ExternAbi".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + let all = + rustc_abi::ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect::<Vec<_>>(); + schemars::json_schema! ({ + "type": "string", + "enum": all, + }) + .into() + } +} -#[derive(serde_derive::Deserialize)] +#[derive(serde_derive::Deserialize, schemars::JsonSchema)] struct TargetSpecJsonMetadata { description: Option<StaticCow<str>>, tier: Option<u64>, @@ -450,7 +476,7 @@ struct TargetSpecJsonMetadata { std: Option<bool>, } -#[derive(serde_derive::Deserialize)] +#[derive(serde_derive::Deserialize, schemars::JsonSchema)] #[serde(rename_all = "kebab-case")] // Ensure that all unexpected fields get turned into errors. // This helps users stay up to date when the schema changes instead of silently @@ -593,3 +619,7 @@ struct TargetSpecJson { supports_xray: Option<bool>, entry_abi: Option<ExternAbiWrapper>, } + +pub fn json_schema() -> schemars::Schema { + schemars::schema_for!(TargetSpecJson) +} diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 4d9f06c568b..07fb1ce63f7 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -70,6 +70,7 @@ mod json; pub use abi_map::{AbiMap, AbiMapping}; pub use base::apple; pub use base::avr::ef_avr_arch; +pub use json::json_schema; /// Linker is called through a C/C++ compiler. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] @@ -183,50 +184,15 @@ impl LinkerFlavorCli { } } -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum LldFlavor { - Wasm, - Ld64, - Ld, - Link, -} - -impl LldFlavor { - pub fn as_str(&self) -> &'static str { - match self { - LldFlavor::Wasm => "wasm", - LldFlavor::Ld64 => "darwin", - LldFlavor::Ld => "gnu", - LldFlavor::Link => "link", - } - } -} - -impl FromStr for LldFlavor { - type Err = String; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - Ok(match s { - "darwin" => LldFlavor::Ld64, - "gnu" => LldFlavor::Ld, - "link" => LldFlavor::Link, - "wasm" => LldFlavor::Wasm, - _ => { - return Err( - "invalid value for lld flavor: '{s}', expected one of 'darwin', 'gnu', 'link', 'wasm'" - .into(), - ); - } - }) +crate::target_spec_enum! { + pub enum LldFlavor { + Wasm = "wasm", + Ld64 = "darwin", + Ld = "gnu", + Link = "link", } -} -crate::json::serde_deserialize_from_str!(LldFlavor); - -impl ToJson for LldFlavor { - fn to_json(&self) -> Json { - self.as_str().to_json() - } + parse_error_type = "LLD flavor"; } impl LinkerFlavor { @@ -558,6 +524,20 @@ linker_flavor_cli_impls! { } crate::json::serde_deserialize_from_str!(LinkerFlavorCli); +impl schemars::JsonSchema for LinkerFlavorCli { + fn schema_name() -> std::borrow::Cow<'static, str> { + "LinkerFlavor".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + let all: Vec<&'static str> = + Self::all().iter().map(|flavor| flavor.desc()).collect::<Vec<_>>(); + schemars::json_schema! ({ + "type": "string", + "enum": all + }) + .into() + } +} impl ToJson for LinkerFlavorCli { fn to_json(&self) -> Json { @@ -611,6 +591,18 @@ impl FromStr for LinkSelfContainedDefault { } crate::json::serde_deserialize_from_str!(LinkSelfContainedDefault); +impl schemars::JsonSchema for LinkSelfContainedDefault { + fn schema_name() -> std::borrow::Cow<'static, str> { + "LinkSelfContainedDefault".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + schemars::json_schema! ({ + "type": "string", + "enum": ["false", "true", "wasm", "musl", "mingw"] + }) + .into() + } +} impl ToJson for LinkSelfContainedDefault { fn to_json(&self) -> Json { @@ -743,6 +735,20 @@ impl FromStr for LinkSelfContainedComponents { } crate::json::serde_deserialize_from_str!(LinkSelfContainedComponents); +impl schemars::JsonSchema for LinkSelfContainedComponents { + fn schema_name() -> std::borrow::Cow<'static, str> { + "LinkSelfContainedComponents".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + let all = + Self::all_components().iter().map(|component| component.as_str()).collect::<Vec<_>>(); + schemars::json_schema! ({ + "type": "string", + "enum": all, + }) + .into() + } +} impl ToJson for LinkSelfContainedComponents { fn to_json(&self) -> Json { @@ -823,10 +829,14 @@ impl LinkerFeatures { } } -#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)] -pub enum PanicStrategy { - Unwind, - Abort, +crate::target_spec_enum! { + #[derive(Encodable, Decodable, HashStable_Generic)] + pub enum PanicStrategy { + Unwind = "unwind", + Abort = "abort", + } + + parse_error_type = "panic strategy"; } #[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)] @@ -838,13 +848,6 @@ pub enum OnBrokenPipe { } impl PanicStrategy { - pub fn desc(&self) -> &str { - match *self { - PanicStrategy::Unwind => "unwind", - PanicStrategy::Abort => "abort", - } - } - pub const fn desc_symbol(&self) -> Symbol { match *self { PanicStrategy::Unwind => sym::unwind, @@ -857,24 +860,16 @@ impl PanicStrategy { } } -impl FromStr for PanicStrategy { - type Err = String; - fn from_str(s: &str) -> Result<Self, Self::Err> { - Ok(match s { - "unwind" => PanicStrategy::Unwind, - "abort" => PanicStrategy::Abort, - _ => { - return Err(format!( - "'{}' is not a valid value for \ - panic-strategy. Use 'unwind' or 'abort'.", - s - )); - } - }) +crate::target_spec_enum! { + pub enum RelroLevel { + Full = "full", + Partial = "partial", + Off = "off", + None = "none", } -} -crate::json::serde_deserialize_from_str!(PanicStrategy); + parse_error_type = "relro level"; +} impl IntoDiagArg for PanicStrategy { fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue { @@ -882,109 +877,14 @@ impl IntoDiagArg for PanicStrategy { } } -impl ToJson for PanicStrategy { - fn to_json(&self) -> Json { - match *self { - PanicStrategy::Abort => "abort".to_json(), - PanicStrategy::Unwind => "unwind".to_json(), - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Hash)] -pub enum RelroLevel { - Full, - Partial, - Off, - None, -} - -impl RelroLevel { - pub fn desc(&self) -> &str { - match *self { - RelroLevel::Full => "full", - RelroLevel::Partial => "partial", - RelroLevel::Off => "off", - RelroLevel::None => "none", - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Hash)] -pub enum SymbolVisibility { - Hidden, - Protected, - Interposable, -} - -impl SymbolVisibility { - pub fn desc(&self) -> &str { - match *self { - SymbolVisibility::Hidden => "hidden", - SymbolVisibility::Protected => "protected", - SymbolVisibility::Interposable => "interposable", - } - } -} - -impl FromStr for SymbolVisibility { - type Err = String; - - fn from_str(s: &str) -> Result<SymbolVisibility, Self::Err> { - match s { - "hidden" => Ok(SymbolVisibility::Hidden), - "protected" => Ok(SymbolVisibility::Protected), - "interposable" => Ok(SymbolVisibility::Interposable), - _ => Err(format!( - "'{}' is not a valid value for \ - symbol-visibility. Use 'hidden', 'protected, or 'interposable'.", - s - )), - } - } -} - -crate::json::serde_deserialize_from_str!(SymbolVisibility); - -impl ToJson for SymbolVisibility { - fn to_json(&self) -> Json { - match *self { - SymbolVisibility::Hidden => "hidden".to_json(), - SymbolVisibility::Protected => "protected".to_json(), - SymbolVisibility::Interposable => "interposable".to_json(), - } - } -} - -impl FromStr for RelroLevel { - type Err = String; - - fn from_str(s: &str) -> Result<RelroLevel, Self::Err> { - match s { - "full" => Ok(RelroLevel::Full), - "partial" => Ok(RelroLevel::Partial), - "off" => Ok(RelroLevel::Off), - "none" => Ok(RelroLevel::None), - _ => Err(format!( - "'{}' is not a valid value for \ - relro-level. Use 'full', 'partial, 'off', or 'none'.", - s - )), - } +crate::target_spec_enum! { + pub enum SymbolVisibility { + Hidden = "hidden", + Protected = "protected", + Interposable = "interposable", } -} - -crate::json::serde_deserialize_from_str!(RelroLevel); -impl ToJson for RelroLevel { - fn to_json(&self) -> Json { - match *self { - RelroLevel::Full => "full".to_json(), - RelroLevel::Partial => "partial".to_json(), - RelroLevel::Off => "off".to_json(), - RelroLevel::None => "None".to_json(), - } - } + parse_error_type = "symbol visibility"; } #[derive(Clone, Debug, PartialEq, Hash)] @@ -1014,6 +914,18 @@ impl FromStr for SmallDataThresholdSupport { } crate::json::serde_deserialize_from_str!(SmallDataThresholdSupport); +impl schemars::JsonSchema for SmallDataThresholdSupport { + fn schema_name() -> std::borrow::Cow<'static, str> { + "SmallDataThresholdSupport".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + schemars::json_schema! ({ + "type": "string", + "pattern": r#"^none|default-for-arch|llvm-module-flag=.+|llvm-arg=.+$"#, + }) + .into() + } +} impl ToJson for SmallDataThresholdSupport { fn to_json(&self) -> Value { @@ -1026,76 +938,31 @@ impl ToJson for SmallDataThresholdSupport { } } -#[derive(Clone, Copy, Debug, PartialEq, Hash)] -pub enum MergeFunctions { - Disabled, - Trampolines, - Aliases, -} - -impl MergeFunctions { - pub fn desc(&self) -> &str { - match *self { - MergeFunctions::Disabled => "disabled", - MergeFunctions::Trampolines => "trampolines", - MergeFunctions::Aliases => "aliases", - } +crate::target_spec_enum! { + pub enum MergeFunctions { + Disabled = "disabled", + Trampolines = "trampolines", + Aliases = "aliases", } -} - -impl FromStr for MergeFunctions { - type Err = String; - fn from_str(s: &str) -> Result<MergeFunctions, Self::Err> { - match s { - "disabled" => Ok(MergeFunctions::Disabled), - "trampolines" => Ok(MergeFunctions::Trampolines), - "aliases" => Ok(MergeFunctions::Aliases), - _ => Err(format!( - "'{}' is not a valid value for \ - merge-functions. Use 'disabled', \ - 'trampolines', or 'aliases'.", - s - )), - } - } + parse_error_type = "value for merge-functions"; } -crate::json::serde_deserialize_from_str!(MergeFunctions); - -impl ToJson for MergeFunctions { - fn to_json(&self) -> Json { - match *self { - MergeFunctions::Disabled => "disabled".to_json(), - MergeFunctions::Trampolines => "trampolines".to_json(), - MergeFunctions::Aliases => "aliases".to_json(), - } +crate::target_spec_enum! { + pub enum RelocModel { + Static = "static", + Pic = "pic", + Pie = "pie", + DynamicNoPic = "dynamic-no-pic", + Ropi = "ropi", + Rwpi = "rwpi", + RopiRwpi = "ropi-rwpi", } -} -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum RelocModel { - Static, - Pic, - Pie, - DynamicNoPic, - Ropi, - Rwpi, - RopiRwpi, + parse_error_type = "relocation model"; } impl RelocModel { - pub fn desc(&self) -> &str { - match *self { - RelocModel::Static => "static", - RelocModel::Pic => "pic", - RelocModel::Pie => "pie", - RelocModel::DynamicNoPic => "dynamic-no-pic", - RelocModel::Ropi => "ropi", - RelocModel::Rwpi => "rwpi", - RelocModel::RopiRwpi => "ropi-rwpi", - } - } pub const fn desc_symbol(&self) -> Symbol { match *self { RelocModel::Static => kw::Static, @@ -1121,236 +988,75 @@ impl RelocModel { } } -impl FromStr for RelocModel { - type Err = String; - - fn from_str(s: &str) -> Result<RelocModel, Self::Err> { - Ok(match s { - "static" => RelocModel::Static, - "pic" => RelocModel::Pic, - "pie" => RelocModel::Pie, - "dynamic-no-pic" => RelocModel::DynamicNoPic, - "ropi" => RelocModel::Ropi, - "rwpi" => RelocModel::Rwpi, - "ropi-rwpi" => RelocModel::RopiRwpi, - _ => { - return Err(format!( - "invalid relocation model '{s}'. - Run `rustc --print relocation-models` to \ - see the list of supported values.'" - )); - } - }) +crate::target_spec_enum! { + pub enum CodeModel { + Tiny = "tiny", + Small = "small", + Kernel = "kernel", + Medium = "medium", + Large = "large", } -} -crate::json::serde_deserialize_from_str!(RelocModel); - -impl ToJson for RelocModel { - fn to_json(&self) -> Json { - self.desc().to_json() - } -} - -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum CodeModel { - Tiny, - Small, - Kernel, - Medium, - Large, + parse_error_type = "code model"; } -impl FromStr for CodeModel { - type Err = String; - - fn from_str(s: &str) -> Result<CodeModel, Self::Err> { - Ok(match s { - "tiny" => CodeModel::Tiny, - "small" => CodeModel::Small, - "kernel" => CodeModel::Kernel, - "medium" => CodeModel::Medium, - "large" => CodeModel::Large, - _ => { - return Err(format!( - "'{s}' is not a valid code model. \ - Run `rustc --print code-models` to \ - see the list of supported values." - )); - } - }) +crate::target_spec_enum! { + /// The float ABI setting to be configured in the LLVM target machine. + pub enum FloatAbi { + Soft = "soft", + Hard = "hard", } -} -crate::json::serde_deserialize_from_str!(CodeModel); - -impl ToJson for CodeModel { - fn to_json(&self) -> Json { - match *self { - CodeModel::Tiny => "tiny", - CodeModel::Small => "small", - CodeModel::Kernel => "kernel", - CodeModel::Medium => "medium", - CodeModel::Large => "large", - } - .to_json() - } + parse_error_type = "float abi"; } -/// The float ABI setting to be configured in the LLVM target machine. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum FloatAbi { - Soft, - Hard, -} - -impl FromStr for FloatAbi { - type Err = String; - - fn from_str(s: &str) -> Result<FloatAbi, Self::Err> { - Ok(match s { - "soft" => FloatAbi::Soft, - "hard" => FloatAbi::Hard, - _ => { - return Err(format!( - "'{}' is not a valid value for \ - llvm-floatabi. Use 'soft' or 'hard'.", - s - )); - } - }) +crate::target_spec_enum! { + /// The Rustc-specific variant of the ABI used for this target. + pub enum RustcAbi { + /// On x86-32 only: make use of SSE and SSE2 for ABI purposes. + X86Sse2 = "x86-sse2", + /// On x86-32/64 only: do not use any FPU or SIMD registers for the ABI. + X86Softfloat = "x86-softfloat", } -} - -crate::json::serde_deserialize_from_str!(FloatAbi); -impl ToJson for FloatAbi { - fn to_json(&self) -> Json { - match *self { - FloatAbi::Soft => "soft", - FloatAbi::Hard => "hard", - } - .to_json() - } -} - -/// The Rustc-specific variant of the ABI used for this target. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum RustcAbi { - /// On x86-32 only: make use of SSE and SSE2 for ABI purposes. - X86Sse2, - /// On x86-32/64 only: do not use any FPU or SIMD registers for the ABI. - X86Softfloat, + parse_error_type = "rustc abi"; } -impl FromStr for RustcAbi { - type Err = String; - - fn from_str(s: &str) -> Result<RustcAbi, Self::Err> { - Ok(match s { - "x86-sse2" => RustcAbi::X86Sse2, - "x86-softfloat" => RustcAbi::X86Softfloat, - _ => { - return Err(format!( - "'{s}' is not a valid value for rustc-abi. \ - Use 'x86-softfloat' or leave the field unset." - )); - } - }) - } -} - -crate::json::serde_deserialize_from_str!(RustcAbi); - -impl ToJson for RustcAbi { - fn to_json(&self) -> Json { - match *self { - RustcAbi::X86Sse2 => "x86-sse2", - RustcAbi::X86Softfloat => "x86-softfloat", - } - .to_json() +crate::target_spec_enum! { + pub enum TlsModel { + GeneralDynamic = "global-dynamic", + LocalDynamic = "local-dynamic", + InitialExec = "initial-exec", + LocalExec = "local-exec", + Emulated = "emulated", } -} -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum TlsModel { - GeneralDynamic, - LocalDynamic, - InitialExec, - LocalExec, - Emulated, -} - -impl FromStr for TlsModel { - type Err = String; - - fn from_str(s: &str) -> Result<TlsModel, Self::Err> { - Ok(match s { - // Note the difference "general" vs "global" difference. The model name is "general", - // but the user-facing option name is "global" for consistency with other compilers. - "global-dynamic" => TlsModel::GeneralDynamic, - "local-dynamic" => TlsModel::LocalDynamic, - "initial-exec" => TlsModel::InitialExec, - "local-exec" => TlsModel::LocalExec, - "emulated" => TlsModel::Emulated, - _ => { - return Err(format!( - "'{s}' is not a valid TLS model. \ - Run `rustc --print tls-models` to \ - see the list of supported values." - )); - } - }) - } + parse_error_type = "TLS model"; } -crate::json::serde_deserialize_from_str!(TlsModel); - -impl ToJson for TlsModel { - fn to_json(&self) -> Json { - match *self { - TlsModel::GeneralDynamic => "global-dynamic", - TlsModel::LocalDynamic => "local-dynamic", - TlsModel::InitialExec => "initial-exec", - TlsModel::LocalExec => "local-exec", - TlsModel::Emulated => "emulated", - } - .to_json() +crate::target_spec_enum! { + /// Everything is flattened to a single enum to make the json encoding/decoding less annoying. + pub enum LinkOutputKind { + /// Dynamically linked non position-independent executable. + DynamicNoPicExe = "dynamic-nopic-exe", + /// Dynamically linked position-independent executable. + DynamicPicExe = "dynamic-pic-exe", + /// Statically linked non position-independent executable. + StaticNoPicExe = "static-nopic-exe", + /// Statically linked position-independent executable. + StaticPicExe = "static-pic-exe", + /// Regular dynamic library ("dynamically linked"). + DynamicDylib = "dynamic-dylib", + /// Dynamic library with bundled libc ("statically linked"). + StaticDylib = "static-dylib", + /// WASI module with a lifetime past the _initialize entry point + WasiReactorExe = "wasi-reactor-exe", } -} -/// Everything is flattened to a single enum to make the json encoding/decoding less annoying. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] -pub enum LinkOutputKind { - /// Dynamically linked non position-independent executable. - DynamicNoPicExe, - /// Dynamically linked position-independent executable. - DynamicPicExe, - /// Statically linked non position-independent executable. - StaticNoPicExe, - /// Statically linked position-independent executable. - StaticPicExe, - /// Regular dynamic library ("dynamically linked"). - DynamicDylib, - /// Dynamic library with bundled libc ("statically linked"). - StaticDylib, - /// WASI module with a lifetime past the _initialize entry point - WasiReactorExe, + parse_error_type = "CRT object kind"; } impl LinkOutputKind { - fn as_str(&self) -> &'static str { - match self { - LinkOutputKind::DynamicNoPicExe => "dynamic-nopic-exe", - LinkOutputKind::DynamicPicExe => "dynamic-pic-exe", - LinkOutputKind::StaticNoPicExe => "static-nopic-exe", - LinkOutputKind::StaticPicExe => "static-pic-exe", - LinkOutputKind::DynamicDylib => "dynamic-dylib", - LinkOutputKind::StaticDylib => "static-dylib", - LinkOutputKind::WasiReactorExe => "wasi-reactor-exe", - } - } - pub fn can_link_dylib(self) -> bool { match self { LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => false, @@ -1363,171 +1069,64 @@ impl LinkOutputKind { } } -impl FromStr for LinkOutputKind { - type Err = String; - - fn from_str(s: &str) -> Result<LinkOutputKind, Self::Err> { - Ok(match s { - "dynamic-nopic-exe" => LinkOutputKind::DynamicNoPicExe, - "dynamic-pic-exe" => LinkOutputKind::DynamicPicExe, - "static-nopic-exe" => LinkOutputKind::StaticNoPicExe, - "static-pic-exe" => LinkOutputKind::StaticPicExe, - "dynamic-dylib" => LinkOutputKind::DynamicDylib, - "static-dylib" => LinkOutputKind::StaticDylib, - "wasi-reactor-exe" => LinkOutputKind::WasiReactorExe, - _ => { - return Err(format!( - "invalid value for CRT object kind. \ - Use '(dynamic,static)-(nopic,pic)-exe' or \ - '(dynamic,static)-dylib' or 'wasi-reactor-exe'" - )); - } - }) - } -} - -crate::json::serde_deserialize_from_str!(LinkOutputKind); - -impl fmt::Display for LinkOutputKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} - pub type LinkArgs = BTreeMap<LinkerFlavor, Vec<StaticCow<str>>>; pub type LinkArgsCli = BTreeMap<LinkerFlavorCli, Vec<StaticCow<str>>>; -/// Which kind of debuginfo does the target use? -/// -/// Useful in determining whether a target supports Split DWARF (a target with -/// `DebuginfoKind::Dwarf` and supporting `SplitDebuginfo::Unpacked` for example). -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub enum DebuginfoKind { - /// DWARF debuginfo (such as that used on `x86_64_unknown_linux_gnu`). - #[default] - Dwarf, - /// DWARF debuginfo in dSYM files (such as on Apple platforms). - DwarfDsym, - /// Program database files (such as on Windows). - Pdb, -} - -impl DebuginfoKind { - fn as_str(&self) -> &'static str { - match self { - DebuginfoKind::Dwarf => "dwarf", - DebuginfoKind::DwarfDsym => "dwarf-dsym", - DebuginfoKind::Pdb => "pdb", - } - } -} - -impl FromStr for DebuginfoKind { - type Err = String; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - Ok(match s { - "dwarf" => DebuginfoKind::Dwarf, - "dwarf-dsym" => DebuginfoKind::DwarfDsym, - "pdb" => DebuginfoKind::Pdb, - _ => { - return Err(format!( - "'{s}' is not a valid value for debuginfo-kind. Use 'dwarf', \ - 'dwarf-dsym' or 'pdb'." - )); - } - }) - } -} - -crate::json::serde_deserialize_from_str!(DebuginfoKind); - -impl ToJson for DebuginfoKind { - fn to_json(&self) -> Json { - self.as_str().to_json() - } -} - -impl fmt::Display for DebuginfoKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } -} - -#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] -pub enum SplitDebuginfo { - /// Split debug-information is disabled, meaning that on supported platforms - /// you can find all debug information in the executable itself. This is - /// only supported for ELF effectively. - /// - /// * Windows - not supported - /// * macOS - don't run `dsymutil` - /// * ELF - `.debug_*` sections - #[default] - Off, - - /// Split debug-information can be found in a "packed" location separate - /// from the final artifact. This is supported on all platforms. +crate::target_spec_enum! { + /// Which kind of debuginfo does the target use? /// - /// * Windows - `*.pdb` - /// * macOS - `*.dSYM` (run `dsymutil`) - /// * ELF - `*.dwp` (run `thorin`) - Packed, - - /// Split debug-information can be found in individual object files on the - /// filesystem. The main executable may point to the object files. - /// - /// * Windows - not supported - /// * macOS - supported, scattered object files - /// * ELF - supported, scattered `*.dwo` or `*.o` files (see `SplitDwarfKind`) - Unpacked, -} - -impl SplitDebuginfo { - fn as_str(&self) -> &'static str { - match self { - SplitDebuginfo::Off => "off", - SplitDebuginfo::Packed => "packed", - SplitDebuginfo::Unpacked => "unpacked", - } - } -} - -impl FromStr for SplitDebuginfo { - type Err = String; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - Ok(match s { - "off" => SplitDebuginfo::Off, - "unpacked" => SplitDebuginfo::Unpacked, - "packed" => SplitDebuginfo::Packed, - _ => { - return Err(format!( - "'{s}' is not a valid value for \ - split-debuginfo. Use 'off', 'unpacked', or 'packed'.", - )); - } - }) - } -} - -crate::json::serde_deserialize_from_str!(SplitDebuginfo); - -impl ToJson for SplitDebuginfo { - fn to_json(&self) -> Json { - self.as_str().to_json() - } -} - -impl fmt::Display for SplitDebuginfo { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } + /// Useful in determining whether a target supports Split DWARF (a target with + /// `DebuginfoKind::Dwarf` and supporting `SplitDebuginfo::Unpacked` for example). + #[derive(Default)] + pub enum DebuginfoKind { + /// DWARF debuginfo (such as that used on `x86_64_unknown_linux_gnu`). + #[default] + Dwarf = "dwarf", + /// DWARF debuginfo in dSYM files (such as on Apple platforms). + DwarfDsym = "dwarf-dsym", + /// Program database files (such as on Windows). + Pdb = "pdb", + } + + parse_error_type = "debuginfo kind"; +} + +crate::target_spec_enum! { + #[derive(Default)] + pub enum SplitDebuginfo { + /// Split debug-information is disabled, meaning that on supported platforms + /// you can find all debug information in the executable itself. This is + /// only supported for ELF effectively. + /// + /// * Windows - not supported + /// * macOS - don't run `dsymutil` + /// * ELF - `.debug_*` sections + #[default] + Off = "off", + + /// Split debug-information can be found in a "packed" location separate + /// from the final artifact. This is supported on all platforms. + /// + /// * Windows - `*.pdb` + /// * macOS - `*.dSYM` (run `dsymutil`) + /// * ELF - `*.dwp` (run `thorin`) + Packed = "packed", + + /// Split debug-information can be found in individual object files on the + /// filesystem. The main executable may point to the object files. + /// + /// * Windows - not supported + /// * macOS - supported, scattered object files + /// * ELF - supported, scattered `*.dwo` or `*.o` files (see `SplitDwarfKind`) + Unpacked = "unpacked", + } + + parse_error_type = "split debuginfo"; } into_diag_arg_using_display!(SplitDebuginfo); -#[derive(Clone, Debug, PartialEq, Eq, serde_derive::Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, serde_derive::Deserialize, schemars::JsonSchema)] #[serde(tag = "kind")] #[serde(rename_all = "kebab-case")] pub enum StackProbeType { @@ -1688,6 +1287,19 @@ impl FromStr for SanitizerSet { } crate::json::serde_deserialize_from_str!(SanitizerSet); +impl schemars::JsonSchema for SanitizerSet { + fn schema_name() -> std::borrow::Cow<'static, str> { + "SanitizerSet".into() + } + fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema { + let all = Self::all().iter().map(|sanitizer| sanitizer.as_str()).collect::<Vec<_>>(); + schemars::json_schema! ({ + "type": "string", + "enum": all, + }) + .into() + } +} impl ToJson for SanitizerSet { fn to_json(&self) -> Json { @@ -1699,17 +1311,20 @@ impl ToJson for SanitizerSet { } } -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum FramePointer { - /// Forces the machine code generator to always preserve the frame pointers. - Always, - /// Forces the machine code generator to preserve the frame pointers except for the leaf - /// functions (i.e. those that don't call other functions). - NonLeaf, - /// Allows the machine code generator to omit the frame pointers. - /// - /// This option does not guarantee that the frame pointers will be omitted. - MayOmit, +crate::target_spec_enum! { + pub enum FramePointer { + /// Forces the machine code generator to always preserve the frame pointers. + Always = "always", + /// Forces the machine code generator to preserve the frame pointers except for the leaf + /// functions (i.e. those that don't call other functions). + NonLeaf = "non-leaf", + /// Allows the machine code generator to omit the frame pointers. + /// + /// This option does not guarantee that the frame pointers will be omitted. + MayOmit = "may-omit", + } + + parse_error_type = "frame pointer"; } impl FramePointer { @@ -1726,93 +1341,43 @@ impl FramePointer { } } -impl FromStr for FramePointer { - type Err = String; - fn from_str(s: &str) -> Result<Self, Self::Err> { - Ok(match s { - "always" => Self::Always, - "non-leaf" => Self::NonLeaf, - "may-omit" => Self::MayOmit, - _ => return Err(format!("'{s}' is not a valid value for frame-pointer")), - }) - } -} - -crate::json::serde_deserialize_from_str!(FramePointer); - -impl ToJson for FramePointer { - fn to_json(&self) -> Json { - match *self { - Self::Always => "always", - Self::NonLeaf => "non-leaf", - Self::MayOmit => "may-omit", - } - .to_json() - } -} - -/// Controls use of stack canaries. -#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq)] -pub enum StackProtector { - /// Disable stack canary generation. - None, - - /// On LLVM, mark all generated LLVM functions with the `ssp` attribute (see - /// llvm/docs/LangRef.rst). This triggers stack canary generation in - /// functions which contain an array of a byte-sized type with more than - /// eight elements. - Basic, +crate::target_spec_enum! { + /// Controls use of stack canaries. + pub enum StackProtector { + /// Disable stack canary generation. + None = "none", - /// On LLVM, mark all generated LLVM functions with the `sspstrong` - /// attribute (see llvm/docs/LangRef.rst). This triggers stack canary - /// generation in functions which either contain an array, or which take - /// the address of a local variable. - Strong, + /// On LLVM, mark all generated LLVM functions with the `ssp` attribute (see + /// llvm/docs/LangRef.rst). This triggers stack canary generation in + /// functions which contain an array of a byte-sized type with more than + /// eight elements. + Basic = "basic", - /// Generate stack canaries in all functions. - All, -} + /// On LLVM, mark all generated LLVM functions with the `sspstrong` + /// attribute (see llvm/docs/LangRef.rst). This triggers stack canary + /// generation in functions which either contain an array, or which take + /// the address of a local variable. + Strong = "strong", -impl StackProtector { - fn as_str(&self) -> &'static str { - match self { - StackProtector::None => "none", - StackProtector::Basic => "basic", - StackProtector::Strong => "strong", - StackProtector::All => "all", - } + /// Generate stack canaries in all functions. + All = "all", } -} -impl FromStr for StackProtector { - type Err = (); - - fn from_str(s: &str) -> Result<StackProtector, ()> { - Ok(match s { - "none" => StackProtector::None, - "basic" => StackProtector::Basic, - "strong" => StackProtector::Strong, - "all" => StackProtector::All, - _ => return Err(()), - }) - } -} - -impl fmt::Display for StackProtector { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.as_str()) - } + parse_error_type = "stack protector"; } into_diag_arg_using_display!(StackProtector); -#[derive(PartialEq, Clone, Debug)] -pub enum BinaryFormat { - Coff, - Elf, - MachO, - Wasm, - Xcoff, +crate::target_spec_enum! { + pub enum BinaryFormat { + Coff = "coff", + Elf = "elf", + MachO = "mach-o", + Wasm = "wasm", + Xcoff = "xcoff", + } + + parse_error_type = "binary format"; } impl BinaryFormat { @@ -1828,38 +1393,6 @@ impl BinaryFormat { } } -impl FromStr for BinaryFormat { - type Err = String; - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "coff" => Ok(Self::Coff), - "elf" => Ok(Self::Elf), - "mach-o" => Ok(Self::MachO), - "wasm" => Ok(Self::Wasm), - "xcoff" => Ok(Self::Xcoff), - _ => Err(format!( - "'{s}' is not a valid value for binary_format. \ - Use 'coff', 'elf', 'mach-o', 'wasm' or 'xcoff' " - )), - } - } -} - -crate::json::serde_deserialize_from_str!(BinaryFormat); - -impl ToJson for BinaryFormat { - fn to_json(&self) -> Json { - match self { - Self::Coff => "coff", - Self::Elf => "elf", - Self::MachO => "mach-o", - Self::Wasm => "wasm", - Self::Xcoff => "xcoff", - } - .to_json() - } -} - impl ToJson for Align { fn to_json(&self) -> Json { self.bits().to_json() diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index c82043f0222..149f5e638b1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -27,8 +27,8 @@ use rustc_middle::ty::print::{ with_forced_trimmed_paths, }; use rustc_middle::ty::{ - self, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, - Upcast, + self, GenericArgKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_span::{BytePos, DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym}; @@ -2316,7 +2316,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cand }) .collect(); - impl_candidates.sort_by_key(|cand| (cand.similarity, cand.trait_ref.to_string())); + impl_candidates.sort_by_key(|cand| { + // When suggesting array types, sort them by the length of the array, not lexicographically (#135098) + let len = if let GenericArgKind::Type(ty) = cand.trait_ref.args[0].kind() + && let ty::Array(_, len) = ty.kind() + { + // Deprioritize suggestions for parameterized arrays. + len.try_to_target_usize(self.tcx).unwrap_or(u64::MAX) + } else { + 0 + }; + + (cand.similarity, len, cand.trait_ref.to_string()) + }); let mut impl_candidates: Vec<_> = impl_candidates.into_iter().map(|cand| cand.trait_ref).collect(); impl_candidates.dedup(); 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/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 8b6d86a2888..98f11e2ea57 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -1433,7 +1433,6 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> { /// # Examples /// /// ``` - /// #![feature(btree_extract_if)] /// use std::collections::BTreeMap; /// /// // Splitting a map into even and odd keys, reusing the original map: @@ -1450,7 +1449,7 @@ impl<K, V, A: Allocator + Clone> BTreeMap<K, V, A> { /// assert_eq!(low.keys().copied().collect::<Vec<_>>(), [0, 1, 2, 3]); /// assert_eq!(high.keys().copied().collect::<Vec<_>>(), [4, 5, 6, 7]); /// ``` - #[unstable(feature = "btree_extract_if", issue = "70530")] + #[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] pub fn extract_if<F, R>(&mut self, range: R, pred: F) -> ExtractIf<'_, K, V, R, F, A> where K: Ord, @@ -1937,7 +1936,7 @@ impl<K, V> Default for Values<'_, K, V> { } /// An iterator produced by calling `extract_if` on BTreeMap. -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ExtractIf< 'a, @@ -1970,7 +1969,7 @@ pub(super) struct ExtractIfInner<'a, K, V, R> { range: R, } -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] impl<K, V, R, F, A> fmt::Debug for ExtractIf<'_, K, V, R, F, A> where K: fmt::Debug, @@ -1982,7 +1981,7 @@ where } } -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] impl<K, V, R, F, A: Allocator + Clone> Iterator for ExtractIf<'_, K, V, R, F, A> where K: PartialOrd, @@ -2056,7 +2055,7 @@ impl<'a, K, V, R> ExtractIfInner<'a, K, V, R> { } } -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] impl<K, V, R, F> FusedIterator for ExtractIf<'_, K, V, R, F> where K: PartialOrd, diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index d50ce02bda7..e6b0a1f6323 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -1202,7 +1202,6 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> { /// # Examples /// /// ``` - /// #![feature(btree_extract_if)] /// use std::collections::BTreeSet; /// /// // Splitting a set into even and odd values, reusing the original set: @@ -1219,7 +1218,7 @@ impl<T, A: Allocator + Clone> BTreeSet<T, A> { /// assert_eq!(low.into_iter().collect::<Vec<_>>(), [0, 1, 2, 3]); /// assert_eq!(high.into_iter().collect::<Vec<_>>(), [4, 5, 6, 7]); /// ``` - #[unstable(feature = "btree_extract_if", issue = "70530")] + #[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] pub fn extract_if<F, R>(&mut self, range: R, pred: F) -> ExtractIf<'_, T, R, F, A> where T: Ord, @@ -1554,7 +1553,7 @@ impl<'a, T, A: Allocator + Clone> IntoIterator for &'a BTreeSet<T, A> { } /// An iterator produced by calling `extract_if` on BTreeSet. -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] #[must_use = "iterators are lazy and do nothing unless consumed"] pub struct ExtractIf< 'a, @@ -1569,7 +1568,7 @@ pub struct ExtractIf< alloc: A, } -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] impl<T, R, F, A> fmt::Debug for ExtractIf<'_, T, R, F, A> where T: fmt::Debug, @@ -1582,7 +1581,7 @@ where } } -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] impl<T, R, F, A: Allocator + Clone> Iterator for ExtractIf<'_, T, R, F, A> where T: PartialOrd, @@ -1602,7 +1601,7 @@ where } } -#[unstable(feature = "btree_extract_if", issue = "70530")] +#[stable(feature = "btree_extract_if", since = "CURRENT_RUSTC_VERSION")] impl<T, R, F, A: Allocator + Clone> FusedIterator for ExtractIf<'_, T, R, F, A> where T: PartialOrd, diff --git a/library/alloctests/benches/lib.rs b/library/alloctests/benches/lib.rs index 2633154318c..721d685527f 100644 --- a/library/alloctests/benches/lib.rs +++ b/library/alloctests/benches/lib.rs @@ -1,6 +1,5 @@ // Disabling in Miri as these would take too long. #![cfg(not(miri))] -#![feature(btree_extract_if)] #![feature(iter_next_chunk)] #![feature(repr_simd)] #![feature(slice_partition_dedup)] diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index bf446ae1ba4..8c3ce156f3c 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -2,7 +2,6 @@ #![feature(alloc_layout_extra)] #![feature(iter_array_chunks)] #![feature(assert_matches)] -#![feature(btree_extract_if)] #![feature(wtf8_internals)] #![feature(char_max_len)] #![feature(cow_is_borrowed)] 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/range.rs b/library/core/src/range.rs index 8c49123fdaf..a096a8ceafc 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -49,7 +49,8 @@ pub use crate::ops::{Bound, IntoBounds, OneSidedRange, RangeBounds, RangeFull, R /// 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). @@ -424,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). 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/str/traits.rs b/library/core/src/str/traits.rs index 5adae62b7e6..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. /// 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/alloc.rs b/library/coretests/tests/alloc.rs index 72fdf82c1f8..a4af6fd32a1 100644 --- a/library/coretests/tests/alloc.rs +++ b/library/coretests/tests/alloc.rs @@ -56,6 +56,30 @@ fn layout_array_edge_cases() { } #[test] +fn layout_errors() { + let layout = Layout::new::<[u8; 2]>(); + // Should error if the alignment is not a power of two. + assert!(layout.align_to(3).is_err()); + + // The remaining assertions ensure that the methods error on arithmetic overflow as the + // alignment cannot overflow `isize`. + let size = layout.size(); + let size_max = isize::MAX as usize; + let align_max = size_max / size; + + assert!(layout.align_to(size_max + 1).is_err()); + + assert!(layout.repeat(align_max).is_ok()); + assert!(layout.repeat(align_max + 1).is_err()); + + assert!(layout.repeat_packed(align_max).is_ok()); + assert!(layout.repeat_packed(align_max + 1).is_err()); + + let next = Layout::from_size_align(size_max, 1).unwrap(); + assert!(layout.extend(next).is_err()); +} + +#[test] fn layout_debug_shows_log2_of_alignment() { // `Debug` is not stable, but here's what it does right now let layout = Layout::from_size_align(24576, 8192).unwrap(); diff --git a/library/coretests/tests/char.rs b/library/coretests/tests/char.rs index 852f073bae1..6f94065b2d9 100644 --- a/library/coretests/tests/char.rs +++ b/library/coretests/tests/char.rs @@ -220,6 +220,7 @@ fn test_escape_default() { } assert_eq!(string('\n'), "\\n"); assert_eq!(string('\r'), "\\r"); + assert_eq!(string('\t'), "\\t"); assert_eq!(string('\''), "\\'"); assert_eq!(string('"'), "\\\""); assert_eq!(string(' '), " "); @@ -417,3 +418,45 @@ fn eu_iterator_specializations() { check('\u{12340}'); check('\u{10FFFF}'); } + +#[test] +#[should_panic] +fn test_from_digit_radix_too_high() { + let _ = char::from_digit(0, 37); +} + +#[test] +fn test_from_digit_invalid_radix() { + assert!(char::from_digit(10, 9).is_none()); +} + +#[test] +#[should_panic] +fn test_to_digit_radix_too_low() { + let _ = 'a'.to_digit(1); +} + +#[test] +#[should_panic] +fn test_to_digit_radix_too_high() { + let _ = 'a'.to_digit(37); +} + +#[test] +fn test_as_ascii_invalid() { + assert!('❤'.as_ascii().is_none()); +} + +#[test] +#[should_panic] +fn test_encode_utf8_raw_buffer_too_small() { + let mut buf = [0u8; 1]; + let _ = char::encode_utf8_raw('ß'.into(), &mut buf); +} + +#[test] +#[should_panic] +fn test_encode_utf16_raw_buffer_too_small() { + let mut buf = [0u16; 1]; + let _ = char::encode_utf16_raw('𐐷'.into(), &mut buf); +} 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/hash/mod.rs b/library/coretests/tests/hash/mod.rs index 1f10a4733b0..043a3ee7423 100644 --- a/library/coretests/tests/hash/mod.rs +++ b/library/coretests/tests/hash/mod.rs @@ -53,12 +53,14 @@ fn test_writer_hasher() { assert_eq!(hash(&5_u16), 5); assert_eq!(hash(&5_u32), 5); assert_eq!(hash(&5_u64), 5); + assert_eq!(hash(&5_u128), 5); assert_eq!(hash(&5_usize), 5); assert_eq!(hash(&5_i8), 5); assert_eq!(hash(&5_i16), 5); assert_eq!(hash(&5_i32), 5); assert_eq!(hash(&5_i64), 5); + assert_eq!(hash(&5_i128), 5); assert_eq!(hash(&5_isize), 5); assert_eq!(hash(&false), 0); @@ -85,6 +87,17 @@ fn test_writer_hasher() { let ptr = ptr::without_provenance_mut::<i32>(5_usize); assert_eq!(hash(&ptr), 5); + // Use a newtype to test the `Hash::hash_slice` default implementation. + struct Byte(u8); + + impl Hash for Byte { + fn hash<H: Hasher>(&self, state: &mut H) { + state.write_u8(self.0) + } + } + + assert_eq!(hash(&[Byte(b'a')]), 97 + 1); + if cfg!(miri) { // Miri cannot hash pointers return; diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 1bdaa6965f6..a246c806140 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -13,8 +13,10 @@ #![feature(bool_to_result)] #![feature(bstr)] #![feature(cfg_target_has_reliable_f16_f128)] +#![feature(char_internals)] #![feature(char_max_len)] #![feature(clone_to_uninit)] +#![feature(const_cmp)] #![feature(const_convert)] #![feature(const_destruct)] #![feature(const_eval_select)] 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/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/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/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/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/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/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index a5f0718bc01..99a1062109a 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -590,6 +590,8 @@ impl Step for Rustc { // Debugger scripts builder.ensure(DebuggerScripts { sysroot: image.to_owned(), target }); + generate_target_spec_json_schema(builder, image); + // HTML copyright files let file_list = builder.ensure(super::run::GenerateCopyright); for file in file_list { @@ -618,6 +620,28 @@ impl Step for Rustc { } } +fn generate_target_spec_json_schema(builder: &Builder<'_>, sysroot: &Path) { + // Since we run rustc in bootstrap, we need to ensure that we use the host compiler. + // We do this by using the stage 1 compiler, which is always compiled for the host, + // even in a cross build. + let stage1_host = builder.compiler(1, builder.host_target); + let mut rustc = command(builder.rustc(stage1_host)).fail_fast(); + rustc + .env("RUSTC_BOOTSTRAP", "1") + .args(["--print=target-spec-json-schema", "-Zunstable-options"]); + let schema = rustc.run_capture(builder).stdout(); + + let schema_dir = tmpdir(builder); + t!(fs::create_dir_all(&schema_dir)); + let schema_file = schema_dir.join("target-spec-json-schema.json"); + t!(std::fs::write(&schema_file, schema)); + + let dst = sysroot.join("etc"); + t!(fs::create_dir_all(&dst)); + + builder.install(&schema_file, &dst, FileType::Regular); +} + /// Copies debugger scripts for `target` into the given compiler `sysroot`. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct DebuggerScripts { 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/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 4f839bdf7b8..723ba80eaf8 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2086,11 +2086,25 @@ HELP: You can add it into `bootstrap.toml` in `rust.codegen-backends = [{name:?} } // Get paths from cmd args - let paths = match &builder.config.cmd { + let mut paths = match &builder.config.cmd { Subcommand::Test { .. } => &builder.config.paths[..], _ => &[], }; + // in rustdoc-js mode, allow filters to be rs files or js files. + // use a late-initialized Vec to avoid cloning for other modes. + let mut paths_v; + if mode == "rustdoc-js" { + paths_v = paths.to_vec(); + for p in &mut paths_v { + if let Some(ext) = p.extension() + && ext == "js" + { + p.set_extension("rs"); + } + } + paths = &paths_v; + } // Get test-args by striping suite path let mut test_args: Vec<&str> = paths .iter() 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/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index 777c8601aa1..db2369097d6 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -43,6 +43,7 @@ pub static CRATES: &[&str] = &[ "rustc-hash", "self_cell", "serde", + "serde_derive_internals", "sha2", "smallvec", "stable_deref_trait", diff --git a/src/doc/rustc/src/targets/custom.md b/src/doc/rustc/src/targets/custom.md index 6c1494186a4..e1750e27f0b 100644 --- a/src/doc/rustc/src/targets/custom.md +++ b/src/doc/rustc/src/targets/custom.md @@ -16,6 +16,21 @@ rustc +nightly -Z unstable-options --target=wasm32-unknown-unknown --print targe To use a custom target, see the (unstable) [`build-std` feature](../../cargo/reference/unstable.html#build-std) of `cargo`. +<div class="warning"> + +The target JSON properties are not stable and subject to change. +Always pin your compiler version when using custom targets! + +</div> + +## JSON Schema + +`rustc` provides a JSON schema for the custom target JSON specification. +Because the schema is subject to change, you should always use the schema from the version of rustc which you are passing the target to. + +It can be found in `etc/target-spec-json-schema.json` in the sysroot (`rustc --print sysroot`) or printed with `rustc +nightly -Zunstable-options --print target-spec-json-schema`. +The existence and name of this schema is, just like the properties of the JSON specification, not stable and subject to change. + ## Custom Target Lookup Path When `rustc` is given an option `--target=TARGET` (where `TARGET` is any string), it uses the following logic: 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/tools/cargo b/src/tools/cargo -Subproject 761c4658d0079d607e6d33cf0c060e61a617cad +Subproject 24bb93c388fb8c211a37986539f24a819dc669d 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/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/btreemap.rs b/src/tools/miri/tests/pass/btreemap.rs index 1d65e69bf72..7af6d7b5551 100644 --- a/src/tools/miri/tests/pass/btreemap.rs +++ b/src/tools/miri/tests/pass/btreemap.rs @@ -1,7 +1,6 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-strict-provenance -#![feature(btree_extract_if)] use std::collections::{BTreeMap, BTreeSet}; use std::mem; diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 60347b2ea64..a9804761400 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -267,6 +267,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "digest", "displaydoc", "dissimilar", + "dyn-clone", "either", "elsa", "ena", @@ -346,6 +347,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "rand_xorshift", // dependency for doc-tests in rustc_thread_pool "rand_xoshiro", "redox_syscall", + "ref-cast", + "ref-cast-impl", "regex", "regex-automata", "regex-syntax", @@ -357,11 +360,14 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "rustix", "ruzstd", // via object in thorin-dwp "ryu", + "schemars", + "schemars_derive", "scoped-tls", "scopeguard", "self_cell", "serde", "serde_derive", + "serde_derive_internals", "serde_json", "serde_path_to_error", "sha1", 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/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/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/run-make/print-request-help-stable-unstable/help-diff.diff b/tests/run-make/print-request-help-stable-unstable/help-diff.diff index 07eafca3271..044302a19a0 100644 --- a/tests/run-make/print-request-help-stable-unstable/help-diff.diff +++ b/tests/run-make/print-request-help-stable-unstable/help-diff.diff @@ -2,6 +2,6 @@ error: unknown print request: `xxx` | - = help: valid print requests are: `calling-conventions`, `cfg`, `code-models`, `crate-name`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `tls-models` -+ = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` ++ = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err b/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err index 50ef340e3dd..cc6c3c909b3 100644 --- a/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err +++ b/tests/run-make/print-request-help-stable-unstable/unstable-invalid-print-request-help.err @@ -1,5 +1,5 @@ error: unknown print request: `xxx` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/run-make/rustc-help/help-v.stdout b/tests/run-make/rustc-help/help-v.stdout index 3fc297fb08e..cd161c51ee3 100644 --- a/tests/run-make/rustc-help/help-v.stdout +++ b/tests/run-make/rustc-help/help-v.stdout @@ -43,7 +43,7 @@ Options: --print <INFO>[=<FILE>] Compiler information to print on stdout (or to a file) INFO may be one of - <all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models>. + <all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|target-spec-json-schema|tls-models>. -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 -o <FILENAME> Write output to FILENAME diff --git a/tests/run-make/rustc-help/help.stdout b/tests/run-make/rustc-help/help.stdout index caffe28f498..74ec083bdee 100644 --- a/tests/run-make/rustc-help/help.stdout +++ b/tests/run-make/rustc-help/help.stdout @@ -43,7 +43,7 @@ Options: --print <INFO>[=<FILE>] Compiler information to print on stdout (or to a file) INFO may be one of - <all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models>. + <all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|target-spec-json-schema|tls-models>. -g Equivalent to -C debuginfo=2 -O Equivalent to -C opt-level=3 -o <FILENAME> Write output to FILENAME 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/issues/issue-56806.rs b/tests/ui/abi/invalid-self-parameter-type-56806.rs index b1dac26d65a..60229df3005 100644 --- a/tests/ui/issues/issue-56806.rs +++ b/tests/ui/abi/invalid-self-parameter-type-56806.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/56806 pub trait Trait { fn dyn_instead_of_self(self: Box<dyn Trait>); //~^ ERROR invalid `self` parameter type diff --git a/tests/ui/issues/issue-56806.stderr b/tests/ui/abi/invalid-self-parameter-type-56806.stderr index ec50d863758..ac249b8f108 100644 --- a/tests/ui/issues/issue-56806.stderr +++ b/tests/ui/abi/invalid-self-parameter-type-56806.stderr @@ -1,5 +1,5 @@ error[E0307]: invalid `self` parameter type: `Box<(dyn Trait + 'static)>` - --> $DIR/issue-56806.rs:2:34 + --> $DIR/invalid-self-parameter-type-56806.rs:3:34 | LL | fn dyn_instead_of_self(self: Box<dyn Trait>); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/issues/issue-56870.rs b/tests/ui/associated-consts/traits-associated-consts-ice-56870.rs index fc6deedd029..0c5a2b84773 100644 --- a/tests/ui/issues/issue-56870.rs +++ b/tests/ui/associated-consts/traits-associated-consts-ice-56870.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/56870 //@ build-pass // Regression test for #56870: Internal compiler error (traits & associated consts) diff --git a/tests/ui/issues/issue-59326.rs b/tests/ui/associated-types/duplicate-associated-type-resolution-59326.rs index e9634ad9fd8..0439e229e14 100644 --- a/tests/ui/issues/issue-59326.rs +++ b/tests/ui/associated-types/duplicate-associated-type-resolution-59326.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/59326 //@ check-pass trait Service { type S; diff --git a/tests/ui/issues/issue-57399-self-return-impl-trait.rs b/tests/ui/associated-types/impl-trait-member-type-resolution-57399.rs index bcf1b18a9ff..3342dd0631a 100644 --- a/tests/ui/issues/issue-57399-self-return-impl-trait.rs +++ b/tests/ui/associated-types/impl-trait-member-type-resolution-57399.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57399 //@ check-pass trait T { @@ -12,7 +13,6 @@ struct S<A> { a: A, } - impl From<u32> for S<<i32 as T>::T> { fn from(a: u32) -> Self { Self { a } 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/issues/issue-59488.rs b/tests/ui/binop/function-comparison-errors-59488.rs index 384501e3e5d..8ded781ef95 100644 --- a/tests/ui/issues/issue-59488.rs +++ b/tests/ui/binop/function-comparison-errors-59488.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/59488 fn foo() -> i32 { 42 } diff --git a/tests/ui/issues/issue-59488.stderr b/tests/ui/binop/function-comparison-errors-59488.stderr index b6611ad63a8..615458bc45b 100644 --- a/tests/ui/issues/issue-59488.stderr +++ b/tests/ui/binop/function-comparison-errors-59488.stderr @@ -1,5 +1,5 @@ error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}` - --> $DIR/issue-59488.rs:14:9 + --> $DIR/function-comparison-errors-59488.rs:15:9 | LL | foo > 12; | --- ^ -- {integer} @@ -12,7 +12,7 @@ LL | foo() > 12; | ++ error[E0308]: mismatched types - --> $DIR/issue-59488.rs:14:11 + --> $DIR/function-comparison-errors-59488.rs:15:11 | LL | foo > 12; | ^^ expected fn item, found `i32` @@ -21,7 +21,7 @@ LL | foo > 12; found type `i32` error[E0369]: binary operation `>` cannot be applied to type `fn(i64) -> i64 {bar}` - --> $DIR/issue-59488.rs:18:9 + --> $DIR/function-comparison-errors-59488.rs:19:9 | LL | bar > 13; | --- ^ -- {integer} @@ -34,7 +34,7 @@ LL | bar(/* i64 */) > 13; | +++++++++++ error[E0308]: mismatched types - --> $DIR/issue-59488.rs:18:11 + --> $DIR/function-comparison-errors-59488.rs:19:11 | LL | bar > 13; | ^^ expected fn item, found `i64` @@ -43,7 +43,7 @@ LL | bar > 13; found type `i64` error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}` - --> $DIR/issue-59488.rs:22:9 + --> $DIR/function-comparison-errors-59488.rs:23:9 | LL | foo > foo; | --- ^ --- fn() -> i32 {foo} @@ -56,7 +56,7 @@ LL | foo() > foo(); | ++ ++ error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}` - --> $DIR/issue-59488.rs:25:9 + --> $DIR/function-comparison-errors-59488.rs:26:9 | LL | foo > bar; | --- ^ --- fn(i64) -> i64 {bar} @@ -64,7 +64,7 @@ LL | foo > bar; | fn() -> i32 {foo} error[E0308]: mismatched types - --> $DIR/issue-59488.rs:25:11 + --> $DIR/function-comparison-errors-59488.rs:26:11 | LL | foo > bar; | ^^^ expected fn item, found a different fn item @@ -73,7 +73,7 @@ LL | foo > bar; found fn item `fn(i64) -> i64 {bar}` error[E0369]: binary operation `==` cannot be applied to type `fn(usize) -> Foo {Foo::Bar}` - --> $DIR/issue-59488.rs:30:5 + --> $DIR/function-comparison-errors-59488.rs:31:5 | LL | assert_eq!(Foo::Bar, i); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -84,7 +84,7 @@ LL | assert_eq!(Foo::Bar, i); = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug` - --> $DIR/issue-59488.rs:30:5 + --> $DIR/function-comparison-errors-59488.rs:31:5 | LL | assert_eq!(Foo::Bar, i); | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}` @@ -92,7 +92,7 @@ LL | assert_eq!(Foo::Bar, i); = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `fn(usize) -> Foo {Foo::Bar}` doesn't implement `Debug` - --> $DIR/issue-59488.rs:30:5 + --> $DIR/function-comparison-errors-59488.rs:31:5 | LL | assert_eq!(Foo::Bar, i); | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for fn item `fn(usize) -> Foo {Foo::Bar}` diff --git a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741-1.rs b/tests/ui/box/boxed-value-matching-57741.rs index d0aae23b2fc..7e2f089dad8 100644 --- a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741-1.rs +++ b/tests/ui/box/boxed-value-matching-57741.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57741 #![allow(warnings)] // This tests that the `help: consider dereferencing the boxed value` suggestion isn't made diff --git a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741-1.stderr b/tests/ui/box/boxed-value-matching-57741.stderr index 76f03bab6d1..33d7a6759ad 100644 --- a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741-1.stderr +++ b/tests/ui/box/boxed-value-matching-57741.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-57741-1.rs:14:9 + --> $DIR/boxed-value-matching-57741.rs:15:9 | LL | let y = match x { | - this expression has type `Box<u32>` @@ -10,7 +10,7 @@ LL | S::A { a } | S::B { b: a } => a, found enum `S` error[E0308]: mismatched types - --> $DIR/issue-57741-1.rs:14:22 + --> $DIR/boxed-value-matching-57741.rs:15:22 | LL | let y = match x { | - this expression has type `Box<u32>` diff --git a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.fixed b/tests/ui/box/dereferencing-boxed-enum-in-match-57741.fixed index 1823f0d3d4c..ee796b56272 100644 --- a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.fixed +++ b/tests/ui/box/dereferencing-boxed-enum-in-match-57741.fixed @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57741 //@ run-rustfix #![allow(warnings)] diff --git a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.rs b/tests/ui/box/dereferencing-boxed-enum-in-match-57741.rs index 47ab91177e0..3a45a8b56ff 100644 --- a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.rs +++ b/tests/ui/box/dereferencing-boxed-enum-in-match-57741.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57741 //@ run-rustfix #![allow(warnings)] diff --git a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr b/tests/ui/box/dereferencing-boxed-enum-in-match-57741.stderr index 62d83a54614..c07387b21bd 100644 --- a/tests/ui/issues/issue-57741-dereference-boxed-value/issue-57741.stderr +++ b/tests/ui/box/dereferencing-boxed-enum-in-match-57741.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-57741.rs:20:9 + --> $DIR/dereferencing-boxed-enum-in-match-57741.rs:21:9 | LL | let y = match x { | - this expression has type `Box<T>` @@ -14,7 +14,7 @@ LL | let y = match *x { | + error[E0308]: mismatched types - --> $DIR/issue-57741.rs:20:19 + --> $DIR/dereferencing-boxed-enum-in-match-57741.rs:21:19 | LL | let y = match x { | - this expression has type `Box<T>` @@ -29,7 +29,7 @@ LL | let y = match *x { | + error[E0308]: mismatched types - --> $DIR/issue-57741.rs:27:9 + --> $DIR/dereferencing-boxed-enum-in-match-57741.rs:28:9 | LL | let y = match x { | - this expression has type `Box<S>` @@ -44,7 +44,7 @@ LL | let y = match *x { | + error[E0308]: mismatched types - --> $DIR/issue-57741.rs:27:22 + --> $DIR/dereferencing-boxed-enum-in-match-57741.rs:28:22 | LL | let y = match x { | - this expression has type `Box<S>` diff --git a/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs b/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs index afb16cf58e8..4ae77ab6439 100644 --- a/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs +++ b/tests/ui/closures/2229_closure_analysis/run_pass/lit-pattern-matching-with-methods.rs @@ -2,7 +2,6 @@ //@check-pass #![warn(unused)] #![feature(rustc_attrs)] -#![feature(btree_extract_if)] use std::collections::BTreeMap; use std::panic::{catch_unwind, AssertUnwindSafe}; diff --git a/tests/ui/issues/issue-59494.rs b/tests/ui/closures/generic-typed-nested-closures-59494.rs index b4d50bd4ce7..04d7b00ff7f 100644 --- a/tests/ui/issues/issue-59494.rs +++ b/tests/ui/closures/generic-typed-nested-closures-59494.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/59494 fn t7p<A, B, C>(f: impl Fn(B) -> C, g: impl Fn(A) -> B) -> impl Fn(A) -> C { move |a: A| -> C { f(g(a)) } } diff --git a/tests/ui/issues/issue-59494.stderr b/tests/ui/closures/generic-typed-nested-closures-59494.stderr index 33d3e48c1aa..9706fea82a3 100644 --- a/tests/ui/issues/issue-59494.stderr +++ b/tests/ui/closures/generic-typed-nested-closures-59494.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-59494.rs:20:40 + --> $DIR/generic-typed-nested-closures-59494.rs:21:40 | LL | let t7 = |env| |a| |b| t7p(f, g)(((env, a), b)); | ^^^ cyclic type of infinite size diff --git a/tests/ui/issues/issue-58375-monomorphize-default-impls.rs b/tests/ui/codegen/mono-item-collector-default-impl-58375.rs index 769a1176edd..f00e79e0dc5 100644 --- a/tests/ui/issues/issue-58375-monomorphize-default-impls.rs +++ b/tests/ui/codegen/mono-item-collector-default-impl-58375.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/58375 // Make sure that the mono-item collector does not crash when trying to // instantiate a default impl for DecodeUtf16<<u8 as A>::Item> // See https://github.com/rust-lang/rust/issues/58375 diff --git a/tests/ui/issues/issue-57162.rs b/tests/ui/coherence/trait-implementation-coherence-check-57162.rs index 5e62d0eb010..a57e827ca8b 100644 --- a/tests/ui/issues/issue-57162.rs +++ b/tests/ui/coherence/trait-implementation-coherence-check-57162.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57162 //@ check-pass trait Foo {} 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/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/consts/missing-larger-array-impl.stderr b/tests/ui/consts/missing-larger-array-impl.stderr index ff4fa36d684..33d7a46339b 100644 --- a/tests/ui/consts/missing-larger-array-impl.stderr +++ b/tests/ui/consts/missing-larger-array-impl.stderr @@ -8,11 +8,11 @@ LL | <[X; 35] as Default>::default(); &[T] &mut [T] [T; 0] - [T; 10] - [T; 11] - [T; 12] - [T; 13] - [T; 14] + [T; 1] + [T; 2] + [T; 3] + [T; 4] + [T; 5] and 27 others error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-57781.rs b/tests/ui/consts/oncecell-const-init-57781.rs index 7f0d2eda9bb..27426ef2549 100644 --- a/tests/ui/issues/issue-57781.rs +++ b/tests/ui/consts/oncecell-const-init-57781.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57781 //@ run-pass use std::cell::UnsafeCell; diff --git a/tests/ui/issues/issue-58463.rs b/tests/ui/debuginfo/impl-copy-function-debuginfo-58463.rs index 6e4b909bc38..72388c36ce4 100644 --- a/tests/ui/issues/issue-58463.rs +++ b/tests/ui/debuginfo/impl-copy-function-debuginfo-58463.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/58463 //@ run-pass //@ compile-flags:-C debuginfo=2 diff --git a/tests/ui/issues/issue-58734.rs b/tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.rs index e5b371f5530..3e9ebb497a2 100644 --- a/tests/ui/issues/issue-58734.rs +++ b/tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/58734 trait Trait { fn exists(self) -> (); diff --git a/tests/ui/issues/issue-58734.stderr b/tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.stderr index 2336a94f150..140461283f3 100644 --- a/tests/ui/issues/issue-58734.stderr +++ b/tests/ui/dyn-compatibility/spurious-dyn-compat-errors-58734.stderr @@ -1,5 +1,5 @@ warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/issue-58734.rs:20:5 + --> $DIR/spurious-dyn-compat-errors-58734.rs:21:5 | LL | Trait::nonexistent(()); | ^^^^^ @@ -13,14 +13,14 @@ LL | <dyn Trait>::nonexistent(()); | ++++ + error[E0038]: the trait `Trait` is not dyn compatible - --> $DIR/issue-58734.rs:20:5 + --> $DIR/spurious-dyn-compat-errors-58734.rs:21:5 | LL | Trait::nonexistent(()); | ^^^^^ `Trait` 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/issue-58734.rs:4:8 + --> $DIR/spurious-dyn-compat-errors-58734.rs:5:8 | LL | trait Trait { | ----- this trait is not dyn compatible... 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/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/issues/issue-56237.rs b/tests/ui/generics/generic-associated-type-deref-target-56237.rs index 3c0a235f3ec..2050ca377e8 100644 --- a/tests/ui/issues/issue-56237.rs +++ b/tests/ui/generics/generic-associated-type-deref-target-56237.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/56237 //@ run-pass use std::ops::Deref; diff --git a/tests/ui/issues/issue-55731.rs b/tests/ui/higher-ranked/hrtb-associated-type-leak-check-55731.rs index 7b4f4e2cd3b..978abd9fcf5 100644 --- a/tests/ui/issues/issue-55731.rs +++ b/tests/ui/higher-ranked/hrtb-associated-type-leak-check-55731.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/55731 use std::marker::PhantomData; trait DistributedIterator { diff --git a/tests/ui/issues/issue-55731.stderr b/tests/ui/higher-ranked/hrtb-associated-type-leak-check-55731.stderr index 2c38041642d..40ac1d9d041 100644 --- a/tests/ui/issues/issue-55731.stderr +++ b/tests/ui/higher-ranked/hrtb-associated-type-leak-check-55731.stderr @@ -1,5 +1,5 @@ error: implementation of `DistributedIteratorMulti` is not general enough - --> $DIR/issue-55731.rs:48:5 + --> $DIR/hrtb-associated-type-leak-check-55731.rs:49:5 | LL | / multi(Map { LL | | i: Cloned(PhantomData), 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/imports/auxiliary/reexported-trait-56175.rs b/tests/ui/imports/auxiliary/reexported-trait-56175.rs new file mode 100644 index 00000000000..51a991bef59 --- /dev/null +++ b/tests/ui/imports/auxiliary/reexported-trait-56175.rs @@ -0,0 +1,17 @@ +mod private { + pub trait Trait { + fn trait_method(&self) { + } + } + pub trait TraitB { + fn trait_method_b(&self) { + } + } +} + +pub struct FooStruct; +pub use crate::private::Trait; +impl crate::private::Trait for FooStruct {} + +pub use crate::private::TraitB as TraitBRename; +impl crate::private::TraitB for FooStruct {} diff --git a/tests/ui/issues/issue-56175.rs b/tests/ui/imports/private-types-suggested-without-extern-crate-56175.rs index daffe806a90..ce001edad1b 100644 --- a/tests/ui/issues/issue-56175.rs +++ b/tests/ui/imports/private-types-suggested-without-extern-crate-56175.rs @@ -1,5 +1,6 @@ +// https://github.com/rust-lang/rust/issues/56175 //@ edition:2018 -//@ aux-crate:reexported_trait=reexported-trait.rs +//@ aux-crate:reexported_trait=reexported-trait-56175.rs fn main() { reexported_trait::FooStruct.trait_method(); diff --git a/tests/ui/issues/issue-56175.stderr b/tests/ui/imports/private-types-suggested-without-extern-crate-56175.stderr index df4cd6ce8a7..1e8285c80ac 100644 --- a/tests/ui/issues/issue-56175.stderr +++ b/tests/ui/imports/private-types-suggested-without-extern-crate-56175.stderr @@ -1,10 +1,10 @@ error[E0599]: no method named `trait_method` found for struct `FooStruct` in the current scope - --> $DIR/issue-56175.rs:5:33 + --> $DIR/private-types-suggested-without-extern-crate-56175.rs:6:33 | LL | reexported_trait::FooStruct.trait_method(); | ^^^^^^^^^^^^ | - ::: $DIR/auxiliary/reexported-trait.rs:3:12 + ::: $DIR/auxiliary/reexported-trait-56175.rs:3:12 | LL | fn trait_method(&self) { | ------------ the method is available for `FooStruct` here @@ -12,7 +12,7 @@ LL | fn trait_method(&self) { = help: items from traits can only be used if the trait is in scope help: trait `Trait` which provides `trait_method` is implemented but not in scope; perhaps you want to import it | -LL + use reexported_trait::Trait; +LL + use reexported_trait_56175::Trait; | help: there is a method `trait_method_b` with a similar name | @@ -20,12 +20,12 @@ LL | reexported_trait::FooStruct.trait_method_b(); | ++ error[E0599]: no method named `trait_method_b` found for struct `FooStruct` in the current scope - --> $DIR/issue-56175.rs:7:33 + --> $DIR/private-types-suggested-without-extern-crate-56175.rs:8:33 | LL | reexported_trait::FooStruct.trait_method_b(); | ^^^^^^^^^^^^^^ | - ::: $DIR/auxiliary/reexported-trait.rs:7:12 + ::: $DIR/auxiliary/reexported-trait-56175.rs:7:12 | LL | fn trait_method_b(&self) { | -------------- the method is available for `FooStruct` here @@ -33,7 +33,7 @@ LL | fn trait_method_b(&self) { = help: items from traits can only be used if the trait is in scope help: trait `TraitB` which provides `trait_method_b` is implemented but not in scope; perhaps you want to import it | -LL + use reexported_trait::TraitBRename; +LL + use reexported_trait_56175::TraitBRename; | help: there is a method `trait_method` with a similar name | diff --git a/tests/ui/issues/auxiliary/issue-57271-lib.rs b/tests/ui/infinite/auxiliary/aux-57271-lib.rs index ff625668a9d..ff625668a9d 100644 --- a/tests/ui/issues/auxiliary/issue-57271-lib.rs +++ b/tests/ui/infinite/auxiliary/aux-57271-lib.rs diff --git a/tests/ui/issues/issue-57271.rs b/tests/ui/infinite/mutually-recursive-infinite-types-57271.rs index 20d081ecb3c..cb20770b486 100644 --- a/tests/ui/issues/issue-57271.rs +++ b/tests/ui/infinite/mutually-recursive-infinite-types-57271.rs @@ -1,8 +1,9 @@ -//@ aux-build:issue-57271-lib.rs +// https://github.com/rust-lang/rust/issues/57271 +//@ aux-build:aux-57271-lib.rs -extern crate issue_57271_lib; +extern crate aux_57271_lib; -use issue_57271_lib::BaseType; +use aux_57271_lib::BaseType; pub enum ObjectType { //~ ERROR recursive types `ObjectType` and `TypeSignature` have infinite size Class(ClassTypeSignature), diff --git a/tests/ui/issues/issue-57271.stderr b/tests/ui/infinite/mutually-recursive-infinite-types-57271.stderr index a61419c61d7..8bf1b470062 100644 --- a/tests/ui/issues/issue-57271.stderr +++ b/tests/ui/infinite/mutually-recursive-infinite-types-57271.stderr @@ -1,5 +1,5 @@ error[E0072]: recursive types `ObjectType` and `TypeSignature` have infinite size - --> $DIR/issue-57271.rs:7:1 + --> $DIR/mutually-recursive-infinite-types-57271.rs:8:1 | LL | pub enum ObjectType { | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/invalid-compile-flags/print-without-arg.stderr b/tests/ui/invalid-compile-flags/print-without-arg.stderr index 3048a59d0d0..4163d4e0602 100644 --- a/tests/ui/invalid-compile-flags/print-without-arg.stderr +++ b/tests/ui/invalid-compile-flags/print-without-arg.stderr @@ -3,5 +3,5 @@ error: Argument to option 'print' missing --print <INFO>[=<FILE>] Compiler information to print on stdout (or to a file) INFO may be one of - <all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|tls-models>. + <all-target-specs-json|calling-conventions|cfg|check-cfg|code-models|crate-name|crate-root-lint-levels|deployment-target|file-names|host-tuple|link-args|native-static-libs|relocation-models|split-debuginfo|stack-protector-strategies|supported-crate-types|sysroot|target-cpus|target-features|target-libdir|target-list|target-spec-json|target-spec-json-schema|tls-models>. diff --git a/tests/ui/invalid-compile-flags/print.stderr b/tests/ui/invalid-compile-flags/print.stderr index e3374eb1e6e..e8adbfd87d7 100644 --- a/tests/ui/invalid-compile-flags/print.stderr +++ b/tests/ui/invalid-compile-flags/print.stderr @@ -1,5 +1,5 @@ error: unknown print request: `yyyy` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/ui/issues/issue-56943.rs b/tests/ui/issues/issue-56943.rs deleted file mode 100644 index 9664567ec9e..00000000000 --- a/tests/ui/issues/issue-56943.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ aux-build:issue-56943.rs - -extern crate issue_56943; - -fn main() { - let _: issue_56943::S = issue_56943::S2; - //~^ ERROR mismatched types [E0308] -} diff --git a/tests/ui/issues/issue-57198-pass.rs b/tests/ui/keyword/raw-identifier-for-function-57198.rs index 06f30603c31..41a0cbf4619 100644 --- a/tests/ui/issues/issue-57198-pass.rs +++ b/tests/ui/keyword/raw-identifier-for-function-57198.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57198 //@ run-pass mod m { 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/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/issues/auxiliary/issue-56943.rs b/tests/ui/mismatched_types/auxiliary/aux-56943.rs index 65b9beb91f9..65b9beb91f9 100644 --- a/tests/ui/issues/auxiliary/issue-56943.rs +++ b/tests/ui/mismatched_types/auxiliary/aux-56943.rs diff --git a/tests/ui/mismatched_types/type-mismatch-in-extern-crate-56943.rs b/tests/ui/mismatched_types/type-mismatch-in-extern-crate-56943.rs new file mode 100644 index 00000000000..9970b27c847 --- /dev/null +++ b/tests/ui/mismatched_types/type-mismatch-in-extern-crate-56943.rs @@ -0,0 +1,9 @@ +// https://github.com/rust-lang/rust/issues/56943 +//@ aux-build:aux-56943.rs + +extern crate aux_56943; + +fn main() { + let _: aux_56943::S = aux_56943::S2; + //~^ ERROR mismatched types [E0308] +} diff --git a/tests/ui/issues/issue-56943.stderr b/tests/ui/mismatched_types/type-mismatch-in-extern-crate-56943.stderr index 60a2e92dc71..2315267701d 100644 --- a/tests/ui/issues/issue-56943.stderr +++ b/tests/ui/mismatched_types/type-mismatch-in-extern-crate-56943.stderr @@ -1,8 +1,8 @@ error[E0308]: mismatched types - --> $DIR/issue-56943.rs:6:29 + --> $DIR/type-mismatch-in-extern-crate-56943.rs:7:27 | -LL | let _: issue_56943::S = issue_56943::S2; - | -------------- ^^^^^^^^^^^^^^^ expected `S`, found `S2` +LL | let _: aux_56943::S = aux_56943::S2; + | ------------ ^^^^^^^^^^^^^ expected `S`, found `S2` | | | expected due to this diff --git a/tests/ui/issues/issue-56128.rs b/tests/ui/modules/pub-use-handling-in-modules-56128.rs index cc170f60250..cc170f60250 100644 --- a/tests/ui/issues/issue-56128.rs +++ b/tests/ui/modules/pub-use-handling-in-modules-56128.rs diff --git a/tests/ui/issues/issue-55587.rs b/tests/ui/parser/invalid-variable-definition-55587.rs index d9100cf555b..f2c7c0a8e6c 100644 --- a/tests/ui/issues/issue-55587.rs +++ b/tests/ui/parser/invalid-variable-definition-55587.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/55587 use std::path::Path; fn main() { diff --git a/tests/ui/issues/issue-55587.stderr b/tests/ui/parser/invalid-variable-definition-55587.stderr index 7a5d0e28100..08c951582e3 100644 --- a/tests/ui/issues/issue-55587.stderr +++ b/tests/ui/parser/invalid-variable-definition-55587.stderr @@ -1,5 +1,5 @@ error[E0164]: expected tuple struct or tuple variant, found associated function `Path::new` - --> $DIR/issue-55587.rs:4:9 + --> $DIR/invalid-variable-definition-55587.rs:5:9 | LL | let Path::new(); | ^^^^^^^^^^^ `fn` calls are not allowed in patterns diff --git a/tests/ui/print-request/print-lints-help.stderr b/tests/ui/print-request/print-lints-help.stderr index bc48b2fa73c..297a3aa79e1 100644 --- a/tests/ui/print-request/print-lints-help.stderr +++ b/tests/ui/print-request/print-lints-help.stderr @@ -1,6 +1,6 @@ error: unknown print request: `lints` | - = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `tls-models` + = help: valid print requests are: `all-target-specs-json`, `calling-conventions`, `cfg`, `check-cfg`, `code-models`, `crate-name`, `crate-root-lint-levels`, `deployment-target`, `file-names`, `host-tuple`, `link-args`, `native-static-libs`, `relocation-models`, `split-debuginfo`, `stack-protector-strategies`, `supported-crate-types`, `sysroot`, `target-cpus`, `target-features`, `target-libdir`, `target-list`, `target-spec-json`, `target-spec-json-schema`, `tls-models` = help: use `-Whelp` to print a list of lints = help: for more information, see the rustc book: https://doc.rust-lang.org/rustc/command-line-arguments.html#--print-print-compiler-information diff --git a/tests/ui/issues/issue-58712.rs b/tests/ui/resolve/missing-type-in-scope-58712.rs index 930bec6889b..b9ff74e426d 100644 --- a/tests/ui/issues/issue-58712.rs +++ b/tests/ui/resolve/missing-type-in-scope-58712.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/58712 struct AddrVec<H, A> { h: H, a: A, diff --git a/tests/ui/issues/issue-58712.stderr b/tests/ui/resolve/missing-type-in-scope-58712.stderr index f4bd4d1e826..d7e06eee856 100644 --- a/tests/ui/issues/issue-58712.stderr +++ b/tests/ui/resolve/missing-type-in-scope-58712.stderr @@ -1,5 +1,5 @@ error[E0412]: cannot find type `DeviceId` in this scope - --> $DIR/issue-58712.rs:6:20 + --> $DIR/missing-type-in-scope-58712.rs:7:20 | LL | impl<H> AddrVec<H, DeviceId> { | ^^^^^^^^ not found in this scope @@ -10,7 +10,7 @@ LL | impl<H, DeviceId> AddrVec<H, DeviceId> { | ++++++++++ error[E0412]: cannot find type `DeviceId` in this scope - --> $DIR/issue-58712.rs:8:29 + --> $DIR/missing-type-in-scope-58712.rs:9:29 | LL | pub fn device(&self) -> DeviceId { | ^^^^^^^^ not found in this scope diff --git a/tests/ui/issues/issue-59020.rs b/tests/ui/std/park-timeout-wakeup-59020.rs index 2a34ba52b88..af530bb586c 100644 --- a/tests/ui/issues/issue-59020.rs +++ b/tests/ui/std/park-timeout-wakeup-59020.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/59020 //@ edition:2018 //@ run-pass //@ needs-threads diff --git a/tests/ui/issues/issue-56835.rs b/tests/ui/structs/invalid-self-constructor-56835.rs index 7132d15ee5f..fd8763443f0 100644 --- a/tests/ui/issues/issue-56835.rs +++ b/tests/ui/structs/invalid-self-constructor-56835.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/56835 pub struct Foo {} impl Foo { diff --git a/tests/ui/issues/issue-56835.stderr b/tests/ui/structs/invalid-self-constructor-56835.stderr index e949ae7b324..045781ec42b 100644 --- a/tests/ui/issues/issue-56835.stderr +++ b/tests/ui/structs/invalid-self-constructor-56835.stderr @@ -1,11 +1,11 @@ error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/issue-56835.rs:4:12 + --> $DIR/invalid-self-constructor-56835.rs:5:12 | LL | fn bar(Self(foo): Self) {} | ^^^^^^^^^ help: use curly brackets: `Self { /* fields */ }` error[E0164]: expected tuple struct or tuple variant, found self constructor `Self` - --> $DIR/issue-56835.rs:4:12 + --> $DIR/invalid-self-constructor-56835.rs:5:12 | LL | fn bar(Self(foo): Self) {} | ^^^^^^^^^ not a tuple struct or tuple variant diff --git a/tests/ui/issues/issue-59756.fixed b/tests/ui/suggestions/incompatible-types-in-try-expression-59756.fixed index 954ba917626..954ba917626 100644 --- a/tests/ui/issues/issue-59756.fixed +++ b/tests/ui/suggestions/incompatible-types-in-try-expression-59756.fixed diff --git a/tests/ui/issues/issue-59756.rs b/tests/ui/suggestions/incompatible-types-in-try-expression-59756.rs index de349f43f46..570aa7d373b 100644 --- a/tests/ui/issues/issue-59756.rs +++ b/tests/ui/suggestions/incompatible-types-in-try-expression-59756.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/59756 //@ run-rustfix //@ ignore-test (rustfix needs multiple suggestions) // diff --git a/tests/ui/issues/issue-59756.stderr b/tests/ui/suggestions/incompatible-types-in-try-expression-59756.stderr index 27c07fecd5b..7182109b5a0 100644 --- a/tests/ui/issues/issue-59756.stderr +++ b/tests/ui/suggestions/incompatible-types-in-try-expression-59756.stderr @@ -1,5 +1,5 @@ error[E0308]: try expression alternatives have incompatible types - --> $DIR/issue-59756.rs:13:5 + --> $DIR/incompatible-types-in-try-expression-59756.rs:13:5 | LL | foo()? | ^^^^^^ expected enum `std::result::Result`, found struct `A` diff --git a/tests/ui/suggestions/issue-71394-no-from-impl.stderr b/tests/ui/suggestions/issue-71394-no-from-impl.stderr index 31f8f1d455a..9e068c311ae 100644 --- a/tests/ui/suggestions/issue-71394-no-from-impl.stderr +++ b/tests/ui/suggestions/issue-71394-no-from-impl.stderr @@ -5,14 +5,14 @@ LL | let _: &[i8] = data.into(); | ^^^^ the trait `From<&[u8]>` is not implemented for `&[i8]` | = help: the following other types implement trait `From<T>`: - `[T; 10]` implements `From<(T, T, T, T, T, T, T, T, T, T)>` - `[T; 11]` implements `From<(T, T, T, T, T, T, T, T, T, T, T)>` - `[T; 12]` implements `From<(T, T, T, T, T, T, T, T, T, T, T, T)>` `[T; 1]` implements `From<(T,)>` `[T; 2]` implements `From<(T, T)>` `[T; 3]` implements `From<(T, T, T)>` `[T; 4]` implements `From<(T, T, T, T)>` `[T; 5]` implements `From<(T, T, T, T, T)>` + `[T; 6]` implements `From<(T, T, T, T, T, T)>` + `[T; 7]` implements `From<(T, T, T, T, T, T, T)>` + `[T; 8]` implements `From<(T, T, T, T, T, T, T, T)>` and 6 others = note: required for `&[u8]` to implement `Into<&[i8]>` diff --git a/tests/ui/issues/issue-58857.rs b/tests/ui/trait-bounds/negative-bound-not-supported-58857.rs index 4350d7e5b40..0a0b70a216b 100644 --- a/tests/ui/issues/issue-58857.rs +++ b/tests/ui/trait-bounds/negative-bound-not-supported-58857.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/58857 struct Conj<A> {a : A} trait Valid {} diff --git a/tests/ui/issues/issue-58857.stderr b/tests/ui/trait-bounds/negative-bound-not-supported-58857.stderr index ac70bc725e2..05347a891b4 100644 --- a/tests/ui/issues/issue-58857.stderr +++ b/tests/ui/trait-bounds/negative-bound-not-supported-58857.stderr @@ -1,5 +1,5 @@ error: negative bounds are not supported - --> $DIR/issue-58857.rs:4:9 + --> $DIR/negative-bound-not-supported-58857.rs:5:9 | LL | impl<A: !Valid> Conj<A>{} | ^ 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/issues/issue-58212.rs b/tests/ui/traits/generic-trait-impl-aliased-array-58212.rs index f266db603bf..a71194ba806 100644 --- a/tests/ui/issues/issue-58212.rs +++ b/tests/ui/traits/generic-trait-impl-aliased-array-58212.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/58212 //@ check-pass trait FromUnchecked { diff --git a/tests/ui/issues/issue-57156.rs b/tests/ui/traits/trait-object-lifetime-bounds-57156.rs index 12251509abd..8f5bef7fe13 100644 --- a/tests/ui/issues/issue-57156.rs +++ b/tests/ui/traits/trait-object-lifetime-bounds-57156.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57156 //@ check-pass trait Foo<Args> { diff --git a/tests/ui/issues/issue-56229.rs b/tests/ui/traits/trait-objects-with-supertraits-56229.rs index 1c6dd72ed2d..27cae968a6a 100644 --- a/tests/ui/issues/issue-56229.rs +++ b/tests/ui/traits/trait-objects-with-supertraits-56229.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/56229 //@ check-pass trait Mirror { diff --git a/tests/ui/issues/issue-57924.rs b/tests/ui/typeck/self-constructor-type-args-not-allowed-57924.rs index 8846912a8ff..2b3b11c3f38 100644 --- a/tests/ui/issues/issue-57924.rs +++ b/tests/ui/typeck/self-constructor-type-args-not-allowed-57924.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/57924 pub struct Gcm<E>(E); impl<E> Gcm<E> { diff --git a/tests/ui/issues/issue-57924.stderr b/tests/ui/typeck/self-constructor-type-args-not-allowed-57924.stderr index 40435fd0f0a..b5be5b39eb8 100644 --- a/tests/ui/issues/issue-57924.stderr +++ b/tests/ui/typeck/self-constructor-type-args-not-allowed-57924.stderr @@ -1,5 +1,5 @@ error[E0109]: type arguments are not allowed on self constructor - --> $DIR/issue-57924.rs:5:16 + --> $DIR/self-constructor-type-args-not-allowed-57924.rs:6:16 | LL | Self::<E>(e) | ---- ^ type argument not allowed diff --git a/tests/ui/issues/issue-56199.rs b/tests/ui/typeck/self-constructor-type-error-56199.rs index ba11582a9d5..b08d6918980 100644 --- a/tests/ui/issues/issue-56199.rs +++ b/tests/ui/typeck/self-constructor-type-error-56199.rs @@ -1,3 +1,4 @@ +// https://github.com/rust-lang/rust/issues/56199 enum Foo {} struct Bar {} diff --git a/tests/ui/issues/issue-56199.stderr b/tests/ui/typeck/self-constructor-type-error-56199.stderr index eb6d7005979..6e9d0fcd90c 100644 --- a/tests/ui/issues/issue-56199.stderr +++ b/tests/ui/typeck/self-constructor-type-error-56199.stderr @@ -1,5 +1,5 @@ error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/issue-56199.rs:6:17 + --> $DIR/self-constructor-type-error-56199.rs:7:17 | LL | let _ = Self; | ^^^^ @@ -7,7 +7,7 @@ LL | let _ = Self; = help: did you mean to use one of the enum's variants? error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/issue-56199.rs:8:17 + --> $DIR/self-constructor-type-error-56199.rs:9:17 | LL | let _ = Self(); | ^^^^^^ @@ -15,13 +15,13 @@ LL | let _ = Self(); = help: did you mean to use one of the enum's variants? error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/issue-56199.rs:15:17 + --> $DIR/self-constructor-type-error-56199.rs:16:17 | LL | let _ = Self; | ^^^^ help: use curly brackets: `Self { /* fields */ }` error: the `Self` constructor can only be used with tuple or unit structs - --> $DIR/issue-56199.rs:17:17 + --> $DIR/self-constructor-type-error-56199.rs:18:17 | LL | let _ = Self(); | ^^^^^^ help: use curly brackets: `Self { /* fields */ }` 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 |
