Skip to content

Commit b5a904a

Browse files
Use placeholders to prevent using inferred RPITIT types to imply their own WF-ness
1 parent 0fd7ce9 commit b5a904a

File tree

3 files changed

+114
-6
lines changed

3 files changed

+114
-6
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+75-6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi
1414
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
1515
use rustc_infer::traits::util;
1616
use rustc_middle::ty::error::{ExpectedFound, TypeError};
17+
use rustc_middle::ty::fold::BottomUpFolder;
1718
use rustc_middle::ty::util::ExplicitSelf;
1819
use rustc_middle::ty::{
1920
self, GenericArgs, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
@@ -692,9 +693,9 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
692693
let ocx = ObligationCtxt::new(infcx);
693694

694695
// Normalize the impl signature with fresh variables for lifetime inference.
695-
let norm_cause = ObligationCause::misc(return_span, impl_m_def_id);
696+
let misc_cause = ObligationCause::misc(return_span, impl_m_def_id);
696697
let impl_sig = ocx.normalize(
697-
&norm_cause,
698+
&misc_cause,
698699
param_env,
699700
tcx.liberate_late_bound_regions(
700701
impl_m.def_id,
@@ -725,12 +726,68 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
725726
);
726727
}
727728

728-
let trait_sig = ocx.normalize(&norm_cause, param_env, unnormalized_trait_sig);
729+
let trait_sig = ocx.normalize(&misc_cause, param_env, unnormalized_trait_sig);
729730
trait_sig.error_reported()?;
730731
let trait_return_ty = trait_sig.output();
731732

733+
// RPITITs are allowed to use the implied predicates of the method that
734+
// defines them. This is because we want code like:
735+
// ```
736+
// trait Foo {
737+
// fn test<'a, T>(_: &'a T) -> impl Sized;
738+
// }
739+
// impl Foo for () {
740+
// fn test<'a, T>(x: &'a T) -> &'a T { x }
741+
// }
742+
// ```
743+
// .. to compile. However, since we use both the normalized and unnormalized
744+
// inputs and outputs from the substituted trait signature, we will end up
745+
// seeing the hidden type of an RPIT in the signature itself. Naively, this
746+
// means that we will use the hidden type to imply the hidden type's own
747+
// well-formedness.
748+
//
749+
// To avoid this, we replace the infer vars used for hidden type inference
750+
// with placeholders, which imply nothing about outlives bounds, and then
751+
// prove below that the hidden types are well formed.
752+
let universe = infcx.create_next_universe();
753+
let mut idx = 0;
754+
let mapping: FxHashMap<_, _> = collector
755+
.types
756+
.iter()
757+
.map(|(_, &(ty, _))| {
758+
assert!(
759+
infcx.resolve_vars_if_possible(ty) == ty && ty.is_ty_var(),
760+
"{ty:?} should not have been constrained via normalization",
761+
ty = infcx.resolve_vars_if_possible(ty)
762+
);
763+
idx += 1;
764+
(
765+
ty,
766+
Ty::new_placeholder(
767+
tcx,
768+
ty::Placeholder {
769+
universe,
770+
bound: ty::BoundTy {
771+
var: ty::BoundVar::from_usize(idx),
772+
kind: ty::BoundTyKind::Anon,
773+
},
774+
},
775+
),
776+
)
777+
})
778+
.collect();
779+
let mut type_mapper = BottomUpFolder {
780+
tcx,
781+
ty_op: |ty| *mapping.get(&ty).unwrap_or(&ty),
782+
lt_op: |lt| lt,
783+
ct_op: |ct| ct,
784+
};
732785
let wf_tys = FxIndexSet::from_iter(
733-
unnormalized_trait_sig.inputs_and_output.iter().chain(trait_sig.inputs_and_output.iter()),
786+
unnormalized_trait_sig
787+
.inputs_and_output
788+
.iter()
789+
.chain(trait_sig.inputs_and_output.iter())
790+
.map(|ty| ty.fold_with(&mut type_mapper)),
734791
);
735792

736793
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>(
787844
}
788845
}
789846

847+
// FIXME: This has the same issue as #108544, but since this isn't breaking
848+
// existing code, I'm not particularly inclined to do the same hack as above
849+
// where we process wf obligations manually. This can be fixed in a forward-
850+
// compatible way later.
851+
let collected_types = collector.types;
852+
for (_, &(ty, _)) in &collected_types {
853+
ocx.register_obligation(traits::Obligation::new(
854+
tcx,
855+
misc_cause.clone(),
856+
param_env,
857+
ty::ClauseKind::WellFormed(ty.into()),
858+
));
859+
}
860+
790861
// Check that all obligations are satisfied by the implementation's
791862
// RPITs.
792863
let errors = ocx.select_all_or_error();
@@ -795,8 +866,6 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
795866
return Err(reported);
796867
}
797868

798-
let collected_types = collector.types;
799-
800869
// Finally, resolve all regions. This catches wily misuses of
801870
// lifetime parameters.
802871
let outlives_env = OutlivesEnvironment::with_bounds(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![feature(return_position_impl_trait_in_trait)]
2+
3+
trait Extend {
4+
fn extend(_: &str) -> (impl Sized + '_, &'static str);
5+
}
6+
7+
impl Extend for () {
8+
fn extend(s: &str) -> (Option<&'static &'_ ()>, &'static str) {
9+
//~^ ERROR in type `&'static &()`, reference has a longer lifetime than the data it references
10+
(None, s)
11+
}
12+
}
13+
14+
// This indirection is not necessary for reproduction,
15+
// but it makes this test future-proof against #114936.
16+
fn extend<T: Extend>(s: &str) -> &'static str {
17+
<T as Extend>::extend(s).1
18+
}
19+
20+
fn main() {
21+
let use_after_free = extend::<()>(&String::from("temporary"));
22+
println!("{}", use_after_free);
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0491]: in type `&'static &()`, reference has a longer lifetime than the data it references
2+
--> $DIR/rpitit-hidden-types-self-implied-wf.rs:8:27
3+
|
4+
LL | fn extend(s: &str) -> (Option<&'static &'_ ()>, &'static str) {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: the pointer is valid for the static lifetime
8+
note: but the referenced data is only valid for the anonymous lifetime defined here
9+
--> $DIR/rpitit-hidden-types-self-implied-wf.rs:8:18
10+
|
11+
LL | fn extend(s: &str) -> (Option<&'static &'_ ()>, &'static str) {
12+
| ^^^^
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0491`.

0 commit comments

Comments
 (0)