summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-12-06 12:29:38 +0000
committerbors <bors@rust-lang.org>2022-12-06 12:29:38 +0000
commitb6852428a8ea9728369b64b9964cad8e258403d3 (patch)
tree07c892e607e39ad00b0420417dc7ec109c2e12ee
parent9db224fc908059986c179fc6ec433944e9cfce50 (diff)
parenta9ffe7c738e4e9a9367726b591f3145c784391ed (diff)
downloadrust-b6852428a8ea9728369b64b9964cad8e258403d3.tar.gz
rust-b6852428a8ea9728369b64b9964cad8e258403d3.zip
Auto merge of #105365 - matthiaskrgr:rollup-g0mrrt7, r=matthiaskrgr
Rollup of 11 pull requests

Successful merges:

 - #104439 (Add prototype to generate `COPYRIGHT` from REUSE metadata)
 - #105005 (On E0195 point at where clause lifetime bounds)
 - #105098 (propagate the error from parsing enum variant to the parser and emit out)
 - #105243 (remove no-op 'let _ = ')
 - #105254 (Recurse into nested impl-trait when computing variance.)
 - #105287 (Synthesize substitutions for bad auto traits in dyn types)
 - #105310 (Be more careful about unresolved exprs in suggestion)
 - #105318 (Make `get_impl_future_output_ty` work with AFIT)
 - #105339 (support `ConstKind::Expr` in `is_const_evaluatable` and `WfPredicates::compute`)
 - #105340 (Avoid ICE by accounting for missing type)
 - #105342 (Make `note_obligation_cause_code`  take a `impl ToPredicate` for predicate)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--Cargo.lock77
-rw-r--r--Cargo.toml2
-rw-r--r--compiler/rustc_borrowck/src/type_check/canonical.rs6
-rw-r--r--compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl2
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_method.rs51
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/variance/mod.rs42
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs23
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs10
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs14
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs18
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs13
-rw-r--r--compiler/rustc_middle/src/ty/subst.rs16
-rw-r--r--compiler/rustc_parse/src/parser/item.rs5
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/codegen.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs58
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs86
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs19
-rw-r--r--config.toml.example10
-rw-r--r--library/std/src/sync/mpsc/mod.rs12
-rw-r--r--src/bootstrap/builder.rs2
-rw-r--r--src/bootstrap/config.rs3
-rw-r--r--src/bootstrap/run.rs63
-rw-r--r--src/bootstrap/sanity.rs7
-rw-r--r--src/bootstrap/tool.rs2
-rw-r--r--src/test/ui/async-await/in-trait/return-type-suggestion.rs14
-rw-r--r--src/test/ui/async-await/in-trait/return-type-suggestion.stderr23
-rw-r--r--src/test/ui/auto-traits/bad-generics-on-dyn.rs11
-rw-r--r--src/test/ui/auto-traits/bad-generics-on-dyn.stderr11
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs22
-rw-r--r--src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr20
-rw-r--r--src/test/ui/impl-trait/nested-return-type4.rs8
-rw-r--r--src/test/ui/impl-trait/nested-return-type4.stderr20
-rw-r--r--src/test/ui/issues/issue-105330.rs21
-rw-r--r--src/test/ui/issues/issue-105330.stderr109
-rw-r--r--src/test/ui/macros/syntax-error-recovery.stderr1
-rw-r--r--src/test/ui/parser/issue-101477-enum.stderr2
-rw-r--r--src/test/ui/parser/issue-103869.rs9
-rw-r--r--src/test/ui/parser/issue-103869.stderr16
-rw-r--r--src/test/ui/parser/macro/issue-37113.stderr1
-rw-r--r--src/test/ui/structs/struct-fn-in-definition.rs1
-rw-r--r--src/test/ui/structs/struct-fn-in-definition.stderr1
-rw-r--r--src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.rs38
-rw-r--r--src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr36
-rw-r--r--src/test/ui/typeck/path-to-method-sugg-unresolved-expr.rs4
-rw-r--r--src/test/ui/typeck/path-to-method-sugg-unresolved-expr.stderr9
-rw-r--r--src/tools/collect-license-metadata/Cargo.toml10
-rw-r--r--src/tools/collect-license-metadata/src/licenses.rs65
-rw-r--r--src/tools/collect-license-metadata/src/main.rs30
-rw-r--r--src/tools/collect-license-metadata/src/path_tree.rs294
-rw-r--r--src/tools/collect-license-metadata/src/reuse.rs49
-rw-r--r--src/tools/generate-copyright/Cargo.toml11
-rw-r--r--src/tools/generate-copyright/src/main.rs94
59 files changed, 1404 insertions, 122 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 970b1719aa1..150a70341f5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -581,6 +581,7 @@ dependencies = [
  "libc",
  "num-integer",
  "num-traits",
+ "serde",
  "time",
  "winapi",
 ]
@@ -731,6 +732,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "collect-license-metadata"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "serde",
+ "serde_json",
+ "spdx-rs",
+]
+
+[[package]]
 name = "color-eyre"
 version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1553,6 +1564,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "generate-copyright"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
 name = "generic-array"
 version = "0.14.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4865,6 +4885,35 @@ dependencies = [
 ]
 
 [[package]]
+name = "spdx-expression"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d7ac03c67c572d85049d6db815e20a4a19b41b3d5cca732ac582342021ad77"
+dependencies = [
+ "nom",
+ "serde",
+ "thiserror",
+ "tracing",
+]
+
+[[package]]
+name = "spdx-rs"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3c02f6eb7e7b4100c272f685a9ccaccaab302324e8c7ec3e2ee72340fb29ff3"
+dependencies = [
+ "chrono",
+ "log",
+ "nom",
+ "serde",
+ "spdx-expression",
+ "strum",
+ "strum_macros",
+ "thiserror",
+ "uuid",
+]
+
+[[package]]
 name = "stable_deref_trait"
 version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4968,6 +5017,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
 [[package]]
+name = "strum"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
+
+[[package]]
+name = "strum_macros"
+version = "0.24.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn",
+]
+
+[[package]]
 name = "syn"
 version = "1.0.102"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5597,6 +5665,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
 
 [[package]]
+name = "uuid"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
+dependencies = [
+ "getrandom 0.2.0",
+]
+
+[[package]]
 name = "valuable"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 13a98eedde8..000c10a1f90 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -39,6 +39,8 @@ members = [
   "src/tools/bump-stage0",
   "src/tools/replace-version-placeholder",
   "src/tools/lld-wrapper",
+  "src/tools/collect-license-metadata",
+  "src/tools/generate-copyright",
 ]
 
 exclude = [
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
index 1aad6738bba..3617bf58be9 100644
--- a/compiler/rustc_borrowck/src/type_check/canonical.rs
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -121,9 +121,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
     pub(super) fn prove_predicates(
         &mut self,
-        predicates: impl IntoIterator<
-            Item = impl ToPredicate<'tcx, ty::Predicate<'tcx>> + std::fmt::Debug,
-        >,
+        predicates: impl IntoIterator<Item = impl ToPredicate<'tcx> + std::fmt::Debug>,
         locations: Locations,
         category: ConstraintCategory<'tcx>,
     ) {
@@ -135,7 +133,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
     #[instrument(skip(self), level = "debug")]
     pub(super) fn prove_predicate(
         &mut self,
-        predicate: impl ToPredicate<'tcx, ty::Predicate<'tcx>> + std::fmt::Debug,
+        predicate: impl ToPredicate<'tcx> + std::fmt::Debug,
         locations: Locations,
         category: ConstraintCategory<'tcx>,
     ) {
diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
index 0894bbcaad4..a5cb8a88819 100644
--- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl
@@ -17,6 +17,8 @@ hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
     lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration
     .label = lifetimes do not match {$item_kind} in trait
     .generics_label = lifetimes in impl do not match this {$item_kind} in trait
+    .where_label = this `where` clause might not match the one in the trait
+    .bounds_label = this bound might be missing in the impl
 
 hir_analysis_drop_impl_on_wrong_item =
     the `Drop` trait may only be implemented for local structs, enums, and unions
diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs
index 82a77416a19..1d6f9b29176 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_method.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs
@@ -751,17 +751,45 @@ fn check_region_bounds_on_impl_item<'tcx>(
             .get_generics(impl_m.def_id.expect_local())
             .expect("expected impl item to have generics or else we can't compare them")
             .span;
-        let generics_span = if let Some(local_def_id) = trait_m.def_id.as_local() {
-            Some(
-                tcx.hir()
-                    .get_generics(local_def_id)
-                    .expect("expected trait item to have generics or else we can't compare them")
-                    .span,
-            )
-        } else {
-            None
-        };
 
+        let mut generics_span = None;
+        let mut bounds_span = vec![];
+        let mut where_span = None;
+        if let Some(trait_node) = tcx.hir().get_if_local(trait_m.def_id)
+            && let Some(trait_generics) = trait_node.generics()
+        {
+            generics_span = Some(trait_generics.span);
+            // FIXME: we could potentially look at the impl's bounds to not point at bounds that
+            // *are* present in the impl.
+            for p in trait_generics.predicates {
+                if let hir::WherePredicate::BoundPredicate(pred) = p {
+                    for b in pred.bounds {
+                        if let hir::GenericBound::Outlives(lt) = b {
+                            bounds_span.push(lt.ident.span);
+                        }
+                    }
+                }
+            }
+            if let Some(impl_node) = tcx.hir().get_if_local(impl_m.def_id)
+                && let Some(impl_generics) = impl_node.generics()
+            {
+                let mut impl_bounds = 0;
+                for p in impl_generics.predicates {
+                    if let hir::WherePredicate::BoundPredicate(pred) = p {
+                        for b in pred.bounds {
+                            if let hir::GenericBound::Outlives(_) = b {
+                                impl_bounds += 1;
+                            }
+                        }
+                    }
+                }
+                if impl_bounds == bounds_span.len() {
+                    bounds_span = vec![];
+                } else if impl_generics.has_where_clause_predicates {
+                    where_span = Some(impl_generics.where_clause_span);
+                }
+            }
+        }
         let reported = tcx
             .sess
             .create_err(LifetimesOrBoundsMismatchOnTrait {
@@ -769,9 +797,10 @@ fn check_region_bounds_on_impl_item<'tcx>(
                 item_kind: assoc_item_kind_str(impl_m),
                 ident: impl_m.ident(tcx),
                 generics_span,
+                bounds_span,
+                where_span,
             })
             .emit_unless(delay);
-
         return Err(reported);
     }
 
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index afbb27155a2..5156d432b5b 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -43,6 +43,10 @@ pub struct LifetimesOrBoundsMismatchOnTrait {
     pub span: Span,
     #[label(generics_label)]
     pub generics_span: Option<Span>,
+    #[label(where_label)]
+    pub where_span: Option<Span>,
+    #[label(bounds_label)]
+    pub bounds_span: Vec<Span>,
     pub item_kind: &'static str,
     pub ident: Ident,
 }
diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs
index 9db05eedbde..8b2719c2f8a 100644
--- a/compiler/rustc_hir_analysis/src/variance/mod.rs
+++ b/compiler/rustc_hir_analysis/src/variance/mod.rs
@@ -7,7 +7,8 @@ use rustc_arena::DroplessArena;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt, TypeSuperVisitable, TypeVisitable};
+use rustc_middle::ty::{self, CrateVariancesMap, SubstsRef, Ty, TyCtxt};
+use rustc_middle::ty::{DefIdTree, TypeSuperVisitable, TypeVisitable};
 use std::ops::ControlFlow;
 
 /// Defines the `TermsContext` basically houses an arena where we can
@@ -75,11 +76,30 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
     // type Foo<'a, 'b, 'c> = impl Trait<'a> + 'b;
     // ```
     // we may not use `'c` in the hidden type.
-    struct OpaqueTypeLifetimeCollector {
+    struct OpaqueTypeLifetimeCollector<'tcx> {
+        tcx: TyCtxt<'tcx>,
+        root_def_id: DefId,
         variances: Vec<ty::Variance>,
     }
 
-    impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector {
+    impl<'tcx> OpaqueTypeLifetimeCollector<'tcx> {
+        #[instrument(level = "trace", skip(self), ret)]
+        fn visit_opaque(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) -> ControlFlow<!> {
+            if def_id != self.root_def_id && self.tcx.is_descendant_of(def_id, self.root_def_id) {
+                let child_variances = self.tcx.variances_of(def_id);
+                for (a, v) in substs.iter().zip(child_variances) {
+                    if *v != ty::Bivariant {
+                        a.visit_with(self)?;
+                    }
+                }
+                ControlFlow::CONTINUE
+            } else {
+                substs.visit_with(self)
+            }
+        }
+    }
+
+    impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector<'tcx> {
         #[instrument(level = "trace", skip(self), ret)]
         fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
             if let ty::RegionKind::ReEarlyBound(ebr) = r.kind() {
@@ -87,6 +107,19 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
             }
             r.super_visit_with(self)
         }
+
+        #[instrument(level = "trace", skip(self), ret)]
+        fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+            match t.kind() {
+                ty::Opaque(def_id, substs) => self.visit_opaque(*def_id, substs),
+                ty::Projection(proj)
+                    if self.tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder =>
+                {
+                    self.visit_opaque(proj.item_def_id, proj.substs)
+                }
+                _ => t.super_visit_with(self),
+            }
+        }
     }
 
     // By default, RPIT are invariant wrt type and const generics, but they are bivariant wrt
