about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2025-03-26 18:57:13 +0000
committerMichael Goulet <michael@errs.io>2025-04-09 20:26:57 +0000
commit27836e1e5794dfa8db8565adaf7aed05d8bfff92 (patch)
tree7d36dcf9ef0069efb57761e667d2ced8f1b29fa3
parent48f89e7659678f91a68c0c2d868180a0036ab32d (diff)
downloadrust-27836e1e5794dfa8db8565adaf7aed05d8bfff92.tar.gz
rust-27836e1e5794dfa8db8565adaf7aed05d8bfff92.zip
Rigidly project missing item due to guaranteed impossible sized predicate
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs8
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs37
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs104
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs33
-rw-r--r--tests/ui/traits/trivial-unsized-projection.bad.stderr44
-rw-r--r--tests/ui/traits/trivial-unsized-projection.bad_new.stderr61
-rw-r--r--tests/ui/traits/trivial-unsized-projection.rs34
8 files changed, 281 insertions, 52 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 18ef00dc8b1..5b89dcf7dd8 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -943,7 +943,7 @@ fn check_impl_items_against_trait<'tcx>(
     let cause = ObligationCause::misc(tcx.def_span(impl_id), impl_id);
     let param_env = tcx.param_env(impl_id);
 
-    let self_is_guaranteed_unsized = match tcx
+    let self_is_guaranteed_unsized = tcx
         .struct_tail_raw(
             trait_ref.self_ty(),
             |ty| {
@@ -957,11 +957,7 @@ fn check_impl_items_against_trait<'tcx>(
             },
             || (),
         )
