Skip to content

Commit a61f6f3

Browse files
committed
Auto merge of rust-lang#116072 - compiler-errors:rpitit-implied-bounds, r=aliemjay
Use placeholders to prevent using inferred RPITIT types to imply their own well-formedness The issue here is that we use the same signature to do RPITIT inference as we do to compute implied bounds. To fix this, when gathering the assumed wf types for the method, we replace all of the infer vars (that will be eventually used to infer RPITIT types) with type placeholders, which imply nothing about lifetime bounds. This solution kind of sucks, but I'm not certain there's another feasible way to fix this. If anyone has a better solution, I'd be glad to hear it. My naive first solution was, instead of using placeholders, to replace the signature with the RPITIT projections that it originally started out with. But turns out that we can't just use the unnormalized signature of the trait method in `implied_outlives_bounds` since we normalize during WF computation -- that would cause a query cycle in `collect_return_position_impl_trait_in_trait_tys`. idk who to request review... r? `@lcnr` or `@aliemjay` i guess. Fixes rust-lang#116060
2 parents c614c17 + 8568121 commit a61f6f3

5 files changed

+169
-8
lines changed

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+88-8
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,
@@ -661,8 +662,6 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
661662
let trait_m = tcx.opt_associated_item(impl_m.trait_item_def_id.unwrap()).unwrap();
662663
let impl_trait_ref =
663664
tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap().instantiate_identity();
664-
let param_env = tcx.param_env(impl_m_def_id);
665-
666665
// First, check a few of the same things as `compare_impl_method`,
667666
// just so we don't ICE during substitution later.
668667
check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, true)?;
@@ -688,13 +687,26 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
688687
let trait_to_placeholder_args =
689688
impl_to_placeholder_args.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_args);
690689

690+
let hybrid_preds = tcx
691+
.predicates_of(impl_m.container_id(tcx))
692+
.instantiate_identity(tcx)
693+
.into_iter()
694+
.chain(tcx.predicates_of(trait_m.def_id).instantiate_own(tcx, trait_to_placeholder_args))
695+
.map(|(clause, _)| clause);
696+
let param_env = ty::ParamEnv::new(tcx.mk_clauses_from_iter(hybrid_preds), Reveal::UserFacing);
697+
let param_env = traits::normalize_param_env_or_error(
698+
tcx,
699+
param_env,
700+
ObligationCause::misc(tcx.def_span(impl_m_def_id), impl_m_def_id),
701+
);
702+
691703
let infcx = &tcx.infer_ctxt().build();
692704
let ocx = ObligationCtxt::new(infcx);
693705

694706
// Normalize the impl signature with fresh variables for lifetime inference.
695-
let norm_cause = ObligationCause::misc(return_span, impl_m_def_id);
707+
let misc_cause = ObligationCause::misc(return_span, impl_m_def_id);
696708
let impl_sig = ocx.normalize(
697-
&norm_cause,
709+
&misc_cause,
698710
param_env,
699711
tcx.liberate_late_bound_regions(
700712
impl_m.def_id,
@@ -725,12 +737,68 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
725737
);
726738
}
727739

728-
let trait_sig = ocx.normalize(&norm_cause, param_env, unnormalized_trait_sig);
740+
let trait_sig = ocx.normalize(&misc_cause, param_env, unnormalized_trait_sig);
729741
trait_sig.error_reported()?;
730742
let trait_return_ty = trait_sig.output();
731743

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

736804
match ocx.eq(&cause, param_env, trait_return_ty, impl_return_ty) {
@@ -787,6 +855,20 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
787855
}
788856
}
789857

858+
// FIXME: This has the same issue as #108544, but since this isn't breaking
859+
// existing code, I'm not particularly inclined to do the same hack as above
860+
// where we process wf obligations manually. This can be fixed in a forward-
861+
// compatible way later.
862+
let collected_types = collector.types;
863+
for (_, &(ty, _)) in &collected_types {
864+
ocx.register_obligation(traits::Obligation::new(
865+
tcx,
866+
misc_cause.clone(),
867+
param_env,
868+
ty::ClauseKind::WellFormed(ty.into()),
869+
));
870+
}
871+
790872
// Check that all obligations are satisfied by the implementation's
791873
// RPITs.
792874
let errors = ocx.select_all_or_error();
@@ -795,8 +877,6 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
795877
return Err(reported);
796878
}
797879

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