Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 454df5c

Browse files
committedMar 26, 2025
Rigidly project missing item due to guaranteed impossible sized predicate
1 parent 19cab6b commit 454df5c

File tree

8 files changed

+281
-52
lines changed

8 files changed

+281
-52
lines changed
 

‎compiler/rustc_hir_analysis/src/check/check.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,7 @@ fn check_impl_items_against_trait<'tcx>(
955955
let cause = ObligationCause::misc(tcx.def_span(impl_id), impl_id);
956956
let param_env = tcx.param_env(impl_id);
957957

958-
let self_is_guaranteed_unsized = match tcx
958+
let self_is_guaranteed_unsized = tcx
959959
.struct_tail_raw(
960960
trait_ref.self_ty(),
961961
|ty| {
@@ -969,11 +969,7 @@ fn check_impl_items_against_trait<'tcx>(
969969
},
970970
|| (),
971971
)
972-
.kind()
973-
{
974-
ty::Dynamic(_, _, ty::DynKind::Dyn) | ty::Slice(_) | ty::Str => true,
975-
_ => false,
976-
};
972+
.is_guaranteed_unsized_raw();
977973

978974
for &impl_item in impl_item_refs {
979975
let ty_impl_item = tcx.associated_item(impl_item);

‎compiler/rustc_middle/src/ty/sty.rs

+37
Original file line numberDiff line numberDiff line change
@@ -2070,6 +2070,43 @@ impl<'tcx> Ty<'tcx> {
20702070
Error(_) | Infer(_) | Alias(_, _) | Param(_) | Bound(_, _) | Placeholder(_) => false,
20712071
}
20722072
}
2073+
2074+
/// Returns true if the type is guaranteed to be one of the three built-in unsized types:
2075+
/// `dyn Trait`/`[T]`/`str`. This function is *raw* because it does not compute the struct
2076+
/// tail of the type, so you are responsible for doing that yourself.
2077+
// NOTE: Keep this in sync with `rustc_type_ir`'s copy.
2078+
pub fn is_guaranteed_unsized_raw(self) -> bool {
2079+
match self.kind() {
2080+
Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true,
2081+
Bool
2082+
| Char
2083+
| Int(_)
2084+
| Uint(_)
2085+
| Float(_)
2086+
| Adt(_, _)
2087+
| Foreign(_)
2088+
| Array(_, _)
2089+
| Pat(_, _)
2090+
| RawPtr(_, _)
2091+
| Ref(_, _, _)
2092+
| FnDef(_, _)
2093+
| FnPtr(_, _)
2094+
| UnsafeBinder(_)
2095+
| Closure(_, _)
2096+
| CoroutineClosure(_, _)
2097+
| Coroutine(_, _)
2098+
| CoroutineWitness(_, _)
2099+
| Never
2100+
| Tuple(_)
2101+
| Alias(_, _)
2102+
| Param(_)
2103+
| Bound(_, _)
2104+
| Placeholder(_)
2105+
| Infer(_)
2106+
| Error(_)
2107+
| Dynamic(_, _, ty::DynStar) => false,
2108+
}
2109+
}
20732110
}
20742111

20752112
impl<'tcx> rustc_type_ir::inherent::Tys<TyCtxt<'tcx>> for &'tcx ty::List<Ty<'tcx>> {

‎compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,17 @@ where
232232
};
233233

234234
if !cx.has_item_definition(target_item_def_id) {
235-
return error_response(ecx, cx.delay_bug("missing item"));
235+
// If the impl is missing an item, it's either because the user forgot to
236+
// provide it, or the user is not *obligated* to provide it (because it
237+
// has a trivially false `Sized` predicate). If it's the latter, we cannot
238+
// delay a bug because we can have trivially false where clauses, so we
239+
// treat it as rigid.
240+
if goal_trait_ref.self_ty().is_guaranteed_unsized_raw() {
241+
ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
242+
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
243+
} else {
244+
return error_response(ecx, cx.delay_bug("missing item"));
245+
}
236246
}
237247

238248
let target_container_def_id = cx.parent(target_item_def_id);

‎compiler/rustc_trait_selection/src/traits/project.rs

