about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc/Cargo.toml1
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs52
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs20
-rw-r--r--compiler/rustc_data_structures/Cargo.toml9
-rw-r--r--compiler/rustc_driver/Cargo.toml2
-rw-r--r--compiler/rustc_interface/Cargo.toml5
-rw-r--r--compiler/rustc_middle/Cargo.toml7
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs28
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs1
-rw-r--r--compiler/rustc_query_impl/Cargo.toml5
-rw-r--r--compiler/rustc_query_system/Cargo.toml5
-rw-r--r--library/core/src/num/mod.rs1
-rw-r--r--library/std/src/env.rs8
-rw-r--r--library/std/src/time.rs9
-rw-r--r--src/bootstrap/compile.rs2
-rw-r--r--src/bootstrap/lib.rs14
-rw-r--r--src/test/ui/box/issue-95036.rs22
-rw-r--r--src/test/ui/generics/issue-95208-ignore-qself.fixed11
-rw-r--r--src/test/ui/generics/issue-95208-ignore-qself.rs11
-rw-r--r--src/test/ui/generics/issue-95208-ignore-qself.stderr10
-rw-r--r--src/test/ui/generics/issue-95208.fixed11
-rw-r--r--src/test/ui/generics/issue-95208.rs11
-rw-r--r--src/test/ui/generics/issue-95208.stderr10
23 files changed, 205 insertions, 50 deletions
diff --git a/compiler/rustc/Cargo.toml b/compiler/rustc/Cargo.toml
index 696c003a587..b642e891956 100644
--- a/compiler/rustc/Cargo.toml
+++ b/compiler/rustc/Cargo.toml
@@ -19,3 +19,4 @@ features = ['unprefixed_malloc_on_supported_platforms']
 jemalloc = ['tikv-jemalloc-sys']
 llvm = ['rustc_driver/llvm']
 max_level_info = ['rustc_driver/max_level_info']
