about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-02-11 09:19:07 +0000
committerbors <bors@rust-lang.org>2023-02-11 09:19:07 +0000
commit71f6675de1faa2a8e897a8faaa861a0157b0e355 (patch)
tree39c775331357fa66cb00d91c4a8e9acd89062c4f
parent1623ab0246deebec4fe32dc525d20bf8a88096f2 (diff)
parenta50c379fcd8d0a4a9bc5ee1a837c299d3e5206f8 (diff)
downloadrust-71f6675de1faa2a8e897a8faaa861a0157b0e355.tar.gz
rust-71f6675de1faa2a8e897a8faaa861a0157b0e355.zip
Auto merge of #107919 - Dylan-DPC:rollup-fkl9swa, r=Dylan-DPC
Rollup of 9 pull requests

Successful merges:

 - #105019 (Add parentheses properly for borrowing suggestion)
 - #106001 (Stop at the first `NULL` argument when iterating `argv`)
 - #107098 (Suggest function call on pattern type mismatch)
 - #107490 (rustdoc: remove inconsistently-present sidebar tooltips)
 - #107855 (Add a couple random projection tests for new solver)
 - #107857 (Add ui test for implementation on projection)
 - #107878 (Clarify `new_size` for realloc means bytes)
 - #107888 (revert #107074, add regression test)
 - #107900 (Zero the `REPARSE_MOUNTPOINT_DATA_BUFFER` header)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs19
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs42
-rw-r--r--library/core/src/alloc/global.rs9
-rw-r--r--library/std/src/sys/unix/args.rs28
-rw-r--r--library/std/src/sys/windows/fs.rs2
-rw-r--r--src/librustdoc/html/render/context.rs9
-rw-r--r--src/librustdoc/html/render/mod.rs3
-rw-r--r--src/librustdoc/html/static/js/main.js6
-rw-r--r--tests/rustdoc/markdown-summaries.rs27
-rw-r--r--tests/ui/const-generics/wrong-normalization.rs19
-rw-r--r--tests/ui/const-generics/wrong-normalization.stderr11
-rw-r--r--tests/ui/impl-trait/nested-return-type2.rs3
-rw-r--r--tests/ui/impl-trait/nested-return-type2.stderr2
-rw-r--r--tests/ui/suggestions/issue-104961.fixed16
-rw-r--r--tests/ui/suggestions/issue-104961.rs16
-rw-r--r--tests/ui/suggestions/issue-104961.stderr37
-rw-r--r--tests/ui/suggestions/suggest-call-on-pat-mismatch.rs16
-rw-r--r--tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr33
-rw-r--r--tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs25
-rw-r--r--tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs30
-rw-r--r--tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr16
25 files changed, 320 insertions, 70 deletions
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 76b316cdf0c..56c60d59d28 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -13,7 +13,7 @@ use rustc_middle::mir::{
     RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind, Terminator,
     TerminatorKind, UnOp, START_BLOCK,
 };
-use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt};
+use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitable};
 use rustc_mir_dataflow::impls::MaybeStorageLive;
 use rustc_mir_dataflow::storage::always_storage_live_locals;
 use rustc_mir_dataflow::{Analysis, ResultsCursor};
@@ -231,6 +231,15 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             return true;
         }
 