@@ -111,7 +144,8 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
         }
     }
 
-    let mut collector = OpaqueTypeLifetimeCollector { variances };
+    let mut collector =
+        OpaqueTypeLifetimeCollector { tcx, root_def_id: item_def_id.to_def_id(), variances };
     let id_substs = ty::InternalSubsts::identity_for_item(tcx, item_def_id.to_def_id());
     for pred in tcx.bound_explicit_item_bounds(item_def_id.to_def_id()).transpose_iter() {
         let pred = pred.map_bound(|(pred, _)| *pred).subst(tcx, id_substs);
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 9ba4ddfd5cf..db93cfab2c0 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -1482,15 +1482,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ident_name: Symbol,
         }
 
+        // FIXME: This really should be taking scoping, etc into account.
         impl<'v> Visitor<'v> for LetVisitor<'v> {
             fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
-                if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind {
-                    if let Binding(_, _, ident, ..) = pat.kind &&
-                        ident.name == self.ident_name {
-                        self.result = *init;
-                    }
+                if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind
+                    && let Binding(_, _, ident, ..) = pat.kind
+                    && ident.name == self.ident_name
+                {
+                    self.result = *init;
+                } else {
+                    hir::intravisit::walk_stmt(self, ex);
                 }
-                hir::intravisit::walk_stmt(self, ex);
             }
         }
 
@@ -1498,9 +1500,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         visitor.visit_body(&body);
 
         let parent = self.tcx.hir().get_parent_node(seg1.hir_id);
-        if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) &&
-            let Some(expr) = visitor.result {
-            let self_ty = self.node_ty(expr.hir_id);
+        if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent)
+            && let Some(expr) = visitor.result
+            && let Some(self_ty) = self.node_ty_opt(expr.hir_id)
+        {
             let probe = self.lookup_probe(
                 seg2.ident,
                 self_ty,
@@ -1513,7 +1516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(),
                     "you may have meant to call an instance method",
                     ".".to_string(),
-                    Applicability::MaybeIncorrect
+                    Applicability::MaybeIncorrect,
                 );
             }
         }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 662136ca18d..3256ca1fb20 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -341,7 +341,15 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
 
 impl<'tcx> InferCtxt<'tcx> {
     pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
-        let ty::Opaque(def_id, substs) = *ty.kind() else { return None; };
+        let (def_id, substs) = match *ty.kind() {
+            ty::Opaque(def_id, substs) => (def_id, substs),
+            ty::Projection(data)
+                if self.tcx.def_kind(data.item_def_id) == DefKind::ImplTraitPlaceholder =>
+            {
+                (data.item_def_id, data.substs)
+            }
+            _ => return None,
+        };
 
         let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
         let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index a8da93e4c69..48329da3e63 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -101,6 +101,20 @@ impl GenericParamDef {
             _ => None,
         }
     }
+
+    pub fn to_error<'tcx>(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        preceding_substs: &[ty::GenericArg<'tcx>],
+    ) -> ty::GenericArg<'tcx> {
+        match &self.kind {
+            ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_static.into(),
+            ty::GenericParamDefKind::Type { .. } => tcx.ty_error().into(),
+            ty::GenericParamDefKind::Const { .. } => {
+                tcx.const_error(tcx.bound_type_of(self.def_id).subst(tcx, preceding_substs)).into()
+            }
+        }
+    }
 }
 
 #[derive(Default)]
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 9e73236f8d5..f89a8cdfdee 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1150,8 +1150,8 @@ impl<'tcx> ToPolyTraitRef<'tcx> for PolyTraitPredicate<'tcx> {
     }
 }
 
-pub trait ToPredicate<'tcx, Predicate> {
-    fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate;
+pub trait ToPredicate<'tcx, P = Predicate<'tcx>> {
+    fn to_predicate(self, tcx: TyCtxt<'tcx>) -> P;
 }
 
 impl<'tcx, T> ToPredicate<'tcx, T> for T {
@@ -1160,21 +1160,21 @@ impl<'tcx, T> ToPredicate<'tcx, T> for T {
     }
 }
 
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Binder<'tcx, PredicateKind<'tcx>> {
+impl<'tcx> ToPredicate<'tcx> for Binder<'tcx, PredicateKind<'tcx>> {
     #[inline(always)]
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
         tcx.mk_predicate(self)
     }
 }
 
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Clause<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for Clause<'tcx> {
     #[inline(always)]
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
         tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::Clause(self)))
     }
 }
 
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Binder<'tcx, TraitRef<'tcx>> {
+impl<'tcx> ToPredicate<'tcx> for Binder<'tcx, TraitRef<'tcx>> {
     #[inline(always)]
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
         let pred: PolyTraitPredicate<'tcx> = self.to_predicate(tcx);
@@ -1193,25 +1193,25 @@ impl<'tcx> ToPredicate<'tcx, PolyTraitPredicate<'tcx>> for Binder<'tcx, TraitRef
     }
 }
 
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyTraitPredicate<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for PolyTraitPredicate<'tcx> {
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
         self.map_bound(|p| PredicateKind::Clause(Clause::Trait(p))).to_predicate(tcx)
     }
 }
 
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyRegionOutlivesPredicate<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> {
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
         self.map_bound(|p| PredicateKind::Clause(Clause::RegionOutlives(p))).to_predicate(tcx)
     }
 }
 
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyTypeOutlivesPredicate<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> {
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
         self.map_bound(|p| PredicateKind::Clause(Clause::TypeOutlives(p))).to_predicate(tcx)
     }
 }
 
-impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyProjectionPredicate<'tcx> {
+impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> {
     fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
         self.map_bound(|p| PredicateKind::Clause(Clause::Projection(p))).to_predicate(tcx)
     }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 5984686044b..1eec119616e 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -722,8 +722,17 @@ impl<'tcx> PolyExistentialPredicate<'tcx> {
                 self.rebind(p.with_self_ty(tcx, self_ty)).to_predicate(tcx)
             }
             ExistentialPredicate::AutoTrait(did) => {
-                let trait_ref = self.rebind(tcx.mk_trait_ref(did, [self_ty]));
-                trait_ref.without_const().to_predicate(tcx)
+                let generics = tcx.generics_of(did);
+                let trait_ref = if generics.params.len() == 1 {
+                    tcx.mk_trait_ref(did, [self_ty])
+                } else {
+                    // If this is an ill-formed auto trait, then synthesize
+                    // new error substs for the missing generics.
+                    let err_substs =
+                        ty::InternalSubsts::extend_with_error(tcx, did, &[self_ty.into()]);
+                    tcx.mk_trait_ref(did, err_substs)
+                };
+                self.rebind(trait_ref).without_const().to_predicate(tcx)
             }
         }
     }
diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs
index 141c8354c18..23507d28045 100644
--- a/compiler/rustc_middle/src/ty/subst.rs
+++ b/compiler/rustc_middle/src/ty/subst.rs
@@ -352,6 +352,22 @@ impl<'tcx> InternalSubsts<'tcx> {
         }
     }
 
