-
-
Notifications
You must be signed in to change notification settings - Fork 14.3k
Description
The following code segfaults. I believe it is reasonable for users to expect this to be sound.
trait Trait {
type Assoc: std::fmt::Display;
}
impl Trait for fn(&()) {
type Assoc = Box<String>;
}
#[expect(coherence_leak_check)]
impl Trait for fn(&'static ()) {
type Assoc = usize;
}
/// SAFETY: `value` must be a type erased object of type `T::Assoc`
unsafe fn interpret_value_as_assoc<T: Trait>(_: T, value: *mut ()) {
// SAFETY: Guaranteed by safety requirement of this function
let value: T::Assoc = unsafe { std::mem::transmute_copy(&value) };
println!("{}", std::any::type_name::<T::Assoc>());
println!("{value}");
}
fn call_me(x: Result<Box<String>, usize>) {
let (func, value) = match x {
Ok(val) => (
interpret_value_as_assoc::<fn(&())>,
Box::into_raw(val) as *mut (),
),
Err(val) => (
interpret_value_as_assoc::<fn(&'static ())>,
val as *mut ()
),
};
// SAFETY: We use the correct function for either `Box<String>`
// or `usize`.
unsafe {
func(|&()| (), value);
}
}
fn main() {
call_me(Ok(Box::new(String::from("hello there"))));
call_me(Err(0));
}interpret_value_as_assoc::<fn(&())> and interpret_value_as_assoc::<fn(&'static())> are subtypes of each other, even though they have fundamentally different behavior. The fact that the signatures of function definitions are subtypes is quite irrelevant for whether the actual behavior of that function is closely related.
cc @rust-lang/types
I am currently experimenting with @spastorino to remove subtyping which impacts behavior by replacing it with coercions. Currently subtyping can impact behavior either via TypeId or via trait selection. It would be great if that works out. Otherwise I would like to try to change function definitions to have invariant arguments