+        // We sometimes have to use `defining_opaque_types` for subtyping
+        // to succeed here and figuring out how exactly that should work
+        // is annoying. It is harmless enough to just not validate anything
+        // in that case. We still check this after analysis as all opque
+        // types have been revealed at this point.
+        if (src, dest).has_opaque_types() {
+            return true;
+        }
+
         crate::util::is_subtype(self.tcx, self.param_env, src, dest)
     }
 }
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index 88fb2653586..e19ef2ff3bf 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -41,7 +41,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // #55810: Type check patterns first so we get types for all bindings.
         let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span);
         for arm in arms {
-            self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), true);
+            self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut));
         }
 
         // Now typecheck the blocks.
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index 1c70c1b71e7..cc515e6c853 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -90,7 +90,7 @@ pub(super) fn check_fn<'a, 'tcx>(
     for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
         // Check the pattern.
         let ty_span = try { inputs_hir?.get(idx)?.span };
-        fcx.check_pat_top(&param.pat, param_ty, ty_span, false);
+        fcx.check_pat_top(&param.pat, param_ty, ty_span, None);
 
         // Check that argument is Sized.
         // The check for a non-trivial pattern is a hack to avoid duplicate warnings
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 2a1265600de..9c7a84ce198 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1330,11 +1330,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Does the expected pattern type originate from an expression and what is the span?
         let (origin_expr, ty_span) = match (decl.ty, decl.init) {
-            (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type.
+            (Some(ty), _) => (None, Some(ty.span)), // Bias towards the explicit user type.
             (_, Some(init)) => {
-                (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span)))
+                (Some(init), Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span)))
             } // No explicit type; so use the scrutinee.
-            _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained.
+            _ => (None, None), // We have `let $pat;`, so the expected type is unconstrained.
         };
 
         // Type check the pattern. Override if necessary to avoid knock-on errors.
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 46799245222..52236ae56ee 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -46,7 +46,7 @@ struct TopInfo<'tcx> {
     /// Was the origin of the `span` from a scrutinee expression?
     ///
     /// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter.
-    origin_expr: bool,
+    origin_expr: Option<&'tcx hir::Expr<'tcx>>,
     /// The span giving rise to the `expected` type, if one could be provided.
     ///
     /// If `origin_expr` is `true`, then this is the span of the scrutinee as in:
@@ -74,7 +74,8 @@ struct TopInfo<'tcx> {
 
 impl<'tcx> FnCtxt<'_, 'tcx> {
     fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
-        let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr };
+        let code =
+            Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr.is_some() };
         self.cause(cause_span, code)
     }
 
@@ -85,7 +86,14 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         actual: Ty<'tcx>,
         ti: TopInfo<'tcx>,
     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
-        self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)
+        let mut diag =
+            self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?;
+        if let Some(expr) = ti.origin_expr {
+            self.suggest_fn_call(&mut diag, expr, expected, |output| {
+                self.can_eq(self.param_env, output, actual).is_ok()
+            });
+        }
+        Some(diag)
     }
 
     fn demand_eqtype_pat(
@@ -127,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pat: &'tcx Pat<'tcx>,
         expected: Ty<'tcx>,
         span: Option<Span>,
-        origin_expr: bool,
+        origin_expr: Option<&'tcx hir::Expr<'tcx>>,
     ) {
         let info = TopInfo { expected, origin_expr, span };
         self.check_pat(pat, expected, INITIAL_BM, info);
@@ -2146,7 +2154,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err.help("the semantics of slice patterns changed recently; see issue #62254");
         } else if self.autoderef(span, expected_ty)
             .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
-            && let (Some(span), true) = (ti.span, ti.origin_expr)
+            && let Some(span) = ti.span
+            && let Some(_) = ti.origin_expr
             && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
         {
             let ty = self.resolve_vars_if_possible(ti.expected);
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 2b543520198..048a5471994 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -19,6 +19,7 @@ use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
+use rustc_hir::is_range_literal;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
 use rustc_hir::{Expr, HirId};
@@ -1349,14 +1350,41 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             Applicability::MaybeIncorrect,
                         );
                     } else {
+                        // Issue #104961, we need to add parentheses properly for compond expressions
+                        // for example, `x.starts_with("hi".to_string() + "you")`
+                        // should be `x.starts_with(&("hi".to_string() + "you"))`
+                        let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return false; };
+                        let body = self.tcx.hir().body(body_id);
+                        let mut expr_finder = FindExprBySpan::new(span);
+                        expr_finder.visit_expr(body.value);
+                        let Some(expr) = expr_finder.result else { return false; };
+                        let needs_parens = match expr.kind {
+                            // parenthesize if needed (Issue #46756)
+                            hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
+                            // parenthesize borrows of range literals (Issue #54505)
+                            _ if is_range_literal(expr) => true,
+                            _ => false,
+                        };
+
                         let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut;
