Use placeholders to prevent using inferred RPITIT types to imply their own WF-ness
This commit is contained in:
parent
0fd7ce99b0
commit
b5a904a9d4
3 changed files with 114 additions and 6 deletions
|
@ -14,6 +14,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
|
||||||
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
|
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
|
||||||
use rustc_infer::traits::util;
|
use rustc_infer::traits::util;
|
||||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||||
|
use rustc_middle::ty::fold::BottomUpFolder;
|
||||||
use rustc_middle::ty::util::ExplicitSelf;
|
use rustc_middle::ty::util::ExplicitSelf;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, GenericArgs, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
|
self, GenericArgs, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
|
||||||
|
@ -692,9 +693,9 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||||
let ocx = ObligationCtxt::new(infcx);
|
let ocx = ObligationCtxt::new(infcx);
|
||||||
|
|
||||||
// Normalize the impl signature with fresh variables for lifetime inference.
|
// Normalize the impl signature with fresh variables for lifetime inference.
|
||||||
let norm_cause = ObligationCause::misc(return_span, impl_m_def_id);
|
let misc_cause = ObligationCause::misc(return_span, impl_m_def_id);
|
||||||
let impl_sig = ocx.normalize(
|
let impl_sig = ocx.normalize(
|
||||||
&norm_cause,
|
&misc_cause,
|
||||||
param_env,
|
param_env,
|
||||||
tcx.liberate_late_bound_regions(
|
tcx.liberate_late_bound_regions(
|
||||||
impl_m.def_id,
|
impl_m.def_id,
|
||||||
|
@ -725,12 +726,68 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let trait_sig = ocx.normalize(&norm_cause, param_env, unnormalized_trait_sig);
|
let trait_sig = ocx.normalize(&misc_cause, param_env, unnormalized_trait_sig);
|
||||||
trait_sig.error_reported()?;
|
trait_sig.error_reported()?;
|
||||||
let trait_return_ty = trait_sig.output();
|
let trait_return_ty = trait_sig.output();
|
||||||
|
|
||||||
|
// RPITITs are allowed to use the implied predicates of the method that
|
||||||
|
// defines them. This is because we want code like:
|
||||||
|
// ```
|
||||||
|
// trait Foo {
|
||||||
|
// fn test<'a, T>(_: &'a T) -> impl Sized;
|
||||||
|
// }
|
||||||
|
// impl Foo for () {
|
||||||
|
// fn test<'a, T>(x: &'a T) -> &'a T { x }
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
// .. to compile. However, since we use both the normalized and unnormalized
|
||||||
|
// inputs and outputs from the substituted trait signature, we will end up
|
||||||
|
// seeing the hidden type of an RPIT in the signature itself. Naively, this
|
||||||
|
// means that we will use the hidden type to imply the hidden type's own
|
||||||
|
// well-formedness.
|
||||||
|
//
|
||||||
|
// To avoid this, we replace the infer vars used for hidden type inference
|
||||||
|
// with placeholders, which imply nothing about outlives bounds, and then
|
||||||
|
// prove below that the hidden types are well formed.
|
||||||
|
let universe = infcx.create_next_universe();
|
||||||
|
let mut idx = 0;
|
||||||
|
let mapping: FxHashMap<_, _> = collector
|
||||||
|
.types
|
||||||
|
.iter()
|
||||||
|
.map(|(_, &(ty, _))| {
|
||||||
|
assert!(
|
||||||
|
infcx.resolve_vars_if_possible(ty) == ty && ty.is_ty_var(),
|
||||||
|
"{ty:?} should not have been constrained via normalization",
|
||||||
|
ty = infcx.resolve_vars_if_possible(ty)
|
||||||
|
);
|
||||||
|
idx += 1;
|
||||||
|
(
|
||||||
|
ty,
|
||||||
|
Ty::new_placeholder(
|
||||||
|
tcx,
|
||||||
|
ty::Placeholder {
|
||||||
|
universe,
|
||||||
|
bound: ty::BoundTy {
|
||||||
|
var: ty::BoundVar::from_usize(idx),
|
||||||
|
kind: ty::BoundTyKind::Anon,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let mut type_mapper = BottomUpFolder {
|
||||||
|
tcx,
|
||||||
|
ty_op: |ty| *mapping.get(&ty).unwrap_or(&ty),
|
||||||
|
lt_op: |lt| lt,
|
||||||
|
ct_op: |ct| ct,
|
||||||
|
};
|
||||||
let wf_tys = FxIndexSet::from_iter(
|
let wf_tys = FxIndexSet::from_iter(
|
||||||
unnormalized_trait_sig.inputs_and_output.iter().chain(trait_sig.inputs_and_output.iter()),
|
unnormalized_trait_sig
|
||||||
|
.inputs_and_output
|
||||||
|
.iter()
|
||||||
|
.chain(trait_sig.inputs_and_output.iter())
|
||||||
|
.map(|ty| ty.fold_with(&mut type_mapper)),
|
||||||
);
|
);
|
||||||
|
|
||||||
match ocx.eq(&cause, param_env, trait_return_ty, impl_return_ty) {
|
match ocx.eq(&cause, param_env, trait_return_ty, impl_return_ty) {
|
||||||
|
@ -787,6 +844,20 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: This has the same issue as #108544, but since this isn't breaking
|
||||||
|
// existing code, I'm not particularly inclined to do the same hack as above
|
||||||
|
// where we process wf obligations manually. This can be fixed in a forward-
|
||||||
|
// compatible way later.
|
||||||
|
let collected_types = collector.types;
|
||||||
|
for (_, &(ty, _)) in &collected_types {
|
||||||
|
ocx.register_obligation(traits::Obligation::new(
|
||||||
|
tcx,
|
||||||
|
misc_cause.clone(),
|
||||||
|
param_env,
|
||||||
|
ty::ClauseKind::WellFormed(ty.into()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// Check that all obligations are satisfied by the implementation's
|
// Check that all obligations are satisfied by the implementation's
|
||||||
// RPITs.
|
// RPITs.
|
||||||
let errors = ocx.select_all_or_error();
|
let errors = ocx.select_all_or_error();
|
||||||
|
@ -795,8 +866,6 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
|
||||||
return Err(reported);
|
return Err(reported);
|
||||||
}
|
}
|
||||||
|
|
||||||
let collected_types = collector.types;
|
|
||||||
|
|
||||||
// Finally, resolve all regions. This catches wily misuses of
|
// Finally, resolve all regions. This catches wily misuses of
|
||||||
// lifetime parameters.
|
// lifetime parameters.
|
||||||
let outlives_env = OutlivesEnvironment::with_bounds(
|
let outlives_env = OutlivesEnvironment::with_bounds(
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
#![feature(return_position_impl_trait_in_trait)]
|
||||||
|
|
||||||
|
trait Extend {
|
||||||
|
fn extend(_: &str) -> (impl Sized + '_, &'static str);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Extend for () {
|
||||||
|
fn extend(s: &str) -> (Option<&'static &'_ ()>, &'static str) {
|
||||||
|
//~^ ERROR in type `&'static &()`, reference has a longer lifetime than the data it references
|
||||||
|
(None, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This indirection is not necessary for reproduction,
|
||||||
|
// but it makes this test future-proof against #114936.
|
||||||
|
fn extend<T: Extend>(s: &str) -> &'static str {
|
||||||
|
<T as Extend>::extend(s).1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let use_after_free = extend::<()>(&String::from("temporary"));
|
||||||
|
println!("{}", use_after_free);
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
error[E0491]: in type `&'static &()`, reference has a longer lifetime than the data it references
|
||||||
|
--> $DIR/rpitit-hidden-types-self-implied-wf.rs:8:27
|
||||||
|
|
|
||||||
|
LL | fn extend(s: &str) -> (Option<&'static &'_ ()>, &'static str) {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: the pointer is valid for the static lifetime
|
||||||
|
note: but the referenced data is only valid for the anonymous lifetime defined here
|
||||||
|
--> $DIR/rpitit-hidden-types-self-implied-wf.rs:8:18
|
||||||
|
|
|
||||||
|
LL | fn extend(s: &str) -> (Option<&'static &'_ ()>, &'static str) {
|
||||||
|
| ^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0491`.
|
Loading…
Add table
Add a link
Reference in a new issue