+59-45
Original file line numberDiff line numberDiff line change
@@ -669,30 +669,11 @@ fn project<'cx, 'tcx>(
669669

670670
match candidates {
671671
ProjectionCandidateSet::Single(candidate) => {
672-
Ok(Projected::Progress(confirm_candidate(selcx, obligation, candidate)))
672+
confirm_candidate(selcx, obligation, candidate)
673673
}
674674
ProjectionCandidateSet::None => {
675675
let tcx = selcx.tcx();
676-
let term = match tcx.def_kind(obligation.predicate.def_id) {
677-
DefKind::AssocTy => Ty::new_projection_from_args(
678-
tcx,
679-
obligation.predicate.def_id,
680-
obligation.predicate.args,
681-
)
682-
.into(),
683-
DefKind::AssocConst => ty::Const::new_unevaluated(
684-
tcx,
685-
ty::UnevaluatedConst::new(
686-
obligation.predicate.def_id,
687-
obligation.predicate.args,
688-
),
689-
)
690-
.into(),
691-
kind => {
692-
bug!("unknown projection def-id: {}", kind.descr(obligation.predicate.def_id))
693-
}
694-
};
695-
676+
let term = obligation.predicate.to_term(tcx);
696677
Ok(Projected::NoProgress(term))
697678
}
698679
// Error occurred while trying to processing impls.
@@ -1243,18 +1224,16 @@ fn confirm_candidate<'cx, 'tcx>(
12431224
selcx: &mut SelectionContext<'cx, 'tcx>,
12441225
obligation: &ProjectionTermObligation<'tcx>,
12451226
candidate: ProjectionCandidate<'tcx>,
1246-
) -> Progress<'tcx> {
1227+
) -> Result<Projected<'tcx>, ProjectionError<'tcx>> {
12471228
debug!(?obligation, ?candidate, "confirm_candidate");
1248-
let mut progress = match candidate {
1229+
let mut result = match candidate {
12491230
ProjectionCandidate::ParamEnv(poly_projection)
1250-
| ProjectionCandidate::Object(poly_projection) => {
1251-
confirm_param_env_candidate(selcx, obligation, poly_projection, false)
1252-
}
1253-
1254-
ProjectionCandidate::TraitDef(poly_projection) => {
1255-
confirm_param_env_candidate(selcx, obligation, poly_projection, true)
1256-
}
1257-
1231+
| ProjectionCandidate::Object(poly_projection) => Ok(Projected::Progress(
1232+
confirm_param_env_candidate(selcx, obligation, poly_projection, false),
1233+
)),
1234+
ProjectionCandidate::TraitDef(poly_projection) => Ok(Projected::Progress(
1235+
confirm_param_env_candidate(selcx, obligation, poly_projection, true),
1236+
)),
12581237
ProjectionCandidate::Select(impl_source) => {
12591238
confirm_select_candidate(selcx, obligation, impl_source)
12601239
}
@@ -1265,23 +1244,26 @@ fn confirm_candidate<'cx, 'tcx>(
12651244
// with new region variables, we need to resolve them to existing variables
12661245
// when possible for this to work. See `auto-trait-projection-recursion.rs`
12671246
// for a case where this matters.
1268-
if progress.term.has_infer_regions() {
1247+
if let Ok(Projected::Progress(progress)) = &mut result
1248+
&& progress.term.has_infer_regions()
1249+
{
12691250
progress.term = progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx));
12701251
}
1271-
progress
1252+
1253+
result
12721254
}
12731255

12741256
fn confirm_select_candidate<'cx, 'tcx>(
12751257
selcx: &mut SelectionContext<'cx, 'tcx>,
12761258
obligation: &ProjectionTermObligation<'tcx>,
12771259
impl_source: Selection<'tcx>,
1278-
) -> Progress<'tcx> {
1260+
) -> Result<Projected<'tcx>, ProjectionError<'tcx>> {
12791261
match impl_source {
12801262
ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
12811263
ImplSource::Builtin(BuiltinImplSource::Misc | BuiltinImplSource::Trivial, data) => {
12821264
let tcx = selcx.tcx();
12831265
let trait_def_id = obligation.predicate.trait_def_id(tcx);
1284-
if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) {
1266+
let progress = if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) {
12851267
confirm_coroutine_candidate(selcx, obligation, data)
12861268
} else if tcx.is_lang_item(trait_def_id, LangItem::Future) {
12871269
confirm_future_candidate(selcx, obligation, data)
@@ -1303,7 +1285,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
13031285
confirm_async_fn_kind_helper_candidate(selcx, obligation, data)
13041286
} else {
13051287
confirm_builtin_candidate(selcx, obligation, data)
1306-
}
1288+
};
1289+
Ok(Projected::Progress(progress))
13071290
}
13081291
ImplSource::Builtin(BuiltinImplSource::Object { .. }, _)
13091292
| ImplSource::Param(..)
@@ -1999,7 +1982,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
19991982
selcx: &mut SelectionContext<'cx, 'tcx>,
20001983
obligation: &ProjectionTermObligation<'tcx>,
20011984
impl_impl_source: ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>,
2002-
) -> Progress<'tcx> {
1985+
) -> Result<Projected<'tcx>, ProjectionError<'tcx>> {
20031986
let tcx = selcx.tcx();
20041987

20051988
let ImplSourceUserDefinedData { impl_def_id, args, mut nested } = impl_impl_source;
@@ -2010,19 +1993,47 @@ fn confirm_impl_candidate<'cx, 'tcx>(
20101993
let param_env = obligation.param_env;
20111994
let assoc_ty = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) {
20121995
Ok(assoc_ty) => assoc_ty,
2013-
Err(guar) => return Progress::error(tcx, guar),
1996+
Err(guar) => return Ok(Projected::Progress(Progress::error(tcx, guar))),
20141997
};
1998+
1999+
// This means that the impl is missing a definition for the
2000+
// associated type. This is either because the associate item
2001+
// has impossible-to-satisfy predicates (since those were
2002+
// allowed in <https://github.com/rust-lang/rust/pull/135480>),
2003+
// or because the impl is literally missing the definition.
20152004
if !assoc_ty.item.defaultness(tcx).has_value() {
2016-
// This means that the impl is missing a definition for the
2017-
// associated type. This error will be reported by the type
2018-
// checker method `check_impl_items_against_trait`, so here we
2019-
// just return Error.
20202005
debug!(
20212006
"confirm_impl_candidate: no associated type {:?} for {:?}",
20222007
assoc_ty.item.name, obligation.predicate
20232008
);
2024-
return Progress { term: Ty::new_misc_error(tcx).into(), obligations: nested };
2009+
let tail = selcx.tcx().struct_tail_raw(
2010+
tcx.type_of(impl_def_id).instantiate(tcx, args),
2011+
|ty| {
2012+
normalize_with_depth_to(
2013+
selcx,
2014+
obligation.param_env,
2015+
obligation.cause.clone(),
2016+
obligation.recursion_depth + 1,
2017+
ty,
2018+
&mut nested,
2019+
)
2020+
},
2021+
|| {},
2022+
);
2023+
if tail.is_guaranteed_unsized_raw() {
2024+
// We treat this projection as rigid here, which is represented via
2025+
// `Projected::NoProgress`. This will ensure that the projection is
2026+
// checked for well-formedness, and it's either satisfied by a trivial
2027+
// where clause in its env or it results in an error.
2028+
return Ok(Projected::NoProgress(obligation.predicate.to_term(tcx)));
2029+
} else {
2030+
return Ok(Projected::Progress(Progress {
2031+
term: Ty::new_misc_error(tcx).into(),
2032+
obligations: nested,
2033+
}));
2034+
}
20252035
}
2036+
20262037
// If we're trying to normalize `<Vec<u32> as X>::A<S>` using
20272038
//`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then:
20282039
//
@@ -2032,6 +2043,7 @@ fn confirm_impl_candidate<'cx, 'tcx>(
20322043
let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args);
20332044
let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node);
20342045
let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst);
2046+
20352047
let term: ty::EarlyBinder<'tcx, ty::Term<'tcx>> = if is_const {
20362048
let did = assoc_ty.item.def_id;
20372049
let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did);
@@ -2040,7 +2052,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
20402052
} else {
20412053
tcx.type_of(assoc_ty.item.def_id).map_bound(|ty| ty.into())
20422054
};
2043-
if !tcx.check_args_compatible(assoc_ty.item.def_id, args) {
2055+
2056+
let progress = if !tcx.check_args_compatible(assoc_ty.item.def_id, args) {
20442057
let err = Ty::new_error_with_message(
20452058
tcx,
20462059
obligation.cause.span,
@@ -2050,7 +2063,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
20502063
} else {
20512064
assoc_ty_own_obligations(selcx, obligation, &mut nested);
20522065
Progress { term: term.instantiate(tcx, args), obligations: nested }
2053-
}
2066+
};
2067+
Ok(Projected::Progress(progress))
20542068
}
20552069

