Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check #[thread_local] statics correctly in the compiler. #43746

Merged
merged 2 commits into from Aug 12, 2017

Conversation

eddyb
Copy link
Member

@eddyb eddyb commented Aug 8, 2017

Fixes #43733 by introducing #[allow_internal_unsafe] analogous to #[allow_internal_unstable], for letting a macro expand to unsafe blocks and functions even in #![forbid(unsafe_code)] crates.

Fixes #17954 by not letting references to #[thread_local] statics escape the function they're taken in - we can't just use a magical lifetime because Rust has lifetime parametrism, so if we added the often-proposed 'thread lifetime, we'd have no way to check it in generic code.
To avoid potential edge cases in the compiler, the lifetime is actually that of a temporary at the same position, i.e. &TLS_STATIC has the same lifetime &non_const_fn() would.

Referring to #[thread_local] statics at compile-time is banned now (as per PR discussion).

Additionally, to remove unsafe impl Sync from std::thread::local::fast::Key, #[thread_local] statics are now not required to implement Sync, as they are not shared between threads.

@rust-highfive
Copy link
Collaborator

r? @nikomatsakis

(rust_highfive has picked a reviewer for you, use r? to override)

@eddyb
Copy link
Member Author

eddyb commented Aug 8, 2017

cc @rust-lang/libs @rust-lang/compiler

@est31
Copy link
Member

est31 commented Aug 8, 2017

If #[allow_internal_unsafe] is ever going to be stabilized, it should imo be forbidden to declare macros with that label in #[forbid(unsafe)] contexts as otherwise the #[forbid(unsafe)] attribute gets meaningless.

@eddyb
Copy link
Member Author

eddyb commented Aug 8, 2017

@est31 I expect it be replaced by macros 2.0 hygiene.

@@ -643,7 +643,13 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
Ok(self.cat_rvalue_node(id, span, expr_ty))
}

