From 27836e1e5794dfa8db8565adaf7aed05d8bfff92 Mon Sep 17 00:00:00 2001 From: Michael Goulet <michael@errs.io> Date: Wed, 26 Mar 2025 18:57:13 +0000 Subject: [PATCH 1/4] Rigidly project missing item due to guaranteed impossible sized predicate --- .../rustc_hir_analysis/src/check/check.rs | 8 +- compiler/rustc_middle/src/ty/sty.rs | 37 +++++++ .../src/solve/normalizes_to/mod.rs | 12 +- .../src/traits/project.rs | 104 ++++++++++-------- compiler/rustc_type_ir/src/inherent.rs | 33 ++++++ .../trivial-unsized-projection.bad.stderr | 44 ++++++++ .../trivial-unsized-projection.bad_new.stderr | 61 ++++++++++ tests/ui/traits/trivial-unsized-projection.rs | 34 ++++++ 8 files changed, 281 insertions(+), 52 deletions(-) create mode 100644 tests/ui/traits/trivial-unsized-projection.bad.stderr create mode 100644 tests/ui/traits/trivial-unsized-projection.bad_new.stderr create mode 100644 tests/ui/traits/trivial-unsized-projection.rs 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() {} From ccdfd310be12f0c46be8b3266b3ff4e2ce5b3806 Mon Sep 17 00:00:00 2001 From: Michael Goulet <michael@errs.io> Date: Wed, 26 Mar 2025 18:59:44 +0000 Subject: [PATCH 2/4] Mark GAT WC as GoalSource::AliasWellFormed so that we recurse into them in error reporting --- .../src/solve/normalizes_to/mod.rs | 4 ++-- compiler/rustc_type_ir/src/solve/mod.rs | 7 +++++-- .../trivial-unsized-projection.bad_new.stderr | 21 ++----------------- tests/ui/traits/trivial-unsized-projection.rs | 2 -- 4 files changed, 9 insertions(+), 25 deletions(-) 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 4cfc727d6b7..f4f15ff6d46 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 @@ -134,7 +134,7 @@ where // Add GAT where clauses from the trait's definition // FIXME: We don't need these, since these are the type's own WF obligations. ecx.add_goals( - GoalSource::Misc, + GoalSource::AliasWellFormed, cx.own_predicates_of(goal.predicate.def_id()) .iter_instantiated(cx, goal.predicate.alias.args) .map(|pred| goal.with(cx, pred)), @@ -199,7 +199,7 @@ where // Add GAT where clauses from the trait's definition. // FIXME: We don't need these, since these are the type's own WF obligations. ecx.add_goals( - GoalSource::Misc, + GoalSource::AliasWellFormed, cx.own_predicates_of(goal.predicate.def_id()) .iter_instantiated(cx, goal.predicate.alias.args) .map(|pred| goal.with(cx, pred)), diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index 3aec4804b27..4e9b87fdf74 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -83,8 +83,11 @@ pub enum GoalSource { /// Instantiating a higher-ranked goal and re-proving it. InstantiateHigherRanked, /// Predicate required for an alias projection to be well-formed. - /// This is used in two places: projecting to an opaque whose hidden type - /// is already registered in the opaque type storage, and for rigid projections. + /// This is used in three places: + /// 1. projecting to an opaque whose hidden type is already registered in + /// the opaque type storage, + /// 2. for rigid projections's trait goal, + /// 3. for GAT where clauses. AliasWellFormed, /// In case normalizing aliases in nested goals cycles, eagerly normalizing these /// aliases in the context of the parent may incorrectly change the cycle kind. diff --git a/tests/ui/traits/trivial-unsized-projection.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr index 9f01d71ef15..4aea63329b3 100644 --- a/tests/ui/traits/trivial-unsized-projection.bad_new.stderr +++ b/tests/ui/traits/trivial-unsized-projection.bad_new.stderr @@ -1,11 +1,3 @@ -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 | @@ -47,15 +39,6 @@ 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 2 previous errors -error: aborting due to 4 previous errors - -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/trivial-unsized-projection.rs b/tests/ui/traits/trivial-unsized-projection.rs index 82a309e9e13..62ff25fb7ac 100644 --- a/tests/ui/traits/trivial-unsized-projection.rs +++ b/tests/ui/traits/trivial-unsized-projection.rs @@ -22,8 +22,6 @@ const FOO: <[()] as Bad>::Assert = todo!(); //[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 From 830aeb610289924b5910f409f6751fcf4e497f1e Mon Sep 17 00:00:00 2001 From: Michael Goulet <michael@errs.io> Date: Sun, 30 Mar 2025 03:40:14 +0000 Subject: [PATCH 3/4] Use a query rather than recomputing the tail repeatedly --- .../rustc_hir_analysis/src/check/check.rs | 26 +-------- compiler/rustc_middle/src/query/mod.rs | 7 +++ compiler/rustc_middle/src/ty/context.rs | 4 ++ compiler/rustc_middle/src/ty/sty.rs | 37 ------------ .../src/solve/normalizes_to/mod.rs | 2 +- .../src/traits/project.rs | 16 +----- compiler/rustc_ty_utils/src/ty.rs | 57 +++++++++++++++++++ compiler/rustc_type_ir/src/interner.rs | 2 + tests/ui/associated-types/impl-wf-cycle-4.rs | 2 +- .../associated-types/impl-wf-cycle-4.stderr | 29 +++++----- .../trivial-unsized-projection-2.bad.stderr | 54 ++++++++++++++++++ ...rivial-unsized-projection-2.bad_new.stderr | 54 ++++++++++++++++++ .../ui/traits/trivial-unsized-projection-2.rs | 34 +++++++++++ 13 files changed, 234 insertions(+), 90 deletions(-) create mode 100644 tests/ui/traits/trivial-unsized-projection-2.bad.stderr create mode 100644 tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr create mode 100644 tests/ui/traits/trivial-unsized-projection-2.rs diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 5b89dcf7dd8..bcfbd7b0e97 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -937,27 +937,7 @@ fn check_impl_items_against_trait<'tcx>( let trait_def = tcx.trait_def(trait_ref.def_id); - let infcx = tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis()); - - let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let cause = ObligationCause::misc(tcx.def_span(impl_id), impl_id); - let param_env = tcx.param_env(impl_id); - - let self_is_guaranteed_unsized = tcx - .struct_tail_raw( - trait_ref.self_ty(), - |ty| { - ocx.structurally_normalize_ty(&cause, param_env, ty).unwrap_or_else(|_| { - Ty::new_error_with_message( - tcx, - tcx.def_span(impl_id), - "struct tail should be computable", - ) - }) - }, - || (), - ) - .is_guaranteed_unsized_raw(); + let self_is_guaranteed_unsize_self = tcx.impl_self_is_guaranteed_unsized(impl_id); for &impl_item in impl_item_refs { let ty_impl_item = tcx.associated_item(impl_item); @@ -988,7 +968,7 @@ fn check_impl_items_against_trait<'tcx>( } } - if self_is_guaranteed_unsized && tcx.generics_require_sized_self(ty_trait_item.def_id) { + if self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(ty_trait_item.def_id) { tcx.emit_node_span_lint( rustc_lint_defs::builtin::DEAD_CODE, tcx.local_def_id_to_hir_id(ty_impl_item.def_id.expect_local()), @@ -1023,7 +1003,7 @@ fn check_impl_items_against_trait<'tcx>( if !is_implemented && tcx.defaultness(impl_id).is_final() // unsized types don't need to implement methods that have `Self: Sized` bounds. - && !(self_is_guaranteed_unsized && tcx.generics_require_sized_self(trait_item_id)) + && !(self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(trait_item_id)) { missing_items.push(tcx.associated_item(trait_item_id)); } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index a1df27ac788..40d0028db86 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1026,6 +1026,13 @@ rustc_queries! { separate_provide_extern } + /// Given an `impl_def_id`, return true if the self type is guaranteed to be unsized due + /// to either being one of the built-in unsized types (str/slice/dyn) or to be a struct + /// whose tail is one of those types. + query impl_self_is_guaranteed_unsized(impl_def_id: DefId) -> bool { + desc { |tcx| "computing whether `{}` has a guaranteed unsized self type", tcx.def_path_str(impl_def_id) } + } + /// Maps a `DefId` of a type to a list of its inherent impls. /// Contains implementations of methods that are inherent to a type. /// Methods in these implementations don't need to be exported. diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 162ca1f4af8..abf6cbbcd87 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -437,6 +437,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) } + fn impl_self_is_guaranteed_unsized(self, impl_def_id: DefId) -> bool { + self.impl_self_is_guaranteed_unsized(impl_def_id) + } + fn has_target_features(self, def_id: DefId) -> bool { !self.codegen_fn_attrs(def_id).target_features.is_empty() } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index fb15ab8d848..27ee363f1c1 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2029,43 +2029,6 @@ 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 f4f15ff6d46..87fb61623d1 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 @@ -237,7 +237,7 @@ where // 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() { + if cx.impl_self_is_guaranteed_unsized(impl_def_id) { ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } else { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 8c5f1809a49..83591219b14 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -2007,21 +2007,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( "confirm_impl_candidate: no associated type {:?} for {:?}", assoc_ty.item.name, obligation.predicate ); - 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() { + if tcx.impl_self_is_guaranteed_unsized(impl_def_id) { // 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 diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 9dc4f11e456..31d69eef5ec 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -3,6 +3,7 @@ use rustc_hir as hir; use rustc_hir::LangItem; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{ @@ -312,6 +313,61 @@ fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> DenseBitSe unsizing_params } +fn impl_self_is_guaranteed_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_def_id: DefId) -> bool { + debug_assert_eq!(tcx.def_kind(impl_def_id), DefKind::Impl { of_trait: true }); + + let infcx = tcx.infer_ctxt().ignoring_regions().build(ty::TypingMode::non_body_analysis()); + + let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx); + let cause = traits::ObligationCause::dummy(); + let param_env = tcx.param_env(impl_def_id); + + let tail = tcx.struct_tail_raw( + tcx.type_of(impl_def_id).instantiate_identity(), + |ty| { + ocx.structurally_normalize_ty(&cause, param_env, ty).unwrap_or_else(|_| { + Ty::new_error_with_message( + tcx, + tcx.def_span(impl_def_id), + "struct tail should be computable", + ) + }) + }, + || (), + ); + + match tail.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(crate) fn provide(providers: &mut Providers) { *providers = Providers { asyncness, @@ -320,6 +376,7 @@ pub(crate) fn provide(providers: &mut Providers) { param_env_normalized_for_post_analysis, defaultness, unsizing_params_for_adt, + impl_self_is_guaranteed_unsized, ..*providers }; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index fce93b735d7..a9e6764e218 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -255,6 +255,8 @@ pub trait Interner: def_id: Self::DefId, ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = ty::Binder<Self, ty::TraitRef<Self>>>>; + fn impl_self_is_guaranteed_unsized(self, def_id: Self::DefId) -> bool; + fn has_target_features(self, def_id: Self::DefId) -> bool; fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId; diff --git a/tests/ui/associated-types/impl-wf-cycle-4.rs b/tests/ui/associated-types/impl-wf-cycle-4.rs index bfa8adc71a1..1c1b3991d32 100644 --- a/tests/ui/associated-types/impl-wf-cycle-4.rs +++ b/tests/ui/associated-types/impl-wf-cycle-4.rs @@ -2,7 +2,7 @@ trait Filter { type ToMatch; } -impl<T> Filter for T //~ ERROR overflow evaluating the requirement +impl<T> Filter for T //~ ERROR cycle detected when where T: Fn(Self::ToMatch), { diff --git a/tests/ui/associated-types/impl-wf-cycle-4.stderr b/tests/ui/associated-types/impl-wf-cycle-4.stderr index cdbac267d34..c966579aecf 100644 --- a/tests/ui/associated-types/impl-wf-cycle-4.stderr +++ b/tests/ui/associated-types/impl-wf-cycle-4.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow evaluating the requirement `<T as Filter>::ToMatch == <T as Filter>::ToMatch` +error[E0391]: cycle detected when computing normalized predicates of `<impl at $DIR/impl-wf-cycle-4.rs:5:1: 7:26>` --> $DIR/impl-wf-cycle-4.rs:5:1 | LL | / impl<T> Filter for T @@ -6,20 +6,23 @@ LL | | where LL | | T: Fn(Self::ToMatch), | |_________________________^ | -note: required for `T` to implement `Filter` - --> $DIR/impl-wf-cycle-4.rs:5:9 +note: ...which requires computing whether `<impl at $DIR/impl-wf-cycle-4.rs:5:1: 7:26>` has a guaranteed unsized self type... + --> $DIR/impl-wf-cycle-4.rs:5:1 | -LL | impl<T> Filter for T - | ^^^^^^ ^ -LL | where -LL | T: Fn(Self::ToMatch), - | ----------------- unsatisfied trait bound introduced here -note: associated types for the current `impl` cannot be restricted in `where` clauses - --> $DIR/impl-wf-cycle-4.rs:7:11 +LL | / impl<T> Filter for T +LL | | where +LL | | T: Fn(Self::ToMatch), + | |_________________________^ + = note: ...which again requires computing normalized predicates of `<impl at $DIR/impl-wf-cycle-4.rs:5:1: 7:26>`, completing the cycle +note: cycle used when checking that `<impl at $DIR/impl-wf-cycle-4.rs:5:1: 7:26>` is well-formed + --> $DIR/impl-wf-cycle-4.rs:5:1 | -LL | T: Fn(Self::ToMatch), - | ^^^^^^^^^^^^^ +LL | / impl<T> Filter for T +LL | | where +LL | | T: Fn(Self::ToMatch), + | |_________________________^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0275`. +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/traits/trivial-unsized-projection-2.bad.stderr b/tests/ui/traits/trivial-unsized-projection-2.bad.stderr new file mode 100644 index 00000000000..bf8d3c40cf6 --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection-2.bad.stderr @@ -0,0 +1,54 @@ +error[E0277]: the size for values of type `[()]` cannot be known at compilation time + --> $DIR/trivial-unsized-projection-2.rs:22:12 + | +LL | const FOO: <Tail as Bad>::Assert = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `Tail`, the trait `Sized` is not implemented for `[()]` +note: required because it appears within the type `Tail` + --> $DIR/trivial-unsized-projection-2.rs:17:8 + | +LL | struct Tail([()]); + | ^^^^ +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection-2.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-2.rs:22:12 + | +LL | const FOO: <Tail as Bad>::Assert = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `Tail`, the trait `Sized` is not implemented for `[()]` +note: required because it appears within the type `Tail` + --> $DIR/trivial-unsized-projection-2.rs:17:8 + | +LL | struct Tail([()]); + | ^^^^ +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection-2.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-2.bad_new.stderr b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr new file mode 100644 index 00000000000..bf8d3c40cf6 --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection-2.bad_new.stderr @@ -0,0 +1,54 @@ +error[E0277]: the size for values of type `[()]` cannot be known at compilation time + --> $DIR/trivial-unsized-projection-2.rs:22:12 + | +LL | const FOO: <Tail as Bad>::Assert = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `Tail`, the trait `Sized` is not implemented for `[()]` +note: required because it appears within the type `Tail` + --> $DIR/trivial-unsized-projection-2.rs:17:8 + | +LL | struct Tail([()]); + | ^^^^ +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection-2.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-2.rs:22:12 + | +LL | const FOO: <Tail as Bad>::Assert = todo!(); + | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: within `Tail`, the trait `Sized` is not implemented for `[()]` +note: required because it appears within the type `Tail` + --> $DIR/trivial-unsized-projection-2.rs:17:8 + | +LL | struct Tail([()]); + | ^^^^ +note: required by a bound in `Bad::Assert` + --> $DIR/trivial-unsized-projection-2.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-2.rs b/tests/ui/traits/trivial-unsized-projection-2.rs new file mode 100644 index 00000000000..af4e12f6f90 --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection-2.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; +} + +struct Tail([()]); + +impl Bad for Tail {} + +#[cfg(any(bad, bad_new))] +const FOO: <Tail 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 + +#[cfg(any(good, good_new))] +// Well-formed in trivially false param-env +fn foo() where Tail: Sized { + let _: <Tail as Bad>::Assert; +} + +fn main() {} From 6cd724bb43b92ae573112c3d39804f227c7ebf02 Mon Sep 17 00:00:00 2001 From: Michael Goulet <michael@errs.io> Date: Mon, 31 Mar 2025 18:56:09 +0000 Subject: [PATCH 4/4] Make unnormalizable item ambiguous in coherence --- .../src/solve/normalizes_to/mod.rs | 20 ++++++++- ...trivial-unsized-projection-in-coherence.rs | 45 +++++++++++++++++++ ...ial-unsized-projection-in-coherence.stderr | 15 +++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 tests/ui/traits/trivial-unsized-projection-in-coherence.rs create mode 100644 tests/ui/traits/trivial-unsized-projection-in-coherence.stderr 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 87fb61623d1..2d027f16e5d 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 @@ -238,8 +238,24 @@ where // delay a bug because we can have trivially false where clauses, so we // treat it as rigid. if cx.impl_self_is_guaranteed_unsized(impl_def_id) { - ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + match ecx.typing_mode() { + ty::TypingMode::Coherence => { + return ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + ty::TypingMode::Analysis { .. } + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } + | ty::TypingMode::PostAnalysis => { + 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")); } diff --git a/tests/ui/traits/trivial-unsized-projection-in-coherence.rs b/tests/ui/traits/trivial-unsized-projection-in-coherence.rs new file mode 100644 index 00000000000..4407d544b4e --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection-in-coherence.rs @@ -0,0 +1,45 @@ +// Make sure we don't treat missing associated items as rigid +// during coherence, even if we know they've got an impossible +// `Sized`-bound. As we check whether the self type is definitely +// not `Sized` outside of coherence, this check can be incomplete. +// +// In this test we only use `impl<T> Overlap<u32> for T` to normalize +// the field of `MaybeUnsized<T, u32>` when checking whether it's +// definitely not `Sized`. However, for `MaybeUnsized<u32, u32>` we +// could also use `impl<U> Overlap<U> for u32` for normalization, which +// would result in a `Sized` type. cc #139000 + +struct MaybeUnsized<T: Overlap<U>, U>(<T as Overlap<U>>::MaybeUnsized); + +trait ReqSized { + type Missing1 + where + Self: Sized; + type Missing2 + where + Self: Sized; +} +impl<T> ReqSized for MaybeUnsized<T, u32> {} + +struct W<T: ?Sized>(T); +trait Eq<T> {} +impl<T> Eq<T> for W<T> {} + +trait RelateReqSized {} +impl<T: ReqSized> RelateReqSized for T where W<T::Missing1>: Eq<T::Missing2> {} + +trait Overlap<U> { + type MaybeUnsized: ?Sized; +} +impl<T> Overlap<u32> for T { + type MaybeUnsized = str; +} +impl<U> Overlap<U> for u32 +//~^ ERROR conflicting implementations of trait `Overlap<u32>` for type `u32` +where + MaybeUnsized<U, u32>: RelateReqSized, +{ + type MaybeUnsized = u32; +} + +fn main() {} diff --git a/tests/ui/traits/trivial-unsized-projection-in-coherence.stderr b/tests/ui/traits/trivial-unsized-projection-in-coherence.stderr new file mode 100644 index 00000000000..52fca9479ca --- /dev/null +++ b/tests/ui/traits/trivial-unsized-projection-in-coherence.stderr @@ -0,0 +1,15 @@ +error[E0119]: conflicting implementations of trait `Overlap<u32>` for type `u32` + --> $DIR/trivial-unsized-projection-in-coherence.rs:37:1 + | +LL | impl<T> Overlap<u32> for T { + | -------------------------- first implementation here +... +LL | / impl<U> Overlap<U> for u32 +LL | | +LL | | where +LL | | MaybeUnsized<U, u32>: RelateReqSized, + | |_________________________________________^ conflicting implementation for `u32` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0119`.