diff options
51 files changed, 940 insertions, 172 deletions
diff --git a/RELEASES.md b/RELEASES.md index e66bf60b7f7..147ff3561a3 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -27,6 +27,7 @@ Libraries - [Extend `ptr::null` and `null_mut` to all thin (including extern) types.][94954] - [`impl Read and Write for VecDeque<u8>`.][95632] - [STD support for the Nintendo 3DS.][95897] +- [Use rounding in float to Duration conversion methods.][96051] - [Make write/print macros eagerly drop temporaries.][96455] - [Implement internal traits that enable `[OsStr]::join`.][96881] - [Implement `Hash` for `core::alloc::Layout`.][97034] @@ -99,6 +100,8 @@ Compatibility Notes - [`#[link]` attributes are now checked more strictly,][96885] which may introduce errors for invalid attribute arguments that were previously ignored. +- [Rounding is now used when converting a float to a `Duration`.][96051] The converted + duration can differ slightly from what it was. Internal Changes ---------------- @@ -118,6 +121,7 @@ and related tools. [95818]: https://github.com/rust-lang/rust/pull/95818/ [95897]: https://github.com/rust-lang/rust/pull/95897/ [95953]: https://github.com/rust-lang/rust/pull/95953/ +[96051]: https://github.com/rust-lang/rust/pull/96051/ [96296]: https://github.com/rust-lang/rust/pull/96296/ [96455]: https://github.com/rust-lang/rust/pull/96455/ [96737]: https://github.com/rust-lang/rust/pull/96737/ diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 953761a7820..7f6947e3c79 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -20,7 +20,7 @@ use rustc_session::utils::NativeLibKind; use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_span::DebuggerVisualizerFile; -use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; +use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo}; use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target}; @@ -764,15 +764,15 @@ fn link_natively<'a>( "Linker does not support -static-pie command line option. Retrying with -static instead." ); // Mirror `add_(pre,post)_link_objects` to replace CRT objects. - let self_contained = crt_objects_fallback(sess, crate_type); + let self_contained = self_contained(sess, crate_type); let opts = &sess.target; let pre_objects = if self_contained { - &opts.pre_link_objects_fallback + &opts.pre_link_objects_self_contained } else { &opts.pre_link_objects }; let post_objects = if self_contained { - &opts.post_link_objects_fallback + &opts.post_link_objects_self_contained } else { &opts.post_link_objects }; @@ -1556,26 +1556,26 @@ fn detect_self_contained_mingw(sess: &Session) -> bool { true } -/// Whether we link to our own CRT objects instead of relying on gcc to pull them. +/// Various toolchain components used during linking are used from rustc distribution +/// instead of being found somewhere on the host system. /// We only provide such support for a very limited number of targets. -fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool { +fn self_contained(sess: &Session, crate_type: CrateType) -> bool { if let Some(self_contained) = sess.opts.cg.link_self_contained { return self_contained; } - match sess.target.crt_objects_fallback { + match sess.target.link_self_contained { + LinkSelfContainedDefault::False => false, + LinkSelfContainedDefault::True => true, // FIXME: Find a better heuristic for "native musl toolchain is available", // based on host and linker path, for example. // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). - Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)), - Some(CrtObjectsFallback::Mingw) => { + LinkSelfContainedDefault::Musl => sess.crt_static(Some(crate_type)), + LinkSelfContainedDefault::Mingw => { sess.host == sess.target && sess.target.vendor != "uwp" && detect_self_contained_mingw(&sess) } - // FIXME: Figure out cases in which WASM needs to link with a native toolchain. - Some(CrtObjectsFallback::Wasm) => true, - None => false, } } @@ -1592,7 +1592,7 @@ fn add_pre_link_objects( let opts = &sess.target; let empty = Default::default(); let objects = if self_contained { - &opts.pre_link_objects_fallback + &opts.pre_link_objects_self_contained } else if !(sess.target.os == "fuchsia" && flavor == LinkerFlavor::Gcc) { &opts.pre_link_objects } else { @@ -1610,9 +1610,11 @@ fn add_post_link_objects( link_output_kind: LinkOutputKind, self_contained: bool, ) { - let opts = &sess.target; - let objects = - if self_contained { &opts.post_link_objects_fallback } else { &opts.post_link_objects }; + let objects = if self_contained { + &sess.target.post_link_objects_self_contained + } else { + &sess.target.post_link_objects + }; for obj in objects.get(&link_output_kind).iter().copied().flatten() { cmd.add_object(&get_object_file_path(sess, obj, self_contained)); } @@ -1891,12 +1893,12 @@ fn linker_with_args<'a>( out_filename: &Path, codegen_results: &CodegenResults, ) -> Result<Command, ErrorGuaranteed> { - let crt_objects_fallback = crt_objects_fallback(sess, crate_type); + let self_contained = self_contained(sess, crate_type); let cmd = &mut *super::linker::get_linker( sess, path, flavor, - crt_objects_fallback, + self_contained, &codegen_results.crate_info.target_cpu, ); let link_output_kind = link_output_kind(sess, crate_type); @@ -1923,7 +1925,7 @@ fn linker_with_args<'a>( // ------------ Object code and libraries, order-dependent ------------ // Pre-link CRT objects. - add_pre_link_objects(cmd, sess, flavor, link_output_kind, crt_objects_fallback); + add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained); add_linked_symbol_object( cmd, @@ -2033,7 +2035,7 @@ fn linker_with_args<'a>( cmd, sess, link_output_kind, - crt_objects_fallback, + self_contained, flavor, crate_type, codegen_results, @@ -2049,7 +2051,7 @@ fn linker_with_args<'a>( // ------------ Object code and libraries, order-dependent ------------ // Post-link CRT objects. - add_post_link_objects(cmd, sess, link_output_kind, crt_objects_fallback); + add_post_link_objects(cmd, sess, link_output_kind, self_contained); // ------------ Late order-dependent options ------------ @@ -2066,7 +2068,7 @@ fn add_order_independent_options( cmd: &mut dyn Linker, sess: &Session, link_output_kind: LinkOutputKind, - crt_objects_fallback: bool, + self_contained: bool, flavor: LinkerFlavor, crate_type: CrateType, codegen_results: &CodegenResults, @@ -2098,7 +2100,7 @@ fn add_order_independent_options( // Make the binary compatible with data execution prevention schemes. cmd.add_no_exec(); - if crt_objects_fallback { + if self_contained { cmd.no_crt_objects(); } @@ -2127,7 +2129,7 @@ fn add_order_independent_options( cmd.linker_plugin_lto(); - add_library_search_dirs(cmd, sess, crt_objects_fallback); + add_library_search_dirs(cmd, sess, self_contained); cmd.output_filename(out_filename); diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index c6dd5e7fda3..83fab0829a1 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1162,6 +1162,16 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ) .emit(); + } else if self.eat_keyword(kw::Let) { + let span = self.prev_token.span; + self.struct_span_err(const_span.to(span), "`const` and `let` are mutually exclusive") + .span_suggestion( + const_span.to(span), + "remove `let`", + "const", + Applicability::MaybeIncorrect, + ) + .emit(); } } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 51bd9d2d386..d8b39a406cc 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -55,6 +55,19 @@ impl<'a> Parser<'a> { return Ok(Some(stmt.into_inner())); } + if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) { + self.bump(); + let mut_let_span = lo.to(self.token.span); + self.struct_span_err(mut_let_span, "invalid variable declaration") + .span_suggestion( + mut_let_span, + "switch the order of `mut` and `let`", + "let mut", + Applicability::MaybeIncorrect, + ) + .emit(); + } + Ok(Some(if self.token.is_keyword(kw::Let) { self.parse_local_mk(lo, attrs, capture_semi, force_collect)? } else if self.is_kw_followed_by_ident(kw::Mut) { @@ -247,6 +260,22 @@ impl<'a> Parser<'a> { /// Parses a local variable declaration. fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> { let lo = self.prev_token.span; + + if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) { + self.struct_span_err( + lo.to(self.token.span), + "`const` and `let` are mutually exclusive", + ) + .span_suggestion( + lo.to(self.token.span), + "remove `let`", + "const", + Applicability::MaybeIncorrect, + ) + .emit(); + self.bump(); + } + let (pat, colon) = self.parse_pat_before_ty(None, RecoverComma::Yes, "`let` bindings")?; let (err, ty) = if colon { diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index 52ac3622eca..c126390f5a9 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -63,7 +63,7 @@ pub(super) fn all(obj: &'static str) -> CrtObjects { ]) } -pub(super) fn pre_musl_fallback() -> CrtObjects { +pub(super) fn pre_musl_self_contained() -> CrtObjects { new(&[ (LinkOutputKind::DynamicNoPicExe, &["crt1.o", "crti.o", "crtbegin.o"]), (LinkOutputKind::DynamicPicExe, &["Scrt1.o", "crti.o", "crtbeginS.o"]), @@ -74,7 +74,7 @@ pub(super) fn pre_musl_fallback() -> CrtObjects { ]) } -pub(super) fn post_musl_fallback() -> CrtObjects { +pub(super) fn post_musl_self_contained() -> CrtObjects { new(&[ (LinkOutputKind::DynamicNoPicExe, &["crtend.o", "crtn.o"]), (LinkOutputKind::DynamicPicExe, &["crtendS.o", "crtn.o"]), @@ -85,7 +85,7 @@ pub(super) fn post_musl_fallback() -> CrtObjects { ]) } -pub(super) fn pre_mingw_fallback() -> CrtObjects { +pub(super) fn pre_mingw_self_contained() -> CrtObjects { new(&[ (LinkOutputKind::DynamicNoPicExe, &["crt2.o", "rsbegin.o"]), (LinkOutputKind::DynamicPicExe, &["crt2.o", "rsbegin.o"]), @@ -96,7 +96,7 @@ pub(super) fn pre_mingw_fallback() -> CrtObjects { ]) } -pub(super) fn post_mingw_fallback() -> CrtObjects { +pub(super) fn post_mingw_self_contained() -> CrtObjects { all("rsend.o") } @@ -108,7 +108,7 @@ pub(super) fn post_mingw() -> CrtObjects { all("rsend.o") } -pub(super) fn pre_wasi_fallback() -> CrtObjects { +pub(super) fn pre_wasi_self_contained() -> CrtObjects { // Use crt1-command.o instead of crt1.o to enable support for new-style // commands. See https://reviews.llvm.org/D81689 for more info. new(&[ @@ -120,37 +120,41 @@ pub(super) fn pre_wasi_fallback() -> CrtObjects { ]) } -pub(super) fn post_wasi_fallback() -> CrtObjects { +pub(super) fn post_wasi_self_contained() -> CrtObjects { new(&[]) } -/// Which logic to use to determine whether to fall back to the "self-contained" mode or not. +/// Which logic to use to determine whether to use self-contained linking mode +/// if `-Clink-self-contained` is not specified explicitly. #[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum CrtObjectsFallback { +pub enum LinkSelfContainedDefault { + False, + True, Musl, Mingw, - Wasm, } -impl FromStr for CrtObjectsFallback { +impl FromStr for LinkSelfContainedDefault { type Err = (); - fn from_str(s: &str) -> Result<CrtObjectsFallback, ()> { + fn from_str(s: &str) -> Result<LinkSelfContainedDefault, ()> { Ok(match s { - "musl" => CrtObjectsFallback::Musl, - "mingw" => CrtObjectsFallback::Mingw, - "wasm" => CrtObjectsFallback::Wasm, + "false" => LinkSelfContainedDefault::False, + "true" | "wasm" => LinkSelfContainedDefault::True, + "musl" => LinkSelfContainedDefault::Musl, + "mingw" => LinkSelfContainedDefault::Mingw, _ => return Err(()), }) } } -impl ToJson for CrtObjectsFallback { +impl ToJson for LinkSelfContainedDefault { fn to_json(&self) -> Json { match *self { - CrtObjectsFallback::Musl => "musl", - CrtObjectsFallback::Mingw => "mingw", - CrtObjectsFallback::Wasm => "wasm", + LinkSelfContainedDefault::False => "false", + LinkSelfContainedDefault::True => "true", + LinkSelfContainedDefault::Musl => "musl", + LinkSelfContainedDefault::Mingw => "mingw", } .to_json() } diff --git a/compiler/rustc_target/src/spec/linux_musl_base.rs b/compiler/rustc_target/src/spec/linux_musl_base.rs index 207a87ab039..61553e71b45 100644 --- a/compiler/rustc_target/src/spec/linux_musl_base.rs +++ b/compiler/rustc_target/src/spec/linux_musl_base.rs @@ -1,13 +1,13 @@ -use crate::spec::crt_objects::{self, CrtObjectsFallback}; +use crate::spec::crt_objects::{self, LinkSelfContainedDefault}; use crate::spec::TargetOptions; pub fn opts() -> TargetOptions { let mut base = super::linux_base::opts(); base.env = "musl".into(); - base.pre_link_objects_fallback = crt_objects::pre_musl_fallback(); - base.post_link_objects_fallback = crt_objects::post_musl_fallback(); - base.crt_objects_fallback = Some(CrtObjectsFallback::Musl); + base.pre_link_objects_self_contained = crt_objects::pre_musl_self_contained(); + base.post_link_objects_self_contained = crt_objects::post_musl_self_contained(); + base.link_self_contained = LinkSelfContainedDefault::Musl; // These targets statically link libc by default base.crt_static_default = true; diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index f7abeafd38f..0b49edc232c 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -37,7 +37,7 @@ use crate::abi::Endian; use crate::json::{Json, ToJson}; use crate::spec::abi::{lookup as lookup_abi, Abi}; -use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; +use crate::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::symbol::{sym, Symbol}; @@ -1172,13 +1172,10 @@ pub struct TargetOptions { /// Objects to link before and after all other object code. pub pre_link_objects: CrtObjects, pub post_link_objects: CrtObjects, - /// Same as `(pre|post)_link_objects`, but when we fail to pull the objects with help of the - /// target's native gcc and fall back to the "self-contained" mode and pull them manually. - /// See `crt_objects.rs` for some more detailed documentation. - pub pre_link_objects_fallback: CrtObjects, - pub post_link_objects_fallback: CrtObjects, - /// Which logic to use to determine whether to fall back to the "self-contained" mode or not. - pub crt_objects_fallback: Option<CrtObjectsFallback>, + /// Same as `(pre|post)_link_objects`, but when self-contained linking mode is enabled. + pub pre_link_objects_self_contained: CrtObjects, + pub post_link_objects_self_contained: CrtObjects, + pub link_self_contained: LinkSelfContainedDefault, /// Linker arguments that are unconditionally passed after any /// user-defined but before post-link objects. Standard platform @@ -1554,9 +1551,9 @@ impl Default for TargetOptions { relro_level: RelroLevel::None, pre_link_objects: Default::default(), post_link_objects: Default::default(), - pre_link_objects_fallback: Default::default(), - post_link_objects_fallback: Default::default(), - crt_objects_fallback: None, + pre_link_objects_self_contained: Default::default(), + post_link_objects_self_contained: Default::default(), + link_self_contained: LinkSelfContainedDefault::False, late_link_args: LinkArgs::new(), late_link_args_dynamic: LinkArgs::new(), late_link_args_static: LinkArgs::new(), @@ -1977,20 +1974,20 @@ impl Target { Ok::<(), String>(()) } ); - ($key_name:ident, crt_objects_fallback) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - obj.remove(&name).and_then(|o| o.as_str().and_then(|s| { - match s.parse::<CrtObjectsFallback>() { - Ok(fallback) => base.$key_name = Some(fallback), - _ => return Some(Err(format!("'{}' is not a valid CRT objects fallback. \ - Use 'musl', 'mingw' or 'wasm'", s))), + ($key_name:ident = $json_name:expr, link_self_contained) => ( { + let name = $json_name; + obj.remove(name).and_then(|o| o.as_str().and_then(|s| { + match s.parse::<LinkSelfContainedDefault>() { + Ok(lsc_default) => base.$key_name = lsc_default, + _ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \ + Use 'false', 'true', 'musl' or 'mingw'", s))), } Some(Ok(())) })).unwrap_or(Ok(())) } ); - ($key_name:ident, link_objects) => ( { - let name = (stringify!($key_name)).replace("_", "-"); - if let Some(val) = obj.remove(&name) { + ($key_name:ident = $json_name:expr, link_objects) => ( { + let name = $json_name; + if let Some(val) = obj.remove(name) { let obj = val.as_object().ok_or_else(|| format!("{}: expected a \ JSON object with fields per CRT object kind.", name))?; let mut args = CrtObjects::new(); @@ -2112,11 +2109,11 @@ impl Target { key!(linker_flavor, LinkerFlavor)?; key!(linker, optional); key!(lld_flavor, LldFlavor)?; - key!(pre_link_objects, link_objects); - key!(post_link_objects, link_objects); - key!(pre_link_objects_fallback, link_objects); - key!(post_link_objects_fallback, link_objects); - key!(crt_objects_fallback, crt_objects_fallback)?; + key!(pre_link_objects = "pre-link-objects", link_objects); + key!(post_link_objects = "post-link-objects", link_objects); + key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects); + key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects); + key!(link_self_contained = "crt-objects-fallback", link_self_contained)?; key!(pre_link_args, link_args); key!(late_link_args, link_args); key!(late_link_args_dynamic, link_args); @@ -2357,9 +2354,9 @@ impl ToJson for Target { target_option_val!(lld_flavor); target_option_val!(pre_link_objects); target_option_val!(post_link_objects); - target_option_val!(pre_link_objects_fallback); - target_option_val!(post_link_objects_fallback); - target_option_val!(crt_objects_fallback); + target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback"); + target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback"); + target_option_val!(link_self_contained, "crt-objects-fallback"); target_option_val!(link_args - pre_link_args); target_option_val!(link_args - late_link_args); target_option_val!(link_args - late_link_args_dynamic); diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index 1db6db78b17..03e579aee0a 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -110,9 +110,9 @@ impl Target { } assert!( - (self.pre_link_objects_fallback.is_empty() - && self.post_link_objects_fallback.is_empty()) - || self.crt_objects_fallback.is_some() + (self.pre_link_objects_self_contained.is_empty() + && self.post_link_objects_self_contained.is_empty()) + || self.link_self_contained != LinkSelfContainedDefault::False ); // If your target really needs to deviate from the rules below, diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index 280457d68b9..9c30487f4ab 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -82,8 +82,8 @@ pub fn target() -> Target { options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm); options.add_pre_link_args(LinkerFlavor::Gcc, &["--target=wasm32-wasi"]); - options.pre_link_objects_fallback = crt_objects::pre_wasi_fallback(); - options.post_link_objects_fallback = crt_objects::post_wasi_fallback(); + options.pre_link_objects_self_contained = crt_objects::pre_wasi_self_contained(); + options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained(); // Right now this is a bit of a workaround but we're currently saying that // the target by default has a static crt which we're taking as a signal diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs index 9216d3e7b65..28a07701eae 100644 --- a/compiler/rustc_target/src/spec/wasm_base.rs +++ b/compiler/rustc_target/src/spec/wasm_base.rs @@ -1,4 +1,4 @@ -use super::crt_objects::CrtObjectsFallback; +use super::crt_objects::LinkSelfContainedDefault; use super::{cvs, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel}; pub fn options() -> TargetOptions { @@ -96,7 +96,8 @@ pub fn options() -> TargetOptions { pre_link_args, - crt_objects_fallback: Some(CrtObjectsFallback::Wasm), + // FIXME: Figure out cases in which WASM needs to link with a native toolchain. + link_self_contained: LinkSelfContainedDefault::True, // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when // PIC code is implemented this has quite a drastic effect if it stays diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index 90e0af3e38a..0107f7a52c6 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -1,4 +1,4 @@ -use crate::spec::crt_objects::{self, CrtObjectsFallback}; +use crate::spec::crt_objects::{self, LinkSelfContainedDefault}; use crate::spec::{cvs, LinkerFlavor, TargetOptions}; pub fn opts() -> TargetOptions { @@ -76,9 +76,9 @@ pub fn opts() -> TargetOptions { pre_link_args, pre_link_objects: crt_objects::pre_mingw(), post_link_objects: crt_objects::post_mingw(), - pre_link_objects_fallback: crt_objects::pre_mingw_fallback(), - post_link_objects_fallback: crt_objects::post_mingw_fallback(), - crt_objects_fallback: Some(CrtObjectsFallback::Mingw), + pre_link_objects_self_contained: crt_objects::pre_mingw_self_contained(), + post_link_objects_self_contained: crt_objects::post_mingw_self_contained(), + link_self_contained: LinkSelfContainedDefault::Mingw, late_link_args, late_link_args_dynamic, late_link_args_static, diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 8ab1aa65d3a..a54b36262b2 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -734,7 +734,21 @@ impl<'tcx> TypeVisitor<'tcx> for OrphanChecker<'tcx> { result } - // FIXME: Constants should participate in orphan checking. + /// All possible values for a constant parameter already exist + /// in the crate defining the trait, so they are always non-local[^1]. + /// + /// Because there's no way to have an impl where the first local + /// generic argument is a constant, we also don't have to fail + /// the orphan check when encountering a parameter or a generic constant. + /// + /// This means that we can completely ignore constants during the orphan check. + /// + /// See `src/test/ui/coherence/const-generics-orphan-check-ok.rs` for examples. + /// + /// [^1]: This might not hold for function pointers or trait objects in the future. + /// As these should be quite rare as const arguments and especially rare as impl + /// parameters, allowing uncovered const parameters in impls seems more useful + /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile. fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { ControlFlow::CONTINUE } diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs index 3f2a0da8d65..05686be5d4b 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_typeck/src/check/intrinsic.rs @@ -69,6 +69,9 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { // to note that it's safe to call, since // safe extern fns are otherwise unprecedented. sym::abort + | sym::assert_inhabited + | sym::assert_zero_valid + | sym::assert_uninit_valid | sym::size_of | sym::min_align_of | sym::needs_drop diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs index f549807c39c..ebf1f506183 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_typeck/src/check/writeback.rs @@ -292,6 +292,17 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { intravisit::walk_expr(self, e); } + fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { + match &p.kind { + hir::GenericParamKind::Lifetime { .. } => { + // Nothing to write back here + } + hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => { + self.tcx().sess.delay_span_bug(p.span, format!("unexpected generic param: {p:?}")); + } + } + } + fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) { self.visit_node_id(b.span, b.hir_id); intravisit::walk_block(self, b); diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs new file mode 100644 index 00000000000..9b479a9f8ad --- /dev/null +++ b/library/core/src/iter/adapters/array_chunks.rs @@ -0,0 +1,182 @@ +use crate::array; +use crate::iter::{ByRefSized, FusedIterator, Iterator}; +use crate::ops::{ControlFlow, NeverShortCircuit, Try}; + +/// An iterator over `N` elements of the iterator at a time. +/// +/// The chunks do not overlap. If `N` does not divide the length of the +/// iterator, then the last up to `N-1` elements will be omitted. +/// +/// This `struct` is created by the [`array_chunks`][Iterator::array_chunks] +/// method on [`Iterator`]. See its documentation for more. +#[derive(Debug, Clone)] +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +pub struct ArrayChunks<I: Iterator, const N: usize> { + iter: I, + remainder: Option<array::IntoIter<I::Item, N>>, +} + +impl<I, const N: usize> ArrayChunks<I, N> +where + I: Iterator, +{ + #[track_caller] + pub(in crate::iter) fn new(iter: I) -> Self { + assert!(N != 0, "chunk size must be non-zero"); + Self { iter, remainder: None } + } + + /// Returns an iterator over the remaining elements of the original iterator + /// that are not going to be returned by this iterator. The returned + /// iterator will yield at most `N-1` elements. + #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] + #[inline] + pub fn into_remainder(self) -> Option<array::IntoIter<I::Item, N>> { + self.remainder + } +} + +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +impl<I, const N: usize> Iterator for ArrayChunks<I, N> +where + I: Iterator, +{ + type Item = [I::Item; N]; + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + self.try_for_each(ControlFlow::Break).break_value() + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + let (lower, upper) = self.iter.size_hint(); + + (lower / N, upper.map(|n| n / N)) + } + + #[inline] + fn count(self) -> usize { + self.iter.count() / N + } + + fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try<Output = B>, + { + let mut acc = init; + loop { + match self.iter.next_chunk() { + Ok(chunk) => acc = f(acc, chunk)?, + Err(remainder) => { + // Make sure to not override `self.remainder` with an empty array + // when `next` is called after `ArrayChunks` exhaustion. + self.remainder.get_or_insert(remainder); + + break try { acc }; + } + } + } + } + + fn fold<B, F>(mut self, init: B, f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + self.try_fold(init, NeverShortCircuit::wrap_mut_2(f)).0 + } +} + +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +impl<I, const N: usize> DoubleEndedIterator for ArrayChunks<I, N> +where + I: DoubleEndedIterator + ExactSizeIterator, +{ + #[inline] + fn next_back(&mut self) -> Option<Self::Item> { + self.try_rfold((), |(), x| ControlFlow::Break(x)).break_value() + } + + fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R + where + Self: Sized, + F: FnMut(B, Self::Item) -> R, + R: Try<Output = B>, + { + // We are iterating from the back we need to first handle the remainder. + self.next_back_remainder(); + + let mut acc = init; + let mut iter = ByRefSized(&mut self.iter).rev(); + + // NB remainder is handled by `next_back_remainder`, so + // `next_chunk` can't return `Err` with non-empty remainder + // (assuming correct `I as ExactSizeIterator` impl). + while let Ok(mut chunk) = iter.next_chunk() { + // FIXME: do not do double reverse + // (we could instead add `next_chunk_back` for example) + chunk.reverse(); + acc = f(acc, chunk)? + } + + try { acc } + } + + fn rfold<B, F>(mut self, init: B, f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + self.try_rfold(init, NeverShortCircuit::wrap_mut_2(f)).0 + } +} + +impl<I, const N: usize> ArrayChunks<I, N> +where + I: DoubleEndedIterator + ExactSizeIterator, +{ + /// Updates `self.remainder` such that `self.iter.len` is divisible by `N`. + fn next_back_remainder(&mut self) { + // Make sure to not override `self.remainder` with an empty array + // when `next_back` is called after `ArrayChunks` exhaustion. + if self.remainder.is_some() { + return; + } + + // We use the `ExactSizeIterator` implementation of the underlying + // iterator to know how many remaining elements there are. + let rem = self.iter.len() % N; + + // Take the last `rem` elements out of `self.iter`. + let mut remainder = + // SAFETY: `unwrap_err` always succeeds because x % N < N for all x. + unsafe { self.iter.by_ref().rev().take(rem).next_chunk().unwrap_err_unchecked() }; + + // We used `.rev()` above, so we need to re-reverse the reminder + remainder.as_mut_slice().reverse(); + self.remainder = Some(remainder); + } +} + +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +impl<I, const N: usize> FusedIterator for ArrayChunks<I, N> where I: FusedIterator {} + +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +impl<I, const N: usize> ExactSizeIterator for ArrayChunks<I, N> +where + I: ExactSizeIterator, +{ + #[inline] + fn len(&self) -> usize { + self.iter.len() / N + } + + #[inline] + fn is_empty(&self) -> bool { + self.iter.len() < N + } +} diff --git a/library/core/src/iter/adapters/mod.rs b/library/core/src/iter/adapters/mod.rs index 916a26e2424..bf4fabad32a 100644 --- a/library/core/src/iter/adapters/mod.rs +++ b/library/core/src/iter/adapters/mod.rs @@ -1,6 +1,7 @@ use crate::iter::{InPlaceIterable, Iterator}; use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, NeverShortCircuit, Residual, Try}; +mod array_chunks; mod by_ref_sized; mod chain; mod cloned; @@ -32,6 +33,9 @@ pub use self::{ scan::Scan, skip::Skip, skip_while::SkipWhile, take::Take, take_while::TakeWhile, zip::Zip, }; +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +pub use self::array_chunks::ArrayChunks; + #[unstable(feature = "std_internals", issue = "none")] pub use self::by_ref_sized::ByRefSized; diff --git a/library/core/src/iter/mod.rs b/library/core/src/iter/mod.rs index d5c6aed5b6c..9514466bd0c 100644 --- a/library/core/src/iter/mod.rs +++ b/library/core/src/iter/mod.rs @@ -398,6 +398,8 @@ pub use self::traits::{ #[stable(feature = "iter_zip", since = "1.59.0")] pub use self::adapters::zip; +#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] +pub use self::adapters::ArrayChunks; #[unstable(feature = "std_internals", issue = "none")] pub use self::adapters::ByRefSized; #[stable(feature = "iter_cloned", since = "1.1.0")] diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 275412b57b5..b2d08f4b0f6 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -5,7 +5,7 @@ use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try}; use super::super::try_process; use super::super::ByRefSized; use super::super::TrustedRandomAccessNoCoerce; -use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; +use super::super::{ArrayChunks, Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse}; use super::super::{FlatMap, Flatten}; use super::super::{FromIterator, Intersperse, IntersperseWith, Product, Sum, Zip}; use super::super::{ @@ -3316,6 +3316,49 @@ pub trait Iterator { Cycle::new(self) } + /// Returns an iterator over `N` elements of the iterator at a time. + /// + /// The chunks do not overlap. If `N` does not divide the length of the + /// iterator, then the last up to `N-1` elements will be omitted and can be + /// retrieved from the [`.into_remainder()`][ArrayChunks::into_remainder] + /// function of the iterator. + /// + /// # Panics + /// + /// Panics if `N` is 0. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_array_chunks)] + /// + /// let mut iter = "lorem".chars().array_chunks(); + /// assert_eq!(iter.next(), Some(['l', 'o'])); + /// assert_eq!(iter.next(), Some(['r', 'e'])); + /// assert_eq!(iter.next(), None); + /// assert_eq!(iter.into_remainder().unwrap().as_slice(), &['m']); + /// ``` + /// + /// ``` + /// #![feature(iter_array_chunks)] + /// + /// let data = [1, 1, 2, -2, 6, 0, 3, 1]; + /// // ^-----^ ^------^ + /// for [x, y, z] in data.iter().array_chunks() { + /// assert_eq!(x + y + z, 4); + /// } + /// ``` + #[track_caller] + #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")] + fn array_chunks<const N: usize>(self) -> ArrayChunks<Self, N> + where + Self: Sized, + { + ArrayChunks::new(self) + } + /// Sums the elements of an iterator. /// /// Takes each element, adds them together, and returns the result. diff --git a/library/core/tests/iter/adapters/array_chunks.rs b/library/core/tests/iter/adapters/array_chunks.rs new file mode 100644 index 00000000000..4e9d89e1e58 --- /dev/null +++ b/library/core/tests/iter/adapters/array_chunks.rs @@ -0,0 +1,179 @@ +use core::cell::Cell; +use core::iter::{self, Iterator}; + +use super::*; + +#[test] +fn test_iterator_array_chunks_infer() { + let xs = [1, 1, 2, -2, 6, 0, 3, 1]; + for [a, b, c] in xs.iter().copied().array_chunks() { + assert_eq!(a + b + c, 4); + } +} + +#[test] +fn test_iterator_array_chunks_clone_and_drop() { + let count = Cell::new(0); + let mut it = (0..5).map(|_| CountDrop::new(&count)).array_chunks::<3>(); + assert_eq!(it.by_ref().count(), 1); + assert_eq!(count.get(), 3); + let mut it2 = it.clone(); + assert_eq!(count.get(), 3); + assert_eq!(it.into_remainder().unwrap().len(), 2); + assert_eq!(count.get(), 5); + assert!(it2.next().is_none()); + assert_eq!(it2.into_remainder().unwrap().len(), 2); + assert_eq!(count.get(), 7); +} + +#[test] +fn test_iterator_array_chunks_remainder() { + let mut it = (0..11).array_chunks::<4>(); + assert_eq!(it.next(), Some([0, 1, 2, 3])); + assert_eq!(it.next(), Some([4, 5, 6, 7])); + assert_eq!(it.next(), None); + assert_eq!(it.into_remainder().unwrap().as_slice(), &[8, 9, 10]); +} + +#[test] +fn test_iterator_array_chunks_size_hint() { + let it = (0..6).array_chunks::<1>(); + assert_eq!(it.size_hint(), (6, Some(6))); + + let it = (0..6).array_chunks::<3>(); + assert_eq!(it.size_hint(), (2, Some(2))); + + let it = (0..6).array_chunks::<5>(); + assert_eq!(it.size_hint(), (1, Some(1))); + + let it = (0..6).array_chunks::<7>(); + assert_eq!(it.size_hint(), (0, Some(0))); + + let it = (1..).array_chunks::<2>(); + assert_eq!(it.size_hint(), (usize::MAX / 2, None)); + + let it = (1..).filter(|x| x % 2 != 0).array_chunks::<2>(); + assert_eq!(it.size_hint(), (0, None)); +} + +#[test] +fn test_iterator_array_chunks_count() { + let it = (0..6).array_chunks::<1>(); + assert_eq!(it.count(), 6); + + let it = (0..6).array_chunks::<3>(); + assert_eq!(it.count(), 2); + + let it = (0..6).array_chunks::<5>(); + assert_eq!(it.count(), 1); + + let it = (0..6).array_chunks::<7>(); + assert_eq!(it.count(), 0); + + let it = (0..6).filter(|x| x % 2 == 0).array_chunks::<2>(); + assert_eq!(it.count(), 1); + + let it = iter::empty::<i32>().array_chunks::<2>(); + assert_eq!(it.count(), 0); + + let it = [(); usize::MAX].iter().array_chunks::<2>(); + assert_eq!(it.count(), usize::MAX / 2); +} + +#[test] +fn test_iterator_array_chunks_next_and_next_back() { + let mut it = (0..11).array_chunks::<3>(); + assert_eq!(it.next(), Some([0, 1, 2])); + assert_eq!(it.next_back(), Some([6, 7, 8])); + assert_eq!(it.next(), Some([3, 4, 5])); + assert_eq!(it.next_back(), None); + assert_eq!(it.next(), None); + assert_eq!(it.next_back(), None); + assert_eq!(it.next(), None); + assert_eq!(it.into_remainder().unwrap().as_slice(), &[9, 10]); +} + +#[test] +fn test_iterator_array_chunks_rev_remainder() { + let mut it = (0..11).array_chunks::<4>(); + { + let mut it = it.by_ref().rev(); + assert_eq!(it.next(), Some([4, 5, 6, 7])); + assert_eq!(it.next(), Some([0, 1, 2, 3])); + assert_eq!(it.next(), None); + assert_eq!(it.next(), None); + } + assert_eq!(it.into_remainder().unwrap().as_slice(), &[8, 9, 10]); +} + +#[test] +fn test_iterator_array_chunks_try_fold() { + let count = Cell::new(0); + let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>(); + let result: Result<_, ()> = it.by_ref().try_fold(0, |acc, _item| Ok(acc + 1)); + assert_eq!(result, Ok(3)); + assert_eq!(count.get(), 9); + drop(it); + assert_eq!(count.get(), 10); + + let count = Cell::new(0); + let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>(); + let result = it.by_ref().try_fold(0, |acc, _item| if acc < 2 { Ok(acc + 1) } else { Err(acc) }); + assert_eq!(result, Err(2)); + assert_eq!(count.get(), 9); + drop(it); + assert_eq!(count.get(), 9); +} + +#[test] +fn test_iterator_array_chunks_fold() { + let result = (1..11).array_chunks::<3>().fold(0, |acc, [a, b, c]| { + assert_eq!(acc + 1, a); + assert_eq!(acc + 2, b); + assert_eq!(acc + 3, c); + acc + 3 + }); + assert_eq!(result, 9); + + let count = Cell::new(0); + let result = + (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>().fold(0, |acc, _item| acc + 1); + assert_eq!(result, 3); + assert_eq!(count.get(), 10); +} + +#[test] +fn test_iterator_array_chunks_try_rfold() { + let count = Cell::new(0); + let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>(); + let result: Result<_, ()> = it.try_rfold(0, |acc, _item| Ok(acc + 1)); + assert_eq!(result, Ok(3)); + assert_eq!(count.get(), 9); + drop(it); + assert_eq!(count.get(), 10); + + let count = Cell::new(0); + let mut it = (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>(); + let result = it.try_rfold(0, |acc, _item| if acc < 2 { Ok(acc + 1) } else { Err(acc) }); + assert_eq!(result, Err(2)); + assert_eq!(count.get(), 9); + drop(it); + assert_eq!(count.get(), 10); +} + +#[test] +fn test_iterator_array_chunks_rfold() { + let result = (1..11).array_chunks::<3>().rfold(0, |acc, [a, b, c]| { + assert_eq!(10 - (acc + 1), c); + assert_eq!(10 - (acc + 2), b); + assert_eq!(10 - (acc + 3), a); + acc + 3 + }); + assert_eq!(result, 9); + + let count = Cell::new(0); + let result = + (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>().rfold(0, |acc, _item| acc + 1); + assert_eq!(result, 3); + assert_eq!(count.get(), 10); +} diff --git a/library/core/tests/iter/adapters/mod.rs b/library/core/tests/iter/adapters/mod.rs index 567d9fe49ca..96539c0c394 100644 --- a/library/core/tests/iter/adapters/mod.rs +++ b/library/core/tests/iter/adapters/mod.rs @@ -1,3 +1,4 @@ +mod array_chunks; mod chain; mod cloned; mod copied; @@ -183,3 +184,25 @@ impl Clone for CountClone { ret } } + +#[derive(Debug, Clone)] +struct CountDrop<'a> { + dropped: bool, + count: &'a Cell<usize>, +} + +impl<'a> CountDrop<'a> { + pub fn new(count: &'a Cell<usize>) -> Self { + Self { dropped: false, count } + } +} + +impl Drop for CountDrop<'_> { + fn drop(&mut self) { + if self.dropped { + panic!("double drop"); + } + self.dropped = true; + self.count.set(self.count.get() + 1); + } +} diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index df9b1073a09..09f1500f564 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -62,6 +62,7 @@ #![feature(slice_partition_dedup)] #![feature(int_log)] #![feature(iter_advance_by)] +#![feature(iter_array_chunks)] #![feature(iter_collect_into)] #![feature(iter_partition_in_place)] #![feature(iter_intersperse)] diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 1f2f9d97bdd..7778033eaa9 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -544,11 +544,11 @@ impl Default for FileTimes { fn default() -> Self { // Redox doesn't appear to support `UTIME_OMIT`, so we stub it out here, and always return // an error in `set_times`. - // ESP-IDF does not support `futimens` at all and the behavior for that OS is therefore + // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore // the same as for Redox. - #[cfg(any(target_os = "redox", target_os = "espidf"))] + #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))] let omit = libc::timespec { tv_sec: 0, tv_nsec: 0 }; - #[cfg(not(any(target_os = "redox", target_os = "espidf")))] + #[cfg(not(any(target_os = "redox", target_os = "espidf", target_os = "horizon")))] let omit = libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }; Self([omit; 2]) } @@ -1083,9 +1083,9 @@ impl File { pub fn set_times(&self, times: FileTimes) -> io::Result<()> { cfg_if::cfg_if! { - if #[cfg(any(target_os = "redox", target_os = "espidf"))] { + if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon"))] { // Redox doesn't appear to support `UTIME_OMIT`. - // ESP-IDF does not support `futimens` at all and the behavior for that OS is therefore + // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore // the same as for Redox. drop(times); Err(io::const_io_error!( diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 39d9ce1621b..1edb513f0b6 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -80,6 +80,7 @@ pub struct Flags { pub llvm_profile_generate: bool, } +#[derive(Debug)] #[cfg_attr(test, derive(Clone))] pub enum Subcommand { Build { diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index a5a39a5a3cf..eb7da1bda73 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -11,7 +11,7 @@ use std::{ io::{self, Write}, }; -#[derive(Clone, Copy, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Profile { Compiler, Codegen, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index addd6ffa11e..971617a8400 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -44,10 +44,6 @@ use utils::*; pub(crate) use self::types::*; pub(crate) use self::utils::{get_auto_trait_and_blanket_impls, krate, register_res}; -pub(crate) trait Clean<'tcx, T> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> T; -} - pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<'tcx>) -> Item { let mut items: Vec<Item> = vec![]; let mut inserted = FxHashSet::default(); @@ -1925,7 +1921,7 @@ fn clean_maybe_renamed_item<'tcx>( })) } ItemKind::Enum(ref def, generics) => EnumItem(Enum { - variants: def.variants.iter().map(|v| v.clean(cx)).collect(), + variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(), generics: clean_generics(generics, cx), }), ItemKind::TraitAlias(generics, bounds) => TraitAliasItem(TraitAlias { @@ -1978,14 +1974,12 @@ fn clean_maybe_renamed_item<'tcx>( }) } -impl<'tcx> Clean<'tcx, Item> for hir::Variant<'tcx> { - fn clean(&self, cx: &mut DocContext<'tcx>) -> Item { - let kind = VariantItem(clean_variant_data(&self.data, cx)); - let what_rustc_thinks = - Item::from_hir_id_and_parts(self.id, Some(self.ident.name), kind, cx); - // don't show `pub` for variants, which are always public - Item { visibility: Inherited, ..what_rustc_thinks } - } +fn clean_variant<'tcx>(variant: &hir::Variant<'tcx>, cx: &mut DocContext<'tcx>) -> Item { + let kind = VariantItem(clean_variant_data(&variant.data, cx)); + let what_rustc_thinks = + Item::from_hir_id_and_parts(variant.id, Some(variant.ident.name), kind, cx); + // don't show `pub` for variants, which are always public + Item { visibility: Inherited, ..what_rustc_thinks } } fn clean_impl<'tcx>( diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 27ccff9a276..9d8ee52a3fa 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -111,6 +111,70 @@ fn write_header(out: &mut Buffer, class: &str, extra_content: Option<Buffer>) { write!(out, "<code>"); } +/// Write all the pending elements sharing a same (or at mergeable) `Class`. +/// +/// If there is a "parent" (if a `EnterSpan` event was encountered) and the parent can be merged +/// with the elements' class, then we simply write the elements since the `ExitSpan` event will +/// close the tag. +/// +/// Otherwise, if there is only one pending element, we let the `string` function handle both +/// opening and closing the tag, otherwise we do it into this function. +fn write_pending_elems( + out: &mut Buffer, + href_context: &Option<HrefContext<'_, '_, '_>>, + pending_elems: &mut Vec<(&str, Option<Class>)>, + current_class: &mut Option<Class>, + closing_tags: &[(&str, Class)], +) { + if pending_elems.is_empty() { + return; + } + let mut done = false; + if let Some((_, parent_class)) = closing_tags.last() { + if can_merge(*current_class, Some(*parent_class), "") { + for (text, class) in pending_elems.iter() { + string(out, Escape(text), *class, &href_context, false); + } + done = true; + } + } + if !done { + // We only want to "open" the tag ourselves if we have more than one pending and if the current + // parent tag is not the same as our pending content. + let open_tag_ourselves = pending_elems.len() > 1; + let close_tag = if open_tag_ourselves { + enter_span(out, current_class.unwrap(), &href_context) + } else { + "" + }; + for (text, class) in pending_elems.iter() { + string(out, Escape(text), *class, &href_context, !open_tag_ourselves); + } + if open_tag_ourselves { + exit_span(out, close_tag); + } + } + pending_elems.clear(); + *current_class = None; +} + +/// Check if two `Class` can be merged together. In the following rules, "unclassified" means `None` +/// basically (since it's `Option<Class>`). The following rules apply: +/// +/// * If two `Class` have the same variant, then they can be merged. +/// * If the other `Class` is unclassified and only contains white characters (backline, +/// whitespace, etc), it can be merged. +/// * `Class::Ident` is considered the same as unclassified (because it doesn't have an associated +/// CSS class). +fn can_merge(class1: Option<Class>, class2: Option<Class>, text: &str) -> bool { + match (class1, class2) { + (Some(c1), Some(c2)) => c1.is_equal_to(c2), + (Some(Class::Ident(_)), None) | (None, Some(Class::Ident(_))) => true, + (Some(_), None) | (None, Some(_)) => text.trim().is_empty(), + _ => false, + } +} + /// Convert the given `src` source code into HTML by adding classes for highlighting. /// /// This code is used to render code blocks (in the documentation) as well as the source code pages. @@ -130,7 +194,15 @@ fn write_code( ) { // This replace allows to fix how the code source with DOS backline characters is displayed. let src = src.replace("\r\n", "\n"); - let mut closing_tags: Vec<&'static str> = Vec::new(); + // It contains the closing tag and the associated `Class`. + let mut closing_tags: Vec<(&'static str, Class)> = Vec::new(); + // The following two variables are used to group HTML elements with same `class` attributes + // to reduce the DOM size. + let mut current_class: Option<Class> = None; + // We need to keep the `Class` for each element because it could contain a `Span` which is + // used to generate links. + let mut pending_elems: Vec<(&str, Option<Class>)> = Vec::new(); + Classifier::new( &src, href_context.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP), @@ -138,15 +210,48 @@ fn write_code( ) .highlight(&mut |highlight| { match highlight { - Highlight::Token { text, class } => string(out, Escape(text), class, &href_context), + Highlight::Token { text, class } => { + // If the two `Class` are different, time to flush the current content and start + // a new one. + if !can_merge(current_class, class, text) { + write_pending_elems( + out, + &href_context, + &mut pending_elems, + &mut current_class, + &closing_tags, + ); + current_class = class.map(Class::dummy); + } else if current_class.is_none() { + current_class = class.map(Class::dummy); + } + pending_elems.push((text, class)); + } Highlight::EnterSpan { class } => { - closing_tags.push(enter_span(out, class, &href_context)) + // We flush everything just in case... + write_pending_elems( + out, + &href_context, + &mut pending_elems, + &mut current_class, + &closing_tags, + ); + closing_tags.push((enter_span(out, class, &href_context), class)) } Highlight::ExitSpan => { - exit_span(out, closing_tags.pop().expect("ExitSpan without EnterSpan")) + // We flush everything just in case... + write_pending_elems( + out, + &href_context, + &mut pending_elems, + &mut current_class, + &closing_tags, + ); + exit_span(out, closing_tags.pop().expect("ExitSpan without EnterSpan").0) } }; }); + write_pending_elems(out, &href_context, &mut pending_elems, &mut current_class, &closing_tags); } fn write_footer(out: &mut Buffer, playground_button: Option<&str>) { @@ -160,7 +265,7 @@ enum Class { DocComment, Attribute, KeyWord, - // Keywords that do pointer/reference stuff. + /// Keywords that do pointer/reference stuff. RefKeyWord, Self_(Span), Macro(Span), @@ -168,6 +273,7 @@ enum Class { String, Number, Bool, + /// `Ident` isn't rendered in the HTML but we still need it for the `Span` it contains. Ident(Span), Lifetime, PreludeTy, @@ -177,6 +283,31 @@ enum Class { } impl Class { + /// It is only looking at the variant, not the variant content. + /// + /// It is used mostly to group multiple similar HTML elements into one `<span>` instead of + /// multiple ones. + fn is_equal_to(self, other: Self) -> bool { + match (self, other) { + (Self::Self_(_), Self::Self_(_)) + | (Self::Macro(_), Self::Macro(_)) + | (Self::Ident(_), Self::Ident(_)) + | (Self::Decoration(_), Self::Decoration(_)) => true, + (x, y) => x == y, + } + } + + /// If `self` contains a `Span`, it'll be replaced with `DUMMY_SP` to prevent creating links + /// on "empty content" (because of the attributes merge). + fn dummy(self) -> Self { + match self { + Self::Self_(_) => Self::Self_(DUMMY_SP), + Self::Macro(_) => Self::Macro(DUMMY_SP), + Self::Ident(_) => Self::Ident(DUMMY_SP), + s => s, + } + } + /// Returns the css class expected by rustdoc for each `Class`. fn as_html(self) -> &'static str { match self { @@ -191,7 +322,7 @@ impl Class { Class::String => "string", Class::Number => "number", Class::Bool => "bool-val", - Class::Ident(_) => "ident", + Class::Ident(_) => "", Class::Lifetime => "lifetime", Class::PreludeTy => "prelude-ty", Class::PreludeVal => "prelude-val", @@ -630,7 +761,7 @@ impl<'a> Classifier<'a> { TokenKind::CloseBracket => { if self.in_attribute { self.in_attribute = false; - sink(Highlight::Token { text: "]", class: None }); + sink(Highlight::Token { text: "]", class: Some(Class::Attribute) }); sink(Highlight::ExitSpan); return; } @@ -701,7 +832,7 @@ fn enter_span( klass: Class, href_context: &Option<HrefContext<'_, '_, '_>>, ) -> &'static str { - string_without_closing_tag(out, "", Some(klass), href_context).expect( + string_without_closing_tag(out, "", Some(klass), href_context, true).expect( "internal error: enter_span was called with Some(klass) but did not return a \ closing HTML tag", ) @@ -733,8 +864,10 @@ fn string<T: Display>( text: T, klass: Option<Class>, href_context: &Option<HrefContext<'_, '_, '_>>, + open_tag: bool, ) { - if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context) { + if let Some(closing_tag) = string_without_closing_tag(out, text, klass, href_context, open_tag) + { out.write_str(closing_tag); } } @@ -753,6 +886,7 @@ fn string_without_closing_tag<T: Display>( text: T, klass: Option<Class>, href_context: &Option<HrefContext<'_, '_, '_>>, + open_tag: bool, ) -> Option<&'static str> { let Some(klass) = klass else { @@ -761,6 +895,10 @@ fn string_without_closing_tag<T: Display>( }; let Some(def_span) = klass.get_span() else { + if !open_tag { + write!(out, "{}", text); + return None; + } write!(out, "<span class=\"{}\">{}", klass.as_html(), text); return Some("</span>"); }; @@ -784,6 +922,7 @@ fn string_without_closing_tag<T: Display>( path }); } + if let Some(href_context) = href_context { if let Some(href) = href_context.context.shared.span_correspondance_map.get(&def_span).and_then(|href| { @@ -812,12 +951,33 @@ fn string_without_closing_tag<T: Display>( } }) { - write!(out, "<a class=\"{}\" href=\"{}\">{}", klass.as_html(), href, text_s); + if !open_tag { + // We're already inside an element which has the same klass, no need to give it + // again. + write!(out, "<a href=\"{}\">{}", href, text_s); + } else { + let klass_s = klass.as_html(); + if klass_s.is_empty() { + write!(out, "<a href=\"{}\">{}", href, text_s); + } else { + write!(out, "<a class=\"{}\" href=\"{}\">{}", klass_s, href, text_s); + } + } return Some("</a>"); } } - write!(out, "<span class=\"{}\">{}", klass.as_html(), text_s); - Some("</span>") + if !open_tag { + write!(out, "{}", text_s); + return None; + } + let klass_s = klass.as_html(); + if klass_s.is_empty() { + write!(out, "{}", text_s); + Some("") + } else { + write!(out, "<span class=\"{}\">{}", klass_s, text_s); + Some("</span>") + } } #[cfg(test)] diff --git a/src/librustdoc/html/highlight/fixtures/decorations.html b/src/librustdoc/html/highlight/fixtures/decorations.html index ae4dba116d6..21848978721 100644 --- a/src/librustdoc/html/highlight/fixtures/decorations.html +++ b/src/librustdoc/html/highlight/fixtures/decorations.html @@ -1,2 +1,2 @@ -<span class="example"><span class="kw">let</span> <span class="ident">x</span> = <span class="number">1</span>;</span> -<span class="kw">let</span> <span class="ident">y</span> = <span class="number">2</span>; \ No newline at end of file +<span class="example"><span class="kw">let </span>x = <span class="number">1</span>;</span> +<span class="kw">let </span>y = <span class="number">2</span>; \ No newline at end of file diff --git a/src/librustdoc/html/highlight/fixtures/dos_line.html b/src/librustdoc/html/highlight/fixtures/dos_line.html index 1c8dbffe78c..30b50ca7c66 100644 --- a/src/librustdoc/html/highlight/fixtures/dos_line.html +++ b/src/librustdoc/html/highlight/fixtures/dos_line.html @@ -1,3 +1,3 @@ -<span class="kw">pub</span> <span class="kw">fn</span> <span class="ident">foo</span>() { +<span class="kw">pub fn </span>foo() { <span class="macro">println!</span>(<span class="string">"foo"</span>); } diff --git a/src/librustdoc/html/highlight/fixtures/highlight.html b/src/librustdoc/html/highlight/fixtures/highlight.html index 17f23278ec1..9f73e03f95e 100644 --- a/src/librustdoc/html/highlight/fixtures/highlight.html +++ b/src/librustdoc/html/highlight/fixtures/highlight.html @@ -1,4 +1,4 @@ -<span class="kw">use</span> <span class="ident"><span class="kw">crate</span>::a::foo</span>; -<span class="kw">use</span> <span class="ident"><span class="self">self</span>::whatever</span>; -<span class="kw">let</span> <span class="ident">x</span> = <span class="ident"><span class="kw">super</span>::b::foo</span>; -<span class="kw">let</span> <span class="ident">y</span> = <span class="ident"><span class="self">Self</span>::whatever</span>; \ No newline at end of file +<span class="kw">use </span><span class="kw">crate</span>::a::foo; +<span class="kw">use </span><span class="self">self</span>::whatever; +<span class="kw">let </span>x = <span class="kw">super</span>::b::foo; +<span class="kw">let </span>y = <span class="self">Self</span>::whatever; \ No newline at end of file diff --git a/src/librustdoc/html/highlight/fixtures/sample.html b/src/librustdoc/html/highlight/fixtures/sample.html index ea797fd99d3..ae2650528eb 100644 --- a/src/librustdoc/html/highlight/fixtures/sample.html +++ b/src/librustdoc/html/highlight/fixtures/sample.html @@ -8,30 +8,30 @@ .lifetime { color: #B76514; } .question-mark { color: #ff9011; } </style> -<pre><code><span class="attribute">#![<span class="ident">crate_type</span> = <span class="string">"lib"</span>]</span> +<pre><code><span class="attribute">#![crate_type = <span class="string">"lib"</span>]</span> -<span class="kw">use</span> <span class="ident">std::path</span>::{<span class="ident">Path</span>, <span class="ident">PathBuf</span>}; +<span class="kw">use </span>std::path::{Path, PathBuf}; -<span class="attribute">#[<span class="ident">cfg</span>(<span class="ident">target_os</span> = <span class="string">"linux"</span>)]</span> -<span class="kw">fn</span> <span class="ident">main</span>() -> () { - <span class="kw">let</span> <span class="ident">foo</span> = <span class="bool-val">true</span> && <span class="bool-val">false</span> || <span class="bool-val">true</span>; - <span class="kw">let</span> <span class="kw">_</span>: <span class="kw-2">*const</span> () = <span class="number">0</span>; - <span class="kw">let</span> <span class="kw">_</span> = <span class="kw-2">&</span><span class="ident">foo</span>; - <span class="kw">let</span> <span class="kw">_</span> = &&<span class="ident">foo</span>; - <span class="kw">let</span> <span class="kw">_</span> = <span class="kw-2">*</span><span class="ident">foo</span>; - <span class="macro">mac!</span>(<span class="ident">foo</span>, <span class="kw-2">&mut</span> <span class="ident">bar</span>); - <span class="macro">assert!</span>(<span class="self">self</span>.<span class="ident">length</span> < <span class="ident">N</span> && <span class="ident">index</span> <= <span class="self">self</span>.<span class="ident">length</span>); - <span class="ident">::std::env::var</span>(<span class="string">"gateau"</span>).<span class="ident">is_ok</span>(); - <span class="attribute">#[<span class="ident">rustfmt::skip</span>]</span> - <span class="kw">let</span> <span class="ident">s</span>:<span class="ident">std::path::PathBuf</span> = <span class="ident">std::path::PathBuf::new</span>(); - <span class="kw">let</span> <span class="kw-2">mut</span> <span class="ident">s</span> = <span class="ident">String::new</span>(); +<span class="attribute">#[cfg(target_os = <span class="string">"linux"</span>)]</span> +<span class="kw">fn </span>main() -> () { + <span class="kw">let </span>foo = <span class="bool-val">true </span>&& <span class="bool-val">false </span>|| <span class="bool-val">true</span>; + <span class="kw">let _</span>: <span class="kw-2">*const </span>() = <span class="number">0</span>; + <span class="kw">let _ </span>= <span class="kw-2">&</span>foo; + <span class="kw">let _ </span>= &&foo; + <span class="kw">let _ </span>= <span class="kw-2">*</span>foo; + <span class="macro">mac!</span>(foo, <span class="kw-2">&mut </span>bar); + <span class="macro">assert!</span>(<span class="self">self</span>.length < N && index <= <span class="self">self</span>.length); + ::std::env::var(<span class="string">"gateau"</span>).is_ok(); + <span class="attribute">#[rustfmt::skip]</span> + <span class="kw">let </span>s:std::path::PathBuf = std::path::PathBuf::new(); + <span class="kw">let </span><span class="kw-2">mut </span>s = String::new(); - <span class="kw">match</span> <span class="kw-2">&</span><span class="ident">s</span> { - <span class="kw-2">ref</span> <span class="kw-2">mut</span> <span class="ident">x</span> => {} + <span class="kw">match </span><span class="kw-2">&</span>s { + <span class="kw-2">ref mut </span>x => {} } } -<span class="macro">macro_rules!</span> <span class="ident">bar</span> { - (<span class="macro-nonterminal">$</span><span class="macro-nonterminal">foo</span>:<span class="ident">tt</span>) => {}; +<span class="macro">macro_rules!</span> bar { + (<span class="macro-nonterminal">$foo</span>:tt) => {}; } </code></pre> diff --git a/src/librustdoc/html/highlight/fixtures/union.html b/src/librustdoc/html/highlight/fixtures/union.html index ac8bd28f6c3..9f891528264 100644 --- a/src/librustdoc/html/highlight/fixtures/union.html +++ b/src/librustdoc/html/highlight/fixtures/union.html @@ -1,8 +1,8 @@ -<span class="kw">union</span> <span class="ident">Foo</span> { - <span class="ident">i</span>: <span class="ident">i8</span>, - <span class="ident">u</span>: <span class="ident">i8</span>, +<span class="kw">union </span>Foo { + i: i8, + u: i8, } -<span class="kw">fn</span> <span class="ident">main</span>() { - <span class="kw">let</span> <span class="ident">union</span> = <span class="number">0</span>; +<span class="kw">fn </span>main() { + <span class="kw">let </span>union = <span class="number">0</span>; } diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index b8218867a8b..4dfb64abbeb 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -250,9 +250,6 @@ pre.rust .self { pre.rust .attribute { color: #e6e1cf; } -pre.rust .attribute .ident { - color: #e6e1cf; -} .example-wrap > pre.line-number { color: #5c67736e; @@ -398,8 +395,7 @@ pre.rust .comment {} .block a.current.method,.content span.tymethod,.content a.tymethod,.block a.current.tymethod, .content .fnname {} pre.rust .kw {} -pre.rust .self,pre.rust .bool-val,pre.rust .prelude-val,pre.rust .attribute, -pre.rust .attribute .ident {} +pre.rust .self,pre.rust .bool-val,pre.rust .prelude-val,pre.rust .attribute {} .content span.foreigntype,.content a.foreigntype,.block a.current.foreigntype {} pre.rust .doccomment {} .stab.deprecated {} diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index 6188e0ac0a9..39f83c99808 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -202,7 +202,7 @@ pre.rust .kw { color: #ab8ac1; } pre.rust .kw-2, pre.rust .prelude-ty { color: #769acb; } pre.rust .number, pre.rust .string { color: #83a300; } pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, -pre.rust .attribute, pre.rust .attribute .ident { color: #ee6868; } +pre.rust .attribute { color: #ee6868; } pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; } pre.rust .lifetime { color: #d97f26; } pre.rust .question-mark { diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index fba790b6193..5698088c790 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -184,7 +184,7 @@ pre.rust .kw { color: #8959A8; } pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; } pre.rust .number, pre.rust .string { color: #718C00; } pre.rust .self, pre.rust .bool-val, pre.rust .prelude-val, -pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; } +pre.rust .attribute { color: #C82829; } pre.rust .comment { color: #8E908C; } pre.rust .doccomment { color: #4D4D4C; } pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; } diff --git a/src/test/rustdoc/issue-41783.codeblock.html b/src/test/rustdoc/issue-41783.codeblock.html new file mode 100644 index 00000000000..b919935e4b4 --- /dev/null +++ b/src/test/rustdoc/issue-41783.codeblock.html @@ -0,0 +1,5 @@ +<code># single +## double +### triple +<span class="attribute">#[outer]</span> +<span class="attribute">#![inner]</span></code> \ No newline at end of file diff --git a/src/test/rustdoc/issue-41783.rs b/src/test/rustdoc/issue-41783.rs index 58a55a73815..d6771602879 100644 --- a/src/test/rustdoc/issue-41783.rs +++ b/src/test/rustdoc/issue-41783.rs @@ -1,11 +1,9 @@ // @has issue_41783/struct.Foo.html // @!hasraw - 'space' // @!hasraw - 'comment' -// @hasraw - '# <span class="ident">single' -// @hasraw - '## <span class="ident">double</span>' -// @hasraw - '### <span class="ident">triple</span>' -// @hasraw - '<span class="attribute">#[<span class="ident">outer</span>]</span>' -// @hasraw - '<span class="attribute">#![<span class="ident">inner</span>]</span>' +// @hasraw - '<span class="attribute">#[outer]</span>' +// @hasraw - '<span class="attribute">#![inner]</span>' +// @snapshot 'codeblock' - '//*[@class="rustdoc-toggle top-doc"]/*[@class="docblock"]//pre/code' /// ```no_run /// # # space diff --git a/src/test/rustdoc/macro_rules-matchers.rs b/src/test/rustdoc/macro_rules-matchers.rs index 98026663e5a..96f4126c7c2 100644 --- a/src/test/rustdoc/macro_rules-matchers.rs +++ b/src/test/rustdoc/macro_rules-matchers.rs @@ -5,14 +5,12 @@ // @has 'foo/macro.todo.html' // @has - '//span[@class="macro"]' 'macro_rules!' -// @has - '//span[@class="ident"]' 'todo' +// @hasraw - ' todo {' // @hasraw - '{ () => { ... }; ($(' // @has - '//span[@class="macro-nonterminal"]' '$' // @has - '//span[@class="macro-nonterminal"]' 'arg' -// @hasraw - ':' -// @has - '//span[@class="ident"]' 'tt' -// @hasraw - ')+' +// @hasraw - ':tt)+' // @hasraw - ') => { ... }; }' pub use std::todo; diff --git a/src/test/ui/closures/binder/disallow-const.rs b/src/test/ui/closures/binder/disallow-const.rs new file mode 100644 index 00000000000..72ad6185d37 --- /dev/null +++ b/src/test/ui/closures/binder/disallow-const.rs @@ -0,0 +1,6 @@ +#![feature(closure_lifetime_binder)] + +fn main() { + for<const N: i32> || -> () {}; + //~^ ERROR only lifetime parameters can be used in this context +} diff --git a/src/test/ui/closures/binder/disallow-const.stderr b/src/test/ui/closures/binder/disallow-const.stderr new file mode 100644 index 00000000000..3c3b43d8cf3 --- /dev/null +++ b/src/test/ui/closures/binder/disallow-const.stderr @@ -0,0 +1,8 @@ +error: only lifetime parameters can be used in this context + --> $DIR/disallow-const.rs:4:15 + | +LL | for<const N: i32> || -> () {}; + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/closures/binder/disallow-ty.rs b/src/test/ui/closures/binder/disallow-ty.rs new file mode 100644 index 00000000000..bbe3d8488d9 --- /dev/null +++ b/src/test/ui/closures/binder/disallow-ty.rs @@ -0,0 +1,6 @@ +#![feature(closure_lifetime_binder)] + +fn main() { + for<T> || -> () {}; + //~^ ERROR only lifetime parameters can be used in this context +} diff --git a/src/test/ui/closures/binder/disallow-ty.stderr b/src/test/ui/closures/binder/disallow-ty.stderr new file mode 100644 index 00000000000..51b6773edea --- /dev/null +++ b/src/test/ui/closures/binder/disallow-ty.stderr @@ -0,0 +1,8 @@ +error: only lifetime parameters can be used in this context + --> $DIR/disallow-ty.rs:4:9 + | +LL | for<T> || -> () {}; + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/coherence/auxiliary/trait-with-const-param.rs b/src/test/ui/coherence/auxiliary/trait-with-const-param.rs new file mode 100644 index 00000000000..a44eb14f8e4 --- /dev/null +++ b/src/test/ui/coherence/auxiliary/trait-with-const-param.rs @@ -0,0 +1 @@ +pub trait Trait<const N: usize, T> {} diff --git a/src/test/ui/coherence/const-generics-orphan-check-ok.rs b/src/test/ui/coherence/const-generics-orphan-check-ok.rs new file mode 100644 index 00000000000..217e8aed234 --- /dev/null +++ b/src/test/ui/coherence/const-generics-orphan-check-ok.rs @@ -0,0 +1,28 @@ +// check-pass +// aux-build:trait-with-const-param.rs +extern crate trait_with_const_param; +use trait_with_const_param::*; + +// Trivial case, const param after local type. +struct Local1; +impl<const N: usize, T> Trait<N, T> for Local1 {} + +// Concrete consts behave the same as foreign types, +// so this also trivially works. +impl Trait<3, Local1> for i32 {} + +// This case isn't as trivial as we would forbid type +// parameters here, we do allow const parameters though. +// +// The reason that type parameters are forbidden for +// `impl<T> Trait<T, LocalInA> for i32 {}` is that another +// downstream crate can add `impl<T> Trait<LocalInB, T> for i32`. +// As these two impls would overlap we forbid any impls which +// have a type parameter in front of a local type. +// +// With const parameters this issue does not exist as there are no +// constants local to another downstream crate. +struct Local2; +impl<const N: usize> Trait<N, Local2> for i32 {} + +fn main() {} diff --git a/src/test/ui/consts/assert-type-intrinsics.rs b/src/test/ui/consts/assert-type-intrinsics.rs index 38e5c454edf..3ce3e1bdbac 100644 --- a/src/test/ui/consts/assert-type-intrinsics.rs +++ b/src/test/ui/consts/assert-type-intrinsics.rs @@ -13,10 +13,10 @@ fn main() { const _BAD1: () = unsafe { MaybeUninit::<!>::uninit().assume_init(); }; - const _BAD2: () = unsafe { + const _BAD2: () = { intrinsics::assert_uninit_valid::<bool>(); }; - const _BAD3: () = unsafe { + const _BAD3: () = { intrinsics::assert_zero_valid::<&'static i32>(); }; } diff --git a/src/test/ui/consts/assert-type-intrinsics.stderr b/src/test/ui/consts/assert-type-intrinsics.stderr index f3b9170d428..6eab10197b8 100644 --- a/src/test/ui/consts/assert-type-intrinsics.stderr +++ b/src/test/ui/consts/assert-type-intrinsics.stderr @@ -13,7 +13,7 @@ LL | MaybeUninit::<!>::uninit().assume_init(); error: any use of this value will cause an error --> $DIR/assert-type-intrinsics.rs:17:9 | -LL | const _BAD2: () = unsafe { +LL | const _BAD2: () = { | --------------- LL | intrinsics::assert_uninit_valid::<bool>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to leave type `bool` uninitialized, which is invalid @@ -24,7 +24,7 @@ LL | intrinsics::assert_uninit_valid::<bool>(); error: any use of this value will cause an error --> $DIR/assert-type-intrinsics.rs:20:9 | -LL | const _BAD3: () = unsafe { +LL | const _BAD3: () = { | --------------- LL | intrinsics::assert_zero_valid::<&'static i32>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to zero-initialize type `&i32`, which is invalid @@ -51,7 +51,7 @@ Future breakage diagnostic: error: any use of this value will cause an error --> $DIR/assert-type-intrinsics.rs:17:9 | -LL | const _BAD2: () = unsafe { +LL | const _BAD2: () = { | --------------- LL | intrinsics::assert_uninit_valid::<bool>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to leave type `bool` uninitialized, which is invalid @@ -64,7 +64,7 @@ Future breakage diagnostic: error: any use of this value will cause an error --> $DIR/assert-type-intrinsics.rs:20:9 | -LL | const _BAD3: () = unsafe { +LL | const _BAD3: () = { | --------------- LL | intrinsics::assert_zero_valid::<&'static i32>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to zero-initialize type `&i32`, which is invalid diff --git a/src/test/ui/parser/issue-100197-mut-let.fixed b/src/test/ui/parser/issue-100197-mut-let.fixed new file mode 100644 index 00000000000..5a895622200 --- /dev/null +++ b/src/test/ui/parser/issue-100197-mut-let.fixed @@ -0,0 +1,6 @@ +// run-rustfix + +fn main() { + let mut _x = 123; + //~^ ERROR invalid variable declaration +} diff --git a/src/test/ui/parser/issue-100197-mut-let.rs b/src/test/ui/parser/issue-100197-mut-let.rs new file mode 100644 index 00000000000..71103813a6e --- /dev/null +++ b/src/test/ui/parser/issue-100197-mut-let.rs @@ -0,0 +1,6 @@ +// run-rustfix + +fn main() { + mut let _x = 123; + //~^ ERROR invalid variable declaration +} diff --git a/src/test/ui/parser/issue-100197-mut-let.stderr b/src/test/ui/parser/issue-100197-mut-let.stderr new file mode 100644 index 00000000000..86658e4f39f --- /dev/null +++ b/src/test/ui/parser/issue-100197-mut-let.stderr @@ -0,0 +1,8 @@ +error: invalid variable declaration + --> $DIR/issue-100197-mut-let.rs:4:5 + | +LL | mut let _x = 123; + | ^^^^^^^ help: switch the order of `mut` and `let`: `let mut` + +error: aborting due to previous error + diff --git a/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.fixed b/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.fixed new file mode 100644 index 00000000000..64ab6f62b77 --- /dev/null +++ b/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.fixed @@ -0,0 +1,8 @@ +// run-rustfix + +fn main() { + const _FOO: i32 = 123; + //~^ ERROR const` and `let` are mutually exclusive + const _BAR: i32 = 123; + //~^ ERROR `const` and `let` are mutually exclusive +} diff --git a/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.rs b/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.rs new file mode 100644 index 00000000000..50520971ffb --- /dev/null +++ b/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.rs @@ -0,0 +1,8 @@ +// run-rustfix + +fn main() { + const let _FOO: i32 = 123; + //~^ ERROR const` and `let` are mutually exclusive + let const _BAR: i32 = 123; + //~^ ERROR `const` and `let` are mutually exclusive +} diff --git a/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.stderr b/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.stderr new file mode 100644 index 00000000000..72377fc379c --- /dev/null +++ b/src/test/ui/parser/issue-99910-const-let-mutually-exclusive.stderr @@ -0,0 +1,14 @@ +error: `const` and `let` are mutually exclusive + --> $DIR/issue-99910-const-let-mutually-exclusive.rs:4:5 + | +LL | const let _FOO: i32 = 123; + | ^^^^^^^^^ help: remove `let`: `const` + +error: `const` and `let` are mutually exclusive + --> $DIR/issue-99910-const-let-mutually-exclusive.rs:6:5 + | +LL | let const _BAR: i32 = 123; + | ^^^^^^^^^ help: remove `let`: `const` + +error: aborting due to 2 previous errors + |
