[unsafe] Fix non-const global initializers and [unsafe, refcount] order of global initialization - #366
Conversation
|
It's weird that Also, the new function call could be skipped if unneeded to reduce test case churn. |
There are new tests: tests/unit/global_non_const_init.cpp and tests/unit/global_init_side_effect.cpp
Yes, I think that could be done |
Not emitting the empty function would equally churn the tests because main would have to be moved at the end of each file. That's the only place where we know if a global needs initialization or not. |
You can check if there are any globals with non-const initializers when emitting main. The new tests don't have LazyCell, which is very weird. |
| pub static mut a_1: std::cell::LazyCell<S> = std::cell::LazyCell::new(|| unsafe { S::S({ 1 }) }); | ||
| pub static mut b_2: std::cell::LazyCell<S> = std::cell::LazyCell::new(|| unsafe { S::S({ 10 }) }); |
There was a problem hiding this comment.
LazyCell is used in the new tests here
There was a problem hiding this comment.
What I forgot to mention explicitly is that LazyCell is only for unsafe. Refcount continues to use thread_local Value
There was a problem hiding this comment.
Then don't generate the function call in refcount.
There was a problem hiding this comment.
Refcount also needs that. Thread locals are initialized lazily. We need them to be initialized before main starts
| pub static mut default_ctor_7: std::cell::LazyCell<Ctor> = | ||
| std::cell::LazyCell::new(|| unsafe { Ctor::Ctor1() }); | ||
| pub static mut arg_ctor_8: std::cell::LazyCell<Ctor> = | ||
| std::cell::LazyCell::new(|| unsafe { Ctor::Ctor2({ 7 }) }); | ||
| pub static mut str_9: std::cell::LazyCell<Vec<libc::c_char>> = | ||
| std::cell::LazyCell::new(|| unsafe { | ||
| { | ||
| let s = c"abc".as_ptr(); | ||
| std::slice::from_raw_parts(s, (0..).take_while(|&i| *s.add(i) != 0).count() + 1) | ||
| .to_vec() | ||
| } | ||
| }); |
There was a problem hiding this comment.
LazyCell is used in the new tests here
This does not work if globals are declared after main in both single-TU or multi-TU. We still need to move main at the end of the file which will equally churn the tests. I propose always emitting the function and generating an empty body if no globals need initialization. This keeps the converter simpler. |
|
Branch with conflicts |
Can't you traverse the AST and look for globals? |
I can, but I usually avoided traversing the AST more than once |
Rust requires initializers of global variables to be const. C++ does not require that.
To fix this tension, wrap global variables in
std::cell::LazyCellso that initialization does not depend on the constness of the initializer.Another problem that this PR fixes is the moment when global variables are initialized in both refcount and unsafe.
thread_localin refcount and the newLazyCellin unsafe initialize the variable on the first usage. This is wrong if the constructors of the global variables have side effects that must be visible in the program, for example:To fix this, I added a new function that runs before main and forces the initialization of global variables:
__cpp2rust_init_globals().