about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs6
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs26
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs48
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs118
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs29
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs38
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs23
9 files changed, 192 insertions, 108 deletions
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 76c9ac6614a..f961cd2d00b 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -461,6 +461,7 @@ pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll D
         }
         ty::FnDef(..) | ty::FnPtr(_) => build_subroutine_type_di_node(cx, unique_type_id),
         ty::Closure(..) => build_closure_env_di_node(cx, unique_type_id),
+        ty::CoroutineClosure(..) => build_closure_env_di_node(cx, unique_type_id),
         ty::Coroutine(..) => enums::build_coroutine_di_node(cx, unique_type_id),
         ty::Adt(def, ..) => match def.adt_kind() {
             AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id),
@@ -1068,6 +1069,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>(
     let (&def_id, up_var_tys) = match closure_or_coroutine_ty.kind() {
         ty::Coroutine(def_id, args) => (def_id, args.as_coroutine().prefix_tys()),
         ty::Closure(def_id, args) => (def_id, args.as_closure().upvar_tys()),
+        ty::CoroutineClosure(def_id, args) => (def_id, args.as_coroutine_closure().upvar_tys()),
         _ => {
             bug!(
                 "build_upvar_field_di_nodes() called with non-closure-or-coroutine-type: {:?}",
@@ -1153,7 +1155,8 @@ fn build_closure_env_di_node<'ll, 'tcx>(
     unique_type_id: UniqueTypeId<'tcx>,
 ) -> DINodeCreationResult<'ll> {
     let closure_env_type = unique_type_id.expect_ty();
-    let &ty::Closure(def_id, _args) = closure_env_type.kind() else {
+    let &(ty::Closure(def_id, _) | ty::CoroutineClosure(def_id, _)) = closure_env_type.kind()
+    else {
         bug!("build_closure_env_di_node() called with non-closure-type: {:?}", closure_env_type)
     };
     let containing_scope = get_namespace_for_item(cx, def_id);
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index b4512af38e3..e3e48ecb3aa 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -1985,10 +1985,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
 
         match in_elem.kind() {
             ty::RawPtr(p) => {
-                let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
+                let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
                     bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
                 });
-                assert!(!check_sized); // we are in codegen, so we shouldn't see these types
                 require!(
                     metadata.is_unit(),
                     InvalidMonomorphization::CastFatPointer { span, name, ty: in_elem }
@@ -2000,10 +1999,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
         }
         match out_elem.kind() {
             ty::RawPtr(p) => {
-                let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
+                let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
                     bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
                 });
-                assert!(!check_sized); // we are in codegen, so we shouldn't see these types
                 require!(
                     metadata.is_unit(),
                     InvalidMonomorphization::CastFatPointer { span, name, ty: out_elem }
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 85a2e4778d2..ff20fc5092c 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -377,12 +377,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // to fields, which can yield non-normalized types. So we need to provide a
                 // normalization function.
                 let normalize = |ty| self.tcx.normalize_erasing_regions(self.param_env, ty);
-                let (meta, only_if_sized) = ty.ptr_metadata_ty(*self.tcx, normalize);
-                assert!(
-                    !only_if_sized,
-                    "there should be no more 'maybe has that metadata' types during interpretation"
-                );
-                meta
+                ty.ptr_metadata_ty(*self.tcx, normalize)
             };
             return Ok(meta_ty(caller) == meta_ty(callee));
         }
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 58823ea30ce..f21de1609cb 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -448,13 +448,35 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                             );
                         }
                     }
-                    let msg = "an `as` expression can only be used to convert between primitive \
-                               types or to coerce to a specific trait object";
+
+                    let (msg, note) = if let ty::Adt(adt, _) = self.expr_ty.kind()
+                        && adt.is_enum()
+                        && self.cast_ty.is_numeric()
+                    {
+                        (
+                            "an `as` expression can be used to convert enum types to numeric \
+                             types only if the enum type is unit-only or field-less",
+                            Some(
+                                "see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information",
+                            ),
+                        )
+                    } else {
+                        (
+                            "an `as` expression can only be used to convert between primitive \
+                             types or to coerce to a specific trait object",
+                            None,
+                        )
+                    };
+
                     if label {
                         err.span_label(self.span, msg);
                     } else {
                         err.note(msg);
                     }
+
+                    if let Some(note) = note {
+                        err.note(note);
+                    }
                 } else {
                     err.span_label(self.span, "invalid cast");
                 }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index d6e3385c600..f592acd4b6f 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2279,12 +2279,12 @@ impl<'tcx> Ty<'tcx> {
     }
 
     /// Returns the type of metadata for (potentially fat) pointers to this type,
