Skip to content

function definition subtyping can impact behavior #148821

@lcnr

Description

@lcnr

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-coercionsArea: implicit and explicit `expr as Type` coercionsA-higher-rankedArea: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs)A-varianceArea: Variance (https://doc.rust-lang.org/nomicon/subtyping.html)C-bugCategory: This is a bug.T-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions