Description
I tried this code: (playground)
// This trait is unsealed. Imagine it's in crate A.
pub trait Example {
unsafe fn demo(&self) {}
}
// Imagine this type is in crate B,
// which depends on A.
pub struct S;
impl Example for S {
// This isn't applied to the trait's fn!
#[target_feature(enable = "avx2,aes")]
unsafe fn demo(&self) {}
}
// Imagine this function is in crate C,
// which also depends on A but might or might not be used
// together with A.
//
// It seems to be impossible to write the safety comment below.
pub fn accept_dyn(value: &dyn Example) {
// SAFETY: umm ???
// Who knows what #[target_feature]
// attributes the trait impl has imposed?!
unsafe { value.demo() }
}
The "imagine this type is in crate X" is not strictly necessary — this is still a problem within a single crate too. It's just much easier to spot the problem when the code is all together instead of in 3 different and separately evolving crates.
I expected to see this happen: to soundly use unsafe items from a trait, reading the safety comments on the trait and its items should generally be sufficient. A safe (non-unsafe
) attribute shouldn't be able to be used in a manner that causes hard-to-find unsoundness and UB. This code should either be rejected outright for tightening the trait's safety requirements in the impl, or at least require #[unsafe(target_feature)]
plus raise a lint for the requirements-tightening.
Instead, this happened: this code compiles fine with no warning. The attribute is not unsafe
. Even an unintentional "editing error" is likely to silently cause impls' requirements to diverge from the trait's requirements and lead to unsoundness.
Meta
rustc --version --verbose
:
1.88.0-nightly (2025-04-02 d5b4c2e4f19b6d703737)
From the playground.