-    /// and a boolean signifying if this is conditional on this type being `Sized`.
-    pub fn ptr_metadata_ty(
+    /// or the struct tail if the metadata type cannot be determined.
+    pub fn ptr_metadata_ty_or_tail(
         self,
         tcx: TyCtxt<'tcx>,
         normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
-    ) -> (Ty<'tcx>, bool) {
+    ) -> Result<Ty<'tcx>, Ty<'tcx>> {
         let tail = tcx.struct_tail_with_normalize(self, normalize, || {});
         match tail.kind() {
             // Sized types
@@ -2307,31 +2307,47 @@ impl<'tcx> Ty<'tcx> {
             | ty::Error(_)
             // Extern types have metadata = ().
             | ty::Foreign(..)
-            // `dyn*` has no metadata
+            // `dyn*` has metadata = ().
             | ty::Dynamic(_, _, ty::DynStar)
-            // If returned by `struct_tail_without_normalization` this is a unit struct
+            // If returned by `struct_tail_with_normalize` this is a unit struct
             // without any fields, or not a struct, and therefore is Sized.
             | ty::Adt(..)
-            // If returned by `struct_tail_without_normalization` this is the empty tuple,
+            // If returned by `struct_tail_with_normalize` this is the empty tuple,
             // a.k.a. unit type, which is Sized
-            | ty::Tuple(..) => (tcx.types.unit, false),
+            | ty::Tuple(..) => Ok(tcx.types.unit),
+
+            ty::Str | ty::Slice(_) => Ok(tcx.types.usize),
 
-            ty::Str | ty::Slice(_) => (tcx.types.usize, false),
             ty::Dynamic(_, _, ty::Dyn) => {
                 let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
-                (tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]), false)
-            },
+                Ok(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]))
+            }
 
-            // type parameters only have unit metadata if they're sized, so return true
-            // to make sure we double check this during confirmation
-            ty::Param(_) |  ty::Alias(..) => (tcx.types.unit, true),
+            // We don't know the metadata of `self`, but it must be equal to the
+            // metadata of `tail`.
+            ty::Param(_) | ty::Alias(..) => Err(tail),
 
             ty::Infer(ty::TyVar(_))
             | ty::Bound(..)
             | ty::Placeholder(..)
-            | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
-                bug!("`ptr_metadata_ty` applied to unexpected type: {:?} (tail = {:?})", self, tail)
-            }
+            | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(
+                "`ptr_metadata_ty_or_tail` applied to unexpected type: {self:?} (tail = {tail:?})"
+            ),
+        }
+    }
+
+    /// Returns the type of metadata for (potentially fat) pointers to this type.
+    /// Causes an ICE if the metadata type cannot be determined.
+    pub fn ptr_metadata_ty(
+        self,
+        tcx: TyCtxt<'tcx>,
+        normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
+    ) -> Ty<'tcx> {
+        match self.ptr_metadata_ty_or_tail(tcx, normalize) {
+            Ok(metadata) => metadata,
+            Err(tail) => bug!(
+                "`ptr_metadata_ty` failed to get metadata for type: {self:?} (tail = {tail:?})"
+            ),
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 239072dfc8e..3b9515e1670 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -8,7 +8,7 @@ use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::Reveal;
 use rustc_middle::traits::solve::inspect::ProbeKind;
 use rustc_middle::traits::solve::{
-    CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult,
+    CandidateSource, CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult,
 };
 use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams};
