about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-03-12 09:44:17 +0000
committerbors <bors@rust-lang.org>2023-03-12 09:44:17 +0000
commit24c0b81c1fd5de8e00276524896d3352ed91a8cb (patch)
tree884da1d0a42415c5c2aac83cac2d7f7f2b8d8df9
parent150cb381471533050751111e5faf1d9f05c02f77 (diff)
parent3166b4a24599aa4237fae54ac7d8054fb7048b75 (diff)
downloadrust-24c0b81c1fd5de8e00276524896d3352ed91a8cb.tar.gz
rust-24c0b81c1fd5de8e00276524896d3352ed91a8cb.zip
Auto merge of #109043 - matthiaskrgr:rollup-genkz0e, r=matthiaskrgr
Rollup of 9 pull requests

Successful merges:

 - #108726 (tidy: enforce comment blocks to have an even number of backticks)
 - #108797 (Allow binary files to go through the `FileLoader`)
 - #108841 (Add suggestion to diagnostic when user has array but trait wants slice. (rebased))
 - #108984 (bootstrap: document tidy)
 - #109013 (Give proper error message when tcx wasn't passed to decoder)
 - #109017 (remove duplicated calls to sort_string)
 - #109018 (Expand on the allocator comment in `rustc-main`)
 - #109028 (Add eslint checks for rustdoc-js tester)
 - #109034 (Commit some tests for the new solver + lazy norm)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc/src/main.rs9
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs1
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs1
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0368.md2
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0710.md4
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs23
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs1
-rw-r--r--compiler/rustc_middle/src/ty/error.rs8
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs1
-rw-r--r--compiler/rustc_span/src/source_map.rs11
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs20
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs79
-rw-r--r--compiler/rustc_type_ir/src/fold.rs2
-rw-r--r--src/bootstrap/builder.rs1
-rw-r--r--src/bootstrap/doc.rs1
-rw-r--r--src/ci/docker/host-x86_64/mingw-check/Dockerfile3
-rw-r--r--src/tools/rustdoc-js/.eslintrc.js96
-rw-r--r--src/tools/rustdoc-js/tester.js90
-rw-r--r--src/tools/tidy/src/style.rs69
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-1.rs20
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-1.stderr56
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-2.rs28
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-2.stderr94
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-3.rs22
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr75
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-4.rs26
-rw-r--r--tests/ui/dst/issue-90528-unsizing-suggestion-4.stderr79
-rw-r--r--tests/ui/traits/new-solver/cast-checks-handling-projections.rs (renamed from tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs)0
-rw-r--r--tests/ui/traits/new-solver/equating-projection-cyclically.rs (renamed from tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs)0
-rw-r--r--tests/ui/traits/new-solver/equating-projection-cyclically.stderr (renamed from tests/ui/typeck/lazy-norm/equating-projection-cyclically.stderr)0
-rw-r--r--tests/ui/traits/new-solver/lazy-nested-obligations-1.rs13
-rw-r--r--tests/ui/traits/new-solver/lazy-nested-obligations-2.rs23
-rw-r--r--tests/ui/traits/new-solver/lazy-nested-obligations-3.rs38
-rw-r--r--tests/ui/traits/new-solver/normalize-param-env-1.rs40
-rw-r--r--tests/ui/traits/new-solver/normalize-param-env-2.rs26
-rw-r--r--tests/ui/traits/new-solver/normalize-param-env-3.rs32
39 files changed, 914 insertions, 87 deletions
diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs
index e21c9b66044..434b978ae31 100644
--- a/compiler/rustc/src/main.rs
+++ b/compiler/rustc/src/main.rs
@@ -24,6 +24,15 @@
 // The two crates we link to here, `std` and `rustc_driver`, are both dynamic
 // libraries. So we must reference jemalloc symbols one way or another, because
 // this file is the only object code in the rustc executable.
