Description
When implementing Drop
on a type that has type parameters (which requires #[unsafe_destructor])
, if the Drop
impl is specialized to some concrete type parameter, the specialization is ignored in the drop glue and the Drop
impl is instead run on all values of the outer type regardless of type parameters.
Example:
#![feature(unsafe_destructor)]
extern crate debug;
struct DropMe<T> {
_x: T
}
struct ImplDrop;
#[unsafe_destructor]
impl Drop for DropMe<ImplDrop> {
fn drop(&mut self) {
println!("DropMe<ImplDrop>.Drop: {:?}", *self);
}
}
fn main() {
let x = DropMe::<ImplDrop> { _x: ImplDrop };
println!("Dropping DropMe<ImplDrop>");
drop(x);
let y = DropMe::<int> { _x: 32 };
println!("Dropping DropMe<int>");
drop(y);
}
This should print
Dropping DropMe<int> Dropping DropMe<ImplDrop> DropMe<ImplDrop>.Drop: DropMe<ImplDrop>{_x: ImplDrop}
Instead it prints
Dropping DropMe<int> DropMe<ImplDrop>.Drop: DropMe<ImplDrop>{_x: ImplDrop} Dropping DropMe<ImplDrop> DropMe<ImplDrop>.Drop: DropMe<ImplDrop>{_x: ImplDrop}
Note that it's calling the Drop
impl on DropMe<int>
, even though the Drop
impl itself believes that the receiver type is DropMe<ImplDrop>
.
It gets worse. If you add a second specialized Drop
impl, that completely overrides the first:
#![feature(unsafe_destructor)]
extern crate debug;
struct DropMe<T> {
_x: T
}
struct ImplDrop;
#[unsafe_destructor]
impl Drop for DropMe<ImplDrop> {
fn drop(&mut self) {
println!("DropMe<ImplDrop>.Drop: {:?}", *self);
}
}
#[unsafe_destructor]
impl Drop for DropMe<int> {
fn drop(&mut self) {
println!("DropMe<int>.Drop: {:?}", *self);
}
}
fn main() {
let x = DropMe::<int> { _x: 32 };
println!("Dropping DropMe<int>");
drop(x);
let y = DropMe::<ImplDrop> { _x: ImplDrop };
println!("Dropping DropMe<ImplDrop>");
drop(y);
}
This prints:
Dropping DropMe<int> DropMe<int>.Drop: DropMe<int>{_x: 32} Dropping DropMe<ImplDrop> DropMe<int>.Drop: DropMe<int>{_x: 140734783283201}
It gets worse. If you use a type parameter with bounds, it incorrectly believes that it needs to call the Drop
impl regardless of parameterization (as it did before), even though it knows that the type doesn't conform to the bounds. At least this time it's a compile-time error:
#![feature(unsafe_destructor)]
extern crate debug;
struct DropMe<T> {
_x: T
}
struct ImplDrop;
trait Foo {}
impl Foo for ImplDrop {}
#[unsafe_destructor]
impl<T: Foo> Drop for DropMe<T> {
fn drop(&mut self) {
println!("DropMe<T: Foo>.Drop: {:?}", *self);
}
}
fn main() {
let x = DropMe::<int> { _x: 32 };
println!("Dropping DropMe<int>");
drop(x);
let y = DropMe::<ImplDrop> { _x: ImplDrop };
println!("Dropping DropMe<ImplDrop>");
drop(y);
}
unnamed.rs:15:5: 17:6 error: failed to find an implementation of trait Foo for int unnamed.rs:15 fn drop(&mut self) { unnamed.rs:16 println!("DropMe<T: Foo>.Drop: {:?}", *self); unnamed.rs:17 }
And of course if you add an impl Drop for DropMe<int>
then it complains about duplicate implementations.