@@ -276,25 +276,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         &mut self,
         goal: Goal<'tcx, G>,
     ) -> Vec<Candidate<'tcx>> {
-        let dummy_candidate = |this: &mut EvalCtxt<'_, 'tcx>, certainty| {
-            let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
-            let result = this.evaluate_added_goals_and_make_canonical_response(certainty).unwrap();
-            let mut dummy_probe = this.inspect.new_probe();
-            dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) });
-            this.inspect.finish_probe(dummy_probe);
-            vec![Candidate { source, result }]
-        };
-
         let Some(normalized_self_ty) =
             self.try_normalize_ty(goal.param_env, goal.predicate.self_ty())
         else {
             debug!("overflow while evaluating self type");
-            return dummy_candidate(self, Certainty::OVERFLOW);
+            return self.forced_ambiguity(MaybeCause::Overflow);
         };
 
         if normalized_self_ty.is_ty_var() {
             debug!("self type has been normalized to infer");
-            return dummy_candidate(self, Certainty::AMBIGUOUS);
+            return self.forced_ambiguity(MaybeCause::Ambiguity);
         }
 
         let goal =
@@ -315,11 +306,26 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
 
         self.assemble_param_env_candidates(goal, &mut candidates);
 
-        self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
+        match self.solver_mode() {
+            SolverMode::Normal => self.discard_impls_shadowed_by_env(goal, &mut candidates),
+            SolverMode::Coherence => {
+                self.assemble_coherence_unknowable_candidates(goal, &mut candidates)
+            }
+        }
 
         candidates
     }
 
+    fn forced_ambiguity(&mut self, cause: MaybeCause) -> Vec<Candidate<'tcx>> {
+        let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
+        let certainty = Certainty::Maybe(cause);
+        let result = self.evaluate_added_goals_and_make_canonical_response(certainty).unwrap();
+        let mut dummy_probe = self.inspect.new_probe();
+        dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) });
+        self.inspect.finish_probe(dummy_probe);
+        vec![Candidate { source, result }]
+    }
+
     #[instrument(level = "debug", skip_all)]
     fn assemble_non_blanket_impl_candidates<G: GoalKind<'tcx>>(
         &mut self,
@@ -779,6 +785,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
     }
 
+    /// In coherence we have to not only care about all impls we know about, but
+    /// also consider impls which may get added in a downstream or sibling crate
+    /// or which an upstream impl may add in a minor release.
+    ///
+    /// To do so we add an ambiguous candidate in case such an unknown impl could
+    /// apply to the current goal.
     #[instrument(level = "debug", skip_all)]
     fn assemble_coherence_unknowable_candidates<G: GoalKind<'tcx>>(
         &mut self,