20562070
// Get obligations corresponding to the predicates from the where-clause of the

‎compiler/rustc_type_ir/src/inherent.rs

+33
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,39 @@ pub trait Ty<I: Interner<Ty = Self>>:
208208
| ty::Placeholder(_) => false,
209209
}
210210
}
211+
212+
fn is_guaranteed_unsized_raw(self) -> bool {
213+
match self.kind() {
214+
ty::Dynamic(_, _, ty::Dyn) | ty::Slice(_) | ty::Str => true,
215+
ty::Bool
216+
| ty::Char
217+
| ty::Int(_)
218+
| ty::Uint(_)
219+
| ty::Float(_)
220+
| ty::Adt(_, _)
221+
| ty::Foreign(_)
222+
| ty::Array(_, _)
223+
| ty::Pat(_, _)
224+
| ty::RawPtr(_, _)
225+
| ty::Ref(_, _, _)
226+
| ty::FnDef(_, _)
227+
| ty::FnPtr(_, _)
228+
| ty::UnsafeBinder(_)
229+
| ty::Closure(_, _)
230+
| ty::CoroutineClosure(_, _)
231+
| ty::Coroutine(_, _)
232+
| ty::CoroutineWitness(_, _)
233+
| ty::Never
234+
| ty::Tuple(_)
235+
| ty::Alias(_, _)
236+
| ty::Param(_)
237+
| ty::Bound(_, _)
238+
| ty::Placeholder(_)
239+
| ty::Infer(_)
240+
| ty::Error(_)
241+
| ty::Dynamic(_, _, ty::DynStar) => false,
242+
}
243+
}
211244
}
212245

