Correct -- Copy and Drop are mutually exclusive, even for indirect Drop in any of its fields.
I don't know the exact mechanism off-hand, but derive(Copy) is a built-in macro, and the compiler already has to know where to insert drop calls in the first place.
I don't think it does, because macro expansion happens before type checking. But even if it knew, it wouldn't need to know. Any eventual, manual Copy impls would fail too, since the Drop xor Copy mutual exclusivity is built into the type system. (Playground)
Actually, if you expand #[derive(Copy)] for a Drop type, it doesn't cause an immediate error; it expands to the trivial impl, like this. (Choose the "Expand macros" option in the menu!)
The other interesting thing here, independent of the above implementation, by the way, but which seems paramount to me as well, is that
in order to implement Copy, all the fields must be Copy
which therefore allows to loosen the "no drop glue" requirement to a "no Drop impl". It does have the side-effect of not allowing Cell<impl Copy> be itself Copy, alas.
Heh, looking at the impl for the "structural check", I noticed that the check ignores lifetimes, so the following is accepted
#[derive(Clone)]
struct Foo<'lt>(&'lt ());
impl Copy for Foo<'static> {}
#[derive(Clone)]
struct Bar<'lt>(Foo<'lt>);
impl<'any> Copy for Bar<'any> {}
I would have expected this to be a case of "bad diagnostics" / another check later down the (compilation) line, such as borrowck, would take care of erroring on use of such impl for non-'static types:
fn copy(b: Bar<'_>) -> (Bar<'_>, Bar<'_>) { (b, b) }
I've managed to come up with a contrived scenario, that of a lib relying on TheirType<'not_static> : !Copy for soundness, where the above surprising impl can be weaponized to break such safety invariant: Rust Playground