@@ -786,11 +798,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         candidates: &mut Vec<Candidate<'tcx>>,
     ) {
         let tcx = self.tcx();
-        match self.solver_mode() {
-            SolverMode::Normal => return,
-            SolverMode::Coherence => {}
-        };
-
         let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| {
             let trait_ref = goal.predicate.trait_ref(tcx);
             #[derive(Debug)]
@@ -820,6 +827,51 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
     }
 
+    /// If there's a where-bound for the current goal, do not use any impl candidates
+    /// to prove the current goal. Most importantly, if there is a where-bound which does
+    /// not specify any associated types, we do not allow normalizing the associated type
+    /// by using an impl, even if it would apply.
+    ///
+    ///  <https://github.com/rust-lang/trait-system-refactor-initiative/issues/76>
+    // FIXME(@lcnr): The current structure here makes me unhappy and feels ugly. idk how
+    // to improve this however. However, this should make it fairly straightforward to refine
+    // the filtering going forward, so it seems alright-ish for now.
+    fn discard_impls_shadowed_by_env<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let tcx = self.tcx();
+        let trait_goal: Goal<'tcx, ty::TraitPredicate<'tcx>> =
+            goal.with(tcx, goal.predicate.trait_ref(tcx));
+        let mut trait_candidates_from_env = Vec::new();
+        self.assemble_param_env_candidates(trait_goal, &mut trait_candidates_from_env);
+        self.assemble_alias_bound_candidates(trait_goal, &mut trait_candidates_from_env);
+        if !trait_candidates_from_env.is_empty() {
+            let trait_env_result = self.merge_candidates(trait_candidates_from_env);
+            match trait_env_result.unwrap().value.certainty {
+                // If proving the trait goal succeeds by using the env,
+                // we freely drop all impl candidates.
+                //
+                // FIXME(@lcnr): It feels like this could easily hide
+                // a forced ambiguity candidate added earlier.
+                // This feels dangerous.
+                Certainty::Yes => {
+                    candidates.retain(|c| match c.source {
+                        CandidateSource::Impl(_) | CandidateSource::BuiltinImpl(_) => false,
+                        CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => true,
+                    });
+                }
+                // If it is still ambiguous we instead just force the whole goal
+                // to be ambig and wait for inference constraints. See
+                // tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs
+                Certainty::Maybe(cause) => {
+                    *candidates = self.forced_ambiguity(cause);
+                }
+            }
+        }
+    }
+
     /// If there are multiple ways to prove a trait or projection goal, we have
     /// to somehow try to merge the candidates into one. If that fails, we return
     /// ambiguity.
@@ -832,34 +884,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         let responses = candidates.iter().map(|c| c.result).collect::<Vec<_>>();
         if let Some(result) = self.try_merge_responses(&responses) {
             return Ok(result);
+        } else {
+            self.flounder(&responses)
         }