-                        err.span_suggestion_verbose(
-                            span.shrink_to_lo(),
-                            &format!(
-                                "consider{} borrowing here",
-                                if is_mut { " mutably" } else { "" }
-                            ),
-                            format!("&{}", if is_mut { "mut " } else { "" }),
+                        let span = if needs_parens { span } else { span.shrink_to_lo() };
+                        let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" });
+                        let sugg_msg = &format!(
+                            "consider{} borrowing here",
+                            if is_mut { " mutably" } else { "" }
+                        );
+
+                        let suggestions = if !needs_parens {
+                            vec![(span.shrink_to_lo(), format!("{}", sugg_prefix))]
+                        } else {
+                            vec![
+                                (span.shrink_to_lo(), format!("{}(", sugg_prefix)),
+                                (span.shrink_to_hi(), ")".to_string()),
+                            ]
+                        };
+                        err.multipart_suggestion_verbose(
+                            sugg_msg,
+                            suggestions,
                             Applicability::MaybeIncorrect,
                         );
                     }
diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs
index 1d80b8bf9ec..18da70451f2 100644
--- a/library/core/src/alloc/global.rs
+++ b/library/core/src/alloc/global.rs
@@ -203,7 +203,7 @@ pub unsafe trait GlobalAlloc {
         ptr
     }
 
-    /// Shrink or grow a block of memory to the given `new_size`.
+    /// Shrink or grow a block of memory to the given `new_size` in bytes.
     /// The block is described by the given `ptr` pointer and `layout`.
     ///
     /// If this returns a non-null pointer, then ownership of the memory block
@@ -211,10 +211,11 @@ pub unsafe trait GlobalAlloc {
     /// Any access to the old `ptr` is Undefined Behavior, even if the
     /// allocation remained in-place. The newly returned pointer is the only valid pointer
     /// for accessing this memory now.
+    ///
     /// The new memory block is allocated with `layout`,
-    /// but with the `size` updated to `new_size`. This new layout must be
-    /// used when deallocating the new memory block with `dealloc`. The range
-    /// `0..min(layout.size(), new_size)` of the new memory block is
+    /// but with the `size` updated to `new_size` in bytes.
+    /// This new layout must be used when deallocating the new memory block with `dealloc`.
+    /// The range `0..min(layout.size(), new_size)` of the new memory block is
     /// guaranteed to have the same values as the original block.
     ///
     /// If this method returns null, then ownership of the memory
diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs
index a342f0f5e85..a5ce6d5120d 100644
--- a/library/std/src/sys/unix/args.rs
+++ b/library/std/src/sys/unix/args.rs
@@ -141,12 +141,28 @@ mod imp {
             // list.
             let argv = ARGV.load(Ordering::Relaxed);
             let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) };
-            (0..argc)
-                .map(|i| {
-                    let cstr = CStr::from_ptr(*argv.offset(i) as *const libc::c_char);
-                    OsStringExt::from_vec(cstr.to_bytes().to_vec())
-                })
-                .collect()
+            let mut args = Vec::with_capacity(argc as usize);
+            for i in 0..argc {
+                let ptr = *argv.offset(i) as *const libc::c_char;
+
+                // Some C commandline parsers (e.g. GLib and Qt) are replacing already
+                // handled arguments in `argv` with `NULL` and move them to the end. That
+                // means that `argc` might be bigger than the actual number of non-`NULL`
+                // pointers in `argv` at this point.
+                //
+                // To handle this we simply stop iterating at the first `NULL` argument.
+                //
+                // `argv` is also guaranteed to be `NULL`-terminated so any non-`NULL` arguments
+                // after the first `NULL` can safely be ignored.
+                if ptr.is_null() {
+                    break;
+                }
+
+                let cstr = CStr::from_ptr(ptr);
+                args.push(OsStringExt::from_vec(cstr.to_bytes().to_vec()));
+            }
+
+            args
         }
     }
 }
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index 37809803828..f1a784b5fd2 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -1393,6 +1393,8 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
         let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
         let data_ptr = data.0.as_mut_ptr();
         let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
