Rigidly project missing item due to guaranteed impossible sized predicate
This commit is contained in:
parent
48f89e7659
commit
27836e1e57
8 changed files with 281 additions and 52 deletions
|
@ -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);
|
||||
|
|
|
@ -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>> {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>>:
|
||||
|
|
44
tests/ui/traits/trivial-unsized-projection.bad.stderr
Normal file
44
tests/ui/traits/trivial-unsized-projection.bad.stderr
Normal file
|
@ -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`.
|
61
tests/ui/traits/trivial-unsized-projection.bad_new.stderr
Normal file
61
tests/ui/traits/trivial-unsized-projection.bad_new.stderr
Normal file
|
@ -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`.
|
34
tests/ui/traits/trivial-unsized-projection.rs
Normal file
34
tests/ui/traits/trivial-unsized-projection.rs
Normal file
|
@ -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() {}
|
Loading…
Add table
Add a link
Reference in a new issue