Def::Static(_, mutbl) => {
Def::Static(def_id, mutbl) => {
// `#[thread_local]` statics may not outlive the current function.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like 2-space indent here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entire file has broken (read: old-skool match rules) indentation, I'm actually on a multiple of 4 and the Def::Static line above is wrong.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I just mentioned this b/c the line just after these modifications is 4-space indented, but yes it looks like the indentation is off in most of this file.

@alexcrichton
Copy link
Member

Is the intention to get thread-local statics "working" in the sense that it's harder to get them wrong in the compiler? Do you think we should get them on the path to stabilization?

Other programs which work today and "shouldn't" work are:

#![feature(thread_local)]

#[thread_local]
static A: u32 = 1;

static B: &'static u32 = &A;

fn main() {}

but maybe this patch starts to reject that?

@eddyb
Copy link
Member Author

eddyb commented Aug 9, 2017

@alexcrichton Interesting, I didn't consider that. Does that not ICE? Trigger an LLVM assertion or linker error? That is, I don't know what meaningful codegen LLVM could possibly have for B.

As for the intention, I want people to stop pitching 'thread and/or complain about the feature.
OTOH, if your platform supports it (and we can probably get LLVM to support even more), a Cell in a #[thread_local] static seems pretty good - but I don't have any preference either way.

@alexcrichton
Copy link
Member

LMRTFY (let me rust that for you)

It works, and I wouldn't expect it to work (I'd expect a rustc error)

@aidanhs aidanhs added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Aug 10, 2017
@eddyb
Copy link
Member Author

eddyb commented Aug 10, 2017

Out of minimal reduction, on Release mode, we have this LLVM IR:

@A = thread_local constant i32 1, align 4
@B = local_unnamed_addr constant i32* @A, align 8

And these assembly entries:

	.type	A,@object
	.section	.tdata.A,"awT",@progbits
	.globl	A
	.p2align	2
A:
	.long	1
	.size	A, 4
	.type	B,@object
	.section	.data.rel.ro.B,"aw",@progbits
	.globl	B
	.p2align	3
B:
	.quad	A
	.size	B, 8

What I suspect is happening is B is pointing to the original data, from which each thread gets a copy. This would not be unsound, as it'd behave as if compile-time was another thread.

EDIT: okay, so I was right about the addresses (see playpen) but... changes to the TLS are reflected in the original?! Or is #[thread_local] static mut just completely broken?
I'll attempt the same experiment with internal mutability next.

EDIT2: oh it just looks like the loads from B get aggressively folded to be &A.

EDIT3: fixed up demo - however, because of the aggressive load folding behavior which changes uses of B from pointing to the initializer of A to the runtime TLS slot, we should ban this anyway.

@eddyb
Copy link
Member Author

eddyb commented Aug 10, 2017

@alexcrichton I've just pushed an updated version of the PR which also checks those cases.

@alexcrichton
Copy link
Member

@bors: r+

👍

@bors
Copy link
Contributor

bors commented Aug 10, 2017

📌 Commit 44dbbeb has been approved by alexcrichton

@bors
Copy link
Contributor

bors commented Aug 11, 2017

⌛ Testing commit 44dbbeb3da7305b94241901f539034cbfd8150cb with merge 86293a2c4fe7cd45130286b270638779e2ae824f...

@bors
Copy link
Contributor

bors commented Aug 11, 2017

💔 Test failed - status-travis

@est31
Copy link
Member

est31 commented Aug 11, 2017

error: /checkout/src/test/compile-fail/thread-local-in-ctfe.rs:16: unexpected "warning": '16:17: 16:18: constant evaluation error: non-constant path in constant expression. This will become a HARD ERROR in the future'

Looks legit

@eddyb
Copy link
Member Author

eddyb commented Aug 11, 2017

Ah, fallout from #43522, I'll rebase and try to fix it.

@eddyb
Copy link
Member Author

eddyb commented Aug 11, 2017

@bors r=alexcrichton

@bors
Copy link
Contributor

bors commented Aug 11, 2017

📌 Commit eba7592 has been approved by alexcrichton

@bors
Copy link
Contributor

bors commented Aug 11, 2017

⌛ Testing commit eba7592 with merge e957a91...

bors added a commit that referenced this pull request Aug 11, 2017
Check #[thread_local] statics correctly in the compiler.

Fixes #43733 by introducing `#[allow_internal_unsafe]` analogous to `#[allow_internal_unstable]`, for letting a macro expand to `unsafe` blocks and functions even in `#![forbid(unsafe_code)]` crates.

Fixes #17954 by not letting references to `#[thread_local]` statics escape the function they're taken in - we can't just use a magical lifetime because Rust has *lifetime parametrism*, so if we added the often-proposed `'thread` lifetime, we'd have no way to check it in generic code.
To avoid potential edge cases in the compiler, the lifetime is actually that of a temporary at the same position, i.e. `&TLS_STATIC` has the same lifetime `&non_const_fn()` would.

Referring to `#[thread_local]` `static`s at compile-time is banned now (as per PR discussion).

Additionally, to remove `unsafe impl Sync` from `std::thread::local::fast::Key`, `#[thread_local]` statics are now not required to implement `Sync`, as they are not shared between threads.
@bors
Copy link
Contributor

bors commented Aug 11, 2017

💔 Test failed - status-appveyor

@est31
Copy link
Member

est31 commented Aug 11, 2017

compile-fail/issue-17954.rs:22: unexpected "error": '22:22: 22:27: dummy does not live long enough [E0597]'
[...]
compile-fail/issue-17954.rs:34: expected error not found: borrowed value does not live long enough

looks legit

@eddyb
Copy link
Member Author

eddyb commented Aug 11, 2017

So the borrow-checker is more clever than I gave it credit for. Maybe I don't need the #[cfg], hmm.

@eddyb
Copy link
Member Author

eddyb commented Aug 12, 2017

@bors r=alexcrichton

@bors
Copy link
Contributor

bors commented Aug 12, 2017

📌 Commit 61ca14f has been approved by alexcrichton

@bors
Copy link
Contributor

bors commented Aug 12, 2017

⌛ Testing commit 61ca14f10f55dac0fbee1525d570fd9e01780431 with merge ffbde4279123b29b6fc73c1e8023751bdbde44ad...

@bors
Copy link
Contributor

bors commented Aug 12, 2017

💔 Test failed - status-travis

@kennytm
Copy link
Member

kennytm commented Aug 12, 2017

arm-android failed. Still complaining about Sync?

[00:59:58] ---- [run-pass] run-pass/thread-local-extern-static.rs stdout ----
[00:59:58] 	
[00:59:58] error: auxiliary build of "/checkout/src/test/run-pass/auxiliary/thread-local-extern-static.rs" failed to compile: 
[00:59:58] status: exit code: 101
[00:59:58] command: /checkout/obj/build/x86_64-unknown-linux-gnu/stage2/bin/rustc /checkout/src/test/run-pass/auxiliary/thread-local-extern-static.rs -L /checkout/obj/build/x86_64-unknown-linux-gnu/test/run-pass --target=arm-linux-androideabi --crate-type=dylib -L /checkout/obj/build/x86_64-unknown-linux-gnu/test/run-pass/thread-local-extern-static.stage2-arm-linux-androideabi.run-pass.libaux -C prefer-dynamic --out-dir /checkout/obj/build/x86_64-unknown-linux-gnu/test/run-pass/thread-local-extern-static.stage2-arm-linux-androideabi.run-pass.libaux -Clinker=/android/ndk/arm-9/bin/arm-linux-androideabi-gcc -Crpath -O -Lnative=/checkout/obj/build/arm-linux-androideabi/native/rust-test-helpers
[00:59:58] stdout:
[00:59:58] ------------------------------------------
[00:59:58] 
[00:59:58] ------------------------------------------
[00:59:58] stderr:
[00:59:58] ------------------------------------------
[00:59:58] error[E0277]: the trait bound `std::cell::Cell<u32>: std::marker::Sync` is not satisfied
[00:59:58]   --> /checkout/src/test/run-pass/auxiliary/thread-local-extern-static.rs:18:1
[00:59:58]    |
[00:59:58] 18 | pub static FOO: Cell<u32> = Cell::new(3);
[00:59:58]    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::cell::Cell<u32>` cannot be shared between threads safely
[00:59:58]    |
[00:59:58]    = help: the trait `std::marker::Sync` is not implemented for `std::cell::Cell<u32>`
[00:59:58]    = note: shared static variables must have a type that implements `Sync`
[00:59:58] 
[00:59:58] error: aborting due to previous error
[00:59:58] 
[00:59:58] 
[00:59:58] ------------------------------------------
[00:59:58] 
[00:59:58] thread '[run-pass] run-pass/thread-local-extern-static.rs' panicked at 'explicit panic', /checkout/src/tools/compiletest/src/runtest.rs:2511:8
[00:59:58] note: Run with `RUST_BACKTRACE=1` for a backtrace.
[00:59:58] 
[00:59:58] 
[00:59:58] failures:
[00:59:58]     [run-pass] run-pass/thread-local-extern-static.rs
[00:59:58] 
[00:59:58] test result: FAILED. 2707 passed; 1 failed; 20 ignored; 0 measured; 0 filtered out

@eddyb
Copy link
Member Author

eddyb commented Aug 12, 2017

@kennytm No, I guess no architecture with OS TLS got to the tests before.

@eddyb
Copy link
Member Author

eddyb commented Aug 12, 2017

@bors r=alexcrichton

@bors
Copy link
Contributor

bors commented Aug 12, 2017

📌 Commit 92892d3 has been approved by alexcrichton

@bors
Copy link
Contributor

bors commented Aug 12, 2017

⌛ Testing commit 92892d3 with merge 4564933...

bors added a commit that referenced this pull request Aug 12, 2017
Check #[thread_local] statics correctly in the compiler.

Fixes #43733 by introducing `#[allow_internal_unsafe]` analogous to `#[allow_internal_unstable]`, for letting a macro expand to `unsafe` blocks and functions even in `#![forbid(unsafe_code)]` crates.

Fixes #17954 by not letting references to `#[thread_local]` statics escape the function they're taken in - we can't just use a magical lifetime because Rust has *lifetime parametrism*, so if we added the often-proposed `'thread` lifetime, we'd have no way to check it in generic code.
To avoid potential edge cases in the compiler, the lifetime is actually that of a temporary at the same position, i.e. `&TLS_STATIC` has the same lifetime `&non_const_fn()` would.

Referring to `#[thread_local]` `static`s at compile-time is banned now (as per PR discussion).

Additionally, to remove `unsafe impl Sync` from `std::thread::local::fast::Key`, `#[thread_local]` statics are now not required to implement `Sync`, as they are not shared between threads.
@bors
Copy link
Contributor

bors commented Aug 12, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: alexcrichton
Pushing 4564933 to master...

@bors bors merged commit 92892d3 into rust-lang:master Aug 12, 2017
@eddyb eddyb deleted the sound-thread-local branch August 12, 2017 14:34
ids1024 added a commit to ids1024/ralloc that referenced this pull request Nov 8, 2017
- New allocator API
- Remove the "allocator" feature, which should be unnecessary due to how
the new allocator API works
- NonZero no longer implements Deref (rust-lang/rust#41064)
- NonZero::new() returns an Option; use NonZero::new_unchecked()
- Thread locals are no longer 'static (rust-lang/rust#43746)
- Changes to feature flags
- Use unsafe to access extern static (rust-lang/rust#36247)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants