-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
revives #91068 which has been fixed by only considering implied bounds from projections if they don't normalize while typechecking the function itself
rust/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
Lines 256 to 289 in 7125846
// We only add implied bounds for the normalized type as the unnormalized | |
// type may not actually get checked by the caller. | |
// | |
// Can otherwise be unsound, see #91068. | |
let TypeOpOutput { output: norm_ty, constraints: constraints1, .. } = self | |
.param_env | |
.and(type_op::normalize::Normalize::new(ty)) | |
.fully_perform(self.infcx) | |
.unwrap_or_else(|_| { | |
self.infcx | |
.tcx | |
.sess | |
.delay_span_bug(DUMMY_SP, &format!("failed to normalize {:?}", ty)); | |
TypeOpOutput { | |
output: self.infcx.tcx.ty_error(), | |
constraints: None, | |
error_info: None, | |
} | |
}); | |
// Note: we need this in examples like | |
// ``` | |
// trait Foo { | |
// type Bar; | |
// fn foo(&self) -> &Self::Bar; | |
// } | |
// impl Foo for () { | |
// type Bar = (); | |
// fn foo(&self) ->&() {} | |
// } | |
// ``` | |
// Both &Self::Bar and &() are WF | |
let constraints_implied = self.add_implied_bounds(norm_ty); | |
normalized_inputs_and_output.push(norm_ty); | |
constraints1.into_iter().chain(constraints_implied) |
This does not mean that the projection won't normalize when getting called:
trait Trait {
type Type;
}
impl<T> Trait for T {
type Type = ();
}
fn f<'a, 'b>(s: &'b str, _: <&'a &'b () as Trait>::Type) -> &'a str
where
&'a &'b (): Trait, // <- adding this bound is the change from #91068
{
s
}
fn main() {
let x = String::from("Hello World!");
let y = f(&x, ());
drop(x);
println!("{}", y);
}
The added bound prevents <&'a &'b () as Trait>::Type
from getting normalized while borrowchecking f
, as we prefer param candidates over impl candidates. When calling f
, we don't have the &'a &'b (): Trait
in our param_env
, so we can now freely use the impl candidate to normalize the projection. The caller therefore doesn't have to prove that &'a &'b ()
is well formed, causing unsoundness.
I am a bit surprised that the caller doesn't have to prove that the &'a &'b (): Trait
obligation is well formed, which would cause this example to not be unsound. It doesn't remove the general unsoundness here though. Here's an alternative test where that isn't enough:
trait Trait {
type Type;
}
impl<T> Trait for T {
type Type = ();
}
fn f<'a, 'b>(s: &'b str, _: <&'a &'b () as Trait>::Type) -> &'a str
where
for<'what, 'ever> &'what &'ever (): Trait,
{
s
}
fn main() {
let x = String::from("Hello World!");
let y = f(&x, ());
drop(x);
println!("{}", y);
}