-
-        // We then check whether we should prioritize `ParamEnv` candidates.
-        //
-        // Doing so is incomplete and would therefore be unsound during coherence.
-        match self.solver_mode() {
-            SolverMode::Coherence => (),
-            // Prioritize `ParamEnv` candidates only if they do not guide inference.
-            //
-            // This is still incomplete as we may add incorrect region bounds.
-            SolverMode::Normal => {
-                let param_env_responses = candidates
-                    .iter()
-                    .filter(|c| {
-                        matches!(
-                            c.source,
-                            CandidateSource::ParamEnv(_) | CandidateSource::AliasBound
-                        )
-                    })
-                    .map(|c| c.result)
-                    .collect::<Vec<_>>();
-                if let Some(result) = self.try_merge_responses(&param_env_responses) {
-                    // We strongly prefer alias and param-env bounds here, even if they affect inference.
-                    // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/11.
-                    return Ok(result);
-                }
-            }
-        }
-        self.flounder(&responses)
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
index 47ba549022d..d177109c420 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -491,6 +491,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx> {
         let tcx = ecx.tcx();
+        let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
+        assert_eq!(metadata_def_id, goal.predicate.def_id());
         ecx.probe_misc_candidate("builtin pointee").enter(|ecx| {
             let metadata_ty = match goal.predicate.self_ty().kind() {
                 ty::Bool
@@ -522,7 +524,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                 }
 
                 ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
-                    // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints.
+                    // This is the "fallback impl" for type parameters, unnormalizable projections
+                    // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`.
+                    // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't
+                    // exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
                     let sized_predicate = ty::TraitRef::from_lang_item(
                         tcx,
                         LangItem::Sized,
@@ -536,30 +541,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
 
                 ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() {
                     None => tcx.types.unit,
-                    Some(field_def) => {
-                        let self_ty = field_def.ty(tcx, args);
-                        // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
-                        ecx.add_goal(
-                            GoalSource::Misc,
-                            goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)),
-                        );
-                        return ecx
-                            .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
+                    Some(tail_def) => {
+                        let tail_ty = tail_def.ty(tcx, args);
+                        Ty::new_projection(tcx, metadata_def_id, [tail_ty])
                     }
                 },
                 ty::Adt(_, _) => tcx.types.unit,
 
                 ty::Tuple(elements) => match elements.last() {
                     None => tcx.types.unit,
-                    Some(&self_ty) => {
-                        // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
-                        ecx.add_goal(
-                            GoalSource::Misc,
-                            goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)),
-                        );
-                        return ecx
-                            .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
-                    }
+                    Some(&tail_ty) => Ty::new_projection(tcx, metadata_def_id, [tail_ty]),
                 },
 
                 ty::Infer(
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index fd8306bbc0b..d271a0ea33b 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1935,10 +1935,11 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                         // Integers and floats are always Sized, and so have unit type metadata.
                         | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,
 
-                        // type parameters, opaques, and unnormalized projections have pointer
-                        // metadata if they're known (e.g. by the param_env) to be sized
+                        // We normalize from `Wrapper<Tail>::Metadata` to `Tail::Metadata` if able.
+                        // Otherwise, type parameters, opaques, and unnormalized projections have
+                        // unit metadata if they're known (e.g. by the param_env) to be sized.
                         ty::Param(_) | ty::Alias(..)
-                            if selcx.infcx.predicate_must_hold_modulo_regions(
+                            if self_ty != tail || selcx.infcx.predicate_must_hold_modulo_regions(
                                 &obligation.with(
                                     selcx.tcx(),
                                     ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]),
@@ -2312,7 +2313,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
         assert_eq!(metadata_def_id, item_def_id);
 
         let mut obligations = Vec::new();
-        let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| {
+        let normalize = |ty| {
             normalize_with_depth_to(
                 selcx,
                 obligation.param_env,
@@ -2321,16 +2322,27 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
                 ty,
                 &mut obligations,
             )
+        };
+        let metadata_ty = self_ty.ptr_metadata_ty_or_tail(tcx, normalize).unwrap_or_else(|tail| {
+            if tail == self_ty {
+                // This is the "fallback impl" for type parameters, unnormalizable projections
+                // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`.
+                // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't
+                // exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
+                let sized_predicate = ty::TraitRef::from_lang_item(
+                    tcx,
+                    LangItem::Sized,
+                    obligation.cause.span(),
+                    [self_ty],
+                );
+                obligations.push(obligation.with(tcx, sized_predicate));
+                tcx.types.unit
+            } else {
+                // We know that `self_ty` has the same metadata as `tail`. This allows us
+                // to prove predicates like `Wrapper<Tail>::Metadata == Tail::Metadata`.
+                Ty::new_projection(tcx, metadata_def_id, [tail])
+            }
         });
-        if check_is_sized {
-            let sized_predicate = ty::TraitRef::from_lang_item(
-                tcx,
-                LangItem::Sized,
-                obligation.cause.span(),
-                [self_ty],
-            );
-            obligations.push(obligation.with(tcx, sized_predicate));
-        }
         (metadata_ty.into(), obligations)
     } else {
         bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate);
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
index 4c0c57377e0..a050b30317a 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -41,7 +41,28 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
     /// not entirely accurate if inference variables are involved.
     ///
     /// This version may conservatively fail when outlives obligations
-    /// are required.
+    /// are required. Therefore, this version should only be used for
+    /// optimizations or diagnostics and be treated as if it can always
+    /// return `false`.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # #![allow(dead_code)]
+    /// trait Trait {}
+    ///
+    /// fn check<T: Trait>() {}
+    ///
+    /// fn foo<T: 'static>()
+    /// where
+    ///     &'static T: Trait,
+    /// {
+    ///     // Evaluating `&'?0 T: Trait` adds a `'?0: 'static` outlives obligation,
+    ///     // which means that `predicate_must_hold_considering_regions` will return
+    ///     // `false`.
+    ///     check::<&'_ T>();
+    /// }
+    /// ```
     fn predicate_must_hold_considering_regions(
         &self,
         obligation: &PredicateObligation<'tcx>,