213246
pub trait Tys<I: Interner<Tys = Self>>:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
error[E0277]: the size for values of type `[()]` cannot be known at compilation time
2+
--> $DIR/trivial-unsized-projection.rs:20:12
3+
|
4+
LL | const FOO: <[()] as Bad>::Assert = todo!();
5+
| ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
6+
|
7+
= help: the trait `Sized` is not implemented for `[()]`
8+
note: required by a bound in `Bad::Assert`
9+
--> $DIR/trivial-unsized-projection.rs:14:15
10+
|
11+
LL | type Assert
12+
| ------ required by a bound in this associated type
13+
LL | where
14+
LL | Self: Sized;
15+
| ^^^^^ required by this bound in `Bad::Assert`
16+
help: consider relaxing the implicit `Sized` restriction
17+
|
18+
LL | type Assert: ?Sized
19+
| ++++++++
20+
21+
error[E0277]: the size for values of type `[()]` cannot be known at compilation time
22+
--> $DIR/trivial-unsized-projection.rs:20:12
23+
|
24+
LL | const FOO: <[()] as Bad>::Assert = todo!();
25+
| ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
26+
|
27+
= help: the trait `Sized` is not implemented for `[()]`
28+
note: required by a bound in `Bad::Assert`
29+
--> $DIR/trivial-unsized-projection.rs:14:15
30+
|
31+
LL | type Assert
32+
| ------ required by a bound in this associated type
33+
LL | where
34+
LL | Self: Sized;
35+
| ^^^^^ required by this bound in `Bad::Assert`
36+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
37+
help: consider relaxing the implicit `Sized` restriction
38+
|
39+
LL | type Assert: ?Sized
40+
| ++++++++
41+
42+
error: aborting due to 2 previous errors
43+
44+
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
error[E0271]: type mismatch resolving `<[()] as Bad>::Assert normalizes-to <[()] as Bad>::Assert`
2+
--> $DIR/trivial-unsized-projection.rs:20:12
3+
|
4+
LL | const FOO: <[()] as Bad>::Assert = todo!();
5+
| ^^^^^^^^^^^^^^^^^^^^^ types differ
6+
|
7+
= note: statics and constants must have a statically known size
8+
9+
error[E0277]: the size for values of type `[()]` cannot be known at compilation time
10+
--> $DIR/trivial-unsized-projection.rs:20:12
11+
|
12+
LL | const FOO: <[()] as Bad>::Assert = todo!();
13+
| ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
14+
|
15+
= help: the trait `Sized` is not implemented for `[()]`
16+
note: required by a bound in `Bad::Assert`
17+
--> $DIR/trivial-unsized-projection.rs:14:15
18+
|
19+
LL | type Assert
20+
| ------ required by a bound in this associated type
21+
LL | where
22+
LL | Self: Sized;
23+
| ^^^^^ required by this bound in `Bad::Assert`
24+
help: consider relaxing the implicit `Sized` restriction
25+
|
26+
LL | type Assert: ?Sized
27+
| ++++++++
28+
29+
error[E0277]: the size for values of type `[()]` cannot be known at compilation time
30+
--> $DIR/trivial-unsized-projection.rs:20:12
31+
|
32+
LL | const FOO: <[()] as Bad>::Assert = todo!();
33+
| ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
34+
|
35+
= help: the trait `Sized` is not implemented for `[()]`
36+
note: required by a bound in `Bad::Assert`
37+
--> $DIR/trivial-unsized-projection.rs:14:15
38+
|
39+
LL | type Assert
40+
| ------ required by a bound in this associated type
41+
LL | where
42+
LL | Self: Sized;
43+
| ^^^^^ required by this bound in `Bad::Assert`
44+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
45+
help: consider relaxing the implicit `Sized` restriction
46+
|
47+
LL | type Assert: ?Sized
48+
| ++++++++
49+
50+
error[E0271]: type mismatch resolving `<[()] as Bad>::Assert normalizes-to <[()] as Bad>::Assert`
51+
--> $DIR/trivial-unsized-projection.rs:20:36
52+
|
53+
LL | const FOO: <[()] as Bad>::Assert = todo!();
54+
| ^^^^^^^ types differ
55+
|
56+
= note: this error originates in the macro `todo` (in Nightly builds, run with -Z macro-backtrace for more info)
57+
58+
error: aborting due to 4 previous errors
59+
60+
Some errors have detailed explanations: E0271, E0277.
61+
For more information about an error, try `rustc --explain E0271`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//@ revisions: good bad good_new bad_new
2+
//@[good_new] compile-flags: -Znext-solver
3+
//@[bad_new] compile-flags: -Znext-solver
4+
//@ ignore-compare-mode-next-solver (explicit revisions)
5+
//@[good] check-pass
6+
//@[good_new] check-pass
7+
8+
#![feature(trivial_bounds)]
9+
#![allow(trivial_bounds)]
10+
11+
trait Bad {
12+
type Assert
13+
where
14+
Self: Sized;
15+
}
16+
17+
impl Bad for [()] {}
18+
19+
#[cfg(any(bad, bad_new))]
20+
const FOO: <[()] as Bad>::Assert = todo!();
21+
//[bad]~^ ERROR the size for values of type `[()]` cannot be known at compilation time
22+
//[bad]~| ERROR the size for values of type `[()]` cannot be known at compilation time
23+
//[bad_new]~^^^ ERROR the size for values of type `[()]` cannot be known at compilation time
24+
//[bad_new]~| ERROR the size for values of type `[()]` cannot be known at compilation time
25+
//[bad_new]~| ERROR type mismatch resolving `<[()] as Bad>::Assert normalizes-to <[()] as Bad>::Assert`
26+
//[bad_new]~| ERROR type mismatch resolving `<[()] as Bad>::Assert normalizes-to <[()] as Bad>::Assert`
27+
28+
#[cfg(any(good, good_new))]
29+
// Well-formed in trivially false param-env
30+
fn foo() where [()]: Sized {
31+
let _: <[()] as Bad>::Assert;
32+
}
33+
34+
fn main() {}

0 commit comments

Comments
 (0)
Please sign in to comment.