+//
+// NOTE: if you are reading this comment because you want to set a custom `global_allocator` for
+// benchmarking, consider using the benchmarks in the `rustc-perf` collector suite instead:
+// https://github.com/rust-lang/rustc-perf/blob/master/collector/README.md#profiling
+//
+// NOTE: if you are reading this comment because you want to replace jemalloc with another allocator
+// to compare their performance, see
+// https://github.com/rust-lang/rust/commit/b90cfc887c31c3e7a9e6d462e2464db1fe506175#diff-43914724af6e464c1da2171e4a9b6c7e607d5bc1203fa95c0ab85be4122605ef
+// for an example of how to do so.
 
 #[unix_sigpipe = "sig_dfl"]
 fn main() {
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 1f819beeb5d..6b3053fdfac 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -1052,6 +1052,7 @@ impl<'a> MethodDef<'a> {
     ///         ::core::hash::Hash::hash(&{ self.y }, state)
     ///     }
     /// }
+    /// ```
     fn expand_struct_method_body<'b>(
         &self,
         cx: &mut ExtCtxt<'_>,
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
index 93419d27a62..978141917c6 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
@@ -438,6 +438,7 @@ fn build_enum_variant_member_di_node<'ll, 'tcx>(
 ///         DW_TAG_structure_type            (type of variant 1)
 ///         DW_TAG_structure_type            (type of variant 2)
 ///         DW_TAG_structure_type            (type of variant 3)
+/// ```
 struct VariantMemberInfo<'a, 'll> {
     variant_index: VariantIdx,
     variant_name: Cow<'a, str>,
diff --git a/compiler/rustc_error_codes/src/error_codes/E0368.md b/compiler/rustc_error_codes/src/error_codes/E0368.md
index 7b9d9334821..b18e8758d71 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0368.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0368.md
@@ -41,7 +41,7 @@ impl Add for Foo {
 
 fn main() {
     let mut x: Foo = Foo(5);
-    x += Foo(7); // error, `+= cannot be applied to the type `Foo`
+    x += Foo(7); // error, `+=` cannot be applied to the type `Foo`
 }
 ```
 
diff --git a/compiler/rustc_error_codes/src/error_codes/E0710.md b/compiler/rustc_error_codes/src/error_codes/E0710.md
index b7037ea611b..84d55d52426 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0710.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0710.md
@@ -3,14 +3,14 @@ An unknown tool name was found in a scoped lint.
 Erroneous code examples:
 
 ```compile_fail,E0710
-#[allow(clipp::filter_map)] // error!`
+#[allow(clipp::filter_map)] // error!
 fn main() {
     // business logic
 }
 ```
 
 ```compile_fail,E0710
-#[warn(clipp::filter_map)] // error!`
+#[warn(clipp::filter_map)] // error!
 fn main() {
     // business logic
 }
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index d84fabb7834..773ac0e40c5 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -398,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// Here:
     /// - E would be `fn(&u32) -> &u32`.
-    /// - S would be `fn(&u32) ->
+    /// - S would be `fn(&u32) -> ?T`
     /// - E' is `&'!0 u32 -> &'!0 u32`
     /// - S' is `&'?0 u32 -> ?T`
     ///
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index b06ff10d86e..22c1e387117 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -104,7 +104,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         let (mention_influencer, influencer_point) =
             if sup_origin.span().overlaps(param.param_ty_span) {
                 // Account for `async fn` like in `async-await/issues/issue-62097.rs`.
-                // The desugaring of `async `fn`s causes `sup_origin` and `param` to point at the same
+                // The desugaring of `async fn`s causes `sup_origin` and `param` to point at the same
                 // place (but with different `ctxt`, hence `overlaps` instead of `==` above).
                 //
                 // This avoids the following:
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index b1e59b0a470..771fb091134 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -311,8 +311,11 @@ impl<T: ParameterizedOverTcx> LazyArray<T> {
 impl<'a, 'tcx> DecodeContext<'a, 'tcx> {
     #[inline]
     fn tcx(&self) -> TyCtxt<'tcx> {
-        debug_assert!(self.tcx.is_some(), "missing TyCtxt in DecodeContext");
-        self.tcx.unwrap()
+        let Some(tcx) = self.tcx else {
+            bug!("No TyCtxt found for decoding. \
+                You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`.");
+        };
+        tcx
     }
 
     #[inline]
@@ -454,7 +457,12 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for ast::AttrId {
 impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SyntaxContext {
     fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> SyntaxContext {
         let cdata = decoder.cdata();
-        let sess = decoder.sess.unwrap();
+
+        let Some(sess) = decoder.sess else {
+            bug!("Cannot decode SyntaxContext without Session.\
+                You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`.");
+        };
+
         let cname = cdata.root.name;
         rustc_span::hygiene::decode_syntax_context(decoder, &cdata.hygiene_context, |_, id| {
             debug!("SpecializedDecoder<SyntaxContext>: decoding {}", id);
@@ -471,7 +479,11 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SyntaxContext {
 impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for ExpnId {
     fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> ExpnId {
         let local_cdata = decoder.cdata();
-        let sess = decoder.sess.unwrap();
+
+        let Some(sess) = decoder.sess else {
+            bug!("Cannot decode ExpnId without Session. \
+                You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`.");
+        };
 
         let cnum = CrateNum::decode(decoder);
         let index = u32::decode(decoder);
@@ -520,7 +532,8 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span {
         let hi = lo + len;
 
         let Some(sess) = decoder.sess else {
-            bug!("Cannot decode Span without Session.")
+            bug!("Cannot decode Span without Session. \
+                You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`.")
         };
 
         // Index of the file in the corresponding crate's list of encoded files.
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index d8829e3e782..7e51953599d 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -123,6 +123,7 @@ fn dump_matched_mir_node<'tcx, F>(
         // see notes on #41697 above
         let def_path =
             ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
+        // ignore-tidy-odd-backticks the literal below is fine
         write!(file, "// MIR for `{}", def_path)?;
         match body.source.promoted {
             None => write!(file, "`")?,
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 9c171a69d06..aff6c77e039 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -151,12 +151,8 @@ impl<'tcx> TypeError<'tcx> {
             .into(),
             RegionsPlaceholderMismatch => "one type is more general than the other".into(),
             ArgumentSorts(values, _) | Sorts(values) => {
-                let mut expected = values.expected.sort_string(tcx);
-                let mut found = values.found.sort_string(tcx);
-                if expected == found {
-                    expected = values.expected.sort_string(tcx);
-                    found = values.found.sort_string(tcx);
-                }
+                let expected = values.expected.sort_string(tcx);
+                let found = values.found.sort_string(tcx);
                 report_maybe_different(&expected, &found).into()
             }
             Traits(values) => {
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 9414d9bfa08..3fb8a6db2d2 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -1886,6 +1886,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // let place = Foo::new();
         // match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) }
         //     => { let tmp2 = place; feed(tmp2) }, ... }
+        // ```
         //
         // And an input like:
         //
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 2e339a9d2d2..a1cb810a429 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -100,6 +100,9 @@ pub trait FileLoader {
 
     /// Read the contents of a UTF-8 file into memory.
     fn read_file(&self, path: &Path) -> io::Result<String>;
+
+    /// Read the contents of a potentially non-UTF-8 file into memory.
+    fn read_binary_file(&self, path: &Path) -> io::Result<Vec<u8>>;
 }
 
 /// A FileLoader that uses std::fs to load real files.
@@ -113,6 +116,10 @@ impl FileLoader for RealFileLoader {
     fn read_file(&self, path: &Path) -> io::Result<String> {
         fs::read_to_string(path)
     }
+
+    fn read_binary_file(&self, path: &Path) -> io::Result<Vec<u8>> {
+        fs::read(path)
+    }
 }
 
 /// This is a [SourceFile] identifier that is used to correlate source files between
@@ -220,9 +227,7 @@ impl SourceMap {
     /// Unlike `load_file`, guarantees that no normalization like BOM-removal
     /// takes place.
     pub fn load_binary_file(&self, path: &Path) -> io::Result<Vec<u8>> {
-        // Ideally, this should use `self.file_loader`, but it can't
-        // deal with binary files yet.
-        let bytes = fs::read(path)?;
+        let bytes = self.file_loader.read_binary_file(path)?;
 
         // We need to add file to the `SourceMap`, so that it is present
         // in dep-info. There's also an edge case that file might be both
diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs
index b092503a007..72b1b35e79b 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly.rs
@@ -248,7 +248,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     ///
     /// To deal with this, we first try to normalize the self type and add the candidates for the normalized
     /// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in
-    /// this case as projections as self types add `
+    /// this case as projections as self types add
+    // FIXME complete the unfinished sentence above
     fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 2dfebfcb904..704b0d0bd1c 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1024,7 +1024,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             // Can't show anything else useful, try to find similar impls.
                             let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
                             if !self.report_similar_impl_candidates(
-                                impl_candidates,
+                                &impl_candidates,
                                 trait_ref,
                                 body_def_id,
                                 &mut err,
@@ -1060,7 +1060,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                     let impl_candidates =
                                         self.find_similar_impl_candidates(trait_pred);
                                     self.report_similar_impl_candidates(
-                                        impl_candidates,
+                                        &impl_candidates,
                                         trait_ref,
                                         body_def_id,
                                         &mut err,
@@ -1068,6 +1068,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                     );
                                 }
                             }
+
+                            self.maybe_suggest_convert_to_slice(
+                                &mut err,
+                                trait_ref,
+                                impl_candidates.as_slice(),
+                                span,
+                            );
                         }
 
                         // Changing mutability doesn't make a difference to whether we have
@@ -1514,7 +1521,7 @@ trait InferCtxtPrivExt<'tcx> {
 
     fn report_similar_impl_candidates(
         &self,
-        impl_candidates: Vec<ImplCandidate<'tcx>>,
+        impl_candidates: &[ImplCandidate<'tcx>],
         trait_ref: ty::PolyTraitRef<'tcx>,
         body_def_id: LocalDefId,
         err: &mut Diagnostic,
@@ -2004,7 +2011,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
     fn report_similar_impl_candidates(
         &self,
-        impl_candidates: Vec<ImplCandidate<'tcx>>,
+        impl_candidates: &[ImplCandidate<'tcx>],
         trait_ref: ty::PolyTraitRef<'tcx>,
         body_def_id: LocalDefId,
         err: &mut Diagnostic,
@@ -2113,7 +2120,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         // Prefer more similar candidates first, then sort lexicographically
         // by their normalized string representation.
         let mut normalized_impl_candidates_and_similarities = impl_candidates
-            .into_iter()
+            .iter()
+            .copied()
             .map(|ImplCandidate { trait_ref, similarity }| {
                 // FIXME(compiler-errors): This should be using `NormalizeExt::normalize`
                 let normalized = self
@@ -2326,7 +2334,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             );
                             if impl_candidates.len() < 10 {
                                 self.report_similar_impl_candidates(
-                                    impl_candidates,
+                                    impl_candidates.as_slice(),
                                     trait_ref,
                                     obligation.cause.body_id,
                                     &mut err,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 9ab753c5a48..11567ff39dd 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -1,7 +1,7 @@
 // ignore-tidy-filelength
 
 use super::{
-    DefIdOrName, FindExprBySpan, Obligation, ObligationCause, ObligationCauseCode,
+    DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode,
     PredicateObligation,
 };
 
@@ -382,6 +382,14 @@ pub trait TypeErrCtxtExt<'tcx> {
         body_id: hir::HirId,
         param_env: ty::ParamEnv<'tcx>,
     ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>;
+
+    fn maybe_suggest_convert_to_slice(
+        &self,
+        err: &mut Diagnostic,
+        trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+        candidate_impls: &[ImplCandidate<'tcx>],
+        span: Span,
+    );
 }
 
 fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
@@ -2220,7 +2228,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         // - `BuiltinDerivedObligation` with a generator witness (A)
         // - `BuiltinDerivedObligation` with a generator (A)
         // - `BuiltinDerivedObligation` with `impl std::future::Future` (A)
-        // - `BindingObligation` with `impl_send (Send requirement)
+        // - `BindingObligation` with `impl_send` (Send requirement)
         //
         // The first obligation in the chain is the most useful and has the generator that captured
         // the type. The last generator (`outer_generator` below) has information about where the
@@ -3826,6 +3834,73 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         }
         assocs_in_this_method
     }
+
+    /// If the type that failed selection is an array or a reference to an array,
+    /// but the trait is implemented for slices, suggest that the user converts
+    /// the array into a slice.
+    fn maybe_suggest_convert_to_slice(
+        &self,
+        err: &mut Diagnostic,
+        trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
+        candidate_impls: &[ImplCandidate<'tcx>],
+        span: Span,
+    ) {
+        // Three cases where we can make a suggestion:
+        // 1. `[T; _]` (array of T)
+        // 2. `&[T; _]` (reference to array of T)
+        // 3. `&mut [T; _]` (mutable reference to array of T)
+        let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() {
+            ty::Array(element_ty, _) => (element_ty, None),
+
+            ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() {
+                ty::Array(element_ty, _) => (element_ty, Some(mutability)),
+                _ => return,
+            },
+
+            _ => return,
+        };
+
+        // Go through all the candidate impls to see if any of them is for
+        // slices of `element_ty` with `mutability`.
+        let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() {
+            ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => {
+                if matches!(*t.kind(), ty::Slice(e) if e == element_ty)
+                    && m == mutability.unwrap_or(m)
+                {
+                    // Use the candidate's mutability going forward.
+                    mutability = Some(m);
+                    true
+                } else {
+                    false
+                }
+            }
+            _ => false,
+        };
+
+        // Grab the first candidate that matches, if any, and make a suggestion.
+        if let Some(slice_ty) = candidate_impls
+            .iter()
+            .map(|trait_ref| trait_ref.trait_ref.self_ty())
+            .filter(|t| is_slice(*t))
+            .next()
+        {
+            let msg = &format!("convert the array to a `{}` slice instead", slice_ty);
+
+            if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                let mut suggestions = vec![];
+                if snippet.starts_with('&') {
+                } else if let Some(hir::Mutability::Mut) = mutability {
+                    suggestions.push((span.shrink_to_lo(), "&mut ".into()));
+                } else {
+                    suggestions.push((span.shrink_to_lo(), "&".into()));
+                }
+                suggestions.push((span.shrink_to_hi(), "[..]".into()));
+                err.multipart_suggestion_verbose(msg, suggestions, Applicability::MaybeIncorrect);
+            } else {
+                err.span_help(span, msg);
+            }
+        }
+    }
 }
 
 /// Add a hint to add a missing borrow or remove an unnecessary one.
diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs
index ee4ef57c38f..3a053d4c6a9 100644
--- a/compiler/rustc_type_ir/src/fold.rs
+++ b/compiler/rustc_type_ir/src/fold.rs
@@ -18,7 +18,7 @@
 //!     It defines a "skeleton" of how they should be folded.
 //! - `TypeSuperFoldable`. This is implemented only for each type of interest,
 //!   and defines the folding "skeleton" for these types.
-//! - `TypeFolder`/`FallibleTypeFolder. One of these is implemented for each
+//! - `TypeFolder`/`FallibleTypeFolder`. One of these is implemented for each
 //!   folder. This defines how types of interest are folded.
 //!
 //! This means each fold is a mixture of (a) generic folding operations, and (b)
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 47d742ef796..bb07ca1908e 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -741,6 +741,7 @@ impl<'a> Builder<'a> {
                 doc::EmbeddedBook,
                 doc::EditionGuide,
                 doc::StyleGuide,
+                doc::Tidy,
             ),
             Kind::Dist => describe!(
                 dist::Docs,
diff --git a/src/bootstrap/doc.rs b/src/bootstrap/doc.rs
index cc80763ef44..36fdd4abf4f 100644
--- a/src/bootstrap/doc.rs
+++ b/src/bootstrap/doc.rs
@@ -882,6 +882,7 @@ tool_doc!(
         // "cargo-credential-wincred",
     ]
 );
+tool_doc!(Tidy, "tidy", "src/tools/tidy", ["tidy"]);
 
 #[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
 pub struct ErrorIndex {
diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
index 98bd90210d6..9141d3e8a48 100644
--- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
@@ -52,4 +52,5 @@ ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \
            reuse lint && \
            # Runs checks to ensure that there are no ES5 issues in our JS code.
            es-check es6 ../src/librustdoc/html/static/js/*.js && \
-           eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js
+           eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \
+           eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js
diff --git a/src/tools/rustdoc-js/.eslintrc.js b/src/tools/rustdoc-js/.eslintrc.js
new file mode 100644
index 00000000000..4ab3a315733
--- /dev/null
+++ b/src/tools/rustdoc-js/.eslintrc.js
@@ -0,0 +1,96 @@
+module.exports = {
+    "env": {
+        "browser": true,
+        "node": true,
+        "es6": true
+    },
+    "extends": "eslint:recommended",
+    "parserOptions": {
+        "ecmaVersion": 2015,
+        "sourceType": "module"
+    },
+    "rules": {
+        "linebreak-style": [
+            "error",
+            "unix"
+        ],
+        "semi": [
+            "error",
+            "always"
+        ],
+        "quotes": [
+            "error",
+            "double"
+        ],
+        "linebreak-style": [
+            "error",
+            "unix"
+        ],
+        "no-trailing-spaces": "error",
+        "no-var": ["error"],
+        "prefer-const": ["error"],
+        "prefer-arrow-callback": ["error"],
+        "brace-style": [
+            "error",
+            "1tbs",
+            { "allowSingleLine": false }
+        ],
+        "keyword-spacing": [
+            "error",
+            { "before": true, "after": true }
+        ],
+        "arrow-spacing": [
+            "error",
+            { "before": true, "after": true }
+        ],
+        "key-spacing": [
+            "error",
+            { "beforeColon": false, "afterColon": true, "mode": "strict" }
+        ],
+        "func-call-spacing": ["error", "never"],
+        "space-infix-ops": "error",
+        "space-before-function-paren": ["error", "never"],
+        "space-before-blocks": "error",
+        "comma-dangle": ["error", "always-multiline"],
+        "comma-style": ["error", "last"],
+        "max-len": ["error", { "code": 100, "tabWidth": 4 }],
+        "eol-last": ["error", "always"],
+        "arrow-parens": ["error", "as-needed"],
+        "no-unused-vars": [
+            "error",
+            {
+                "argsIgnorePattern": "^_",
+                "varsIgnorePattern": "^_"
+            }
+        ],
+        "eqeqeq": "error",
+        "no-const-assign": "error",
+        "no-debugger": "error",
+        "no-dupe-args": "error",
+        "no-dupe-else-if": "error",
+        "no-dupe-keys": "error",
+        "no-duplicate-case": "error",
+        "no-ex-assign": "error",
+        "no-fallthrough": "error",
+        "no-invalid-regexp": "error",
+        "no-import-assign": "error",
+        "no-self-compare": "error",
+        "no-template-curly-in-string": "error",
+        "block-scoped-var": "error",
+        "guard-for-in": "error",
+        "no-alert": "error",
+        "no-confusing-arrow": "error",
+        "no-div-regex": "error",
+        "no-floating-decimal": "error",
+        "no-implicit-globals": "error",
+        "no-implied-eval": "error",
+        "no-label-var": "error",
+        "no-lonely-if": "error",
+        "no-mixed-operators": "error",
+        "no-multi-assign": "error",
+        "no-return-assign": "error",
+        "no-script-url": "error",
+        "no-sequences": "error",
+        "no-div-regex": "error",
+    }
+};
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index e617ceba3b9..8d46a8ce7f1 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -1,5 +1,5 @@
-const fs = require('fs');
-const path = require('path');
+const fs = require("fs");
+const path = require("path");
 
 function loadContent(content) {
     const Module = module.constructor;
@@ -15,7 +15,7 @@ function loadContent(content) {
 }
 
 function readFile(filePath) {
-    return fs.readFileSync(filePath, 'utf8');
+    return fs.readFileSync(filePath, "utf8");
 }
 
 function contentToDiffLine(key, value) {
@@ -25,41 +25,41 @@ function contentToDiffLine(key, value) {
 // This function is only called when no matching result was found and therefore will only display
 // the diff between the two items.
 function betterLookingDiff(entry, data) {
-    let output = ' {\n';
-    const spaces = '     ';
+    let output = " {\n";
+    const spaces = "     ";
     for (const key in entry) {
-        if (!entry.hasOwnProperty(key)) {
+        if (!Object.prototype.hasOwnProperty.call(entry, key)) {
             continue;
         }
-        if (!data || !data.hasOwnProperty(key)) {
-            output += '-' + spaces + contentToDiffLine(key, entry[key]) + '\n';
+        if (!data || !Object.prototype.hasOwnProperty.call(data, key)) {
+            output += "-" + spaces + contentToDiffLine(key, entry[key]) + "\n";
             continue;
         }
         const value = data[key];
         if (value !== entry[key]) {
-            output += '-' + spaces + contentToDiffLine(key, entry[key]) + '\n';
-            output += '+' + spaces + contentToDiffLine(key, value) + '\n';
+            output += "-" + spaces + contentToDiffLine(key, entry[key]) + "\n";
+            output += "+" + spaces + contentToDiffLine(key, value) + "\n";
         } else {
-            output += spaces + contentToDiffLine(key, value) + '\n';
+            output += spaces + contentToDiffLine(key, value) + "\n";
         }
     }
-    return output + ' }';
+    return output + " }";
 }
 
 function lookForEntry(entry, data) {
     return data.findIndex(data_entry => {
         let allGood = true;
         for (const key in entry) {
-            if (!entry.hasOwnProperty(key)) {
+            if (!Object.prototype.hasOwnProperty.call(entry, key)) {
                 continue;
             }
             let value = data_entry[key];
             // To make our life easier, if there is a "parent" type, we add it to the path.
-            if (key === 'path' && data_entry['parent'] !== undefined) {
+            if (key === "path" && data_entry["parent"] !== undefined) {
                 if (value.length > 0) {
-                    value += '::' + data_entry['parent']['name'];
+                    value += "::" + data_entry["parent"]["name"];
                 } else {
-                    value = data_entry['parent']['name'];
+                    value = data_entry["parent"]["name"];
                 }
             }
             if (value !== entry[key]) {
@@ -95,7 +95,7 @@ function checkNeededFields(fullPath, expected, error_text, queryName, position)
         fieldsToCheck = [];
     }
     for (const field of fieldsToCheck) {
-        if (!expected.hasOwnProperty(field)) {
+        if (!Object.prototype.hasOwnProperty.call(expected, field)) {
             let text = `${queryName}==> Mandatory key \`${field}\` is not present`;
             if (fullPath.length > 0) {
                 text += ` in field \`${fullPath}\``;
@@ -117,22 +117,22 @@ function valueCheck(fullPath, expected, result, error_text, queryName) {
                 error_text.push(`${queryName}==> EXPECTED has extra value in array from field ` +
                     `\`${fullPath}\` (position ${i}): \`${JSON.stringify(expected[i])}\``);
             } else {
-                valueCheck(fullPath + '[' + i + ']', expected[i], result[i], error_text, queryName);
+                valueCheck(fullPath + "[" + i + "]", expected[i], result[i], error_text, queryName);
             }
         }
         for (; i < result.length; ++i) {
             error_text.push(`${queryName}==> RESULT has extra value in array from field ` +
                 `\`${fullPath}\` (position ${i}): \`${JSON.stringify(result[i])}\` ` +
-                'compared to EXPECTED');
+                "compared to EXPECTED");
         }
     } else if (expected !== null && typeof expected !== "undefined" &&
-               expected.constructor == Object) {
+               expected.constructor == Object) { // eslint-disable-line eqeqeq
         for (const key in expected) {
-            if (!expected.hasOwnProperty(key)) {
+            if (!Object.prototype.hasOwnProperty.call(expected, key)) {
                 continue;
             }
-            if (!result.hasOwnProperty(key)) {
-                error_text.push('==> Unknown key "' + key + '"');
+            if (!Object.prototype.hasOwnProperty.call(result, key)) {
+                error_text.push("==> Unknown key \"" + key + "\"");
                 break;
             }
             let result_v = result[key];
@@ -147,13 +147,13 @@ function valueCheck(fullPath, expected, result, error_text, queryName) {
                 });
                 result_v = result_v.join("");
             }
-            const obj_path = fullPath + (fullPath.length > 0 ? '.' : '') + key;
+            const obj_path = fullPath + (fullPath.length > 0 ? "." : "") + key;
             valueCheck(obj_path, expected[key], result_v, error_text, queryName);
         }
     } else {
         const expectedValue = JSON.stringify(expected);
         const resultValue = JSON.stringify(result);
-        if (expectedValue != resultValue) {
+        if (expectedValue !== resultValue) {
             error_text.push(`${queryName}==> Different values for field \`${fullPath}\`:\n` +
                 `EXPECTED: \`${expectedValue}\`\nRESULT:   \`${resultValue}\``);
         }
@@ -164,7 +164,7 @@ function runParser(query, expected, parseQuery, queryName) {
     const error_text = [];
     checkNeededFields("", expected, error_text, queryName, null);
     if (error_text.length === 0) {
-        valueCheck('', expected, parseQuery(query), error_text, queryName);
+        valueCheck("", expected, parseQuery(query), error_text, queryName);
     }
     return error_text;
 }
@@ -177,16 +177,16 @@ function runSearch(query, expected, doSearch, loadedFile, queryName) {
     const error_text = [];
 
     for (const key in expected) {
-        if (!expected.hasOwnProperty(key)) {
+        if (!Object.prototype.hasOwnProperty.call(expected, key)) {
             continue;
         }
-        if (!results.hasOwnProperty(key)) {
-            error_text.push('==> Unknown key "' + key + '"');
+        if (!Object.prototype.hasOwnProperty.call(results, key)) {
+            error_text.push("==> Unknown key \"" + key + "\"");
             break;
         }
         const entry = expected[key];
 
-        if (exact_check == true && entry.length !== results[key].length) {
+        if (exact_check && entry.length !== results[key].length) {
             error_text.push(queryName + "==> Expected exactly " + entry.length +
                             " results but found " + results[key].length + " in '" + key + "'");
         }
@@ -268,7 +268,7 @@ function runCheck(loadedFile, key, callback) {
 function runChecks(testFile, doSearch, parseQuery) {
     let checkExpected = false;
     let checkParsed = false;
-    let testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;';
+    let testFileContent = readFile(testFile) + "exports.QUERY = QUERY;";
 
     if (testFileContent.indexOf("FILTER_CRATE") !== -1) {
         testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;";
@@ -277,11 +277,11 @@ function runChecks(testFile, doSearch, parseQuery) {
     }
 
     if (testFileContent.indexOf("\nconst EXPECTED") !== -1) {
-        testFileContent += 'exports.EXPECTED = EXPECTED;';
+        testFileContent += "exports.EXPECTED = EXPECTED;";
         checkExpected = true;
     }
     if (testFileContent.indexOf("\nconst PARSED") !== -1) {
-        testFileContent += 'exports.PARSED = PARSED;';
+        testFileContent += "exports.PARSED = PARSED;";
         checkParsed = true;
     }
     if (!checkParsed && !checkExpected) {
@@ -325,7 +325,7 @@ function loadSearchJS(doc_folder, resource_suffix) {
     const searchWords = searchModule.initSearch(searchIndex.searchIndex);
 
     return {
-        doSearch: function (queryStr, filterCrate, currentCrate) {
+        doSearch: function(queryStr, filterCrate, currentCrate) {
             return searchModule.execQuery(searchModule.parseQuery(queryStr), searchWords,
                 filterCrate, currentCrate);
         },
@@ -361,22 +361,24 @@ function parseOptions(args) {
     };
 
     for (let i = 0; i < args.length; ++i) {
-        if (correspondences.hasOwnProperty(args[i])) {
+        const arg = args[i];
+        if (Object.prototype.hasOwnProperty.call(correspondences, arg)) {
             i += 1;
             if (i >= args.length) {
-                console.log("Missing argument after `" + args[i - 1] + "` option.");
+                console.log("Missing argument after `" + arg + "` option.");
                 return null;
             }
-            if (args[i - 1] !== "--test-file") {
-                opts[correspondences[args[i - 1]]] = args[i];
+            const arg_value = args[i];
+            if (arg !== "--test-file") {
+                opts[correspondences[arg]] = arg_value;
             } else {
-                opts[correspondences[args[i - 1]]].push(args[i]);
+                opts[correspondences[arg]].push(arg_value);
             }
-        } else if (args[i] === "--help") {
+        } else if (arg === "--help") {
             showHelp();
             process.exit(0);
         } else {
-            console.log("Unknown option `" + args[i] + "`.");
+            console.log("Unknown option `" + arg + "`.");
             console.log("Use `--help` to see the list of options");
             return null;
         }
@@ -405,17 +407,17 @@ function main(argv) {
     );
     let errors = 0;
 
-    const doSearch = function (queryStr, filterCrate) {
+    const doSearch = function(queryStr, filterCrate) {
         return parseAndSearch.doSearch(queryStr, filterCrate, opts["crate_name"]);
     };
 
     if (opts["test_file"].length !== 0) {
-        opts["test_file"].forEach(function (file) {
+        opts["test_file"].forEach(file => {
             process.stdout.write(`Testing ${file} ... `);
             errors += runChecks(file, doSearch, parseAndSearch.parseQuery);
         });
     } else if (opts["test_folder"].length !== 0) {
-        fs.readdirSync(opts["test_folder"]).forEach(function (file) {
+        fs.readdirSync(opts["test_folder"]).forEach(file => {
             if (!file.endsWith(".js")) {
                 return;
             }
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index 9ecb30529cc..75a4586cb7f 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -171,9 +171,9 @@ fn contains_ignore_directive(can_contain: bool, contents: &str, check: &str) ->
 }
 
 macro_rules! suppressible_tidy_err {
-    ($err:ident, $skip:ident, $msg:expr) => {
+    ($err:ident, $skip:ident, $msg:literal) => {
         if let Directive::Deny = $skip {
-            $err($msg);
+            $err(&format!($msg));
         } else {
             $skip = Directive::Ignore(true);
         }
@@ -300,10 +300,13 @@ pub fn check(path: &Path, bad: &mut bool) {
             contains_ignore_directive(can_contain, &contents, "leading-newlines");
         let mut skip_copyright = contains_ignore_directive(can_contain, &contents, "copyright");
         let mut skip_dbg = contains_ignore_directive(can_contain, &contents, "dbg");
+        let mut skip_odd_backticks =
+            contains_ignore_directive(can_contain, &contents, "odd-backticks");
         let mut leading_new_lines = false;
         let mut trailing_new_lines = 0;
         let mut lines = 0;
         let mut last_safety_comment = false;
+        let mut comment_block: Option<(usize, usize)> = None;
         let is_test = file.components().any(|c| c.as_os_str() == "tests");
         // scanning the whole file for multiple needles at once is more efficient than
         // executing lines times needles separate searches.
@@ -351,7 +354,7 @@ pub fn check(path: &Path, bad: &mut bool) {
                 suppressible_tidy_err!(
                     err,
                     skip_line_length,
-                    &format!("line longer than {max_columns} chars")
+                    "line longer than {max_columns} chars"
                 );
             }
             if !is_style_file && line.contains('\t') {
@@ -415,15 +418,55 @@ pub fn check(path: &Path, bad: &mut bool) {
 
             // For now only enforce in compiler
             let is_compiler = || file.components().any(|c| c.as_os_str() == "compiler");
-            if is_compiler()
-                && line.contains("//")
-                && line
-                    .chars()
-                    .collect::<Vec<_>>()
-                    .windows(4)
-                    .any(|cs| matches!(cs, ['.', ' ', ' ', last] if last.is_alphabetic()))
-            {
-                err(DOUBLE_SPACE_AFTER_DOT)
+
+            if is_compiler() {
+                if line.contains("//")
+                    && line
+                        .chars()
+                        .collect::<Vec<_>>()
+                        .windows(4)
+                        .any(|cs| matches!(cs, ['.', ' ', ' ', last] if last.is_alphabetic()))
+                {
+                    err(DOUBLE_SPACE_AFTER_DOT)
+                }
+
+                if filename.ends_with(".ftl") {
+                    let line_backticks = trimmed.chars().filter(|ch| *ch == '`').count();
+                    if line_backticks % 2 == 1 {
+                        suppressible_tidy_err!(err, skip_odd_backticks, "odd number of backticks");
+                    }
+                } else if trimmed.contains("//") {
+                    let (start_line, mut backtick_count) = comment_block.unwrap_or((i + 1, 0));
+                    let line_backticks = trimmed.chars().filter(|ch| *ch == '`').count();
+                    let comment_text = trimmed.split("//").nth(1).unwrap();
+                    // This check ensures that we don't lint for code that has `//` in a string literal
+                    if line_backticks % 2 == 1 {
+                        backtick_count += comment_text.chars().filter(|ch| *ch == '`').count();
+                    }
+                    comment_block = Some((start_line, backtick_count));
+                } else {
+                    if let Some((start_line, backtick_count)) = comment_block.take() {
+                        if backtick_count % 2 == 1 {
+                            let mut err = |msg: &str| {
+                                tidy_error!(bad, "{}:{start_line}: {msg}", file.display());
+                            };
+                            let block_len = (i + 1) - start_line;
+                            if block_len == 1 {
+                                suppressible_tidy_err!(
+                                    err,
+                                    skip_odd_backticks,
+                                    "comment with odd number of backticks"
+                                );
+                            } else {
+                                suppressible_tidy_err!(
+                                    err,
+                                    skip_odd_backticks,
+                                    "{block_len}-line comment block with odd number of backticks"
+                                );
+                            }
+                        }
+                    }
+                }
             }
         }
         if leading_new_lines {
@@ -441,7 +484,7 @@ pub fn check(path: &Path, bad: &mut bool) {
             n => suppressible_tidy_err!(
                 err,
                 skip_trailing_newlines,
-                &format!("too many trailing newlines ({n})")
+                "too many trailing newlines ({n})"
             ),
         };
         if lines > LINES {
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-1.rs b/tests/ui/dst/issue-90528-unsizing-suggestion-1.rs
new file mode 100644
index 00000000000..52863e22bb6
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-1.rs
@@ -0,0 +1,20 @@
+// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied
+// due to a missed unsizing coercion.
+//
+// This test exercises array literals and a trait implemented on immutable slices.
+
+trait Read {}
+
+impl Read for &[u8] {}
+
+fn wants_read(_: impl Read) {}
+
+fn main() {
+    wants_read([0u8]);
+    //~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied
+    wants_read(&[0u8]);
+    //~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied
+    wants_read(&[0u8][..]);
+    wants_read(&mut [0u8]);
+    //~^ ERROR the trait bound `&mut [u8; 1]: Read` is not satisfied
+}
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-1.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-1.stderr
new file mode 100644
index 00000000000..27ef3fe97a5
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-1.stderr
@@ -0,0 +1,56 @@
+error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-1.rs:13:16
+   |
+LL |     wants_read([0u8]);
+   |     ---------- ^^^^^ the trait `Read` is not implemented for `[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+help: convert the array to a `&[u8]` slice instead
+   |
+LL |     wants_read(&[0u8][..]);
+   |                +     ++++
+
+error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-1.rs:15:16
+   |
+LL |     wants_read(&[0u8]);
+   |     ---------- ^^^^^^ the trait `Read` is not implemented for `&[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+help: convert the array to a `&[u8]` slice instead
+   |
+LL |     wants_read(&[0u8][..]);
+   |                      ++++
+
+error[E0277]: the trait bound `&mut [u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-1.rs:18:16
+   |
+LL |     wants_read(&mut [0u8]);
+   |     ---------- ^^^^^^^^^^ the trait `Read` is not implemented for `&mut [u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-1.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-2.rs b/tests/ui/dst/issue-90528-unsizing-suggestion-2.rs
new file mode 100644
index 00000000000..f2762ad421b
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-2.rs
@@ -0,0 +1,28 @@
+// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied
+// due to a missed unsizing coercion.
+//
+// This test exercises array variables and a trait implemented on immmutable slices.
+
+trait Read {}
+
+impl Read for &[u8] {}
+
+fn wants_read(_: impl Read) {}
+
+fn main() {
+    let x = [0u8];
+    wants_read(x);
+    //~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied
+    wants_read(&x);
+    //~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied
+    wants_read(&x[..]);
+
+    let x = &[0u8];
+    wants_read(x);
+    //~^ ERROR the trait bound `&[u8; 1]: Read` is not satisfied
+    wants_read(&x);
+    //~^ ERROR the trait bound `&&[u8; 1]: Read` is not satisfied
+    wants_read(*x);
+    //~^ ERROR the trait bound `[u8; 1]: Read` is not satisfied
+    wants_read(&x[..]);
+}
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-2.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-2.stderr
new file mode 100644
index 00000000000..ae0c4ca506a
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-2.stderr
@@ -0,0 +1,94 @@
+error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:14:16
+   |
+LL |     wants_read(x);
+   |     ---------- ^ the trait `Read` is not implemented for `[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+help: convert the array to a `&[u8]` slice instead
+   |
+LL |     wants_read(&x[..]);
+   |                + ++++
+
+error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:16:16
+   |
+LL |     wants_read(&x);
+   |     ---------- ^^ the trait `Read` is not implemented for `&[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+help: convert the array to a `&[u8]` slice instead
+   |
+LL |     wants_read(&x[..]);
+   |                  ++++
+
+error[E0277]: the trait bound `&[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:21:16
+   |
+LL |     wants_read(x);
+   |     ---------- ^ the trait `Read` is not implemented for `&[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+help: convert the array to a `&[u8]` slice instead
+   |
+LL |     wants_read(&x[..]);
+   |                + ++++
+
+error[E0277]: the trait bound `&&[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:23:16
+   |
+LL |     wants_read(&x);
+   |     ---------- ^^ the trait `Read` is not implemented for `&&[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+
+error[E0277]: the trait bound `[u8; 1]: Read` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:25:16
+   |
+LL |     wants_read(*x);
+   |     ---------- ^^ the trait `Read` is not implemented for `[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Read` is implemented for `&[u8]`
+note: required by a bound in `wants_read`
+  --> $DIR/issue-90528-unsizing-suggestion-2.rs:10:23
+   |
+LL | fn wants_read(_: impl Read) {}
+   |                       ^^^^ required by this bound in `wants_read`
+help: convert the array to a `&[u8]` slice instead
+   |
+LL |     wants_read(&*x[..]);
+   |                +  ++++
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-3.rs b/tests/ui/dst/issue-90528-unsizing-suggestion-3.rs
new file mode 100644
index 00000000000..218843d0500
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-3.rs
@@ -0,0 +1,22 @@
+// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied
+// due to a missed unsizing coercion.
+//
+// This test exercises array literals and a trait implemented on mutable slices.
+
+trait Write {}
+
+impl Write for &mut [u8] {}
+
+fn wants_write(_: impl Write) {}
+
+fn main() {
+    wants_write([0u8]);
+    //~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied
+    wants_write(&mut [0u8]);
+    //~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied
+    wants_write(&mut [0u8][..]);
+    wants_write(&[0u8]);
+    //~^ ERROR the trait bound `&[u8; 1]: Write` is not satisfied
+    wants_write(&[0u8][..]);
+    //~^ ERROR the trait bound `&[u8]: Write` is not satisfied
+}
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr
new file mode 100644
index 00000000000..774d5ba3c89
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-3.stderr
@@ -0,0 +1,75 @@
+error[E0277]: the trait bound `[u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:13:17
+   |
+LL |     wants_write([0u8]);
+   |     ----------- ^^^^^ the trait `Write` is not implemented for `[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: convert the array to a `&mut [u8]` slice instead
+   |
+LL |     wants_write(&mut [0u8][..]);
+   |                 ++++      ++++
+
+error[E0277]: the trait bound `&mut [u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:15:17
+   |
+LL |     wants_write(&mut [0u8]);
+   |     ----------- ^^^^^^^^^^ the trait `Write` is not implemented for `&mut [u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: convert the array to a `&mut [u8]` slice instead
+   |
+LL |     wants_write(&mut [0u8][..]);
+   |                           ++++
+
+error[E0277]: the trait bound `&[u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:18:17
+   |
+LL |     wants_write(&[0u8]);
+   |     ----------- ^^^^^^ the trait `Write` is not implemented for `&[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+
+error[E0277]: the trait bound `&[u8]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:20:17
+   |
+LL |     wants_write(&[0u8][..]);
+   |     ----------- ^^^^^^^^^^ the trait `Write` is not implemented for `&[u8]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-3.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: consider changing this borrow's mutability
+   |
+LL |     wants_write(&mut [0u8][..]);
+   |                 ~~~~
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-4.rs b/tests/ui/dst/issue-90528-unsizing-suggestion-4.rs
new file mode 100644
index 00000000000..eae953c61ff
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-4.rs
@@ -0,0 +1,26 @@
+// Issue #90528: provide helpful suggestions when a trait bound is unsatisfied
+// due to a missed unsizing coercion.
+//
+// This test exercises array variables and a trait implemented on mutable slices.
+
+trait Write {}
+
+impl Write for &mut [u8] {}
+
+fn wants_write(_: impl Write) {}
+
+fn main() {
+    let mut x = [0u8];
+    wants_write(x);
+    //~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied
+    wants_write(&mut x);
+    //~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied
+    wants_write(&mut x[..]);
+
+    let x = &mut [0u8];
+    wants_write(x);
+    //~^ ERROR the trait bound `&mut [u8; 1]: Write` is not satisfied
+    wants_write(*x);
+    //~^ ERROR the trait bound `[u8; 1]: Write` is not satisfied
+    wants_write(&mut x[..]);
+}
diff --git a/tests/ui/dst/issue-90528-unsizing-suggestion-4.stderr b/tests/ui/dst/issue-90528-unsizing-suggestion-4.stderr
new file mode 100644
index 00000000000..a4020ee0708
--- /dev/null
+++ b/tests/ui/dst/issue-90528-unsizing-suggestion-4.stderr
@@ -0,0 +1,79 @@
+error[E0277]: the trait bound `[u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:14:17
+   |
+LL |     wants_write(x);
+   |     ----------- ^ the trait `Write` is not implemented for `[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: convert the array to a `&mut [u8]` slice instead
+   |
+LL |     wants_write(&mut x[..]);
+   |                 ++++  ++++
+
+error[E0277]: the trait bound `&mut [u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:16:17
+   |
+LL |     wants_write(&mut x);
+   |     ----------- ^^^^^^ the trait `Write` is not implemented for `&mut [u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: convert the array to a `&mut [u8]` slice instead
+   |
+LL |     wants_write(&mut x[..]);
+   |                       ++++
+
+error[E0277]: the trait bound `&mut [u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:21:17
+   |
+LL |     wants_write(x);
+   |     ----------- ^ the trait `Write` is not implemented for `&mut [u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: convert the array to a `&mut [u8]` slice instead
+   |
+LL |     wants_write(&mut x[..]);
+   |                 ++++  ++++
+
+error[E0277]: the trait bound `[u8; 1]: Write` is not satisfied
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:23:17
+   |
+LL |     wants_write(*x);
+   |     ----------- ^^ the trait `Write` is not implemented for `[u8; 1]`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Write` is implemented for `&mut [u8]`
+note: required by a bound in `wants_write`
+  --> $DIR/issue-90528-unsizing-suggestion-4.rs:10:24
+   |
+LL | fn wants_write(_: impl Write) {}
+   |                        ^^^^^ required by this bound in `wants_write`
+help: convert the array to a `&mut [u8]` slice instead
+   |
+LL |     wants_write(&mut *x[..]);
+   |                 ++++   ++++
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs b/tests/ui/traits/new-solver/cast-checks-handling-projections.rs
index 3b261062f78..3b261062f78 100644
--- a/tests/ui/typeck/lazy-norm/cast-checks-handling-projections.rs
+++ b/tests/ui/traits/new-solver/cast-checks-handling-projections.rs
diff --git a/tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs b/tests/ui/traits/new-solver/equating-projection-cyclically.rs
index 019c6e81c50..019c6e81c50 100644
--- a/tests/ui/typeck/lazy-norm/equating-projection-cyclically.rs
+++ b/tests/ui/traits/new-solver/equating-projection-cyclically.rs
diff --git a/tests/ui/typeck/lazy-norm/equating-projection-cyclically.stderr b/tests/ui/traits/new-solver/equating-projection-cyclically.stderr
index 57cbc65a17a..57cbc65a17a 100644
--- a/tests/ui/typeck/lazy-norm/equating-projection-cyclically.stderr
+++ b/tests/ui/traits/new-solver/equating-projection-cyclically.stderr
diff --git a/tests/ui/traits/new-solver/lazy-nested-obligations-1.rs b/tests/ui/traits/new-solver/lazy-nested-obligations-1.rs
new file mode 100644
index 00000000000..af00cbb3ba8
--- /dev/null
+++ b/tests/ui/traits/new-solver/lazy-nested-obligations-1.rs
@@ -0,0 +1,13 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+// Issue 94358
+
+fn foo<C>(_: C)
+where
+    for <'a> &'a C: IntoIterator,
+    for <'a> <&'a C as IntoIterator>::IntoIter: ExactSizeIterator,
+{}
+
+fn main() {
+    foo::<_>(vec![true, false]);
+}
diff --git a/tests/ui/traits/new-solver/lazy-nested-obligations-2.rs b/tests/ui/traits/new-solver/lazy-nested-obligations-2.rs
new file mode 100644
index 00000000000..32addd829dc
--- /dev/null
+++ b/tests/ui/traits/new-solver/lazy-nested-obligations-2.rs
@@ -0,0 +1,23 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+// Issue 95863
+
+pub trait With {
+    type F;
+}
+
+impl With for i32 {
+    type F = fn(&str);
+}
+
+fn f(_: &str) {}
+
+fn main() {
+    let _: V<i32> = V(f);
+    pub struct V<T: With>(<T as With>::F);
+
+    pub enum E3<T: With> {
+        Var(<T as With>::F),
+    }
+    let _: E3<i32> = E3::Var(f);
+}
diff --git a/tests/ui/traits/new-solver/lazy-nested-obligations-3.rs b/tests/ui/traits/new-solver/lazy-nested-obligations-3.rs
new file mode 100644
index 00000000000..baf39957240
--- /dev/null
+++ b/tests/ui/traits/new-solver/lazy-nested-obligations-3.rs
@@ -0,0 +1,38 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+// Issue 96750
+
+use std::marker::PhantomData;
+
+trait AsyncFn<Arg> {
+    type Output;
+}
+trait RequestFamily {
+    type Type<'a>;
+}
+trait Service {}
+
+struct MyFn;
+impl AsyncFn<String> for MyFn {
+    type Output = ();
+}
+
+impl RequestFamily for String {
+    type Type<'a> = String;
+}
+
+struct ServiceFromAsyncFn<F, Req>(F, PhantomData<Req>);
+
+impl<F, Req, O> Service for ServiceFromAsyncFn<F, Req>
+where
+    Req: RequestFamily,
+    F: AsyncFn<Req>,
+    F: for<'a> AsyncFn<Req::Type<'a>, Output = O>,
+{
+}
+
+fn assert_service() -> impl Service {
+    ServiceFromAsyncFn(MyFn, PhantomData)
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/normalize-param-env-1.rs b/tests/ui/traits/new-solver/normalize-param-env-1.rs
new file mode 100644
index 00000000000..b02a5d62330
--- /dev/null
+++ b/tests/ui/traits/new-solver/normalize-param-env-1.rs
@@ -0,0 +1,40 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+// Issue 108933
+
+trait Add<Rhs> {
+    type Sum;
+}
+
+impl Add<()> for () {
+    type Sum = ();
+}
+
+type Unit = <() as Add<()>>::Sum;
+
+trait Trait<C> {
+    type Output;
+}
+
+fn f<T>()
+where
+    T: Trait<()>,
+    <T as Trait<()>>::Output: Sized,
+{
+}
+
+fn g<T>()
+where
+    T: Trait<Unit>,
+    <T as Trait<()>>::Output: Sized,
+{
+}
+
+fn h<T>()
+where
+    T: Trait<()>,
+    <T as Trait<Unit>>::Output: Sized,
+{
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/normalize-param-env-2.rs b/tests/ui/traits/new-solver/normalize-param-env-2.rs
new file mode 100644
index 00000000000..7c2cebdd200
--- /dev/null
+++ b/tests/ui/traits/new-solver/normalize-param-env-2.rs
@@ -0,0 +1,26 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+// Issue 92505
+
+trait A<T> {
+    type I;
+
+    fn f()
+    where
+        Self::I: A<T>,
+    {
+    }
+}
+
+impl<T> A<T> for () {
+    type I = ();
+
+    fn f()
+    where
+        Self::I: A<T>,
+    {
+        <() as A<T>>::f();
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/normalize-param-env-3.rs b/tests/ui/traits/new-solver/normalize-param-env-3.rs
new file mode 100644
index 00000000000..ce2974b2a16
--- /dev/null
+++ b/tests/ui/traits/new-solver/normalize-param-env-3.rs
@@ -0,0 +1,32 @@
+// check-pass
+// compile-flags: -Ztrait-solver=next
+// Issue 100177
+
+trait GenericTrait<T> {}
+
+trait Channel<I>: GenericTrait<Self::T> {
+    type T;
+}
+
+trait Sender {
+    type Msg;
+
+    fn send<C>()
+    where
+        C: Channel<Self::Msg>;
+}
+
+impl<T> Sender for T {
+    type Msg = ();
+
+    fn send<C>()
+    where
+        C: Channel<Self::Msg>,
+    {
+    }
+}
+
+// This works
+fn foo<I, C>(ch: C) where C: Channel<I> {}
+
+fn main() {}