+        // Zero the header to ensure it's fully initialized, including reserved parameters.
+        *db = mem::zeroed();
         let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
         let mut i = 0;
         // FIXME: this conversion is very hacky
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index b59645ec2e2..1216a8d71c8 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -18,7 +18,7 @@ use super::search_index::build_index;
 use super::write_shared::write_shared;
 use super::{
     collect_spans_and_sources, print_sidebar, scrape_examples_help, sidebar_module_like, AllTypes,
-    LinkFromSrc, NameDoc, StylePath,
+    LinkFromSrc, StylePath,
 };
 
 use crate::clean::{self, types::ExternalLocation, ExternalCrate};
@@ -256,7 +256,7 @@ impl<'tcx> Context<'tcx> {
     }
 
     /// Construct a map of items shown in the sidebar to a plain-text summary of their docs.
-    fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<NameDoc>> {
+    fn build_sidebar_items(&self, m: &clean::Module) -> BTreeMap<String, Vec<String>> {
         // BTreeMap instead of HashMap to get a sorted output
         let mut map: BTreeMap<_, Vec<_>> = BTreeMap::new();
         let mut inserted: FxHashMap<ItemType, FxHashSet<Symbol>> = FxHashMap::default();
@@ -274,10 +274,7 @@ impl<'tcx> Context<'tcx> {
             if inserted.entry(short).or_default().insert(myname) {
                 let short = short.to_string();
                 let myname = myname.to_string();
-                map.entry(short).or_default().push((
-                    myname,
-                    Some(item.doc_value().map_or_else(String::new, |s| plain_text_summary(&s))),
-                ));
+                map.entry(short).or_default().push(myname);
             }
         }
 
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index fa22c461205..6a9a7a6b1a0 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -83,9 +83,6 @@ use crate::scrape_examples::{CallData, CallLocation};
 use crate::try_none;
 use crate::DOC_RUST_LANG_ORG_CHANNEL;
 
-/// A pair of name and its optional document.
-pub(crate) type NameDoc = (String, Option<String>);
-
 pub(crate) fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
     crate::html::format::display_fn(move |f| {
         if !v.ends_with('/') && !v.is_empty() { write!(f, "{}/", v) } else { f.write_str(v) }
diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js
index c66f500f423..aa4d65b4bfb 100644
--- a/src/librustdoc/html/static/js/main.js
+++ b/src/librustdoc/html/static/js/main.js
@@ -455,10 +455,7 @@ function loadCss(cssUrl) {
             const ul = document.createElement("ul");
             ul.className = "block " + shortty;
 
-            for (const item of filtered) {
-                const name = item[0];
-                const desc = item[1]; // can be null
-
+            for (const name of filtered) {
                 let path;
                 if (shortty === "mod") {
                     path = name + "/index.html";
@@ -468,7 +465,6 @@ function loadCss(cssUrl) {
                 const current_page = document.location.href.split("/").pop();
                 const link = document.createElement("a");
                 link.href = path;
-                link.title = desc;
                 if (path === current_page) {
                     link.className = "current";
                 }
diff --git a/tests/rustdoc/markdown-summaries.rs b/tests/rustdoc/markdown-summaries.rs
deleted file mode 100644
index 31e7072b5ce..00000000000
--- a/tests/rustdoc/markdown-summaries.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-#![crate_type = "lib"]
-#![crate_name = "summaries"]
-
-//! This *summary* has a [link] and `code`.
-//!
-//! This is the second paragraph.
-//!
-//! [link]: https://example.com
-
-// @hasraw search-index.js 'This <em>summary</em> has a link and <code>code</code>.'
-// @!hasraw - 'second paragraph'
-
-/// This `code` will be rendered in a code tag.
-///
-/// This text should not be rendered.
-pub struct Sidebar;
-
-// @hasraw search-index.js 'This <code>code</code> will be rendered in a code tag.'
-// @hasraw summaries/sidebar-items.js 'This `code` will be rendered in a code tag.'
-// @!hasraw - 'text should not be rendered'
-
-/// ```text
-/// this block should not be rendered
-/// ```
-pub struct Sidebar2;
-
-// @!hasraw summaries/sidebar-items.js 'block should not be rendered'
diff --git a/tests/ui/const-generics/wrong-normalization.rs b/tests/ui/const-generics/wrong-normalization.rs
new file mode 100644
index 00000000000..f1ce317b3f7
--- /dev/null
+++ b/tests/ui/const-generics/wrong-normalization.rs
@@ -0,0 +1,19 @@
+// This test ensures that if implementation on projections is supported,
+// it doesn't end in very weird cycle error.
+
+#![crate_type = "lib"]
+
+pub trait Identity {
+    type Identity: ?Sized;
+}
+
+impl<T: ?Sized> Identity for T {
+    type Identity = Self;
+}
+
+pub struct I8<const F: i8>;
+
+impl <I8<{i8::MIN}> as Identity>::Identity {
+//~^ ERROR no nominal type found for inherent implementation
+    pub fn foo(&self) {}
+}
diff --git a/tests/ui/const-generics/wrong-normalization.stderr b/tests/ui/const-generics/wrong-normalization.stderr
new file mode 100644
index 00000000000..fb806bdb1e7
--- /dev/null
+++ b/tests/ui/const-generics/wrong-normalization.stderr
@@ -0,0 +1,11 @@
+error[E0118]: no nominal type found for inherent implementation
+  --> $DIR/wrong-normalization.rs:16:6
+   |
+LL | impl <I8<{i8::MIN}> as Identity>::Identity {
+   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl requires a nominal type
+   |
+   = note: either implement a trait on it or create a newtype to wrap it instead
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0118`.
diff --git a/tests/ui/impl-trait/nested-return-type2.rs b/tests/ui/impl-trait/nested-return-type2.rs
index cc1f1f4ec44..fe883ce6fc8 100644
--- a/tests/ui/impl-trait/nested-return-type2.rs
+++ b/tests/ui/impl-trait/nested-return-type2.rs
@@ -1,4 +1,7 @@
 // check-pass
+// compile-flags: -Zvalidate-mir
+
+// Using -Zvalidate-mir as a regression test for #107346.
 
 trait Duh {}
 
diff --git a/tests/ui/impl-trait/nested-return-type2.stderr b/tests/ui/impl-trait/nested-return-type2.stderr
index 3aed05ca132..09ad3bd05c1 100644
--- a/tests/ui/impl-trait/nested-return-type2.stderr
+++ b/tests/ui/impl-trait/nested-return-type2.stderr
@@ -1,5 +1,5 @@
 warning: opaque type `impl Trait<Assoc = impl Send>` does not satisfy its associated type bounds
-  --> $DIR/nested-return-type2.rs:25:24
+  --> $DIR/nested-return-type2.rs:28:24
    |
 LL |     type Assoc: Duh;
    |                 --- this associated type bound is unsatisfied for `impl Send`
diff --git a/tests/ui/suggestions/issue-104961.fixed b/tests/ui/suggestions/issue-104961.fixed
new file mode 100644
index 00000000000..520d638b174
--- /dev/null
+++ b/tests/ui/suggestions/issue-104961.fixed
@@ -0,0 +1,16 @@
+// run-rustfix
+
+fn foo(x: &str) -> bool {
+    x.starts_with(&("hi".to_string() + " you"))
+    //~^ ERROR expected a `FnMut<(char,)>` closure, found `String`
+}
+
+fn foo2(x: &str) -> bool {
+    x.starts_with(&"hi".to_string())
+    //~^ ERROR expected a `FnMut<(char,)>` closure, found `String`
+}
+
+fn main() {
+    foo("hi you");
+    foo2("hi");
+}
diff --git a/tests/ui/suggestions/issue-104961.rs b/tests/ui/suggestions/issue-104961.rs
new file mode 100644
index 00000000000..aeb787abb6f
--- /dev/null
+++ b/tests/ui/suggestions/issue-104961.rs
@@ -0,0 +1,16 @@
+// run-rustfix
+
+fn foo(x: &str) -> bool {
+    x.starts_with("hi".to_string() + " you")
+    //~^ ERROR expected a `FnMut<(char,)>` closure, found `String`
+}
+
+fn foo2(x: &str) -> bool {
+    x.starts_with("hi".to_string())
+    //~^ ERROR expected a `FnMut<(char,)>` closure, found `String`
+}
+
+fn main() {
+    foo("hi you");
+    foo2("hi");
+}
diff --git a/tests/ui/suggestions/issue-104961.stderr b/tests/ui/suggestions/issue-104961.stderr
new file mode 100644
index 00000000000..8cec6a3f827
--- /dev/null
+++ b/tests/ui/suggestions/issue-104961.stderr
@@ -0,0 +1,37 @@
+error[E0277]: expected a `FnMut<(char,)>` closure, found `String`
+  --> $DIR/issue-104961.rs:4:19
+   |
+LL |     x.starts_with("hi".to_string() + " you")
+   |       ----------- ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Pattern<'_>` is not implemented for `String`
+   |       |
+   |       required by a bound introduced by this call
+   |
+   = note: the trait bound `String: Pattern<'_>` is not satisfied
+   = note: required for `String` to implement `Pattern<'_>`
+note: required by a bound in `core::str::<impl str>::starts_with`
+  --> $SRC_DIR/core/src/str/mod.rs:LL:COL
+help: consider borrowing here
+   |
+LL |     x.starts_with(&("hi".to_string() + " you"))
+   |                   ++                         +
+
+error[E0277]: expected a `FnMut<(char,)>` closure, found `String`
+  --> $DIR/issue-104961.rs:9:19
+   |
+LL |     x.starts_with("hi".to_string())
+   |       ----------- ^^^^^^^^^^^^^^^^ the trait `Pattern<'_>` is not implemented for `String`
+   |       |
+   |       required by a bound introduced by this call
+   |
+   = note: the trait bound `String: Pattern<'_>` is not satisfied
+   = note: required for `String` to implement `Pattern<'_>`
+note: required by a bound in `core::str::<impl str>::starts_with`
+  --> $SRC_DIR/core/src/str/mod.rs:LL:COL
+help: consider borrowing here
+   |
+LL |     x.starts_with(&"hi".to_string())
+   |                   +
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs b/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs
new file mode 100644
index 00000000000..657dd9c22c2
--- /dev/null
+++ b/tests/ui/suggestions/suggest-call-on-pat-mismatch.rs
@@ -0,0 +1,16 @@
+enum E {
+    One(i32, i32),
+}
+
+fn main() {
+    let var = E::One;
+    if let E::One(var1, var2) = var {
+        //~^ ERROR mismatched types
+        //~| HELP use parentheses to construct this tuple variant
+        println!("{var1} {var2}");
+    }
+
+    let Some(x) = Some;
+    //~^ ERROR mismatched types
+    //~| HELP use parentheses to construct this tuple variant
+}
diff --git a/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr b/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr
new file mode 100644
index 00000000000..7338312bab6
--- /dev/null
+++ b/tests/ui/suggestions/suggest-call-on-pat-mismatch.stderr
@@ -0,0 +1,33 @@
+error[E0308]: mismatched types
+  --> $DIR/suggest-call-on-pat-mismatch.rs:7:12
+   |
+LL |     if let E::One(var1, var2) = var {
+   |            ^^^^^^^^^^^^^^^^^^   --- this expression has type `fn(i32, i32) -> E {E::One}`
+   |            |
+   |            expected enum constructor, found `E`
+   |
+   = note: expected enum constructor `fn(i32, i32) -> E {E::One}`
+                          found enum `E`
+help: use parentheses to construct this tuple variant
+   |
+LL |     if let E::One(var1, var2) = var(/* i32 */, /* i32 */) {
+   |                                    ++++++++++++++++++++++
+
+error[E0308]: mismatched types
+  --> $DIR/suggest-call-on-pat-mismatch.rs:13:9
+   |
+LL |     let Some(x) = Some;
+   |         ^^^^^^^   ---- this expression has type `fn(_) -> Option<_> {Option::<_>::Some}`
+   |         |
+   |         expected enum constructor, found `Option<_>`
+   |
+   = note: expected enum constructor `fn(_) -> Option<_> {Option::<_>::Some}`
+                          found enum `Option<_>`
+help: use parentheses to construct this tuple variant
+   |
+LL |     let Some(x) = Some(/* value */);
+   |                       +++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs b/tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs
new file mode 100644
index 00000000000..bdf999ec5dd
--- /dev/null
+++ b/tests/ui/traits/new-solver/param-candidate-doesnt-shadow-project.rs
@@ -0,0 +1,25 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+trait Foo {
+    type Assoc;
+}
+
+trait Bar {}
+
+impl<T> Foo for T {
+    type Assoc = i32;
+}
+
+impl<T> Bar for T where T: Foo<Assoc = i32> {}
+
+fn require_bar<T: Bar>() {}
+
+fn foo<T: Foo>() {
+    // Unlike the classic solver, `<T as Foo>::Assoc = _` will still project
+    // down to `i32` even though there's a param-env candidate here, since we
+    // don't assemble any param-env projection candidates for `T: Foo` alone.
+    require_bar::<T>();
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs
new file mode 100644
index 00000000000..cde2059ca9b
--- /dev/null
+++ b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.rs
@@ -0,0 +1,30 @@
+// compile-flags: -Ztrait-solver=next
+
+// When we're solving `<T as Foo>::Assoc = i32`, we actually first solve
+// `<T as Foo>::Assoc = _#1t`, then unify `_#1t` with `i32`. That goal
+// with the inference variable is ambiguous when there are >1 param-env
+// candidates.
+
+// We don't unify the RHS of a projection goal eagerly when solving, both
+// for caching reasons and partly to make sure that we don't make the new
+// trait solver smarter than it should be.
+
+// This is (as far as I can tell) a forwards-compatible decision, but if you
+// make this test go from fail to pass, be sure you understand the implications!
+
+trait Foo {
+    type Assoc;
+}
+
+trait Bar {}
+
+impl<T> Bar for T where T: Foo<Assoc = i32> {}
+
+fn needs_bar<T: Bar>() {}
+
+fn foo<T: Foo<Assoc = i32> + Foo<Assoc = u32>>() {
+    needs_bar::<T>();
+    //~^ ERROR type annotations needed: cannot satisfy `T: Bar`
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr
new file mode 100644
index 00000000000..fa5e780ee5e
--- /dev/null
+++ b/tests/ui/traits/new-solver/two-projection-param-candidates-are-ambiguous.stderr
@@ -0,0 +1,16 @@
+error[E0283]: type annotations needed: cannot satisfy `T: Bar`
+  --> $DIR/two-projection-param-candidates-are-ambiguous.rs:26:5
+   |
+LL |     needs_bar::<T>();
+   |     ^^^^^^^^^^^^^^
+   |
+   = note: cannot satisfy `T: Bar`
+note: required by a bound in `needs_bar`
+  --> $DIR/two-projection-param-candidates-are-ambiguous.rs:23:17
+   |
+LL | fn needs_bar<T: Bar>() {}
+   |                 ^^^ required by this bound in `needs_bar`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0283`.