Yes, generics may end up being built redundantly multiple times.
Crates are built in parallel, and then each crate may be further split into codegen units. If multiple codegen units end up needing to instantiate the same generic code with the same type, they will end up compiling the same thing redundantly.
Using dyn avoids this. There will be one concrete implementation built once for all types.
But only if the dyn version is exposed in crate A, right? if B and C use dyn while using entry_to_system directly, won't that need two concrete versions of it?
That's a good point. Code using dyn can be compiled eagerly once for all types, but where concrete implementations of a trait for specific types are built is less clear.
Due to orphan rules, you can't have multiple crates implementing the same trait for the same type. So if there's a specific impl Trait for u32 it would have to be in the crate defining the Trait. However I'm not sure if the dyn impl is built eagerly at that point if you don't use u32 with dyn in the same crate.
If there's a generic blanket impl like impl<T> Trait for T where… then it will behave like generics, and may be built redundantly.
There is an unstable option called -Zshare-generics which means that, if A contains a use of entry_to_system::<MyType>(), then B and C can reuse those monomorphizations.