+    // Extend an `original_substs` list to the full number of substs expected by `def_id`,
+    // filling in the missing parameters with error ty/ct or 'static regions.
+    pub fn extend_with_error(
+        tcx: TyCtxt<'tcx>,
+        def_id: DefId,
+        original_substs: &[GenericArg<'tcx>],
+    ) -> SubstsRef<'tcx> {
+        ty::InternalSubsts::for_item(tcx, def_id, |def, substs| {
+            if let Some(subst) = original_substs.get(def.index as usize) {
+                *subst
+            } else {
+                def.to_error(tcx, substs)
+            }
+        })
+    }
+
     #[inline]
     pub fn types(&'tcx self) -> impl DoubleEndedIterator<Item = Ty<'tcx>> + 'tcx {
         self.iter()
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 84c63219920..03f25392a7c 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1414,7 +1414,10 @@ impl<'a> Parser<'a> {
 
                 Ok((Some(vr), TrailingToken::MaybeComma))
             },
-        )
+        ).map_err(|mut err|{
+            err.help("enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`");
+            err
+        })
     }
 
     /// Parses `struct Foo { ... }`.
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 4d8bff28b05..bebb012660a 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -943,6 +943,10 @@ impl<'a> Parser<'a> {
                                 Err(e) => {
                                     // Parsing failed, therefore it must be something more serious
                                     // than just a missing separator.
+                                    for xx in &e.children {
+                                        // propagate the help message from sub error 'e' to main error 'expect_err;
+                                        expect_err.children.push(xx.clone());
+                                    }
                                     expect_err.emit();
 
                                     e.cancel();
diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs
index 61743d78e9e..0102d268b42 100644
--- a/compiler/rustc_trait_selection/src/traits/codegen.rs
+++ b/compiler/rustc_trait_selection/src/traits/codegen.rs
@@ -70,7 +70,7 @@ pub fn codegen_select_candidate<'tcx>(
         // `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization.
         for err in errors {
             if let FulfillmentErrorCode::CodeCycle(cycle) = err.code {
-                infcx.err_ctxt().report_overflow_error_cycle(&cycle);
+                infcx.err_ctxt().report_overflow_obligation_cycle(&cycle);
             }
         }
         return Err(CodegenObligationError::FulfillmentError);
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index e9e65336299..d01c6bac296 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -25,15 +25,13 @@ use crate::traits::ObligationCtxt;
 #[instrument(skip(infcx), level = "debug")]
 pub fn is_const_evaluatable<'tcx>(
     infcx: &InferCtxt<'tcx>,
-    ct: ty::Const<'tcx>,
+    unexpanded_ct: ty::Const<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     span: Span,
 ) -> Result<(), NotConstEvaluatable> {
     let tcx = infcx.tcx;
-    let uv = match ct.kind() {
-        ty::ConstKind::Unevaluated(uv) => uv,
-        // FIXME(generic_const_exprs): this seems wrong but I couldn't find a way to get this to trigger
-        ty::ConstKind::Expr(_) => bug!("unexpected expr in `is_const_evaluatable: {ct:?}"),
+    match unexpanded_ct.kind() {
+        ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_) => (),
         ty::ConstKind::Param(_)
         | ty::ConstKind::Bound(_, _)
         | ty::ConstKind::Placeholder(_)
@@ -43,7 +41,7 @@ pub fn is_const_evaluatable<'tcx>(
     };
 
     if tcx.features().generic_const_exprs {
-        let ct = tcx.expand_abstract_consts(ct);
+        let ct = tcx.expand_abstract_consts(unexpanded_ct);
 
         let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() {
             tcx.def_kind(uv.def.did) == DefKind::AnonConst
@@ -62,18 +60,40 @@ pub fn is_const_evaluatable<'tcx>(
             }
         }
 
-        let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
-        match concrete {
-            Err(ErrorHandled::TooGeneric) => Err(NotConstEvaluatable::Error(
-                infcx
-                    .tcx
-                    .sess
-                    .delay_span_bug(span, "Missing value for constant, but no error reported?"),
-            )),
-            Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
-            Ok(_) => Ok(()),
+        match unexpanded_ct.kind() {
+            ty::ConstKind::Expr(_) => {
+                // FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete, but
+                // currently it is not possible to evaluate `ConstKind::Expr` so we are unable to tell if it
+                // is evaluatable or not. For now we just ICE until this is implemented this.
+                Err(NotConstEvaluatable::Error(tcx.sess.delay_span_bug(
+                    span,
+                    "evaluating `ConstKind::Expr` is not currently supported",
+                )))
+            }
+            ty::ConstKind::Unevaluated(uv) => {
+                let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
+                match concrete {
+                    Err(ErrorHandled::TooGeneric) => {
+                        Err(NotConstEvaluatable::Error(infcx.tcx.sess.delay_span_bug(
+                            span,
+                            "Missing value for constant, but no error reported?",
+                        )))
+                    }
+                    Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
+                    Ok(_) => Ok(()),
+                }
+            }
+            _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
         }
     } else {
+        let uv = match unexpanded_ct.kind() {
+            ty::ConstKind::Unevaluated(uv) => uv,
+            ty::ConstKind::Expr(_) => {
+                bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled")
+            }
+            _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
+        };
+
         // FIXME: We should only try to evaluate a given constant here if it is fully concrete
         // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
         //
@@ -92,7 +112,7 @@ pub fn is_const_evaluatable<'tcx>(
                     && satisfied_from_param_env(
                         tcx,
                         infcx,
-                        tcx.expand_abstract_consts(ct),
+                        tcx.expand_abstract_consts(unexpanded_ct),
                         param_env,
                     ) =>
             {
@@ -152,6 +172,7 @@ fn satisfied_from_param_env<'tcx>(
     impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> {
         type BreakTy = ();
         fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+            debug!("is_const_evaluatable: candidate={:?}", c);
             if let Ok(()) = self.infcx.commit_if_ok(|_| {
                 let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
                 if let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty())
@@ -187,7 +208,7 @@ fn satisfied_from_param_env<'tcx>(
                 let result = b_ct.visit_with(&mut v);
 
                 if let ControlFlow::Break(()) = result {
-                    debug!("is_const_evaluatable: abstract_const ~~> ok");
+                    debug!("is_const_evaluatable: yes");
                     return true;
                 }
             }
@@ -195,5 +216,6 @@ fn satisfied_from_param_env<'tcx>(
         }
     }
 
+    debug!("is_const_evaluatable: no");
     false
 }
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 12cc72d30c8..dda7b2b2fa5 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -99,26 +99,36 @@ pub trait InferCtxtExt<'tcx> {
 }
 
 pub trait TypeErrCtxtExt<'tcx> {
+    fn report_overflow_error<T>(
+        &self,
+        predicate: &T,
+        span: Span,
+        suggest_increasing_limit: bool,
+        mutate: impl FnOnce(&mut Diagnostic),
+    ) -> !
+    where
+        T: fmt::Display
+            + TypeFoldable<'tcx>
+            + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
+        <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug;
+
     fn report_fulfillment_errors(
         &self,
         errors: &[FulfillmentError<'tcx>],
         body_id: Option<hir::BodyId>,
     ) -> ErrorGuaranteed;
 
-    fn report_overflow_error<T>(
+    fn report_overflow_obligation<T>(
         &self,
         obligation: &Obligation<'tcx, T>,
         suggest_increasing_limit: bool,
     ) -> !
     where
-        T: fmt::Display
-            + TypeFoldable<'tcx>
-            + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
-        <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug;
+        T: ToPredicate<'tcx> + Clone;
 
     fn suggest_new_overflow_limit(&self, err: &mut Diagnostic);
 
-    fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !;
+    fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !;
 
     /// The `root_obligation` parameter should be the `root_obligation` field
     /// from a `FulfillmentError`. If no `FulfillmentError` is available,
@@ -458,8 +468,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
     /// occurrences in any case.
     fn report_overflow_error<T>(
         &self,
-        obligation: &Obligation<'tcx, T>,
+        predicate: &T,
+        span: Span,
         suggest_increasing_limit: bool,
+        mutate: impl FnOnce(&mut Diagnostic),
     ) -> !
     where
         T: fmt::Display
@@ -467,8 +479,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
         <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
     {
-        let predicate = self.resolve_vars_if_possible(obligation.predicate.clone());
+        let predicate = self.resolve_vars_if_possible(predicate.clone());
         let mut pred_str = predicate.to_string();
+
         if pred_str.len() > 50 {
             // We don't need to save the type to a file, we will be talking about this type already
             // in a separate note when we explain the obligation, so it will be available that way.
@@ -483,7 +496,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         }
         let mut err = struct_span_err!(
             self.tcx.sess,
-            obligation.cause.span,
+            span,
             E0275,
             "overflow evaluating the requirement `{}`",
             pred_str,
@@ -493,20 +506,46 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             self.suggest_new_overflow_limit(&mut err);
         }
 
-        self.note_obligation_cause_code(
-            &mut err,
-            &obligation.predicate,
-            obligation.param_env,
-            obligation.cause.code(),
-            &mut vec![],
-            &mut Default::default(),
-        );
+        mutate(&mut err);
 
         err.emit();
         self.tcx.sess.abort_if_errors();
         bug!();
     }
 
+    /// Reports that an overflow has occurred and halts compilation. We
+    /// halt compilation unconditionally because it is important that
+    /// overflows never be masked -- they basically represent computations
+    /// whose result could not be truly determined and thus we can't say
+    /// if the program type checks or not -- and they are unusual
+    /// occurrences in any case.
+    fn report_overflow_obligation<T>(
+        &self,
+        obligation: &Obligation<'tcx, T>,
+        suggest_increasing_limit: bool,
+    ) -> !
+    where
+        T: ToPredicate<'tcx> + Clone,
+    {
+        let predicate = obligation.predicate.clone().to_predicate(self.tcx);
+        let predicate = self.resolve_vars_if_possible(predicate);
+        self.report_overflow_error(
+            &predicate,
+            obligation.cause.span,
+            suggest_increasing_limit,
+            |err| {
+                self.note_obligation_cause_code(
+                    err,
+                    &predicate,
+                    obligation.param_env,
+                    obligation.cause.code(),
+                    &mut vec![],
+                    &mut Default::default(),
+                );
+            },
+        );
+    }
+
     fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) {
         let suggested_limit = match self.tcx.recursion_limit() {
             Limit(0) => Limit(2),
@@ -521,11 +560,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
     }
 
     /// Reports that a cycle was detected which led to overflow and halts
-    /// compilation. This is equivalent to `report_overflow_error` except
+    /// compilation. This is equivalent to `report_overflow_obligation` except
     /// that we can give a more helpful error message (and, in particular,
     /// we do not suggest increasing the overflow limit, which is not
     /// going to help).
-    fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
+    fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
         let cycle = self.resolve_vars_if_possible(cycle.to_owned());
         assert!(!cycle.is_empty());
 
@@ -533,7 +572,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
         // The 'deepest' obligation is most likely to have a useful
         // cause 'backtrace'
-        self.report_overflow_error(cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(), false);
+        self.report_overflow_obligation(
+            cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(),
+            false,
+        );
     }
 
     fn report_selection_error(
@@ -1554,7 +1596,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 diag.emit();
             }
             FulfillmentErrorCode::CodeCycle(ref cycle) => {
-                self.report_overflow_error_cycle(cycle);
+                self.report_overflow_obligation_cycle(cycle);
             }
         }
     }