-        .kind()
-    {
-        ty::Dynamic(_, _, ty::DynKind::Dyn) | ty::Slice(_) | ty::Str => true,
-        _ => false,
-    };
+        .is_guaranteed_unsized_raw();
 
     for &impl_item in impl_item_refs {
         let ty_impl_item = tcx.associated_item(impl_item);
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 27ee363f1c1..fb15ab8d848 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2029,6 +2029,43 @@ impl<'tcx> Ty<'tcx> {
     pub fn is_known_rigid(self) -> bool {
         self.kind().is_known_rigid()
     }
+
+    /// Returns true if the type is guaranteed to be one of the three built-in unsized types:
+    /// `dyn Trait`/`[T]`/`str`. This function is *raw* because it does not compute the struct
+    /// tail of the type, so you are responsible for doing that yourself.
+    // NOTE: Keep this in sync with `rustc_type_ir`'s copy.
+    pub fn is_guaranteed_unsized_raw(self) -> bool {
+        match self.kind() {
+            Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true,
+            Bool
+            | Char
+            | Int(_)
+            | Uint(_)
+            | Float(_)
+            | Adt(_, _)
+            | Foreign(_)
+            | Array(_, _)
+            | Pat(_, _)
+            | RawPtr(_, _)
+            | Ref(_, _, _)
+            | FnDef(_, _)
+            | FnPtr(_, _)
+            | UnsafeBinder(_)
+            | Closure(_, _)
+            | CoroutineClosure(_, _)
+            | Coroutine(_, _)
+            | CoroutineWitness(_, _)
+            | Never
+            | Tuple(_)
+            | Alias(_, _)
+            | Param(_)
+            | Bound(_, _)
+            | Placeholder(_)
+            | Infer(_)
+            | Error(_)
+            | Dynamic(_, _, ty::DynStar) => false,
+        }
+    }
 }
 
 impl<'tcx> rustc_type_ir::inherent::Tys<TyCtxt<'tcx>> for &'tcx ty::List<Ty<'tcx>> {
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index de6d21da0f5..4cfc727d6b7 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -232,7 +232,17 @@ where
             };
 
             if !cx.has_item_definition(target_item_def_id) {
-                return error_response(ecx, cx.delay_bug("missing item"));
+                // If the impl is missing an item, it's either because the user forgot to
+                // provide it, or the user is not *obligated* to provide it (because it
+                // has a trivially false `Sized` predicate). If it's the latter, we cannot
+                // delay a bug because we can have trivially false where clauses, so we
+                // treat it as rigid.
+                if goal_trait_ref.self_ty().is_guaranteed_unsized_raw() {
+                    ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
+                    return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
+                } else {
+                    return error_response(ecx, cx.delay_bug("missing item"));
+                }
             }
 
             let target_container_def_id = cx.parent(target_item_def_id);
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 349569d750e..8c5f1809a49 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -669,30 +669,11 @@ fn project<'cx, 'tcx>(
 
     match candidates {
         ProjectionCandidateSet::Single(candidate) => {
-            Ok(Projected::Progress(confirm_candidate(selcx, obligation, candidate)))
+            confirm_candidate(selcx, obligation, candidate)
         }
         ProjectionCandidateSet::None => {
             let tcx = selcx.tcx();
-            let term = match tcx.def_kind(obligation.predicate.def_id) {
-                DefKind::AssocTy => Ty::new_projection_from_args(
-                    tcx,
-                    obligation.predicate.def_id,
-                    obligation.predicate.args,
-                )
-                .into(),
-                DefKind::AssocConst => ty::Const::new_unevaluated(
-                    tcx,
-                    ty::UnevaluatedConst::new(
-                        obligation.predicate.def_id,
-                        obligation.predicate.args,
-                    ),
-                )
-                .into(),
-                kind => {
-                    bug!("unknown projection def-id: {}", kind.descr(obligation.predicate.def_id))
-                }
-            };
-
+            let term = obligation.predicate.to_term(tcx);
             Ok(Projected::NoProgress(term))
         }
         // Error occurred while trying to processing impls.
@@ -1244,18 +1225,16 @@ fn confirm_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
     candidate: ProjectionCandidate<'tcx>,
-) -> Progress<'tcx> {
+) -> Result<Projected<'tcx>, ProjectionError<'tcx>> {
     debug!(?obligation, ?candidate, "confirm_candidate");
-    let mut progress = match candidate {
+    let mut result = match candidate {
         ProjectionCandidate::ParamEnv(poly_projection)
-        | ProjectionCandidate::Object(poly_projection) => {
-            confirm_param_env_candidate(selcx, obligation, poly_projection, false)
-        }
-
-        ProjectionCandidate::TraitDef(poly_projection) => {
-            confirm_param_env_candidate(selcx, obligation, poly_projection, true)
-        }
-
+        | ProjectionCandidate::Object(poly_projection) => Ok(Projected::Progress(
+            confirm_param_env_candidate(selcx, obligation, poly_projection, false),
+        )),
+        ProjectionCandidate::TraitDef(poly_projection) => Ok(Projected::Progress(
+            confirm_param_env_candidate(selcx, obligation, poly_projection, true),
+        )),
         ProjectionCandidate::Select(impl_source) => {
             confirm_select_candidate(selcx, obligation, impl_source)
         }
@@ -1266,23 +1245,26 @@ fn confirm_candidate<'cx, 'tcx>(
     // with new region variables, we need to resolve them to existing variables
     // when possible for this to work. See `auto-trait-projection-recursion.rs`
     // for a case where this matters.
-    if progress.term.has_infer_regions() {
+    if let Ok(Projected::Progress(progress)) = &mut result
+        && progress.term.has_infer_regions()
+    {
         progress.term = progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx));
     }
-    progress
+
+    result
 }
 
 fn confirm_select_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
     impl_source: Selection<'tcx>,
-) -> Progress<'tcx> {
+) -> Result<Projected<'tcx>, ProjectionError<'tcx>> {
     match impl_source {
         ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
         ImplSource::Builtin(BuiltinImplSource::Misc | BuiltinImplSource::Trivial, data) => {
             let tcx = selcx.tcx();
             let trait_def_id = obligation.predicate.trait_def_id(tcx);
-            if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) {
+            let progress = if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) {
                 confirm_coroutine_candidate(selcx, obligation, data)
             } else if tcx.is_lang_item(trait_def_id, LangItem::Future) {
                 confirm_future_candidate(selcx, obligation, data)
@@ -1304,7 +1286,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
                 confirm_async_fn_kind_helper_candidate(selcx, obligation, data)
             } else {
                 confirm_builtin_candidate(selcx, obligation, data)
-            }
+            };
+            Ok(Projected::Progress(progress))
         }
         ImplSource::Builtin(BuiltinImplSource::Object { .. }, _)
         | ImplSource::Param(..)
@@ -2000,7 +1983,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
     impl_impl_source: ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>,
-) -> Progress<'tcx> {
+) -> Result<Projected<'tcx>, ProjectionError<'tcx>> {
     let tcx = selcx.tcx();
 
     let ImplSourceUserDefinedData { impl_def_id, args, mut nested } = impl_impl_source;
@@ -2011,19 +1994,47 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     let param_env = obligation.param_env;
     let assoc_ty = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) {
         Ok(assoc_ty) => assoc_ty,
-        Err(guar) => return Progress::error(tcx, guar),
+        Err(guar) => return Ok(Projected::Progress(Progress::error(tcx, guar))),
     };
