While investigating a linkage bug, I stumbled upon the weak linkage handling in Rust and I think it’s currently quite questionable. This topic is to brainstorm about a better design.
First of all, to remove all confusion anyone (including myself) might have about weak linkage, here’s how it works. When you define a weak symbol and don’t link it in, referencing that symbol directly is illegal. Conversely, obtaining a pointer to that symbol results in NULL. Consider the following C code
#include <stdio.h>
extern __attribute__((weak)) void weakfunc();
extern __attribute__((weak)) void(*weakfuncptr)();
extern __attribute__((weak)) void* weakvar;
void main() {
printf("%p %p %p\n",&weakfunc,&weakfuncptr,&weakvar);
}
which results in this LLVM code
declare extern_weak void @weakfunc(...) #1
@weakfuncptr = extern_weak global void (...)*
@weakvar = extern_weak global i8*
and the program prints (nil) (nil) (nil)
. If you would’ve typed printf("%p\n",weakvar)
you would have gotten a segfault.
Now that we understand how weak linkage actually works in practice, we can think about what we want in Rust. See issue 31508 and PRs 12556 and 11978 on how weak linkage currently works in Rust. One of the main arguments for the current design is something about non-nullable types. However, this does not seem to have anything to do with whether a type is nullable. It has to do with whether one can safely use a particular variable at all. There are some subtleties with functions and function pointers, so I think it makes more sense to consider the variable case first and extend from there.
It does seem that requiring everything to go through a pointer to the weak symbol makes a lot of sense, but the chosen syntax really does not. Perhaps we can make referencing a weak symbol unsafe (like a static mut) and provider a safe wrapper type/macro. The wrapper would would essentially be Option<&T>
for a weak symbol of type T
. What are other people’s thoughts?
Side note: This would be the converse of the C weakfunc definition in Rust, but it doesn’t work at all (no linkage specified in LLVM):
extern {
#[linkage="extern_weak"] fn weakfunc();
}