@@ -1607,7 +1649,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         | ObligationCauseCode::ObjectCastObligation(..)
                         | ObligationCauseCode::OpaqueType
                 );
-                let expected_ty = data.term.ty().unwrap();
+                let expected_ty = data.term.ty().unwrap_or_else(|| self.tcx.ty_error());
 
                 // constrain inference variables a bit more to nested obligations from normalize so
                 // we can have more helpful errors.
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 1740128727a..6ea54b625bb 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -298,7 +298,7 @@ pub trait TypeErrCtxtExt<'tcx> {
         obligated_types: &mut Vec<Ty<'tcx>>,
         seen_requirements: &mut FxHashSet<DefId>,
     ) where
-        T: fmt::Display + ToPredicate<'tcx, T>;
+        T: fmt::Display + ToPredicate<'tcx>;
 
     /// Suggest to await before try: future? => future.await?
     fn suggest_await_before_try(
@@ -2353,7 +2353,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         obligated_types: &mut Vec<Ty<'tcx>>,
         seen_requirements: &mut FxHashSet<DefId>,
     ) where
-        T: fmt::Display,
+        T: fmt::Display + ToPredicate<'tcx>,
     {
         let tcx = self.tcx;
         match *cause_code {
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index c6818a4e57d..d3cfd61e195 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -150,7 +150,7 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
 fn pred_known_to_hold_modulo_regions<'tcx>(
     infcx: &InferCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
-    pred: impl ToPredicate<'tcx, ty::Predicate<'tcx>> + TypeVisitable<'tcx>,
+    pred: impl ToPredicate<'tcx> + TypeVisitable<'tcx>,
     span: Span,
 ) -> bool {
     let has_non_region_infer = pred.has_non_region_infer();
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 71f6eae45aa..5789754e4fc 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -504,14 +504,12 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
                     Reveal::All => {
                         let recursion_limit = self.tcx().recursion_limit();
                         if !recursion_limit.value_within_limit(self.depth) {
-                            let obligation = Obligation::with_depth(
-                                self.tcx(),
-                                self.cause.clone(),
-                                recursion_limit.0,
-                                self.param_env,
-                                ty,
+                            self.selcx.infcx.err_ctxt().report_overflow_error(
+                                &ty,
+                                self.cause.span,
+                                true,
+                                |_| {},
                             );
-                            self.selcx.infcx.err_ctxt().report_overflow_error(&obligation, true);
                         }
 
                         let substs = substs.fold_with(self);
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index f899321fc01..7ad532d8a34 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -7,7 +7,7 @@ use crate::infer::canonical::OriginalQueryValues;
 use crate::infer::{InferCtxt, InferOk};
 use crate::traits::error_reporting::TypeErrCtxtExt;
 use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
-use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal};
+use crate::traits::{ObligationCause, PredicateObligation, Reveal};
 use rustc_data_structures::sso::SsoHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_infer::traits::Normalized;
@@ -214,14 +214,12 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
                         let substs = substs.try_fold_with(self)?;
                         let recursion_limit = self.tcx().recursion_limit();
                         if !recursion_limit.value_within_limit(self.anon_depth) {
-                            let obligation = Obligation::with_depth(
-                                self.tcx(),
-                                self.cause.clone(),
-                                recursion_limit.0,
-                                self.param_env,
-                                ty,
+                            self.infcx.err_ctxt().report_overflow_error(
+                                &ty,
+                                self.cause.span,
+                                true,
+                                |_| {},
                             );
-                            self.infcx.err_ctxt().report_overflow_error(&obligation, true);
                         }
 
                         let generic_ty = self.tcx().bound_type_of(def_id);
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 8835f2cc1b9..035deb61639 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -43,7 +43,6 @@ use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::fold::BottomUpFolder;
-use rustc_middle::ty::print::{FmtPrinter, Print};
 use rustc_middle::ty::relate::TypeRelation;
 use rustc_middle::ty::SubstsRef;
 use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
@@ -1313,10 +1312,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         error_obligation: &Obligation<'tcx, T>,
     ) -> Result<(), OverflowError>
     where
-        T: fmt::Display
-            + TypeFoldable<'tcx>
-            + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
-        <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
+        T: ToPredicate<'tcx> + Clone,
     {
         if !self.infcx.tcx.recursion_limit().value_within_limit(depth) {
             match self.query_mode {
@@ -1324,7 +1320,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     if let Some(e) = self.infcx.tainted_by_errors() {
                         return Err(OverflowError::Error(e));
                     }
-                    self.infcx.err_ctxt().report_overflow_error(error_obligation, true);
+                    self.infcx.err_ctxt().report_overflow_obligation(error_obligation, true);
                 }
                 TraitQueryMode::Canonical => {
                     return Err(OverflowError::Canonical);
@@ -1345,10 +1341,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         error_obligation: &Obligation<'tcx, V>,
     ) -> Result<(), OverflowError>
     where
-        V: fmt::Display
-            + TypeFoldable<'tcx>
-            + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
-        <V as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
+        V: ToPredicate<'tcx> + Clone,
     {
         self.check_recursion_depth(obligation.recursion_depth, error_obligation)
     }
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 0855d6d1973..e47ba64245f 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -476,9 +476,24 @@ impl<'tcx> WfPredicates<'tcx> {
                                 ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())),
                             ));
                         }
-                        // FIXME(generic_const_exprs): This seems wrong but I could not find a way to get this to trigger
                         ty::ConstKind::Expr(_) => {
-                            bug!("checking wfness of `ConstKind::Expr` is unsupported")
+                            // FIXME(generic_const_exprs): this doesnt verify that given `Expr(N + 1)` the
+                            // trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
+                            // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
+                            // which means that the `DefId` would have been typeck'd elsewhere. However in
+                            // the future we may allow directly lowering to `ConstKind::Expr` in which case
+                            // we would not be proving bounds we should.
+
+                            let predicate =
+                                ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct));
+                            let cause = self.cause(traits::WellFormed(None));
+                            self.out.push(traits::Obligation::with_depth(
+                                self.tcx(),
+                                cause,
+                                self.recursion_depth,
+                                self.param_env,
+                                predicate,
+                            ));
                         }
 
                         ty::ConstKind::Error(_)
diff --git a/config.toml.example b/config.toml.example
index c94a27b12a3..ca54cbd2d68 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -255,6 +255,16 @@ changelog-seen = 2
 # Defaults to the Python interpreter used to execute x.py
 #python = "python"
 
+# The path to the REUSE executable to use. Note that REUSE is not required in
+# most cases, as our tooling relies on a cached (and shrinked) copy of the
+# REUSE output present in the git repository and in our source tarballs.
+#
+# REUSE is only needed if your changes caused the overral licensing of the
+# repository to change, and the cached copy has to be regenerated.
+#
+# Defaults to the "reuse" command in the system path.
+#reuse = "reuse"
+
 # Force Cargo to check that Cargo.lock describes the precise dependency
 # set that all the Cargo.toml files create, instead of updating it.
 #locked-deps = false
diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs
index 27fba761ada..adb488d4378 100644
--- a/library/std/src/sync/mpsc/mod.rs
+++ b/library/std/src/sync/mpsc/mod.rs
@@ -629,9 +629,7 @@ impl<T> Clone for Sender<T> {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> Drop for Sender<T> {
-    fn drop(&mut self) {
-        let _ = self.inner;
-    }
+    fn drop(&mut self) {}
 }
 
 #[stable(feature = "mpsc_debug", since = "1.8.0")]
@@ -751,9 +749,7 @@ impl<T> Clone for SyncSender<T> {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> Drop for SyncSender<T> {
-    fn drop(&mut self) {
-        let _ = self.inner;
-    }
+    fn drop(&mut self) {}
 }
 
 #[stable(feature = "mpsc_debug", since = "1.8.0")]
@@ -1094,9 +1090,7 @@ impl<T> IntoIterator for Receiver<T> {
 
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> Drop for Receiver<T> {
-    fn drop(&mut self) {
-        let _ = self.inner;
-    }
+    fn drop(&mut self) {}
 }
 
 #[stable(feature = "mpsc_debug", since = "1.8.0")]
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index cff5fd8c5b0..8ee6d49da8f 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -754,6 +754,8 @@ impl<'a> Builder<'a> {
                 run::BumpStage0,
                 run::ReplaceVersionPlaceholder,
                 run::Miri,
+                run::CollectLicenseMetadata,
+                run::GenerateCopyright,
             ),
             // These commands either don't use paths, or they're special-cased in Build::build()
             Kind::Clean | Kind::Format | Kind::Setup => vec![],
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index babf09d2b93..d8c15c76e2d 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -213,6 +213,7 @@ pub struct Config {
     pub npm: Option<PathBuf>,
     pub gdb: Option<PathBuf>,
     pub python: Option<PathBuf>,
+    pub reuse: Option<PathBuf>,
     pub cargo_native_static: bool,
     pub configure_args: Vec<String>,
 
@@ -611,6 +612,7 @@ define_config! {
         nodejs: Option<String> = "nodejs",
         npm: Option<String> = "npm",
         python: Option<String> = "python",
+        reuse: Option<String> = "reuse",
         locked_deps: Option<bool> = "locked-deps",
         vendor: Option<bool> = "vendor",
         full_bootstrap: Option<bool> = "full-bootstrap",
@@ -1004,6 +1006,7 @@ impl Config {
         config.npm = build.npm.map(PathBuf::from);
         config.gdb = build.gdb.map(PathBuf::from);
         config.python = build.python.map(PathBuf::from);
+        config.reuse = build.reuse.map(PathBuf::from);
         config.submodules = build.submodules;
         set(&mut config.low_priority, build.low_priority);
         set(&mut config.compiler_docs, build.compiler_docs);
diff --git a/src/bootstrap/run.rs b/src/bootstrap/run.rs
index d49b41c5132..05de51f8cc5 100644
--- a/src/bootstrap/run.rs
+++ b/src/bootstrap/run.rs
@@ -1,3 +1,4 @@
+use std::path::PathBuf;
 use std::process::Command;
 
 use crate::builder::{Builder, RunConfig, ShouldRun, Step};
@@ -189,3 +190,65 @@ impl Step for Miri {
         builder.run(&mut miri);
     }
 }
+
+#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct CollectLicenseMetadata;
+
+impl Step for CollectLicenseMetadata {
+    type Output = PathBuf;
+    const ONLY_HOSTS: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/tools/collect-license-metadata")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(CollectLicenseMetadata);
+    }
+
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
+        let Some(reuse) = &builder.config.reuse else {
+            panic!("REUSE is required to collect the license metadata");
+        };
+
+        // Temporary location, it will be moved to src/etc once it's accurate.
+        let dest = builder.out.join("license-metadata.json");
+
+        let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata);
+        cmd.env("REUSE_EXE", reuse);
+        cmd.env("DEST", &dest);
+        builder.run(&mut cmd);
+
+        dest
+    }
+}
+
+#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct GenerateCopyright;
+
+impl Step for GenerateCopyright {
+    type Output = PathBuf;
+    const ONLY_HOSTS: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/tools/generate-copyright")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(GenerateCopyright);
+    }
+
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
+        let license_metadata = builder.ensure(CollectLicenseMetadata);
+
+        // Temporary location, it will be moved to the proper one once it's accurate.
+        let dest = builder.out.join("COPYRIGHT.md");
+
+        let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
+        cmd.env("LICENSE_METADATA", &license_metadata);
+        cmd.env("DEST", &dest);
+        builder.run(&mut cmd);
+
+        dest
+    }
+}
diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs
index 631d42acb93..8a40b0f64f4 100644
--- a/src/bootstrap/sanity.rs
+++ b/src/bootstrap/sanity.rs
@@ -140,6 +140,13 @@ than building it.
         .map(|p| cmd_finder.must_have(p))
         .or_else(|| cmd_finder.maybe_have("gdb"));
 