+
+    // This means that the impl is missing a definition for the
+    // associated type. This is either because the associate item
+    // has impossible-to-satisfy predicates (since those were
+    // allowed in <https://github.com/rust-lang/rust/pull/135480>),
+    // or because the impl is literally missing the definition.
     if !assoc_ty.item.defaultness(tcx).has_value() {
-        // This means that the impl is missing a definition for the
-        // associated type. This error will be reported by the type
-        // checker method `check_impl_items_against_trait`, so here we
-        // just return Error.
         debug!(
             "confirm_impl_candidate: no associated type {:?} for {:?}",
             assoc_ty.item.name, obligation.predicate
         );
-        return Progress { term: Ty::new_misc_error(tcx).into(), obligations: nested };
+        let tail = selcx.tcx().struct_tail_raw(
+            tcx.type_of(impl_def_id).instantiate(tcx, args),
+            |ty| {
+                normalize_with_depth_to(
+                    selcx,
+                    obligation.param_env,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    ty,
+                    &mut nested,
+                )
+            },
+            || {},
+        );
+        if tail.is_guaranteed_unsized_raw() {
+            // We treat this projection as rigid here, which is represented via
+            // `Projected::NoProgress`. This will ensure that the projection is
+            // checked for well-formedness, and it's either satisfied by a trivial
+            // where clause in its env or it results in an error.
+            return Ok(Projected::NoProgress(obligation.predicate.to_term(tcx)));
+        } else {
+            return Ok(Projected::Progress(Progress {
+                term: Ty::new_misc_error(tcx).into(),
+                obligations: nested,
+            }));
+        }
     }
+
     // If we're trying to normalize `<Vec<u32> as X>::A<S>` using
     //`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then:
     //
@@ -2033,6 +2044,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args);
     let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node);
     let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst);
+
     let term: ty::EarlyBinder<'tcx, ty::Term<'tcx>> = if is_const {
         let did = assoc_ty.item.def_id;
         let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did);
@@ -2041,7 +2053,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     } else {
         tcx.type_of(assoc_ty.item.def_id).map_bound(|ty| ty.into())
     };
-    if !tcx.check_args_compatible(assoc_ty.item.def_id, args) {
+
+    let progress = if !tcx.check_args_compatible(assoc_ty.item.def_id, args) {
         let err = Ty::new_error_with_message(
             tcx,
             obligation.cause.span,
@@ -2051,7 +2064,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     } else {
         assoc_ty_own_obligations(selcx, obligation, &mut nested);
         Progress { term: term.instantiate(tcx, args), obligations: nested }
-    }
+    };
+    Ok(Projected::Progress(progress))
 }
 
 // Get obligations corresponding to the predicates from the where-clause of the
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 59c2d3c2fc8..6e6c40580d8 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -155,6 +155,39 @@ pub trait Ty<I: Interner<Ty = Self>>:
     fn is_known_rigid(self) -> bool {
         self.kind().is_known_rigid()
     }
+
+    fn is_guaranteed_unsized_raw(self) -> bool {
+        match self.kind() {
+            ty::Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true,
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(_, _)
+            | ty::Foreign(_)
+            | ty::Array(_, _)
+            | ty::Pat(_, _)
+            | ty::RawPtr(_, _)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(_, _)
+            | ty::UnsafeBinder(_)
+            | ty::Closure(_, _)
+            | ty::CoroutineClosure(_, _)
+            | ty::Coroutine(_, _)
+            | ty::CoroutineWitness(_, _)
+            | ty::Never
+            | ty::Tuple(_)
+            | ty::Alias(_, _)
+            | ty::Param(_)
+            | ty::Bound(_, _)
+            | ty::Placeholder(_)
+            | ty::Infer(_)
+            | ty::Error(_)
+            | ty::Dynamic(_, _, ty::DynStar) => false,
+        }
+    }
 }
 
 pub trait Tys<I: Interner<Tys = Self>>:
