wf-check type annotations before normalization

This commit is contained in:
Ali MJ Al-Nasrawy 2023-11-08 09:22:24 +00:00 committed by lcnr
parent 8d4693c0f8
commit aac2f84794
3 changed files with 62 additions and 29 deletions

View File

@ -63,13 +63,16 @@ fn relate_mir_and_user_ty<'tcx>(
user_ty: Ty<'tcx>, user_ty: Ty<'tcx>,
) -> Result<(), NoSolution> { ) -> Result<(), NoSolution> {
let cause = ObligationCause::dummy_with_span(span); let cause = ObligationCause::dummy_with_span(span);
ocx.register_obligation(Obligation::new(
ocx.infcx.tcx,
cause.clone(),
param_env,
ty::ClauseKind::WellFormed(user_ty.into()),
));
let user_ty = ocx.normalize(&cause, param_env, user_ty); let user_ty = ocx.normalize(&cause, param_env, user_ty);
ocx.eq(&cause, param_env, mir_ty, user_ty)?; ocx.eq(&cause, param_env, mir_ty, user_ty)?;
// FIXME(#104764): We should check well-formedness before normalization.
let predicate =
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(user_ty.into())));
ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate));
Ok(()) Ok(())
} }
@ -113,31 +116,38 @@ fn relate_mir_and_user_args<'tcx>(
ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate)); ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate));
} }
// Now prove the well-formedness of `def_id` with `substs`.
// Note for some items, proving the WF of `ty` is not sufficient because the
// well-formedness of an item may depend on the WF of gneneric args not present in the
// item's type. Currently this is true for associated consts, e.g.:
// ```rust
// impl<T> MyTy<T> {
// const CONST: () = { /* arbitrary code that depends on T being WF */ };
// }
// ```
for arg in args {
ocx.register_obligation(Obligation::new(
tcx,
cause.clone(),
param_env,
ty::ClauseKind::WellFormed(arg),
));
}
if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
ocx.register_obligation(Obligation::new(
tcx,
cause.clone(),
param_env,
ty::ClauseKind::WellFormed(self_ty.into()),
));
let self_ty = ocx.normalize(&cause, param_env, self_ty); let self_ty = ocx.normalize(&cause, param_env, self_ty);
let impl_self_ty = tcx.type_of(impl_def_id).instantiate(tcx, args); let impl_self_ty = tcx.type_of(impl_def_id).instantiate(tcx, args);
let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty); let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty);
ocx.eq(&cause, param_env, self_ty, impl_self_ty)?; ocx.eq(&cause, param_env, self_ty, impl_self_ty)?;
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
impl_self_ty.into(),
)));
ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate));
} }
// In addition to proving the predicates, we have to
// prove that `ty` is well-formed -- this is because
// the WF of `ty` is predicated on the args being
// well-formed, and we haven't proven *that*. We don't
// want to prove the WF of types from `args` directly because they
// haven't been normalized.
//
// FIXME(nmatsakis): Well, perhaps we should normalize
// them? This would only be relevant if some input
// type were ill-formed but did not appear in `ty`,
// which...could happen with normalization...
let predicate =
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty.into())));
ocx.register_obligation(Obligation::new(tcx, cause, param_env, predicate));
Ok(()) Ok(())
} }

View File

@ -1,8 +1,5 @@
// check that associated consts can assume the impl header is well-formed. // check that associated consts can assume the impl header is well-formed.
// FIXME(aliemjay): we should check the impl header is WF at the use site
// but we currently don't in some cases. This is *unsound*.
trait Foo<'a, 'b, T>: Sized { trait Foo<'a, 'b, T>: Sized {
const EVIL: fn(u: &'b u32) -> &'a u32; const EVIL: fn(u: &'b u32) -> &'a u32;
} }
@ -27,11 +24,13 @@ impl<'a, 'b> Evil<'a, 'b> {
// *check* that it holds at the use site. // *check* that it holds at the use site.
fn evil<'a, 'b>(b: &'b u32) -> &'a u32 { fn evil<'a, 'b>(b: &'b u32) -> &'a u32 {
<()>::EVIL(b) // FIXME: should be an error <()>::EVIL(b)
//~^ ERROR lifetime may not live long enough
} }
fn indirect_evil<'a, 'b>(b: &'b u32) -> &'a u32 { fn indirect_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
<IndirectEvil>::EVIL(b) // FIXME: should be an error <IndirectEvil>::EVIL(b)
//~^ ERROR lifetime may not live long enough
} }
fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 { fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 {

View File

@ -1,5 +1,29 @@
error: lifetime may not live long enough error: lifetime may not live long enough
--> $DIR/wf-associated-const.rs:38:5 --> $DIR/wf-associated-const.rs:27:5
|
LL | fn evil<'a, 'b>(b: &'b u32) -> &'a u32 {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | <()>::EVIL(b)
| ^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`
error: lifetime may not live long enough
--> $DIR/wf-associated-const.rs:32:5
|
LL | fn indirect_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | <IndirectEvil>::EVIL(b)
| ^^^^^^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`
error: lifetime may not live long enough
--> $DIR/wf-associated-const.rs:37:5
| |
LL | fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 { LL | fn inherent_evil<'a, 'b>(b: &'b u32) -> &'a u32 {
| -- -- lifetime `'b` defined here | -- -- lifetime `'b` defined here
@ -10,5 +34,5 @@ LL | <Evil>::INHERENT_EVIL(b)
| |
= help: consider adding the following bound: `'b: 'a` = help: consider adding the following bound: `'b: 'a`
error: aborting due to previous error error: aborting due to 3 previous errors