+rustc_use_parallel_compiler = ['rustc_driver/rustc_use_parallel_compiler']
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 0b31e4b5582..01056024805 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -14,6 +14,8 @@ use crate::{CachedModuleCodegen, CompiledModule, CrateInfo, MemFlags, ModuleCode
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
+
+#[cfg(parallel_compiler)]
 use rustc_data_structures::sync::{par_iter, ParallelIterator};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
@@ -622,34 +624,34 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
     // This likely is a temporary measure. Once we don't have to support the
     // non-parallel compiler anymore, we can compile CGUs end-to-end in
     // parallel and get rid of the complicated scheduling logic.
+    #[cfg(parallel_compiler)]
     let pre_compile_cgus = |cgu_reuse: &[CguReuse]| {
-        if cfg!(parallel_compiler) {
-            tcx.sess.time("compile_first_CGU_batch", || {
-                // Try to find one CGU to compile per thread.
-                let cgus: Vec<_> = cgu_reuse
-                    .iter()
-                    .enumerate()
-                    .filter(|&(_, reuse)| reuse == &CguReuse::No)
-                    .take(tcx.sess.threads())
-                    .collect();
-
-                // Compile the found CGUs in parallel.
-                let start_time = Instant::now();
-
-                let pre_compiled_cgus = par_iter(cgus)
-                    .map(|(i, _)| {
-                        let module = backend.compile_codegen_unit(tcx, codegen_units[i].name());
-                        (i, module)
-                    })
-                    .collect();
-
-                (pre_compiled_cgus, start_time.elapsed())
-            })
-        } else {
-            (FxHashMap::default(), Duration::new(0, 0))
-        }
+        tcx.sess.time("compile_first_CGU_batch", || {
+            // Try to find one CGU to compile per thread.
+            let cgus: Vec<_> = cgu_reuse
+                .iter()
+                .enumerate()
+                .filter(|&(_, reuse)| reuse == &CguReuse::No)
+                .take(tcx.sess.threads())
+                .collect();
+
+            // Compile the found CGUs in parallel.
+            let start_time = Instant::now();
+
+            let pre_compiled_cgus = par_iter(cgus)
+                .map(|(i, _)| {
+                    let module = backend.compile_codegen_unit(tcx, codegen_units[i].name());
+                    (i, module)
+                })
+                .collect();
+
+            (pre_compiled_cgus, start_time.elapsed())
+        })
     };
 
+    #[cfg(not(parallel_compiler))]
+    let pre_compile_cgus = |_: &[CguReuse]| (FxHashMap::default(), Duration::new(0, 0));
+
     let mut cgu_reuse = Vec::new();
     let mut pre_compiled_cgus: Option<FxHashMap<usize, _>> = None;
     let mut total_codegen_time = Duration::new(0, 0);
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index 2b8fa3be56d..17cfb6c5dfb 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -441,11 +441,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     .find(|elem| matches!(elem.1, mir::ProjectionElem::Deref))
                 {
                     base = elem.0 + 1;
-                    self.codegen_consume(
+                    let cg_base = self.codegen_consume(
                         bx,
                         mir::PlaceRef { projection: &place_ref.projection[..elem.0], ..place_ref },
-                    )
-                    .deref(bx.cx())
+                    );
+
+                    // a box with a non-zst allocator should not be directly dereferenced
+                    if cg_base.layout.ty.is_box() && !cg_base.layout.field(cx, 1).is_zst() {
+                        let ptr = cg_base.extract_field(bx, 0).extract_field(bx, 0);
+
+                        ptr.deref(bx.cx())
+                    } else {
+                        cg_base.deref(bx.cx())
+                    }
                 } else {
                     bug!("using operand local {:?} as place", place_ref);
                 }
@@ -454,10 +462,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         for elem in place_ref.projection[base..].iter() {
             cg_base = match elem.clone() {
                 mir::ProjectionElem::Deref => {
-                    // custom allocators can change box's abi, making it unable to be derefed directly
-                    if cg_base.layout.ty.is_box()
-                        && matches!(cg_base.layout.abi, Abi::Aggregate { .. } | Abi::Uninhabited)
-                    {
+                    // a box with a non-zst allocator should not be directly dereferenced
+                    if cg_base.layout.ty.is_box() && !cg_base.layout.field(cx, 1).is_zst() {
                         let ptr = cg_base.project_field(bx, 0).project_field(bx, 0);
 
                         bx.load_operand(ptr).deref(bx.cx())
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index b82e9717261..7cc8b5c2033 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -9,7 +9,7 @@ doctest = false
 [dependencies]
 arrayvec = { version = "0.7", default-features = false }
 ena = "0.14"
-indexmap = { version = "1.8.0", features = ["rustc-rayon"] }
+indexmap = { version = "1.8.0" }
 tracing = "0.1"
 jobserver_crate = { version = "0.1.13", package = "jobserver" }
 rustc_serialize = { path = "../rustc_serialize" }
@@ -17,8 +17,8 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_graphviz = { path = "../rustc_graphviz" }
 cfg-if = "0.1.2"
 stable_deref_trait = "1.0.0"
-rayon = { version = "0.3.2", package = "rustc-rayon" }
-rayon-core = { version = "0.3.2", package = "rustc-rayon-core" }
+rayon = { version = "0.3.2", package = "rustc-rayon", optional = true }
+rayon-core = { version = "0.3.2", package = "rustc-rayon-core", optional = true }
 rustc-hash = "1.1.0"
 smallvec = { version = "1.6.1", features = ["const_generics", "union", "may_dangle"] }
 rustc_index = { path = "../rustc_index", package = "rustc_index" }
@@ -36,3 +36,6 @@ winapi = { version = "0.3", features = ["fileapi", "psapi", "winerror"] }
 
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
 memmap2 = "0.2.1"
+
+[features]
+rustc_use_parallel_compiler = ["indexmap/rustc-rayon", "rayon", "rayon-core"]
diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml
index 872f946bf7d..fd2ca5beade 100644
--- a/compiler/rustc_driver/Cargo.toml
+++ b/compiler/rustc_driver/Cargo.toml
@@ -39,3 +39,5 @@ winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"]
 [features]
 llvm = ['rustc_interface/llvm']
 max_level_info = ['rustc_log/max_level_info']
+rustc_use_parallel_compiler = ['rustc_data_structures/rustc_use_parallel_compiler', 'rustc_interface/rustc_use_parallel_compiler',
+    'rustc_middle/rustc_use_parallel_compiler']
diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml
index e31119c1292..29d1cd0e054 100644
--- a/compiler/rustc_interface/Cargo.toml
+++ b/compiler/rustc_interface/Cargo.toml
@@ -10,8 +10,8 @@ doctest = false
 libc = "0.2"
 libloading = "0.7.1"
 tracing = "0.1"
-rustc-rayon-core = "0.3.2"
-rayon = { version = "0.3.2", package = "rustc-rayon" }
+rustc-rayon-core = { version = "0.3.2", optional = true }
+rayon = { version = "0.3.2", package = "rustc-rayon", optional = true }
 smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
 rustc_ast = { path = "../rustc_ast" }
 rustc_attr = { path = "../rustc_attr" }
@@ -57,3 +57,4 @@ rustc_target = { path = "../rustc_target" }
 
 [features]
 llvm = ['rustc_codegen_llvm']
+rustc_use_parallel_compiler = ['rayon', 'rustc-rayon-core', 'rustc_query_impl/rustc_use_parallel_compiler']
diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml
index 9cfc5f5b444..cd281ea5b38 100644
--- a/compiler/rustc_middle/Cargo.toml
+++ b/compiler/rustc_middle/Cargo.toml
@@ -12,8 +12,8 @@ bitflags = "1.2.1"
 either = "1.5.0"
 gsgdt = "0.1.2"
 tracing = "0.1"
-rustc-rayon = "0.3.2"
-rustc-rayon-core = "0.3.2"
+rustc-rayon = { version = "0.3.2", optional = true }
+rustc-rayon-core = { version = "0.3.2", optional = true }
 polonius-engine = "0.13.0"
 rustc_apfloat = { path = "../rustc_apfloat" }
 rustc_attr = { path = "../rustc_attr" }
@@ -35,3 +35,6 @@ rustc_session = { path = "../rustc_session" }
 rustc_type_ir = { path = "../rustc_type_ir" }
 rand = "0.8.4"
 rand_xoshiro = "0.6.0"
+
+[features]
+rustc_use_parallel_compiler = ["rustc-rayon", "rustc-rayon-core"]
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index b5b628a3f55..534fd0d4816 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -2369,6 +2369,34 @@ impl<'a> Parser<'a> {
         Err(err)
     }
 
+    crate fn maybe_recover_bounds_doubled_colon(&mut self, ty: &Ty) -> PResult<'a, ()> {
+        let TyKind::Path(qself, path) = &ty.kind else { return Ok(()) };
+        let qself_position = qself.as_ref().map(|qself| qself.position);
+        for (i, segments) in path.segments.windows(2).enumerate() {
+            if qself_position.map(|pos| i < pos).unwrap_or(false) {
+                continue;
+            }
+            if let [a, b] = segments {
+                let (a_span, b_span) = (a.span(), b.span());
+                let between_span = a_span.shrink_to_hi().to(b_span.shrink_to_lo());
+                if self.span_to_snippet(between_span).as_ref().map(|a| &a[..]) == Ok(":: ") {
+                    let mut err = self.struct_span_err(
+                        path.span.shrink_to_hi(),
+                        "expected `:` followed by trait or lifetime",
+                    );
+                    err.span_suggestion(
+                        between_span,
+                        "use single colon",
+                        ": ".to_owned(),
+                        Applicability::MachineApplicable,
+                    );
+                    return Err(err);
+                }
+            }
+        }
+        Ok(())
+    }
+
     /// Parse and throw away a parenthesized comma separated
     /// sequence of patterns until `)` is reached.
     fn skip_pat_list(&mut self) -> PResult<'a, ()> {
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index d625080dee4..29fe2b76101 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -312,6 +312,7 @@ impl<'a> Parser<'a> {
                 id: ast::DUMMY_NODE_ID,
             }))
         } else {
+            self.maybe_recover_bounds_doubled_colon(&ty)?;
             self.unexpected()
         }
     }
diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml
index f1899a6fb2b..b7502c4b1e2 100644
--- a/compiler/rustc_query_impl/Cargo.toml
+++ b/compiler/rustc_query_impl/Cargo.toml
@@ -8,7 +8,7 @@ doctest = false
 
 [dependencies]
 measureme = "10.0.0"
-rustc-rayon-core = "0.3.2"
+rustc-rayon-core = { version = "0.3.2", optional = true }
 rustc_ast = { path = "../rustc_ast" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
@@ -20,3 +20,6 @@ rustc_query_system = { path = "../rustc_query_system" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
+
+[features]
+rustc_use_parallel_compiler = ["rustc-rayon-core", "rustc_query_system/rustc_use_parallel_compiler"]
diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml
index 79f791eb754..8a35121f90c 100644
--- a/compiler/rustc_query_system/Cargo.toml
+++ b/compiler/rustc_query_system/Cargo.toml
@@ -9,7 +9,7 @@ doctest = false
 [dependencies]
 rustc_arena = { path = "../rustc_arena" }
 tracing = "0.1"
-rustc-rayon-core = "0.3.2"
+rustc-rayon-core = { version = "0.3.2", optional = true }
 rustc_ast = { path = "../rustc_ast" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
@@ -23,3 +23,6 @@ rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 parking_lot = "0.11"
 smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
+
+[features]
+rustc_use_parallel_compiler = ["rustc-rayon-core"]
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index dca8ffa4e2c..c09f642d969 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -809,6 +809,7 @@ impl u8 {
         ascii::escape_default(self)
     }
 
+    #[inline]
     pub(crate) const fn is_utf8_char_boundary(self) -> bool {
         // This is bit magic equivalent to: b < 128 || b >= 192
         (self as i8) >= -0x40
diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index 05f08c498e6..f03d298d869 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -25,9 +25,11 @@ use crate::sys::os as os_imp;
 ///
 /// # Platform-specific behavior
 ///
-/// This function currently corresponds to the `getcwd` function on Unix
+/// This function [currently] corresponds to the `getcwd` function on Unix
 /// and the `GetCurrentDirectoryW` function on Windows.
 ///
+/// [currently]: crate::io#platform-specific-behavior
+///
 /// # Errors
 ///
 /// Returns an [`Err`] if the current working directory value is invalid.
@@ -56,11 +58,13 @@ pub fn current_dir() -> io::Result<PathBuf> {
 ///
 /// # Platform-specific behavior
 ///
-/// This function currently corresponds to the `chdir` function on Unix
+/// This function [currently] corresponds to the `chdir` function on Unix
 /// and the `SetCurrentDirectoryW` function on Windows.
 ///
 /// Returns an [`Err`] if the operation fails.
 ///
+/// [currently]: crate::io#platform-specific-behavior
+///
 /// # Examples
 ///
 /// ```
diff --git a/library/std/src/time.rs b/library/std/src/time.rs
index 2f8eb557b4f..708e4064e06 100644
--- a/library/std/src/time.rs
+++ b/library/std/src/time.rs
@@ -101,7 +101,9 @@ pub use core::time::FromFloatSecsError;
 /// ```
 ///
 /// # Underlying System calls
-/// Currently, the following system calls are being used to get the current time using `now()`:
+///
+/// The following system calls are [currently] being used by `now()` to find out
+/// the current time:
 ///
 /// |  Platform |               System call                                            |
 /// |-----------|----------------------------------------------------------------------|
@@ -113,6 +115,7 @@ pub use core::time::FromFloatSecsError;
 /// | WASI      | [__wasi_clock_time_get (Monotonic Clock)]                            |
 /// | Windows   | [QueryPerformanceCounter]                                            |
 ///
+/// [currently]: crate::io#platform-specific-behavior
 /// [QueryPerformanceCounter]: https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter
 /// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time
 /// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode
@@ -203,7 +206,8 @@ pub struct Instant(time::Instant);
 /// For example, on Windows the time is represented in 100 nanosecond intervals whereas Linux
 /// can represent nanosecond intervals.
 ///
-/// Currently, the following system calls are being used to get the current time using `now()`:
+/// The following system calls are [currently] being used by `now()` to find out
+/// the current time:
 ///
 /// |  Platform |               System call                                            |
 /// |-----------|----------------------------------------------------------------------|
@@ -215,6 +219,7 @@ pub struct Instant(time::Instant);
 /// | WASI      | [__wasi_clock_time_get (Realtime Clock)]                             |
 /// | Windows   | [GetSystemTimePreciseAsFileTime] / [GetSystemTimeAsFileTime]         |
 ///
+/// [currently]: crate::io#platform-specific-behavior
 /// [`insecure_time` usercall]: https://edp.fortanix.com/docs/api/fortanix_sgx_abi/struct.Usercalls.html#method.insecure_time
 /// [timekeeping in SGX]: https://edp.fortanix.com/docs/concepts/rust-std/#codestdtimecode
 /// [gettimeofday]: https://man7.org/linux/man-pages/man2/gettimeofday.2.html
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index e030e0bc1cf..00fc1f04342 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -689,6 +689,8 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS
     }
 
     if builder.config.rustc_parallel {
+        // keep in sync with `bootstrap/lib.rs:Build::rustc_features`
+        // `cfg` option for rustc, `features` option for cargo, for conditional compilation
         cargo.rustflag("--cfg=parallel_compiler");
         cargo.rustdocflag("--cfg=parallel_compiler");
     }
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 41e2e976162..8f076ad914d 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -729,12 +729,16 @@ impl Build {
 
     /// Gets the space-separated set of activated features for the compiler.
     fn rustc_features(&self, kind: Kind) -> String {
-        let mut features = String::new();
+        let mut features = vec![];
         if self.config.jemalloc {
-            features.push_str("jemalloc");
+            features.push("jemalloc");
         }
         if self.config.llvm_enabled() || kind == Kind::Check {
-            features.push_str(" llvm");
+            features.push("llvm");
+        }
+        // keep in sync with `bootstrap/compile.rs:rustc_cargo_env`
+        if self.config.rustc_parallel {
+            features.push("rustc_use_parallel_compiler");
         }
 
         // If debug logging is on, then we want the default for tracing:
@@ -743,10 +747,10 @@ impl Build {
         // if its unset, if debug_assertions is on, then debug_logging will also be on
         // as well as tracing *ignoring* this feature when debug_assertions is on
         if !self.config.rust_debug_logging {
-            features.push_str(" max_level_info");
+            features.push("max_level_info");
         }
 
-        features
+        features.join(" ")
     }
 
     /// Component directory that Cargo will produce output into (e.g.
diff --git a/src/test/ui/box/issue-95036.rs b/src/test/ui/box/issue-95036.rs
new file mode 100644
index 00000000000..c2d4275aa49
--- /dev/null
+++ b/src/test/ui/box/issue-95036.rs
@@ -0,0 +1,22 @@
+// compile-flags: -O
+// build-pass
+
+#![feature(allocator_api, bench_black_box)]
+
+#[inline(never)]
+pub fn by_ref(node: &mut Box<[u8; 1], &std::alloc::Global>) {
+    node[0] = 9u8;
+}
+
+pub fn main() {
+    let mut node = Box::new_in([5u8], &std::alloc::Global);
+    node[0] = 7u8;
+
+    std::hint::black_box(node);
+
+    let mut node = Box::new_in([5u8], &std::alloc::Global);
+
+    by_ref(&mut node);
+
+    std::hint::black_box(node);
+}
diff --git a/src/test/ui/generics/issue-95208-ignore-qself.fixed b/src/test/ui/generics/issue-95208-ignore-qself.fixed
new file mode 100644
index 00000000000..608b4a20fbc
--- /dev/null
+++ b/src/test/ui/generics/issue-95208-ignore-qself.fixed
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#[allow(unused)]
+struct Struct<T>(T);
+
+impl<T: Iterator> Struct<T> where <T as std:: iter::Iterator>::Item: std::fmt::Display {
+//~^ ERROR expected `:` followed by trait or lifetime
+//~| HELP use single colon
+}
+
+fn main() {}
diff --git a/src/test/ui/generics/issue-95208-ignore-qself.rs b/src/test/ui/generics/issue-95208-ignore-qself.rs
new file mode 100644
index 00000000000..da7efd576d1
--- /dev/null
+++ b/src/test/ui/generics/issue-95208-ignore-qself.rs
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#[allow(unused)]
+struct Struct<T>(T);
+
+impl<T: Iterator> Struct<T> where <T as std:: iter::Iterator>::Item:: std::fmt::Display {
+//~^ ERROR expected `:` followed by trait or lifetime
+//~| HELP use single colon
+}
+
+fn main() {}
diff --git a/src/test/ui/generics/issue-95208-ignore-qself.stderr b/src/test/ui/generics/issue-95208-ignore-qself.stderr
new file mode 100644
index 00000000000..acbc1300d00
--- /dev/null
+++ b/src/test/ui/generics/issue-95208-ignore-qself.stderr
@@ -0,0 +1,10 @@
+error: expected `:` followed by trait or lifetime
+  --> $DIR/issue-95208-ignore-qself.rs:6:88
+   |
+LL | impl<T: Iterator> Struct<T> where <T as std:: iter::Iterator>::Item:: std::fmt::Display {
+   |                                                                    ---                 ^
+   |                                                                    |
+   |                                                                    help: use single colon: `:`
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/generics/issue-95208.fixed b/src/test/ui/generics/issue-95208.fixed
new file mode 100644
index 00000000000..a0b1e886ca2
--- /dev/null
+++ b/src/test/ui/generics/issue-95208.fixed
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#[allow(unused)]
+struct Struct<T>(T);
+
+impl<T> Struct<T> where T: std::fmt::Display {
+//~^ ERROR expected `:` followed by trait or lifetime
+//~| HELP use single colon
+}
+
+fn main() {}
diff --git a/src/test/ui/generics/issue-95208.rs b/src/test/ui/generics/issue-95208.rs
new file mode 100644
index 00000000000..0e3083484ff
--- /dev/null
+++ b/src/test/ui/generics/issue-95208.rs
@@ -0,0 +1,11 @@
+// run-rustfix
+
+#[allow(unused)]
+struct Struct<T>(T);
+
+impl<T> Struct<T> where T:: std::fmt::Display {
+//~^ ERROR expected `:` followed by trait or lifetime
+//~| HELP use single colon
+}
+
+fn main() {}
diff --git a/src/test/ui/generics/issue-95208.stderr b/src/test/ui/generics/issue-95208.stderr
new file mode 100644
index 00000000000..559527663e8
--- /dev/null
+++ b/src/test/ui/generics/issue-95208.stderr
@@ -0,0 +1,10 @@
+error: expected `:` followed by trait or lifetime
+  --> $DIR/issue-95208.rs:6:46
+   |
+LL | impl<T> Struct<T> where T:: std::fmt::Display {
+   |                          ---                 ^
+   |                          |
+   |                          help: use single colon: `:`
+
+error: aborting due to previous error
+