diff --git a/tests/ui/traits/trivial-unsized-projection.bad.stderr b/tests/ui/traits/trivial-unsized-projection.bad.stderr
new file mode 100644
index 00000000000..4aea63329b3
--- /dev/null
+++ b/tests/ui/traits/trivial-unsized-projection.bad.stderr
@@ -0,0 +1,44 @@
+error[E0277]: the size for values of type `[()]` cannot be known at compilation time
+  --> $DIR/trivial-unsized-projection.rs:20:12
+   |
+LL | const FOO: <[()] as Bad>::Assert = todo!();
+   |            ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `[()]`
+note: required by a bound in `Bad::Assert`
+  --> $DIR/trivial-unsized-projection.rs:14:15
+   |
+LL |     type Assert
+   |          ------ required by a bound in this associated type
+LL |     where
+LL |         Self: Sized;
+   |               ^^^^^ required by this bound in `Bad::Assert`
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL |     type Assert: ?Sized
+   |                ++++++++
+
+error[E0277]: the size for values of type `[()]` cannot be known at compilation time
+  --> $DIR/trivial-unsized-projection.rs:20:12
+   |
+LL | const FOO: <[()] as Bad>::Assert = todo!();
+   |            ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `[()]`
+note: required by a bound in `Bad::Assert`
+  --> $DIR/trivial-unsized-projection.rs:14:15
+   |
+LL |     type Assert
+   |          ------ required by a bound in this associated type
+LL |     where
+LL |         Self: Sized;
+   |               ^^^^^ required by this bound in `Bad::Assert`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL |     type Assert: ?Sized
+   |                ++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/trivial-unsized-projection.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr
new file mode 100644
index 00000000000..9f01d71ef15
--- /dev/null
+++ b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr
@@ -0,0 +1,61 @@
+error[E0271]: type mismatch resolving `<[()] as Bad>::Assert normalizes-to <[()] as Bad>::Assert`
+  --> $DIR/trivial-unsized-projection.rs:20:12
+   |
+LL | const FOO: <[()] as Bad>::Assert = todo!();
+   |            ^^^^^^^^^^^^^^^^^^^^^ types differ
+   |
+   = note: statics and constants must have a statically known size
+
+error[E0277]: the size for values of type `[()]` cannot be known at compilation time
+  --> $DIR/trivial-unsized-projection.rs:20:12
+   |
+LL | const FOO: <[()] as Bad>::Assert = todo!();
+   |            ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `[()]`
+note: required by a bound in `Bad::Assert`
+  --> $DIR/trivial-unsized-projection.rs:14:15
+   |
+LL |     type Assert
+   |          ------ required by a bound in this associated type
+LL |     where
+LL |         Self: Sized;
+   |               ^^^^^ required by this bound in `Bad::Assert`
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL |     type Assert: ?Sized
+   |                ++++++++
+
+error[E0277]: the size for values of type `[()]` cannot be known at compilation time
+  --> $DIR/trivial-unsized-projection.rs:20:12
+   |
+LL | const FOO: <[()] as Bad>::Assert = todo!();
+   |            ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+   |
+   = help: the trait `Sized` is not implemented for `[()]`
+note: required by a bound in `Bad::Assert`
+  --> $DIR/trivial-unsized-projection.rs:14:15
+   |
+LL |     type Assert
+   |          ------ required by a bound in this associated type
+LL |     where
+LL |         Self: Sized;
+   |               ^^^^^ required by this bound in `Bad::Assert`
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+help: consider relaxing the implicit `Sized` restriction
+   |
+LL |     type Assert: ?Sized
+   |                ++++++++
+
+error[E0271]: type mismatch resolving `<[()] as Bad>::Assert normalizes-to <[()] as Bad>::Assert`
+  --> $DIR/trivial-unsized-projection.rs:20:36
+   |
+LL | const FOO: <[()] as Bad>::Assert = todo!();
+   |                                    ^^^^^^^ types differ
+   |
+   = note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0271, E0277.
+For more information about an error, try `rustc --explain E0271`.
diff --git a/tests/ui/traits/trivial-unsized-projection.rs b/tests/ui/traits/trivial-unsized-projection.rs
new file mode 100644
index 00000000000..82a309e9e13
--- /dev/null
+++ b/tests/ui/traits/trivial-unsized-projection.rs
@@ -0,0 +1,34 @@
+//@ revisions: good bad good_new bad_new
+//@[good_new] compile-flags: -Znext-solver
+//@[bad_new] compile-flags: -Znext-solver
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[good] check-pass
+//@[good_new] check-pass
+
+#![feature(trivial_bounds)]
+#![allow(trivial_bounds)]
+
+trait Bad {
+    type Assert
+    where
+        Self: Sized;
+}
+
+impl Bad for [()] {}
+
+#[cfg(any(bad, bad_new))]
+const FOO: <[()] as Bad>::Assert = todo!();
+//[bad]~^ ERROR the size for values of type `[()]` cannot be known at compilation time
+//[bad]~| ERROR the size for values of type `[()]` cannot be known at compilation time
+//[bad_new]~^^^ ERROR the size for values of type `[()]` cannot be known at compilation time
+//[bad_new]~| ERROR the size for values of type `[()]` cannot be known at compilation time
+//[bad_new]~| ERROR type mismatch resolving `<[()] as Bad>::Assert normalizes-to <[()] as Bad>::Assert`
+//[bad_new]~| ERROR type mismatch resolving `<[()] as Bad>::Assert normalizes-to <[()] as Bad>::Assert`
+
+#[cfg(any(good, good_new))]
+// Well-formed in trivially false param-env
+fn foo() where [()]: Sized {
+    let _: <[()] as Bad>::Assert;
+}
+
+fn main() {}