+    build.config.reuse = build
+        .config
+        .reuse
+        .take()
+        .map(|p| cmd_finder.must_have(p))
+        .or_else(|| cmd_finder.maybe_have("reuse"));
+
     // We're gonna build some custom C code here and there, host triples
     // also build some C++ shims for LLVM so we need a C++ compiler.
     for target in &build.targets {
diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs
index ba329ea6c75..e0be4c432f1 100644
--- a/src/bootstrap/tool.rs
+++ b/src/bootstrap/tool.rs
@@ -380,6 +380,8 @@ bootstrap_tool!(
     HtmlChecker, "src/tools/html-checker", "html-checker";
     BumpStage0, "src/tools/bump-stage0", "bump-stage0";
     ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder";
+    CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
+    GenerateCopyright, "src/tools/generate-copyright", "generate-copyright";
 );
 
 #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
diff --git a/src/test/ui/async-await/in-trait/return-type-suggestion.rs b/src/test/ui/async-await/in-trait/return-type-suggestion.rs
new file mode 100644
index 00000000000..3446761d119
--- /dev/null
+++ b/src/test/ui/async-await/in-trait/return-type-suggestion.rs
@@ -0,0 +1,14 @@
+// edition: 2021
+
+#![feature(async_fn_in_trait)]
+//~^ WARN the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
+
+trait A {
+    async fn e() {
+        Ok(())
+        //~^ ERROR mismatched types
+        //~| HELP consider using a semicolon here
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/async-await/in-trait/return-type-suggestion.stderr b/src/test/ui/async-await/in-trait/return-type-suggestion.stderr
new file mode 100644
index 00000000000..5a9b15e54a0
--- /dev/null
+++ b/src/test/ui/async-await/in-trait/return-type-suggestion.stderr
@@ -0,0 +1,23 @@
+warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/return-type-suggestion.rs:3:12
+   |
+LL | #![feature(async_fn_in_trait)]
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+error[E0308]: mismatched types
+  --> $DIR/return-type-suggestion.rs:8:9
+   |
+LL |         Ok(())
+   |         ^^^^^^- help: consider using a semicolon here: `;`
+   |         |
+   |         expected `()`, found enum `Result`
+   |
+   = note: expected unit type `()`
+                   found enum `Result<(), _>`
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/auto-traits/bad-generics-on-dyn.rs b/src/test/ui/auto-traits/bad-generics-on-dyn.rs
new file mode 100644
index 00000000000..3f8ac14c72d
--- /dev/null
+++ b/src/test/ui/auto-traits/bad-generics-on-dyn.rs
@@ -0,0 +1,11 @@
+#![feature(auto_traits)]
+
+auto trait Trait1<'a> {}
+//~^ ERROR auto traits cannot have generic parameters
+
+fn f<'a>(x: &dyn Trait1<'a>)
+{}
+
+fn main() {
+    f(&1);
+}
diff --git a/src/test/ui/auto-traits/bad-generics-on-dyn.stderr b/src/test/ui/auto-traits/bad-generics-on-dyn.stderr
new file mode 100644
index 00000000000..ade69ced606
--- /dev/null
+++ b/src/test/ui/auto-traits/bad-generics-on-dyn.stderr
@@ -0,0 +1,11 @@
+error[E0567]: auto traits cannot have generic parameters
+  --> $DIR/bad-generics-on-dyn.rs:3:18
+   |
+LL | auto trait Trait1<'a> {}
+   |            ------^^^^ help: remove the parameters
+   |            |
+   |            auto trait cannot have generic parameters
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0567`.
diff --git a/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs
new file mode 100644
index 00000000000..6093fc70b16
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.rs
@@ -0,0 +1,22 @@
+#![feature(generic_const_exprs, generic_arg_infer)]
+#![allow(incomplete_features)]
+
+// minimized repro for #105205
+//
+// the `foo::<_, L>` call results in a `WellFormed(_)` obligation and a
+// `ConstEvaluatable(Unevaluated(_ + 1 + L))` obligation. Attempting to fulfill the latter
+// unifies the `_` with `Expr(L - 1)` from the paramenv which turns the `WellFormed`
+// obligation into `WellFormed(Expr(L - 1))`
+
+fn foo<const N: usize, const M: usize>(_: [(); N + 1 + M]) {}
+
+fn ice<const L: usize>()
+where
+    [(); (L - 1) + 1 + L]:,
+{
+    foo::<_, L>([(); L + 1 + L]);
+    //~^ ERROR: mismatched types
+    //~^^ ERROR: unconstrained generic constant
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr
new file mode 100644
index 00000000000..da5194696e6
--- /dev/null
+++ b/src/test/ui/const-generics/generic_const_exprs/const_kind_expr/wf_obligation.stderr
@@ -0,0 +1,20 @@
+error[E0308]: mismatched types
+  --> $DIR/wf_obligation.rs:17:17
+   |
+LL |     foo::<_, L>([(); L + 1 + L]);
+   |                 ^^^^^^^^^^^^^^^ expected `N + 1 + M`, found `L + 1 + L`
+   |
+   = note: expected constant `N + 1 + M`
+              found constant `L + 1 + L`
+
+error: unconstrained generic constant
+  --> $DIR/wf_obligation.rs:17:22
+   |
+LL |     foo::<_, L>([(); L + 1 + L]);
+   |                      ^^^^^^^^^
+   |
+   = help: try adding a `where` bound using this expression: `where [(); L + 1 + L]:`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/impl-trait/nested-return-type4.rs b/src/test/ui/impl-trait/nested-return-type4.rs
new file mode 100644
index 00000000000..cec70bb1a0d
--- /dev/null
+++ b/src/test/ui/impl-trait/nested-return-type4.rs
@@ -0,0 +1,8 @@
+// edition: 2021
+
+fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> {
+    async move { let _s = s; }
+    //~^ ERROR hidden type for `impl Future<Output = impl Sized>` captures lifetime that does not appear in bounds
+}
+
+fn main() {}
diff --git a/src/test/ui/impl-trait/nested-return-type4.stderr b/src/test/ui/impl-trait/nested-return-type4.stderr
new file mode 100644
index 00000000000..e761a60e79c
--- /dev/null
+++ b/src/test/ui/impl-trait/nested-return-type4.stderr
@@ -0,0 +1,20 @@
+error[E0700]: hidden type for `impl Future<Output = impl Sized>` captures lifetime that does not appear in bounds
+  --> $DIR/nested-return-type4.rs:4:5
+   |
+LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> {
+   |         -- hidden type `[async block@$DIR/nested-return-type4.rs:4:5: 4:31]` captures the lifetime `'s` as defined here
+LL |     async move { let _s = s; }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: to declare that `impl Future<Output = impl Sized>` captures `'s`, you can add an explicit `'s` lifetime bound
+   |
+LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> + 's {
+   |                                                                              ++++
+help: to declare that `impl Sized` captures `'s`, you can add an explicit `'s` lifetime bound
+   |
+LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized + 's> {
+   |                                                                             ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0700`.
diff --git a/src/test/ui/issues/issue-105330.rs b/src/test/ui/issues/issue-105330.rs
new file mode 100644
index 00000000000..86e45f10b0e
--- /dev/null
+++ b/src/test/ui/issues/issue-105330.rs
@@ -0,0 +1,21 @@
+pub trait TraitWAssocConst {
+    const A:   usize;
+}
+pub struct Demo {}
+
+impl TraitWAssocConst for impl Demo { //~ ERROR E0404
+    //~^ ERROR E0562
+    pubconst A: str = 32; //~ ERROR expected one of
+}
+
+fn foo<A: TraitWAssocConst<A=32>>() { //~ ERROR E0658
+    foo::<Demo>()(); //~ ERROR E0271
+    //~^ ERROR E0618
+    //~| ERROR E0277
+}
+
+fn main<A: TraitWAssocConst<A=32>>() { //~ ERROR E0131
+    //~^ ERROR E0658
+    foo::<Demo>(); //~ ERROR E0277
+    //~^ ERROR E0271
+}
diff --git a/src/test/ui/issues/issue-105330.stderr b/src/test/ui/issues/issue-105330.stderr
new file mode 100644
index 00000000000..92f2ccb6544
--- /dev/null
+++ b/src/test/ui/issues/issue-105330.stderr
@@ -0,0 +1,109 @@
+error: expected one of `!` or `::`, found `A`
+  --> $DIR/issue-105330.rs:8:14
+   |
+LL | impl TraitWAssocConst for impl Demo {
+   |                                     - while parsing this item list starting here
+LL |
+LL |     pubconst A: str = 32;
+   |              ^ expected one of `!` or `::`
+LL | }
+   | - the item list ends here
+
+error[E0404]: expected trait, found struct `Demo`
+  --> $DIR/issue-105330.rs:6:32
+   |
+LL | impl TraitWAssocConst for impl Demo {
+   |                                ^^^^ not a trait
+
+error[E0658]: associated const equality is incomplete
+  --> $DIR/issue-105330.rs:11:28
+   |
+LL | fn foo<A: TraitWAssocConst<A=32>>() {
+   |                            ^^^^
+   |
+   = note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
+   = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
+
+error[E0658]: associated const equality is incomplete
+  --> $DIR/issue-105330.rs:17:29
+   |
+LL | fn main<A: TraitWAssocConst<A=32>>() {
+   |                             ^^^^
+   |
+   = note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
+   = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
+
+error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in type
+  --> $DIR/issue-105330.rs:6:27
+   |
+LL | impl TraitWAssocConst for impl Demo {
+   |                           ^^^^^^^^^
+
+error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied
+  --> $DIR/issue-105330.rs:12:11
+   |
+LL |     foo::<Demo>()();
+   |           ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo`
+   |
+note: required by a bound in `foo`
+  --> $DIR/issue-105330.rs:11:11
+   |
+LL | fn foo<A: TraitWAssocConst<A=32>>() {
+   |           ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `foo`
+
+error[E0271]: type mismatch resolving `<Demo as TraitWAssocConst>::A == 32`
+  --> $DIR/issue-105330.rs:12:11
+   |
+LL |     foo::<Demo>()();
+   |           ^^^^ types differ
+   |
+note: required by a bound in `foo`
+  --> $DIR/issue-105330.rs:11:28
+   |
+LL | fn foo<A: TraitWAssocConst<A=32>>() {
+   |                            ^^^^ required by this bound in `foo`
+
+error[E0618]: expected function, found `()`
+  --> $DIR/issue-105330.rs:12:5
+   |
+LL | fn foo<A: TraitWAssocConst<A=32>>() {
+   | ----------------------------------- `foo::<Demo>` defined here returns `()`
+LL |     foo::<Demo>()();
+   |     ^^^^^^^^^^^^^--
+   |     |
+   |     call expression requires function
+
+error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied
+  --> $DIR/issue-105330.rs:19:11
+   |
+LL |     foo::<Demo>();
+   |           ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo`
+   |
+note: required by a bound in `foo`
+  --> $DIR/issue-105330.rs:11:11
+   |
+LL | fn foo<A: TraitWAssocConst<A=32>>() {
+   |           ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `foo`
+
+error[E0271]: type mismatch resolving `<Demo as TraitWAssocConst>::A == 32`
+  --> $DIR/issue-105330.rs:19:11
+   |
+LL |     foo::<Demo>();
+   |           ^^^^ types differ
+   |
+note: required by a bound in `foo`
+  --> $DIR/issue-105330.rs:11:28
+   |
+LL | fn foo<A: TraitWAssocConst<A=32>>() {
+   |                            ^^^^ required by this bound in `foo`
+
+error[E0131]: `main` function is not allowed to have generic parameters
+  --> $DIR/issue-105330.rs:17:8
+   |
+LL | fn main<A: TraitWAssocConst<A=32>>() {
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` cannot have generic parameters
+
+error: aborting due to 11 previous errors
+
+Some errors have detailed explanations: E0131, E0271, E0277, E0404, E0562, E0618, E0658.
+For more information about an error, try `rustc --explain E0131`.
diff --git a/src/test/ui/macros/syntax-error-recovery.stderr b/src/test/ui/macros/syntax-error-recovery.stderr
index c153b3b910b..c42ee9b295e 100644
--- a/src/test/ui/macros/syntax-error-recovery.stderr
+++ b/src/test/ui/macros/syntax-error-recovery.stderr
@@ -7,6 +7,7 @@ LL |                 $token $($inner)? = $value,
 LL | values!(STRING(1) as (String) => cfg(test),);
    | -------------------------------------------- in this macro invocation
    |
+   = help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
    = note: this error originates in the macro `values` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: macro expansion ignores token `(String)` and any following
diff --git a/src/test/ui/parser/issue-101477-enum.stderr b/src/test/ui/parser/issue-101477-enum.stderr
index bffc881bdc8..1edca391e8f 100644
--- a/src/test/ui/parser/issue-101477-enum.stderr
+++ b/src/test/ui/parser/issue-101477-enum.stderr
@@ -3,6 +3,8 @@ error: unexpected `==`
    |
 LL |     B == 2
    |       ^^ help: try using `=` instead
+   |
+   = help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
 
 error: expected item, found `==`
   --> $DIR/issue-101477-enum.rs:6:7
diff --git a/src/test/ui/parser/issue-103869.rs b/src/test/ui/parser/issue-103869.rs
new file mode 100644
index 00000000000..28c442bdd63
--- /dev/null
+++ b/src/test/ui/parser/issue-103869.rs
@@ -0,0 +1,9 @@
+enum VecOrMap{
+    vec: Vec<usize>,
+    //~^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `:`
+    //~| HELP: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
+    //~| ERROR expected item, found `:`
+    map: HashMap<String,usize>
+}
+
+fn main() {}
diff --git a/src/test/ui/parser/issue-103869.stderr b/src/test/ui/parser/issue-103869.stderr
new file mode 100644
index 00000000000..0b8cd919a9d
--- /dev/null
+++ b/src/test/ui/parser/issue-103869.stderr
@@ -0,0 +1,16 @@
+error: expected one of `(`, `,`, `=`, `{`, or `}`, found `:`
+  --> $DIR/issue-103869.rs:2:8
+   |
+LL |     vec: Vec<usize>,
+   |        ^ expected one of `(`, `,`, `=`, `{`, or `}`
+   |
+   = help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
+
+error: expected item, found `:`
+  --> $DIR/issue-103869.rs:2:8
+   |
+LL |     vec: Vec<usize>,
+   |        ^ expected item
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/parser/macro/issue-37113.stderr b/src/test/ui/parser/macro/issue-37113.stderr
index b1f8674fbdf..da9e743a0b4 100644
--- a/src/test/ui/parser/macro/issue-37113.stderr
+++ b/src/test/ui/parser/macro/issue-37113.stderr
@@ -9,6 +9,7 @@ LL |             $( $t, )*
 LL |     test_macro!(String,);
    |     -------------------- in this macro invocation
    |
+   = help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
    = note: this error originates in the macro `test_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error
diff --git a/src/test/ui/structs/struct-fn-in-definition.rs b/src/test/ui/structs/struct-fn-in-definition.rs
index 5ae1b727dc7..7f48f55fec9 100644
--- a/src/test/ui/structs/struct-fn-in-definition.rs
+++ b/src/test/ui/structs/struct-fn-in-definition.rs
@@ -28,6 +28,7 @@ enum E {
     //~^ ERROR functions are not allowed in enum definitions
     //~| HELP unlike in C++, Java, and C#, functions are declared in `impl` blocks
     //~| HELP see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
+    //~| HELP enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
 }
 
 fn main() {}
diff --git a/src/test/ui/structs/struct-fn-in-definition.stderr b/src/test/ui/structs/struct-fn-in-definition.stderr
index 472365c6ed7..439c86ec22b 100644
--- a/src/test/ui/structs/struct-fn-in-definition.stderr
+++ b/src/test/ui/structs/struct-fn-in-definition.stderr
@@ -33,6 +33,7 @@ LL |     fn foo() {}
    |
    = help: unlike in C++, Java, and C#, functions are declared in `impl` blocks
    = help: see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
+   = help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.rs b/src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.rs
new file mode 100644
index 00000000000..dcdbd022873
--- /dev/null
+++ b/src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.rs
@@ -0,0 +1,38 @@
+trait Trait<T> {
+    fn foo<'a, K>(self, _: T, _: K) where T: 'a, K: 'a;
+}
+
+impl Trait<()> for () {
+    fn foo<'a, K>(self, _: (), _: K) where { //~ ERROR E0195
+        todo!();
+    }
+}
+
+struct State;
+
+trait Foo<T> {
+    fn foo<'a>(&self, state: &'a State) -> &'a T
+    where
+        T: 'a;
+}
+
+impl<F, T> Foo<T> for F
+where
+    F: Fn(&State) -> &T,
+{
+    fn foo<'a>(&self, state: &'a State) -> &'a T { //~ ERROR E0195
+        self(state)
+    }
+}
+
+trait Bar {
+    fn foo<'a>(&'a self) {}
+}
+
+impl Bar for () {
+    fn foo<'a: 'a>(&'a self) {} //~ ERROR E0195
+}
+
+fn main() {
+    ().foo((), ());
+}
diff --git a/src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr b/src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr
new file mode 100644
index 00000000000..e26cb22163f
--- /dev/null
+++ b/src/test/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr
@@ -0,0 +1,36 @@
+error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
+  --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:6:11
+   |
+LL |     fn foo<'a, K>(self, _: T, _: K) where T: 'a, K: 'a;
+   |           -------                            --     -- this bound might be missing in the impl
+   |           |                                  |
+   |           |                                  this bound might be missing in the impl
+   |           lifetimes in impl do not match this method in trait
+...
+LL |     fn foo<'a, K>(self, _: (), _: K) where {
+   |           ^^^^^^^ lifetimes do not match method in trait
+
+error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
+  --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:23:11
+   |
+LL |     fn foo<'a>(&self, state: &'a State) -> &'a T
+   |           ---- lifetimes in impl do not match this method in trait
+LL |     where
+LL |         T: 'a;
+   |            -- this bound might be missing in the impl
+...
+LL |     fn foo<'a>(&self, state: &'a State) -> &'a T {
+   |           ^^^^ lifetimes do not match method in trait
+
+error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
+  --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:33:11
+   |
+LL |     fn foo<'a>(&'a self) {}
+   |           ---- lifetimes in impl do not match this method in trait
+...
+LL |     fn foo<'a: 'a>(&'a self) {}
+   |           ^^^^^^^^ lifetimes do not match method in trait
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0195`.
diff --git a/src/test/ui/typeck/path-to-method-sugg-unresolved-expr.rs b/src/test/ui/typeck/path-to-method-sugg-unresolved-expr.rs
new file mode 100644
index 00000000000..fb56b394493
--- /dev/null
+++ b/src/test/ui/typeck/path-to-method-sugg-unresolved-expr.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let page_size = page_size::get();
+    //~^ ERROR failed to resolve: use of undeclared crate or module `page_size`
+}
diff --git a/src/test/ui/typeck/path-to-method-sugg-unresolved-expr.stderr b/src/test/ui/typeck/path-to-method-sugg-unresolved-expr.stderr
new file mode 100644
index 00000000000..b01e30be54d
--- /dev/null
+++ b/src/test/ui/typeck/path-to-method-sugg-unresolved-expr.stderr
@@ -0,0 +1,9 @@
+error[E0433]: failed to resolve: use of undeclared crate or module `page_size`
+  --> $DIR/path-to-method-sugg-unresolved-expr.rs:2:21
+   |
+LL |     let page_size = page_size::get();
+   |                     ^^^^^^^^^ use of undeclared crate or module `page_size`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0433`.
diff --git a/src/tools/collect-license-metadata/Cargo.toml b/src/tools/collect-license-metadata/Cargo.toml
new file mode 100644
index 00000000000..d0820cfc2a0
--- /dev/null
+++ b/src/tools/collect-license-metadata/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "collect-license-metadata"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0.65"
+serde = { version = "1.0.147", features = ["derive"] }
+serde_json = "1.0.85"
+spdx-rs = "0.5.1"
diff --git a/src/tools/collect-license-metadata/src/licenses.rs b/src/tools/collect-license-metadata/src/licenses.rs
new file mode 100644
index 00000000000..1c95b1bc8e9
--- /dev/null
+++ b/src/tools/collect-license-metadata/src/licenses.rs
@@ -0,0 +1,65 @@
+use std::collections::HashMap;
+
+const COPYRIGHT_PREFIXES: &[&str] = &["SPDX-FileCopyrightText:", "Copyright", "(c)", "(C)", "©"];
+
+pub(crate) struct LicensesInterner {
+    by_id: Vec<License>,
+    by_struct: HashMap<License, usize>,
+}
+
+impl LicensesInterner {
+    pub(crate) fn new() -> Self {
+        LicensesInterner { by_id: Vec::new(), by_struct: HashMap::new() }
+    }
+
+    pub(crate) fn intern(&mut self, mut license: License) -> LicenseId {
+        license.simplify();
+        if let Some(id) = self.by_struct.get(&license) {
+            LicenseId(*id)
+        } else {
+            let id = self.by_id.len();
+            self.by_id.push(license.clone());
+            self.by_struct.insert(license, id);
+            LicenseId(id)
+        }
+    }
+
+    pub(crate) fn resolve(&self, id: LicenseId) -> &License {
+        &self.by_id[id.0]
+    }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
+#[serde(transparent)]
+pub(crate) struct LicenseId(usize);
+
+#[derive(Clone, Hash, PartialEq, Eq, serde::Serialize)]
+pub(crate) struct License {
+    pub(crate) spdx: String,
+    pub(crate) copyright: Vec<String>,
+}
+
+impl License {
+    fn simplify(&mut self) {
+        self.remove_copyright_prefixes();
+        self.copyright.sort();
+        self.copyright.dedup();
+    }
+
+    fn remove_copyright_prefixes(&mut self) {
+        for copyright in &mut self.copyright {
+            let mut stripped = copyright.trim();
+            let mut previous_stripped;
+            loop {
+                previous_stripped = stripped;
+                for pattern in COPYRIGHT_PREFIXES {
+                    stripped = stripped.trim_start_matches(pattern).trim_start();
+                }
+                if stripped == previous_stripped {
+                    break;
+                }
+            }
+            *copyright = stripped.into();
+        }
+    }
+}
diff --git a/src/tools/collect-license-metadata/src/main.rs b/src/tools/collect-license-metadata/src/main.rs
new file mode 100644
index 00000000000..ca2a6f4b8c8
--- /dev/null
+++ b/src/tools/collect-license-metadata/src/main.rs
@@ -0,0 +1,30 @@
+mod licenses;
+mod path_tree;
+mod reuse;
+
+use crate::licenses::LicensesInterner;
+use anyhow::Error;
+use std::path::PathBuf;
+
+fn main() -> Result<(), Error> {
+    let reuse_exe: PathBuf = std::env::var_os("REUSE_EXE").expect("Missing REUSE_EXE").into();
+    let dest: PathBuf = std::env::var_os("DEST").expect("Missing DEST").into();
+
+    let mut interner = LicensesInterner::new();
+    let paths = crate::reuse::collect(&reuse_exe, &mut interner)?;
+
+    let mut tree = crate::path_tree::build(paths);
+    tree.simplify();
+
+    if let Some(parent) = dest.parent() {
+        std::fs::create_dir_all(parent)?;
+    }
+    std::fs::write(
+        &dest,
+        &serde_json::to_vec_pretty(&serde_json::json!({
+            "files": crate::path_tree::expand_interned_licenses(tree, &interner),
+        }))?,
+    )?;
+
+    Ok(())
+}
diff --git a/src/tools/collect-license-metadata/src/path_tree.rs b/src/tools/collect-license-metadata/src/path_tree.rs
new file mode 100644
index 00000000000..133ff683737
--- /dev/null
+++ b/src/tools/collect-license-metadata/src/path_tree.rs
@@ -0,0 +1,294 @@
+//! Tools like REUSE output per-file licensing information, but we need to condense it in the
+//! minimum amount of data that still represents the same licensing metadata. This module is
+//! responsible for that, by turning the list of paths into a tree and executing simplification
+//! passes over the tree to remove redundant information.
+
+use crate::licenses::{License, LicenseId, LicensesInterner};
+use std::collections::BTreeMap;
+use std::path::{Path, PathBuf};
+
+#[derive(serde::Serialize)]
+#[serde(rename_all = "kebab-case", tag = "type")]
+pub(crate) enum Node<L> {
+    Root { childs: Vec<Node<L>> },
+    Directory { name: PathBuf, childs: Vec<Node<L>>, license: Option<L> },
+    File { name: PathBuf, license: L },
+    FileGroup { names: Vec<PathBuf>, license: L },
+    Empty,
+}
+
+impl Node<LicenseId> {
+    pub(crate) fn simplify(&mut self) {
+        self.merge_directories();
+        self.collapse_in_licensed_directories();
+        self.merge_directory_licenses();
+        self.merge_file_groups();
+        self.remove_empty();
+    }
+
+    /// Initially, the build() function constructs a list of separate paths from the file
+    /// system root down to each file, like so:
+    ///
+    /// ```text
+    ///         ┌─► ./ ──► compiler/ ──► rustc/ ──► src/ ──► main.rs
+    ///         │
+    /// <root> ─┼─► ./ ──► compiler/ ──► rustc/ ──► Cargo.toml
+    ///         │
+    ///         └─► ./ ──► library/ ───► std/ ──► Cargo.toml
+    /// ```
+    ///
+    /// This pass is responsible for turning that into a proper directory tree:
+    ///
+    /// ```text
+    ///                 ┌─► compiler/ ──► rustc/ ──┬─► src/ ──► main.rs
+    ///                 │                          │
+    /// <root> ──► ./ ──┤                          └─► Cargo.toml
+    ///                 │
+    ///                 └─► library/ ───► std/ ──► Cargo.toml
+    /// ```
+    fn merge_directories(&mut self) {
+        match self {
+            Node::Root { childs } | Node::Directory { childs, license: None, .. } => {
+                let mut directories = BTreeMap::new();
+                let mut files = Vec::new();
+
+                for child in childs.drain(..) {
+                    match child {
+                        Node::Directory { name, mut childs, license: None } => {
+                            directories.entry(name).or_insert_with(Vec::new).append(&mut childs);
+                        }
+                        file @ Node::File { .. } => {
+                            files.push(file);
+                        }
+                        Node::Empty => {}
+                        Node::Root { .. } => {
+                            panic!("can't have a root inside another element");
+                        }
+                        Node::FileGroup { .. } => {
+                            panic!("FileGroup should not be present at this stage");
+                        }
+                        Node::Directory { license: Some(_), .. } => {
+                            panic!("license should not be set at this stage");
+                        }
+                    }
+                }
+
+                childs.extend(directories.into_iter().map(|(name, childs)| Node::Directory {
+                    name,
+                    childs,
+                    license: None,
+                }));
+                childs.append(&mut files);
+
+                for child in &mut *childs {
+                    child.merge_directories();
+                }
+            }
+            Node::Empty => {}
+            Node::File { .. } => {}
+            Node::FileGroup { .. } => {
+                panic!("FileGroup should not be present at this stage");
+            }
+            Node::Directory { license: Some(_), .. } => {
+                panic!("license should not be set at this stage");
+            }
+        }
+    }
+
+    /// In our codebase, most files in a directory have the same license as the other files in that
+    /// same directory, so it's redundant to store licensing metadata for all the files. Instead,
+    /// we can add a license for a whole directory, and only record the exceptions to a directory
+    /// licensing metadata.
+    ///
+    /// We cannot instead record only the difference to Rust's standard licensing, as the majority
+    /// of the files in our repository are *not* licensed under Rust's standard licensing due to
+    /// our inclusion of LLVM.
+    fn collapse_in_licensed_directories(&mut self) {
+        match self {
+            Node::Directory { childs, license, .. } => {
+                for child in &mut *childs {
+                    child.collapse_in_licensed_directories();
+                }
+
+                let mut licenses_count = BTreeMap::new();
+                for child in &*childs {
+                    let Some(license) = child.license() else { continue };
+                    *licenses_count.entry(license).or_insert(0) += 1;
+                }
+
+                let most_popular_license = licenses_count
+                    .into_iter()
+                    .max_by_key(|(_, count)| *count)
+                    .map(|(license, _)| license);
+
+                if let Some(most_popular_license) = most_popular_license {
+                    childs.retain(|child| child.license() != Some(most_popular_license));
+                    *license = Some(most_popular_license);
+                }
+            }
+            Node::Root { childs } => {
+                for child in &mut *childs {
+                    child.collapse_in_licensed_directories();
+                }
+            }
+            Node::File { .. } => {}
+            Node::FileGroup { .. } => {}
+            Node::Empty => {}
+        }
+    }
+
+    /// Reduce the depth of the tree by merging subdirectories with the same license as their
+    /// parent directory into their parent, and adjusting the paths of the childs accordingly.
+    fn merge_directory_licenses(&mut self) {
+        match self {
+            Node::Root { childs } => {
+                for child in &mut *childs {
+                    child.merge_directory_licenses();
+                }
+            }
+            Node::Directory { childs, license, .. } => {
+                let mut to_add = Vec::new();
+                for child in &mut *childs {
+                    child.merge_directory_licenses();
+
+                    let Node::Directory {
+                        name: child_name,
+                        childs: child_childs,
+                        license: child_license,
+                    } = child else { continue };
+
+                    if child_license != license {
+                        continue;
+                    }
+                    for mut child_child in child_childs.drain(..) {
+                        match &mut child_child {
+                            Node::Root { .. } => {
+                                panic!("can't have a root inside another element");
+                            }
+                            Node::FileGroup { .. } => {
+                                panic!("FileGroup should not be present at this stage");
+                            }
+                            Node::Directory { name: child_child_name, .. } => {
+                                *child_child_name = child_name.join(&child_child_name);
+                            }
+                            Node::File { name: child_child_name, .. } => {
+                                *child_child_name = child_name.join(&child_child_name);
+                            }
+                            Node::Empty => {}
+                        }
+                        to_add.push(child_child);
+                    }
+
+                    *child = Node::Empty;
+                }
+                childs.append(&mut to_add);
+            }
+            Node::Empty => {}
+            Node::File { .. } => {}
+            Node::FileGroup { .. } => {}
+        }
+    }
+
+    /// This pass groups multiple files in a directory with the same license into a single
+    /// "FileGroup", so that the license of all those files can be reported as a group.
+    ///
+    /// Crucially this pass runs after collapse_in_licensed_directories, so the most common license
+    /// will already be marked as the directory's license and won't be turned into a group.
+    fn merge_file_groups(&mut self) {
+        match self {
+            Node::Root { childs } | Node::Directory { childs, .. } => {
+                let mut grouped = BTreeMap::new();
+
+                for child in &mut *childs {
+                    child.merge_file_groups();
+                    if let Node::File { name, license } = child {
+                        grouped.entry(*license).or_insert_with(Vec::new).push(name.clone());
+                        *child = Node::Empty;
+                    }
+                }
+
+                for (license, mut names) in grouped.into_iter() {
+                    if names.len() == 1 {
+                        childs.push(Node::File { license, name: names.pop().unwrap() });
+                    } else {
+                        childs.push(Node::FileGroup { license, names });
+                    }
+                }
+            }
+            Node::File { .. } => {}
+            Node::FileGroup { .. } => panic!("FileGroup should not be present at this stage"),
+            Node::Empty => {}
+        }
+    }
+
+    /// Some nodes were replaced with Node::Empty to mark them for deletion. As the last step, make
+    /// sure to remove them from the tree.
+    fn remove_empty(&mut self) {
+        match self {
+            Node::Root { childs } | Node::Directory { childs, .. } => {
+                for child in &mut *childs {
+                    child.remove_empty();
+                }
+                childs.retain(|child| !matches!(child, Node::Empty));
+            }
+            Node::FileGroup { .. } => {}
+            Node::File { .. } => {}
+            Node::Empty => {}
+        }
+    }
+
+    fn license(&self) -> Option<LicenseId> {
+        match self {
+            Node::Directory { childs, license: Some(license), .. } if childs.is_empty() => {
+                Some(*license)
+            }
+            Node::File { license, .. } => Some(*license),
+            _ => None,
+        }
+    }
+}
+
+pub(crate) fn build(mut input: Vec<(PathBuf, LicenseId)>) -> Node<LicenseId> {
+    let mut childs = Vec::new();
+
+    // Ensure reproducibility of all future steps.
+    input.sort();
+
+    for (path, license) in input {
+        let mut node = Node::File { name: path.file_name().unwrap().into(), license };
+        for component in path.parent().unwrap_or_else(|| Path::new(".")).components().rev() {
+            node = Node::Directory {
+                name: component.as_os_str().into(),
+                childs: vec![node],
+                license: None,
+            };
+        }
+
+        childs.push(node);
+    }
+
+    Node::Root { childs }
+}
+
+/// Convert a `Node<LicenseId>` into a `Node<&License>`, expanding all interned license IDs with a
+/// reference to the actual license metadata.
+pub(crate) fn expand_interned_licenses(
+    node: Node<LicenseId>,
+    interner: &LicensesInterner,
+) -> Node<&License> {
+    match node {
+        Node::Root { childs } => Node::Root {
+            childs: childs.into_iter().map(|child| strip_interning(child, interner)).collect(),
+        },
+        Node::Directory { name, childs, license } => Node::Directory {
+            childs: childs.into_iter().map(|child| strip_interning(child, interner)).collect(),
+            license: license.map(|license| interner.resolve(license)),
+            name,
+        },
+        Node::File { name, license } => Node::File { name, license: interner.resolve(license) },
+        Node::FileGroup { names, license } => {
+            Node::FileGroup { names, license: interner.resolve(license) }
+        }
+        Node::Empty => Node::Empty,
+    }
+}
diff --git a/src/tools/collect-license-metadata/src/reuse.rs b/src/tools/collect-license-metadata/src/reuse.rs
new file mode 100644
index 00000000000..d6b3772ba51
--- /dev/null
+++ b/src/tools/collect-license-metadata/src/reuse.rs
@@ -0,0 +1,49 @@
+use crate::licenses::{License, LicenseId, LicensesInterner};
+use anyhow::Error;
+use std::path::{Path, PathBuf};
+use std::process::{Command, Stdio};
+use std::time::Instant;
+
+pub(crate) fn collect(
+    reuse_exe: &Path,
+    interner: &mut LicensesInterner,
+) -> Result<Vec<(PathBuf, LicenseId)>, Error> {
+    eprintln!("gathering license information from REUSE");
+    let start = Instant::now();
+    let raw = &obtain_spdx_document(reuse_exe)?;
+    eprintln!("finished gathering the license information from REUSE in {:.2?}", start.elapsed());
+
+    let document = spdx_rs::parsers::spdx_from_tag_value(&raw)?;
+
+    let mut result = Vec::new();
+    for file in document.file_information {
+        let license = interner.intern(License {
+            spdx: file.concluded_license.to_string(),
+            copyright: file.copyright_text.split('\n').map(|s| s.into()).collect(),
+        });
+
+        result.push((file.file_name.into(), license));
+    }
+
+    Ok(result)
+}
+
+fn obtain_spdx_document(reuse_exe: &Path) -> Result<String, Error> {
+    let output = Command::new(reuse_exe)
+        .args(&["spdx", "--add-license-concluded", "--creator-person=bors"])
+        .stdout(Stdio::piped())
+        .spawn()?
+        .wait_with_output()?;
+
+    if !output.status.success() {
+        eprintln!();
+        eprintln!("Note that Rust requires some REUSE features that might not be present in the");
+        eprintln!("release you're using. Make sure your REUSE release includes these PRs:");
+        eprintln!();
+        eprintln!(" - https://github.com/fsfe/reuse-tool/pull/623");
+        eprintln!();
+        anyhow::bail!("collecting licensing information with REUSE failed");
+    }
+
+    Ok(String::from_utf8(output.stdout)?)
+}
diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml
new file mode 100644
index 00000000000..899ef0f8a6c
--- /dev/null
+++ b/src/tools/generate-copyright/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "generate-copyright"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+anyhow = "1.0.65"
+serde = { version = "1.0.147", features = ["derive"] }
+serde_json = "1.0.85"
diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs
new file mode 100644
index 00000000000..d172c9e157b
--- /dev/null
+++ b/src/tools/generate-copyright/src/main.rs
@@ -0,0 +1,94 @@
+use anyhow::Error;
+use std::io::Write;
+use std::path::PathBuf;
+
+fn main() -> Result<(), Error> {
+    let dest = env_path("DEST")?;
+    let license_metadata = env_path("LICENSE_METADATA")?;
+
+    let metadata: Metadata = serde_json::from_slice(&std::fs::read(&license_metadata)?)?;
+
+    let mut buffer = Vec::new();
+    render_recursive(&metadata.files, &mut buffer, 0)?;
+
+    std::fs::write(&dest, &buffer)?;
+
+    Ok(())
+}
+
+fn render_recursive(node: &Node, buffer: &mut Vec<u8>, depth: usize) -> Result<(), Error> {
+    let prefix = std::iter::repeat("> ").take(depth + 1).collect::<String>();
+
+    match node {
+        Node::Root { childs } => {
+            for child in childs {
+                render_recursive(child, buffer, depth)?;
+            }
+        }
+        Node::Directory { name, childs, license } => {
+            render_license(&prefix, std::iter::once(name), license, buffer)?;
+            if !childs.is_empty() {
+                writeln!(buffer, "{prefix}")?;
+                writeln!(buffer, "{prefix}*Exceptions:*")?;
+                for child in childs {
+                    writeln!(buffer, "{prefix}")?;
+                    render_recursive(child, buffer, depth + 1)?;
+                }
+            }
+        }
+        Node::FileGroup { names, license } => {
+            render_license(&prefix, names.iter(), license, buffer)?;
+        }
+        Node::File { name, license } => {
+            render_license(&prefix, std::iter::once(name), license, buffer)?;
+        }
+    }
+
+    Ok(())
+}
+
+fn render_license<'a>(
+    prefix: &str,
+    names: impl Iterator<Item = &'a String>,
+    license: &License,
+    buffer: &mut Vec<u8>,
+) -> Result<(), Error> {
+    for name in names {
+        writeln!(buffer, "{prefix}**`{name}`**  ")?;
+    }
+    writeln!(buffer, "{prefix}License: `{}`  ", license.spdx)?;
+    for (i, copyright) in license.copyright.iter().enumerate() {
+        let suffix = if i == license.copyright.len() - 1 { "" } else { "  " };
+        writeln!(buffer, "{prefix}Copyright: {copyright}{suffix}")?;
+    }
+
+    Ok(())
+}
+
+#[derive(serde::Deserialize)]
+struct Metadata {
+    files: Node,
+}
+
+#[derive(serde::Deserialize)]
+#[serde(rename_all = "kebab-case", tag = "type")]
+pub(crate) enum Node {
+    Root { childs: Vec<Node> },
+    Directory { name: String, childs: Vec<Node>, license: License },
+    File { name: String, license: License },
+    FileGroup { names: Vec<String>, license: License },
+}
+
+#[derive(serde::Deserialize)]
+struct License {
+    spdx: String,
+    copyright: Vec<String>,
+}
+
+fn env_path(var: &str) -> Result<PathBuf, Error> {
+    if let Some(var) = std::env::var_os(var) {
+        Ok(var.into())
+    } else {
+        anyhow::bail!("missing environment variable {var}")
+    }
+}