Auto merge of #147261 - matthiaskrgr:rollup-yh3fgvc, r=matthiaskrgr

Rollup of 9 pull requests

Successful merges:

 - rust-lang/rust#146281 (Support `#[rustc_align_static]` inside `thread_local!`)
 - rust-lang/rust#146535 (mbe: Implement `unsafe` attribute rules)
 - rust-lang/rust#146585 (indexing: reword help)
 - rust-lang/rust#147004 (Tweak handling of "struct like start" where a struct isn't supported)
 - rust-lang/rust#147221 (Forbid `//@ compile-flags: -Cincremental=` in tests)
 - rust-lang/rust#147225 (Don't enable shared memory by default with Wasm atomics)
 - rust-lang/rust#147227 (implement `Box::take`)
 - rust-lang/rust#147233 (Initialize llvm submodule if not already the case to run citool)
 - rust-lang/rust#147236 (Update books)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-10-02 11:14:37 +00:00
commit 94ecb52bbe
59 changed files with 1300 additions and 314 deletions

View file

@ -207,10 +207,9 @@ pub fn check_attribute_safety(
}
}
// - Normal builtin attribute, or any non-builtin attribute
// - All non-builtin attributes are currently considered safe; writing `#[unsafe(..)]` is
// not permitted on non-builtin attributes or normal builtin attributes
(Some(AttributeSafety::Normal) | None, Safety::Unsafe(unsafe_span)) => {
// - Normal builtin attribute
// - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes
(Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
psess.dcx().emit_err(errors::InvalidAttrUnsafe {
span: unsafe_span,
name: attr_item.path.clone(),
@ -224,9 +223,8 @@ pub fn check_attribute_safety(
}
// - Non-builtin attribute
// - No explicit `#[unsafe(..)]` written.
(None, Safety::Default) => {
// OK
(None, Safety::Unsafe(_) | Safety::Default) => {
// OK (not checked here)
}
(

View file

@ -17,7 +17,6 @@ use rustc_middle::middle::exported_symbols::{
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
use rustc_span::sym;
use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld};
use tracing::{debug, warn};
@ -1324,37 +1323,7 @@ struct WasmLd<'a> {
impl<'a> WasmLd<'a> {
fn new(cmd: Command, sess: &'a Session) -> WasmLd<'a> {
// If the atomics feature is enabled for wasm then we need a whole bunch
// of flags:
//
// * `--shared-memory` - the link won't even succeed without this, flags
// the one linear memory as `shared`
//
// * `--max-memory=1G` - when specifying a shared memory this must also
// be specified. We conservatively choose 1GB but users should be able
// to override this with `-C link-arg`.
//
// * `--import-memory` - it doesn't make much sense for memory to be
// exported in a threaded module because typically you're
// sharing memory and instantiating the module multiple times. As a
// result if it were exported then we'd just have no sharing.
//
// On wasm32-unknown-unknown, we also export symbols for glue code to use:
// * `--export=*tls*` - when `#[thread_local]` symbols are used these
// symbols are how the TLS segments are initialized and configured.
let mut wasm_ld = WasmLd { cmd, sess };
if sess.target_features.contains(&sym::atomics) {
wasm_ld.link_args(&["--shared-memory", "--max-memory=1073741824", "--import-memory"]);
if sess.target.os == "unknown" || sess.target.os == "none" {
wasm_ld.link_args(&[
"--export=__wasm_init_tls",
"--export=__tls_size",
"--export=__tls_align",
"--export=__tls_base",
]);
}
}
wasm_ld
WasmLd { cmd, sess }
}
}

View file

@ -1,5 +1,5 @@
An attempt to use index on a type which doesn't implement the `std::ops::Index`
trait was performed.
Attempted to index a value whose type doesn't implement the
`std::ops::Index` trait.
Erroneous code example:
@ -7,8 +7,8 @@ Erroneous code example:
0u8[2]; // error: cannot index into a value of type `u8`
```
To be able to index into a type it needs to implement the `std::ops::Index`
trait. Example:
Only values with types that implement the `std::ops::Index` trait
can be indexed with square brackets. Example:
```
let v: Vec<u8> = vec![0, 1, 2, 3];
@ -16,3 +16,10 @@ let v: Vec<u8> = vec![0, 1, 2, 3];
// The `Vec` type implements the `Index` trait so you can do:
println!("{}", v[2]);
```
Tuples and structs are indexed with dot (`.`), not with brackets (`[]`),
and tuple element names are their positions:
```ignore(pseudo code)
// this (pseudo code) expression is true for any tuple:
tuple == (tuple.0, tuple.1, ...)
```

View file

@ -10,7 +10,7 @@ use rustc_ast::attr::{AttributeExt, MarkedAttrs};
use rustc_ast::token::MetaVarKind;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{AssocCtxt, Visitor};
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind, Safety};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_data_structures::sync;
use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult};
@ -345,6 +345,21 @@ pub trait AttrProcMacro {
annotation: TokenStream,
annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed>;
// Default implementation for safe attributes; override if the attribute can be unsafe.
fn expand_with_safety<'cx>(
&self,
ecx: &'cx mut ExtCtxt<'_>,
safety: Safety,
span: Span,
annotation: TokenStream,
annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
if let Safety::Unsafe(span) = safety {
ecx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute");
}
self.expand(ecx, span, annotation, annotated)
}
}
impl<F> AttrProcMacro for F

View file

@ -812,11 +812,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
_ => item.to_tokens(),
};
let attr_item = attr.get_normal_item();
let safety = attr_item.unsafety;
if let AttrArgs::Eq { .. } = attr_item.args {
self.cx.dcx().emit_err(UnsupportedKeyValue { span });
}
let inner_tokens = attr_item.args.inner_tokens();
match expander.expand(self.cx, span, inner_tokens, tokens) {
match expander.expand_with_safety(self.cx, safety, span, inner_tokens, tokens) {
Ok(tok_result) => {
let fragment = self.parse_ast_fragment(
tok_result,
@ -840,6 +841,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
}
} else if let SyntaxExtensionKind::LegacyAttr(expander) = ext {
// `LegacyAttr` is only used for builtin attribute macros, which have their
// safety checked by `check_builtin_meta_item`, so we don't need to check
// `unsafety` here.
match validate_attr::parse_meta(&self.cx.sess.psess, &attr) {
Ok(meta) => {
let item_clone = macro_stats.then(|| item.clone());
@ -882,6 +886,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
}
}
} else if let SyntaxExtensionKind::NonMacroAttr = ext {
if let ast::Safety::Unsafe(span) = attr.get_normal_item().unsafety {
self.cx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute");
}
// `-Zmacro-stats` ignores these because they don't do any real expansion.
self.cx.expanded_inert_attrs.mark(&attr);
item.visit_attrs(|attrs| attrs.insert(pos, attr));

View file

@ -8,7 +8,7 @@ use rustc_ast::token::NtPatKind::*;
use rustc_ast::token::TokenKind::*;
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind};
use rustc_ast::tokenstream::{self, DelimSpan, TokenStream};
use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId, Safety};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan};
@ -131,6 +131,7 @@ pub(super) enum MacroRule {
Func { lhs: Vec<MatcherLoc>, lhs_span: Span, rhs: mbe::TokenTree },
/// An attr rule, for use with `#[m]`
Attr {
unsafe_rule: bool,
args: Vec<MatcherLoc>,
args_span: Span,
body: Vec<MatcherLoc>,
@ -247,8 +248,19 @@ impl TTMacroExpander for MacroRulesMacroExpander {
impl AttrProcMacro for MacroRulesMacroExpander {
fn expand(
&self,
_cx: &mut ExtCtxt<'_>,
_sp: Span,
_args: TokenStream,
_body: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
unreachable!("`expand` called on `MacroRulesMacroExpander`, expected `expand_with_safety`")
}
fn expand_with_safety(
&self,
cx: &mut ExtCtxt<'_>,
safety: Safety,
sp: Span,
args: TokenStream,
body: TokenStream,
@ -260,6 +272,7 @@ impl AttrProcMacro for MacroRulesMacroExpander {
self.node_id,
self.name,
self.transparency,
safety,
args,
body,
&self.rules,
@ -408,6 +421,7 @@ fn expand_macro_attr(
node_id: NodeId,
name: Ident,
transparency: Transparency,
safety: Safety,
args: TokenStream,
body: TokenStream,
rules: &[MacroRule],
@ -429,13 +443,26 @@ fn expand_macro_attr(
// Track nothing for the best performance.
match try_match_macro_attr(psess, name, &args, &body, rules, &mut NoopTracker) {
Ok((i, rule, named_matches)) => {
let MacroRule::Attr { rhs, .. } = rule else {
let MacroRule::Attr { rhs, unsafe_rule, .. } = rule else {
panic!("try_macro_match_attr returned non-attr rule");
};
let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else {
cx.dcx().span_bug(sp, "malformed macro rhs");
};
match (safety, unsafe_rule) {
(Safety::Default, false) | (Safety::Unsafe(_), true) => {}
(Safety::Default, true) => {
cx.dcx().span_err(sp, "unsafe attribute invocation requires `unsafe`");
}
(Safety::Unsafe(span), false) => {
cx.dcx().span_err(span, "unnecessary `unsafe` on safe attribute invocation");
}
(Safety::Safe(span), _) => {
cx.dcx().span_bug(span, "unexpected `safe` keyword");
}
}
let id = cx.current_expansion.id;
let tts = transcribe(psess, &named_matches, rhs, *rhs_span, transparency, id)
.map_err(|e| e.emit())?;
@ -681,6 +708,11 @@ pub fn compile_declarative_macro(
let mut rules = Vec::new();
while p.token != token::Eof {
let unsafe_rule = p.eat_keyword_noexpect(kw::Unsafe);
let unsafe_keyword_span = p.prev_token.span;
if unsafe_rule && let Some(guar) = check_no_eof(sess, &p, "expected `attr`") {
return dummy_syn_ext(guar);
}
let (args, is_derive) = if p.eat_keyword_noexpect(sym::attr) {
kinds |= MacroKinds::ATTR;
if !features.macro_attr() {
@ -705,6 +737,10 @@ pub fn compile_declarative_macro(
feature_err(sess, sym::macro_derive, span, "`macro_rules!` derives are unstable")
.emit();
}
if unsafe_rule {
sess.dcx()
.span_err(unsafe_keyword_span, "`unsafe` is only supported on `attr` rules");
}
if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") {
return dummy_syn_ext(guar);
}
@ -730,6 +766,10 @@ pub fn compile_declarative_macro(
(None, true)
} else {
kinds |= MacroKinds::BANG;
if unsafe_rule {
sess.dcx()
.span_err(unsafe_keyword_span, "`unsafe` is only supported on `attr` rules");
}
(None, false)
};
let lhs_tt = p.parse_token_tree();
@ -741,10 +781,10 @@ pub fn compile_declarative_macro(
if let Some(guar) = check_no_eof(sess, &p, "expected right-hand side of macro rule") {
return dummy_syn_ext(guar);
}
let rhs_tt = p.parse_token_tree();
let rhs_tt = parse_one_tt(rhs_tt, RulePart::Body, sess, node_id, features, edition);
check_emission(check_rhs(sess, &rhs_tt));
check_emission(check_meta_variables(&sess.psess, node_id, args.as_ref(), &lhs_tt, &rhs_tt));
let rhs = p.parse_token_tree();
let rhs = parse_one_tt(rhs, RulePart::Body, sess, node_id, features, edition);
check_emission(check_rhs(sess, &rhs));
check_emission(check_meta_variables(&sess.psess, node_id, args.as_ref(), &lhs_tt, &rhs));
let lhs_span = lhs_tt.span();
// Convert the lhs into `MatcherLoc` form, which is better for doing the
// actual matching.
@ -760,11 +800,11 @@ pub fn compile_declarative_macro(
};
let args = mbe::macro_parser::compute_locs(&delimited.tts);
let body_span = lhs_span;
rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs: rhs_tt });
rules.push(MacroRule::Attr { unsafe_rule, args, args_span, body: lhs, body_span, rhs });
} else if is_derive {
rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs: rhs_tt });
rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs });
} else {
rules.push(MacroRule::Func { lhs, lhs_span, rhs: rhs_tt });
rules.push(MacroRule::Func { lhs, lhs_span, rhs });
}
if p.token == token::Eof {
break;

View file

@ -3551,35 +3551,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
// Try to give some advice about indexing tuples.
if let ty::Tuple(types) = base_t.kind() {
let mut needs_note = true;
// If the index is an integer, we can show the actual
// fixed expression:
err.help(
"tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.",
);
// If index is an unsuffixed integer, show the fixed expression:
if let ExprKind::Lit(lit) = idx.kind
&& let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node
&& i.get()
< types
.len()
.try_into()
.expect("expected tuple index to be < usize length")
&& i.get() < types.len().try_into().expect("tuple length fits in u128")
{
err.span_suggestion(
brackets_span,
"to access tuple elements, use",
format!("to access tuple element `{i}`, use"),
format!(".{i}"),
Applicability::MachineApplicable,
);
needs_note = false;
} else if let ExprKind::Path(..) = idx.peel_borrows().kind {
err.span_label(
idx.span,
"cannot access tuple elements at a variable index",
);
}
if needs_note {
err.help(
"to access tuple elements, use tuple indexing \
syntax (e.g., `tuple.0`)",
);
}
}

View file

@ -2748,28 +2748,7 @@ impl<'a> Parser<'a> {
if token::Colon != self.token.kind {
return first_pat;
}
if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
|| !self.look_ahead(1, |token| token.is_non_reserved_ident())
{
let mut snapshot_type = self.create_snapshot_for_diagnostic();
snapshot_type.bump(); // `:`
match snapshot_type.parse_ty() {
Err(inner_err) => {
inner_err.cancel();
}
Ok(ty) => {
let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else {
return first_pat;
};
err.span_label(ty.span, "specifying the type of a pattern isn't supported");
self.restore_snapshot(snapshot_type);
let span = first_pat.span.to(ty.span);
first_pat = self.mk_pat(span, PatKind::Wild);
err.emit();
}
}
return first_pat;
}
// The pattern looks like it might be a path with a `::` -> `:` typo:
// `match foo { bar:baz => {} }`
let colon_span = self.token.span;
@ -2857,7 +2836,13 @@ impl<'a> Parser<'a> {
Applicability::MaybeIncorrect,
);
} else {
first_pat = self.mk_pat(new_span, PatKind::Wild);
first_pat = self.mk_pat(
new_span,
PatKind::Err(
self.dcx()
.span_delayed_bug(colon_span, "recovered bad path pattern"),
),
);
}
self.restore_snapshot(snapshot_pat);
}
@ -2870,7 +2855,14 @@ impl<'a> Parser<'a> {
err.span_label(ty.span, "specifying the type of a pattern isn't supported");
self.restore_snapshot(snapshot_type);
let new_span = first_pat.span.to(ty.span);
first_pat = self.mk_pat(new_span, PatKind::Wild);
first_pat =
self.mk_pat(
new_span,
PatKind::Err(self.dcx().span_delayed_bug(
colon_span,
"recovered bad pattern with type",
)),
);
}
}
err.emit();

View file

@ -3612,7 +3612,7 @@ impl<'a> Parser<'a> {
self.token.is_keyword(kw::Async) && self.is_gen_block(kw::Gen, 1)
}
fn is_certainly_not_a_block(&self) -> bool {
fn is_likely_struct_lit(&self) -> bool {
// `{ ident, ` and `{ ident: ` cannot start a block.
self.look_ahead(1, |t| t.is_ident())
&& self.look_ahead(2, |t| t == &token::Comma || t == &token::Colon)
@ -3624,24 +3624,50 @@ impl<'a> Parser<'a> {
path: &ast::Path,
) -> Option<PResult<'a, Box<Expr>>> {
let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
if struct_allowed || self.is_certainly_not_a_block() {
if let Err(err) = self.expect(exp!(OpenBrace)) {
return Some(Err(err));
match (struct_allowed, self.is_likely_struct_lit()) {
// A struct literal isn't expected and one is pretty much assured not to be present. The
// only situation that isn't detected is when a struct with a single field was attempted
// in a place where a struct literal wasn't expected, but regular parser errors apply.
// Happy path.
(false, false) => None,
(true, _) => {
// A struct is accepted here, try to parse it and rely on `parse_expr_struct` for
// any kind of recovery. Happy path.
if let Err(err) = self.expect(exp!(OpenBrace)) {
return Some(Err(err));
}
Some(self.parse_expr_struct(qself.clone(), path.clone(), true))
}
let expr = self.parse_expr_struct(qself.clone(), path.clone(), true);
if let (Ok(expr), false) = (&expr, struct_allowed) {
// This is a struct literal, but we don't can't accept them here.
self.dcx().emit_err(errors::StructLiteralNotAllowedHere {
span: expr.span,
sub: errors::StructLiteralNotAllowedHereSugg {
left: path.span.shrink_to_lo(),
right: expr.span.shrink_to_hi(),
},
});
(false, true) => {
// We have something like `match foo { bar,` or `match foo { bar:`, which means the
// user might have meant to write a struct literal as part of the `match`
// discriminant. This is done purely for error recovery.
let snapshot = self.create_snapshot_for_diagnostic();
if let Err(err) = self.expect(exp!(OpenBrace)) {
return Some(Err(err));
}
match self.parse_expr_struct(qself.clone(), path.clone(), false) {
Ok(expr) => {
// This is a struct literal, but we don't accept them here.
self.dcx().emit_err(errors::StructLiteralNotAllowedHere {
span: expr.span,
sub: errors::StructLiteralNotAllowedHereSugg {
left: path.span.shrink_to_lo(),
right: expr.span.shrink_to_hi(),
},
});
Some(Ok(expr))
}
Err(err) => {
// We couldn't parse a valid struct, rollback and let the parser emit an
// error elsewhere.
err.cancel();
self.restore_snapshot(snapshot);
None
}
}
}
return Some(expr);
}
None
}
pub(super) fn parse_struct_fields(

View file

@ -6,11 +6,18 @@ use crate::spec::{Cc, LinkerFlavor, Target, TargetMetadata, base};
pub(crate) fn target() -> Target {
let mut options = base::linux_wasm::opts();
options
.add_pre_link_args(LinkerFlavor::WasmLld(Cc::No), &["--export-memory", "--shared-memory"]);
options.add_pre_link_args(
LinkerFlavor::WasmLld(Cc::No),
&["--export-memory", "--shared-memory", "--max-memory=1073741824"],
);
options.add_pre_link_args(
LinkerFlavor::WasmLld(Cc::Yes),
&["--target=wasm32-wasi-threads", "-Wl,--export-memory,", "-Wl,--shared-memory"],
&[
"--target=wasm32-wasi-threads",
"-Wl,--export-memory,",
"-Wl,--shared-memory",
"-Wl,--max-memory=1073741824",
],
);
Target {

View file

@ -19,7 +19,7 @@ pub(crate) fn target() -> Target {
options.add_pre_link_args(
LinkerFlavor::WasmLld(Cc::No),
&["--import-memory", "--export-memory", "--shared-memory"],
&["--import-memory", "--export-memory", "--shared-memory", "--max-memory=1073741824"],
);
options.add_pre_link_args(
LinkerFlavor::WasmLld(Cc::Yes),
@ -28,6 +28,7 @@ pub(crate) fn target() -> Target {
"-Wl,--import-memory",
"-Wl,--export-memory,",
"-Wl,--shared-memory",
"-Wl,--max-memory=1073741824",
],
);

View file

@ -619,6 +619,37 @@ impl<T, A: Allocator> Box<T, A> {
pub fn into_inner(boxed: Self) -> T {
*boxed
}
/// Consumes the `Box` without consuming its allocation, returning the wrapped value and a `Box`
/// to the uninitialized memory where the wrapped value used to live.
///
/// This can be used together with [`write`](Box::write) to reuse the allocation for multiple
/// boxed values.
///
/// # Examples
///
/// ```
/// #![feature(box_take)]
///
/// let c = Box::new(5);
///
/// // take the value out of the box
/// let (value, uninit) = Box::take(c);
/// assert_eq!(value, 5);
///
/// // reuse the box for a second value
/// let c = Box::write(uninit, 6);
/// assert_eq!(*c, 6);
/// ```
#[unstable(feature = "box_take", issue = "147212")]
pub fn take(boxed: Self) -> (T, Box<mem::MaybeUninit<T>, A>) {
unsafe {
let (raw, alloc) = Box::into_raw_with_allocator(boxed);
let value = raw.read();
let uninit = Box::from_raw_in(raw.cast::<mem::MaybeUninit<T>>(), alloc);
(value, uninit)
}
}
}
impl<T> Box<[T]> {

View file

@ -42,7 +42,7 @@ cfg_select! {
}
_ => {
mod os;
pub use os::{Storage, thread_local_inner};
pub use os::{Storage, thread_local_inner, value_align};
pub(crate) use os::{LocalPointer, local_pointer};
}
}

View file

@ -10,9 +10,11 @@ enum State {
}
#[allow(missing_debug_implementations)]
#[repr(C)]
pub struct Storage<T> {
state: Cell<State>,
// This field must be first, for correctness of `#[rustc_align_static]`
val: UnsafeCell<T>,
state: Cell<State>,
}
impl<T> Storage<T> {

View file

@ -27,9 +27,11 @@ enum State<D> {
}
#[allow(missing_debug_implementations)]
#[repr(C)]
pub struct Storage<T, D> {
state: Cell<State<D>>,
// This field must be first, for correctness of `#[rustc_align_static]`
value: UnsafeCell<MaybeUninit<T>>,
state: Cell<State<D>>,
}
impl<T, D> Storage<T, D>

View file

@ -54,7 +54,7 @@ pub macro thread_local_inner {
// test in `tests/thread.rs` if these types are renamed.
// Used to generate the `LocalKey` value for const-initialized thread locals.
(@key $t:ty, const $init:expr) => {{
(@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{
const __INIT: $t = $init;
unsafe {
@ -62,6 +62,7 @@ pub macro thread_local_inner {
if $crate::mem::needs_drop::<$t>() {
|_| {
#[thread_local]
$(#[$align_attr])*
static VAL: $crate::thread::local_impl::EagerStorage<$t>
= $crate::thread::local_impl::EagerStorage::new(__INIT);
VAL.get()
@ -69,6 +70,7 @@ pub macro thread_local_inner {
} else {
|_| {
#[thread_local]
$(#[$align_attr])*
static VAL: $t = __INIT;
&VAL
}
@ -78,7 +80,7 @@ pub macro thread_local_inner {
}},
// used to generate the `LocalKey` value for `thread_local!`
(@key $t:ty, $init:expr) => {{
(@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{
#[inline]
fn __init() -> $t {
$init
@ -89,6 +91,7 @@ pub macro thread_local_inner {
if $crate::mem::needs_drop::<$t>() {
|init| {
#[thread_local]
$(#[$align_attr])*
static VAL: $crate::thread::local_impl::LazyStorage<$t, ()>
= $crate::thread::local_impl::LazyStorage::new();
VAL.get_or_init(init, __init)
@ -96,6 +99,7 @@ pub macro thread_local_inner {
} else {
|init| {
#[thread_local]
$(#[$align_attr])*
static VAL: $crate::thread::local_impl::LazyStorage<$t, !>
= $crate::thread::local_impl::LazyStorage::new();
VAL.get_or_init(init, __init)
@ -104,10 +108,6 @@ pub macro thread_local_inner {
})
}
}},
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
},
}
#[rustc_macro_transparency = "semitransparent"]

View file

@ -2,6 +2,7 @@
//! thread locals and we can instead just use plain statics!
use crate::cell::{Cell, UnsafeCell};
use crate::mem::MaybeUninit;
use crate::ptr;
#[doc(hidden)]
@ -11,12 +12,13 @@ use crate::ptr;
#[rustc_macro_transparency = "semitransparent"]
pub macro thread_local_inner {
// used to generate the `LocalKey` value for const-initialized thread locals
(@key $t:ty, const $init:expr) => {{
(@key $t:ty, $(#[$align_attr:meta])*, const $init:expr) => {{
const __INIT: $t = $init;
// NOTE: Please update the shadowing test in `tests/thread.rs` if these types are renamed.
unsafe {
$crate::thread::LocalKey::new(|_| {
$(#[$align_attr])*
static VAL: $crate::thread::local_impl::EagerStorage<$t> =
$crate::thread::local_impl::EagerStorage { value: __INIT };
&VAL.value
@ -25,27 +27,22 @@ pub macro thread_local_inner {
}},
// used to generate the `LocalKey` value for `thread_local!`
(@key $t:ty, $init:expr) => {{
(@key $t:ty, $(#[$align_attr:meta])*, $init:expr) => {{
#[inline]
fn __init() -> $t { $init }
unsafe {
use $crate::thread::LocalKey;
use $crate::thread::local_impl::LazyStorage;
LocalKey::new(|init| {
static VAL: LazyStorage<$t> = LazyStorage::new();
$crate::thread::LocalKey::new(|init| {
$(#[$align_attr])*
static VAL: $crate::thread::local_impl::LazyStorage<$t> = $crate::thread::local_impl::LazyStorage::new();
VAL.get(init, __init)
})
}
}},
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
},
}
#[allow(missing_debug_implementations)]
#[repr(transparent)] // Required for correctness of `#[rustc_align_static]`
pub struct EagerStorage<T> {
pub value: T,
}
@ -53,14 +50,27 @@ pub struct EagerStorage<T> {
// SAFETY: the target doesn't have threads.
unsafe impl<T> Sync for EagerStorage<T> {}
#[derive(Clone, Copy, PartialEq, Eq)]
enum State {
Initial,
Alive,
Destroying,
}
#[allow(missing_debug_implementations)]
#[repr(C)]
pub struct LazyStorage<T> {
value: UnsafeCell<Option<T>>,
// This field must be first, for correctness of `#[rustc_align_static]`
value: UnsafeCell<MaybeUninit<T>>,
state: Cell<State>,
}
impl<T> LazyStorage<T> {
pub const fn new() -> LazyStorage<T> {
LazyStorage { value: UnsafeCell::new(None) }
LazyStorage {
value: UnsafeCell::new(MaybeUninit::uninit()),
state: Cell::new(State::Initial),
}
}
/// Gets a pointer to the TLS value, potentially initializing it with the
@ -70,24 +80,39 @@ impl<T> LazyStorage<T> {
/// has occurred.
#[inline]
pub fn get(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
let value = unsafe { &*self.value.get() };
match value {
Some(v) => v,
None => self.initialize(i, f),
if self.state.get() == State::Alive {
self.value.get() as *const T
} else {
self.initialize(i, f)
}
}
#[cold]
fn initialize(&'static self, i: Option<&mut Option<T>>, f: impl FnOnce() -> T) -> *const T {
let value = i.and_then(Option::take).unwrap_or_else(f);
// Destroy the old value, after updating the TLS variable as the
// destructor might reference it.
// Destroy the old value if it is initialized
// FIXME(#110897): maybe panic on recursive initialization.
unsafe {
self.value.get().replace(Some(value));
if self.state.get() == State::Alive {
self.state.set(State::Destroying);
// Safety: we check for no initialization during drop below
unsafe {
ptr::drop_in_place(self.value.get() as *mut T);
}
self.state.set(State::Initial);
}
// SAFETY: we just set this to `Some`.
unsafe { (*self.value.get()).as_ref().unwrap_unchecked() }
// Guard against initialization during drop
if self.state.get() == State::Destroying {
panic!("Attempted to initialize thread-local while it is being dropped");
}
unsafe {
self.value.get().write(MaybeUninit::new(value));
}
self.state.set(State::Alive);
self.value.get() as *const T
}
}

View file

@ -1,8 +1,12 @@
use super::key::{Key, LazyKey, get, set};
use super::{abort_on_dtor_unwind, guard};
use crate::alloc::{self, Layout};
use crate::cell::Cell;
use crate::marker::PhantomData;
use crate::ptr;
use crate::mem::ManuallyDrop;
use crate::ops::Deref;
use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
use crate::ptr::{self, NonNull};
#[doc(hidden)]
#[allow_internal_unstable(thread_local_internals)]
@ -10,17 +14,12 @@ use crate::ptr;
#[unstable(feature = "thread_local_internals", issue = "none")]
#[rustc_macro_transparency = "semitransparent"]
pub macro thread_local_inner {
// used to generate the `LocalKey` value for const-initialized thread locals
(@key $t:ty, const $init:expr) => {
$crate::thread::local_impl::thread_local_inner!(@key $t, { const INIT_EXPR: $t = $init; INIT_EXPR })
},
// NOTE: we cannot import `Storage` or `LocalKey` with a `use` because that can shadow user
// provided type or type alias with a matching name. Please update the shadowing test in
// `tests/thread.rs` if these types are renamed.
// used to generate the `LocalKey` value for `thread_local!`.
(@key $t:ty, $init:expr) => {{
(@key $t:ty, $($(#[$($align_attr:tt)*])+)?, $init:expr) => {{
#[inline]
fn __init() -> $t { $init }
@ -29,37 +28,148 @@ pub macro thread_local_inner {
// in `tests/thread.rs` if these types are renamed.
unsafe {
$crate::thread::LocalKey::new(|init| {
static VAL: $crate::thread::local_impl::Storage<$t>
static VAL: $crate::thread::local_impl::Storage<$t, {
$({
// Ensure that attributes have valid syntax
// and that the proper feature gate is enabled
$(#[$($align_attr)*])+
#[allow(unused)]
static DUMMY: () = ();
})?
#[allow(unused_mut)]
let mut final_align = $crate::thread::local_impl::value_align::<$t>();
$($($crate::thread::local_impl::thread_local_inner!(@align final_align, $($align_attr)*);)+)?
final_align
}>
= $crate::thread::local_impl::Storage::new();
VAL.get(init, __init)
})
}
}},
($(#[$attr:meta])* $vis:vis $name:ident, $t:ty, $($init:tt)*) => {
$(#[$attr])* $vis const $name: $crate::thread::LocalKey<$t> =
$crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*);
// process a single `rustc_align_static` attribute
(@align $final_align:ident, rustc_align_static($($align:tt)*) $(, $($attr_rest:tt)+)?) => {
let new_align: $crate::primitive::usize = $($align)*;
if new_align > $final_align {
$final_align = new_align;
}
$($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
},
// process a single `cfg_attr` attribute
// by translating it into a `cfg`ed block and recursing.
// https://doc.rust-lang.org/reference/conditional-compilation.html#railroad-ConfigurationPredicate
(@align $final_align:ident, cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => {
#[cfg(true)]
{
$crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*);
}
$($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
},
(@align $final_align:ident, cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => {
#[cfg(false)]
{
$crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*);
}
$($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
},
(@align $final_align:ident, cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => {
#[cfg($cfg_pred)]
{
$crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*);
}
$($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)?
},
}
/// Use a regular global static to store this key; the state provided will then be
/// thread-local.
/// INVARIANT: ALIGN must be a valid alignment, and no less than `value_align::<T>`.
#[allow(missing_debug_implementations)]
pub struct Storage<T> {
pub struct Storage<T, const ALIGN: usize> {
key: LazyKey,
marker: PhantomData<Cell<T>>,
}
unsafe impl<T> Sync for Storage<T> {}
unsafe impl<T, const ALIGN: usize> Sync for Storage<T, ALIGN> {}
#[repr(C)]
struct Value<T: 'static> {
// This field must be first, for correctness of `#[rustc_align_static]`
value: T,
// INVARIANT: if this value is stored under a TLS key, `key` must be that `key`.
key: Key,
}
impl<T: 'static> Storage<T> {
pub const fn new() -> Storage<T> {
Storage { key: LazyKey::new(Some(destroy_value::<T>)), marker: PhantomData }
pub const fn value_align<T: 'static>() -> usize {
crate::mem::align_of::<Value<T>>()
}
/// Equivalent to `Box<Value<T>>`, but potentially over-aligned.
struct AlignedBox<T: 'static, const ALIGN: usize> {
ptr: NonNull<Value<T>>,
}
impl<T: 'static, const ALIGN: usize> AlignedBox<T, ALIGN> {
#[inline]
fn new(v: Value<T>) -> Self {
let layout = Layout::new::<Value<T>>().align_to(ALIGN).unwrap();
let ptr: *mut Value<T> = (unsafe { alloc::alloc(layout) }).cast();
let Some(ptr) = NonNull::new(ptr) else {
alloc::handle_alloc_error(layout);
};
unsafe { ptr.write(v) };
Self { ptr }
}
#[inline]
fn into_raw(b: Self) -> *mut Value<T> {
let md = ManuallyDrop::new(b);
md.ptr.as_ptr()
}
#[inline]
unsafe fn from_raw(ptr: *mut Value<T>) -> Self {
Self { ptr: unsafe { NonNull::new_unchecked(ptr) } }
}
}
impl<T: 'static, const ALIGN: usize> Deref for AlignedBox<T, ALIGN> {
type Target = Value<T>;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*(self.ptr.as_ptr()) }
}
}
impl<T: 'static, const ALIGN: usize> Drop for AlignedBox<T, ALIGN> {
#[inline]
fn drop(&mut self) {
let layout = Layout::new::<Value<T>>().align_to(ALIGN).unwrap();
unsafe {
let unwind_result = catch_unwind(AssertUnwindSafe(|| self.ptr.drop_in_place()));
alloc::dealloc(self.ptr.as_ptr().cast(), layout);
if let Err(payload) = unwind_result {
resume_unwind(payload);
}
}
}
}
impl<T: 'static, const ALIGN: usize> Storage<T, ALIGN> {
pub const fn new() -> Storage<T, ALIGN> {
Storage { key: LazyKey::new(Some(destroy_value::<T, ALIGN>)), marker: PhantomData }
}
/// Gets a pointer to the TLS value, potentially initializing it with the
@ -95,8 +205,11 @@ impl<T: 'static> Storage<T> {
return ptr::null();
}
let value = Box::new(Value { value: i.and_then(Option::take).unwrap_or_else(f), key });
let ptr = Box::into_raw(value);
let value = AlignedBox::<T, ALIGN>::new(Value {
value: i.and_then(Option::take).unwrap_or_else(f),
key,
});
let ptr = AlignedBox::into_raw(value);
// SAFETY:
// * key came from a `LazyKey` and is thus correct.
@ -114,7 +227,7 @@ impl<T: 'static> Storage<T> {
// initializer has already returned and the next scope only starts
// after we return the pointer. Therefore, there can be no references
// to the old value.
drop(unsafe { Box::from_raw(old) });
drop(unsafe { AlignedBox::<T, ALIGN>::from_raw(old) });
}
// SAFETY: We just created this value above.
@ -122,7 +235,7 @@ impl<T: 'static> Storage<T> {
}
}
unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
unsafe extern "C" fn destroy_value<T: 'static, const ALIGN: usize>(ptr: *mut u8) {
// SAFETY:
//
// The OS TLS ensures that this key contains a null value when this
@ -133,7 +246,7 @@ unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) {
// Note that to prevent an infinite loop we reset it back to null right
// before we return from the destructor ourselves.
abort_on_dtor_unwind(|| {
let ptr = unsafe { Box::from_raw(ptr as *mut Value<T>) };
let ptr = unsafe { AlignedBox::<T, ALIGN>::from_raw(ptr as *mut Value<T>) };
let key = ptr.key;
// SAFETY: `key` is the TLS key `ptr` was stored under.
unsafe { set(key, ptr::without_provenance_mut(1)) };

View file

@ -132,6 +132,216 @@ impl<T: 'static> fmt::Debug for LocalKey<T> {
}
}
#[doc(hidden)]
#[allow_internal_unstable(thread_local_internals)]
#[unstable(feature = "thread_local_internals", issue = "none")]
#[rustc_macro_transparency = "semitransparent"]
pub macro thread_local_process_attrs {
// Parse `cfg_attr` to figure out whether it's a `rustc_align_static`.
// Each `cfg_attr` can have zero or more attributes on the RHS, and can be nested.
// finished parsing the `cfg_attr`, it had no `rustc_align_static`
(
[] [$(#[$($prev_other_attrs:tt)*])*];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] };
[$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*];
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs_ret)*] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),*)]];
$($rest)*
);
),
// finished parsing the `cfg_attr`, it had nothing but `rustc_align_static`
(
[$(#[$($prev_align_attrs:tt)*])+] [];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] };
[$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*];
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)*];
$($rest)*
);
),
// finished parsing the `cfg_attr`, it had a mix of `rustc_align_static` and other attrs
(
[$(#[$($prev_align_attrs:tt)*])+] [$(#[$($prev_other_attrs:tt)*])+];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [] };
[$($prev_align_attrs_ret:tt)*] [$($prev_other_attrs_ret:tt)*];
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_align_attrs)*),+)]] [$($prev_other_attrs_ret)* #[cfg_attr($($predicate)*, $($($prev_other_attrs)*),+)]];
$($rest)*
);
),
// it's a `rustc_align_static`
(
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [rustc_align_static($($align_static_args:tt)*) $(, $($attr_rhs:tt)*)?] };
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs)* #[rustc_align_static($($align_static_args)*)]] [$($prev_other_attrs)*];
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
$($rest)*
);
),
// it's a nested `cfg_attr(true, ...)`; recurse into RHS
(
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] };
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[] [];
@processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] };
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
$($rest)*
);
),
// it's a nested `cfg_attr(false, ...)`; recurse into RHS
(
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] };
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[] [];
@processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] };
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
$($rest)*
);
),
// it's a nested `cfg_attr(..., ...)`; recurse into RHS
(
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr($cfg_lhs:meta, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] };
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[] [];
@processing_cfg_attr { pred: ($cfg_lhs), rhs: [$($cfg_rhs)*] };
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
$($rest)*
);
),
// it's some other attribute
(
[$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
@processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [$meta:meta $(, $($attr_rhs:tt)*)?] };
$($rest:tt)*
) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs)*] [$($prev_other_attrs)* #[$meta]];
@processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] };
$($rest)*
);
),
// Separate attributes into `rustc_align_static` and everything else:
// `rustc_align_static` attribute
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[rustc_align_static $($attr_rest:tt)*] $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs)* #[rustc_align_static $($attr_rest)*]] [$($prev_other_attrs)*];
$($rest)*
);
),
// `cfg_attr(true, ...)` attribute; parse it
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(true, $($cfg_rhs:tt)*)] $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[] [];
@processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] };
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
$($rest)*
);
),
// `cfg_attr(false, ...)` attribute; parse it
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(false, $($cfg_rhs:tt)*)] $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[] [];
@processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] };
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
$($rest)*
);
),
// `cfg_attr(..., ...)` attribute; parse it
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*)] $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[] [];
@processing_cfg_attr { pred: ($cfg_pred), rhs: [$($cfg_rhs)*] };
[$($prev_align_attrs)*] [$($prev_other_attrs)*];
$($rest)*
);
),
// doc comment not followed by any other attributes; process it all at once to avoid blowing recursion limit
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; $(#[doc $($doc_rhs:tt)*])+ $vis:vis static $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs)*] [$($prev_other_attrs)* $(#[doc $($doc_rhs)*])+];
$vis static $($rest)*
);
),
// 8 lines of doc comment; process them all at once to avoid blowing recursion limit
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*];
#[doc $($doc_rhs_1:tt)*] #[doc $($doc_rhs_2:tt)*] #[doc $($doc_rhs_3:tt)*] #[doc $($doc_rhs_4:tt)*]
#[doc $($doc_rhs_5:tt)*] #[doc $($doc_rhs_6:tt)*] #[doc $($doc_rhs_7:tt)*] #[doc $($doc_rhs_8:tt)*]
$($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs)*] [$($prev_other_attrs)*
#[doc $($doc_rhs_1)*] #[doc $($doc_rhs_2)*] #[doc $($doc_rhs_3)*] #[doc $($doc_rhs_4)*]
#[doc $($doc_rhs_5)*] #[doc $($doc_rhs_6)*] #[doc $($doc_rhs_7)*] #[doc $($doc_rhs_8)*]];
$($rest)*
);
),
// other attribute
([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[$($attr:tt)*] $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_process_attrs!(
[$($prev_align_attrs)*] [$($prev_other_attrs)* #[$($attr)*]];
$($rest)*
);
),
// Delegate to `thread_local_inner` once attributes are fully categorized:
// process `const` declaration and recurse
([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = const $init:block $(; $($($rest:tt)+)?)?) => (
$($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> =
$crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, const $init);
$($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)?
),
// process non-`const` declaration and recurse
([$($align_attrs:tt)*] [$($other_attrs:tt)*]; $vis:vis static $name:ident: $t:ty = $init:expr $(; $($($rest:tt)+)?)?) => (
$($other_attrs)* $vis const $name: $crate::thread::LocalKey<$t> =
$crate::thread::local_impl::thread_local_inner!(@key $t, $($align_attrs)*, $init);
$($($crate::thread::local_impl::thread_local_process_attrs!([] []; $($rest)+);)?)?
),
}
/// Declare a new thread local storage key of type [`std::thread::LocalKey`].
///
/// # Syntax
@ -182,28 +392,11 @@ impl<T: 'static> fmt::Debug for LocalKey<T> {
#[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")]
#[allow_internal_unstable(thread_local_internals)]
macro_rules! thread_local {
// empty (base case for the recursion)
() => {};
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const $init:block; $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, const $init);
$crate::thread_local!($($rest)*);
);
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const $init:block) => (
$crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, const $init);
);
// process multiple declarations
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => (
$crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, $init);
$crate::thread_local!($($rest)*);
);
// handle a single declaration
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => (
$crate::thread::local_impl::thread_local_inner!($(#[$attr])* $vis $name, $t, $init);
);
($($tt:tt)+) => {
$crate::thread::local_impl::thread_local_process_attrs!([] []; $($tt)+);
};
}
/// An error returned by [`LocalKey::try_with`](struct.LocalKey.html#method.try_with).

View file

@ -207,6 +207,7 @@ pub use self::local::{AccessError, LocalKey};
#[doc(hidden)]
#[unstable(feature = "thread_local_internals", issue = "none")]
pub mod local_impl {
pub use super::local::thread_local_process_attrs;
pub use crate::sys::thread_local::*;
}

View file

@ -66,6 +66,8 @@ fn thread_local_hygeiene() {
type Storage = ();
type LazyStorage = ();
type EagerStorage = ();
#[allow(non_camel_case_types)]
type usize = ();
thread_local! {
static A: LocalKey = const { () };
static B: Storage = const { () };

View file

@ -1,7 +1,7 @@
[package]
name = "citool"
version = "0.1.0"
edition = "2021"
edition = "2024"
[dependencies]
anyhow = "1"

View file

@ -24,7 +24,7 @@ use crate::github::JobInfoResolver;
use crate::jobs::RunType;
use crate::metrics::{JobMetrics, download_auto_job_metrics, download_job_metrics, load_metrics};
use crate::test_dashboard::generate_test_dashboard;
use crate::utils::{load_env_var, output_details};
use crate::utils::{init_submodule_if_needed, load_env_var, output_details};
const CI_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/..");
pub const DOCKER_DIRECTORY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../docker");
@ -121,6 +121,8 @@ fn run_workflow_locally(db: JobDatabase, job_type: JobType, name: String) -> any
(key.clone(), value)
}));
init_submodule_if_needed("src/llvm-project/")?;
let mut cmd = Command::new(Path::new(DOCKER_DIRECTORY).join("run.sh"));
cmd.arg(job.image());
cmd.envs(custom_env);

View file

@ -1,5 +1,7 @@
use std::borrow::Cow;
use std::convert::AsRef;
use std::path::Path;
use std::process::Command;
use anyhow::Context;
@ -34,3 +36,19 @@ where
pub fn normalize_path_delimiters(name: &str) -> Cow<'_, str> {
if name.contains("\\") { name.replace('\\', "/").into() } else { name.into() }
}
pub fn init_submodule_if_needed<P: AsRef<Path>>(path_to_submodule: P) -> anyhow::Result<()> {
let path_to_submodule = path_to_submodule.as_ref();
if let Ok(mut iter) = path_to_submodule.read_dir()
&& iter.any(|entry| entry.is_ok())
{
// Seems like the submodule is already initialized, nothing to be done here.
return Ok(());
}
let mut child = Command::new("git")
.args(&["submodule", "update", "--init"])
.arg(path_to_submodule)
.spawn()?;
if !child.wait()?.success() { Err(anyhow::anyhow!("git command failed")) } else { Ok(()) }
}

@ -1 +1 @@
Subproject commit 33f1af40cc44dde7e3e892f7a508e6f427d2cbc6
Subproject commit 1d7c3e6abec2d5a9bfac798b29b7855b95025426

@ -1 +1 @@
Subproject commit aa6ce337c0adf7a63e33960d184270f2a45ab9ef
Subproject commit e2ed891f00361efc26616d82590b1c85d7a8920e

@ -1 +1 @@
Subproject commit f17a018b9989430967d1c58e9a12c51169abc744
Subproject commit 23fc2682f8fcb887f77d0eaabba708809f834c11

@ -1 +1 @@
Subproject commit cc7247d8dfaef4c39000bb12c55c32ba5b5ba976
Subproject commit e11adf6016a362766eea5a3f9832e193994dd0c8

View file

@ -415,10 +415,18 @@ impl TestProps {
config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile)
{
let flags = split_flags(&flags);
for flag in &flags {
for (i, flag) in flags.iter().enumerate() {
if flag == "--edition" || flag.starts_with("--edition=") {
panic!("you must use `//@ edition` to configure the edition");
}
if (flag == "-C"
&& flags.get(i + 1).is_some_and(|v| v.starts_with("incremental=")))
|| flag.starts_with("-Cincremental=")
{
panic!(
"you must use `//@ incremental` to enable incremental compilation"
);
}
}
self.compile_flags.extend(flags);
}

View file

@ -1,4 +1,7 @@
#![feature(static_align)]
#![deny(non_upper_case_globals)]
use std::cell::Cell;
// When a static uses `align(N)`, its address should be a multiple of `N`.
@ -8,7 +11,64 @@ static FOO: u64 = 0;
#[rustc_align_static(512)]
static BAR: u64 = 0;
struct HasDrop(*const HasDrop);
impl Drop for HasDrop {
fn drop(&mut self) {
assert_eq!(core::ptr::from_mut(self).cast_const(), self.0);
}
}
thread_local! {
#[rustc_align_static(4096)]
static LOCAL: u64 = 0;
#[allow(unused_mut, reason = "test attribute handling")]
#[cfg_attr(true, rustc_align_static(4096))]
static CONST_LOCAL: u64 = const { 0 };
#[cfg_attr(any(true), cfg_attr(true, rustc_align_static(4096)))]
#[allow(unused_mut, reason = "test attribute handling")]
static HASDROP_LOCAL: Cell<HasDrop> = Cell::new(HasDrop(core::ptr::null()));
/// I love doc comments.
#[allow(unused_mut, reason = "test attribute handling")]
#[cfg_attr(all(),
cfg_attr(any(true),
cfg_attr(true, rustc_align_static(4096))))]
#[allow(unused_mut, reason = "test attribute handling")]
/// I love doc comments.
static HASDROP_CONST_LOCAL: Cell<HasDrop> = const { Cell::new(HasDrop(core::ptr::null())) };
#[cfg_attr(true,)]
#[cfg_attr(false,)]
#[cfg_attr(
true,
rustc_align_static(32),
cfg_attr(true, allow(non_upper_case_globals, reason = "test attribute handling")),
cfg_attr(false,)
)]
#[cfg_attr(false, rustc_align_static(0))]
static more_attr_testing: u64 = 0;
}
fn thread_local_ptr<T>(key: &'static std::thread::LocalKey<T>) -> *const T {
key.with(|local| core::ptr::from_ref::<T>(local))
}
fn main() {
assert!(core::ptr::from_ref(&FOO).addr().is_multiple_of(256));
assert!(core::ptr::from_ref(&BAR).addr().is_multiple_of(512));
assert!(thread_local_ptr(&LOCAL).addr().is_multiple_of(4096));
assert!(thread_local_ptr(&CONST_LOCAL).addr().is_multiple_of(4096));
assert!(thread_local_ptr(&HASDROP_LOCAL).addr().is_multiple_of(4096));
assert!(thread_local_ptr(&HASDROP_CONST_LOCAL).addr().is_multiple_of(4096));
assert!(thread_local_ptr(&more_attr_testing).addr().is_multiple_of(32));
// Test that address (and therefore alignment) is maintained during drop
let hasdrop_ptr = thread_local_ptr(&HASDROP_LOCAL);
core::mem::forget(HASDROP_LOCAL.replace(HasDrop(hasdrop_ptr.cast())));
let hasdrop_const_ptr = thread_local_ptr(&HASDROP_CONST_LOCAL);
core::mem::forget(HASDROP_CONST_LOCAL.replace(HasDrop(hasdrop_const_ptr.cast())));
}

View file

@ -0,0 +1,8 @@
thread_local! {
static LOCAL: u64 = panic!();
}
fn main() {
let _ = std::panic::catch_unwind(|| LOCAL.with(|_| {}));
}

View file

@ -0,0 +1,5 @@
thread 'main' ($TID) panicked at tests/pass/thread_local-panic.rs:LL:CC:
explicit panic
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect

View file

@ -1,7 +1,7 @@
#[unsafe(unsafe(no_mangle))]
//~^ ERROR expected identifier, found keyword `unsafe`
//~| ERROR cannot find attribute `r#unsafe` in this scope
//~| ERROR `r#unsafe` is not an unsafe attribute
//~| ERROR unnecessary `unsafe`
fn a() {}
fn main() {}

View file

@ -9,13 +9,11 @@ help: escape `unsafe` to use it as an identifier
LL | #[unsafe(r#unsafe(no_mangle))]
| ++
error: `r#unsafe` is not an unsafe attribute
error: unnecessary `unsafe` on safe attribute
--> $DIR/double-unsafe-attributes.rs:1:3
|
LL | #[unsafe(unsafe(no_mangle))]
| ^^^^^^ this is not an unsafe attribute
|
= note: extraneous unsafe is not allowed in attributes
| ^^^^^^
error: cannot find attribute `r#unsafe` in this scope
--> $DIR/double-unsafe-attributes.rs:1:10

View file

@ -1,4 +1,4 @@
#[unsafe(diagnostic::on_unimplemented( //~ ERROR: is not an unsafe attribute
#[unsafe(diagnostic::on_unimplemented( //~ ERROR: unnecessary `unsafe`
message = "testing",
))]
trait Foo {}

View file

@ -1,10 +1,8 @@
error: `diagnostic::on_unimplemented` is not an unsafe attribute
error: unnecessary `unsafe` on safe attribute
--> $DIR/unsafe-safe-attribute_diagnostic.rs:1:3
|
LL | #[unsafe(diagnostic::on_unimplemented(
| ^^^^^^ this is not an unsafe attribute
|
= note: extraneous unsafe is not allowed in attributes
| ^^^^^^
error: aborting due to 1 previous error

View file

@ -0,0 +1,17 @@
//@ revisions: good bad bad-space
//@ check-pass
//@[bad] compile-flags: -Cincremental=true
//@[bad] should-fail
//@[bad-space] compile-flags: -C incremental=dir
//@[bad-space] should-fail
fn main() {}
// Tests should not try to manually enable incremental compilation with
// `-Cincremental`, because that typically results in stray directories being
// created in the repository root.
//
// Instead, use the `//@ incremental` directive, which instructs compiletest
// to handle the details of passing `-Cincremental` with a fresh directory.

View file

@ -0,0 +1,11 @@
// The feature gate error may be emitted twice, but only on certain targets
//@ dont-require-annotations: ERROR
//@ dont-check-compiler-stderr
#![crate_type = "lib"]
thread_local! {
//~^ ERROR the `#[rustc_align_static]` attribute is an experimental feature
#[rustc_align_static(16)]
static THREAD_LOCAL: u16 = 0;
}

View file

@ -2,7 +2,9 @@ error[E0608]: cannot index into a value of type `({integer},)`
--> $DIR/index_message.rs:3:14
|
LL | let _ = z[0];
| ^^^ help: to access tuple elements, use: `.0`
| ^^^ help: to access tuple element `0`, use: `.0`
|
= help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.
error: aborting due to 1 previous error

View file

@ -2,17 +2,17 @@ error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer
--> $DIR/issue-27842.rs:4:16
|
LL | let _ = tup[0];
| ^^^ help: to access tuple elements, use: `.0`
| ^^^ help: to access tuple element `0`, use: `.0`
|
= help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.
error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer})`
--> $DIR/issue-27842.rs:9:16
|
LL | let _ = tup[i];
| ^-^
| |
| cannot access tuple elements at a variable index
| ^^^
|
= help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`)
= help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.
error[E0608]: cannot index into a value of type `({integer},)`
--> $DIR/issue-27842.rs:14:16
@ -20,7 +20,7 @@ error[E0608]: cannot index into a value of type `({integer},)`
LL | let _ = tup[3];
| ^^^
|
= help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`)
= help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.
error: aborting due to 3 previous errors

View file

@ -9,7 +9,7 @@ note: the constant `baz` is defined here
|
LL | thread_local!(static baz: f64 = 0.0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
= note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 1 previous error

View file

@ -50,3 +50,22 @@ macro_rules! forward_referenced_attr {
macro_rules! cyclic_attr {
attr() {} => {}
}
macro_rules! attr_with_safety {
unsafe attr() { struct RequiresUnsafe; } => {};
attr() { struct SafeInvocation; } => {};
}
#[attr_with_safety]
struct SafeInvocation;
//~v ERROR: unnecessary `unsafe` on safe attribute invocation
#[unsafe(attr_with_safety)]
struct SafeInvocation;
//~v ERROR: unsafe attribute invocation requires `unsafe`
#[attr_with_safety]
struct RequiresUnsafe;
#[unsafe(attr_with_safety)]
struct RequiresUnsafe;

View file

@ -9,6 +9,18 @@ LL | #[local_attr]
|
= note: this error originates in the attribute macro `local_attr` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unnecessary `unsafe` on safe attribute invocation
--> $DIR/macro-rules-attr-error.rs:63:3
|
LL | #[unsafe(attr_with_safety)]
| ^^^^^^
error: unsafe attribute invocation requires `unsafe`
--> $DIR/macro-rules-attr-error.rs:67:1
|
LL | #[attr_with_safety]
| ^^^^^^^^^^^^^^^^^^^
error: cannot find macro `local_attr` in this scope
--> $DIR/macro-rules-attr-error.rs:27:5
|
@ -59,5 +71,5 @@ note: a macro with the same name exists, but it appears later
LL | macro_rules! cyclic_attr {
| ^^^^^^^^^^^
error: aborting due to 6 previous errors
error: aborting due to 8 previous errors

View file

@ -37,10 +37,9 @@ fn g1() {
//~| HELP: maybe write a path separator here
_ => {}
}
if let Foo:Bar = f() { //~ WARN: irrefutable `if let` pattern
if let Foo:Bar = f() {
//~^ ERROR: expected one of
//~| HELP: maybe write a path separator here
//~| HELP: consider replacing the `if let` with a `let`
}
}

View file

@ -64,7 +64,7 @@ LL | if let Foo::Bar = f() {
| +
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:49:16
--> $DIR/issue-87086-colon-path-sep.rs:48:16
|
LL | ref qux: Foo::Baz => {}
| ^ -------- specifying the type of a pattern isn't supported
@ -77,7 +77,7 @@ LL | ref qux::Foo::Baz => {}
| ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:58:16
--> $DIR/issue-87086-colon-path-sep.rs:57:16
|
LL | mut qux: Foo::Baz => {}
| ^ -------- specifying the type of a pattern isn't supported
@ -90,7 +90,7 @@ LL | mut qux::Foo::Baz => {}
| ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:69:12
--> $DIR/issue-87086-colon-path-sep.rs:68:12
|
LL | Foo:Bar::Baz => {}
| ^-------- specifying the type of a pattern isn't supported
@ -103,7 +103,7 @@ LL | Foo::Bar::Baz => {}
| +
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:75:12
--> $DIR/issue-87086-colon-path-sep.rs:74:12
|
LL | Foo:Bar => {}
| ^--- specifying the type of a pattern isn't supported
@ -115,15 +115,5 @@ help: maybe write a path separator here
LL | Foo::Bar => {}
| +
warning: irrefutable `if let` pattern
--> $DIR/issue-87086-colon-path-sep.rs:40:8
|
LL | if let Foo:Bar = f() {
| ^^^^^^^^^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
= note: `#[warn(irrefutable_let_patterns)]` on by default
error: aborting due to 9 previous errors; 1 warning emitted
error: aborting due to 9 previous errors

View file

@ -20,3 +20,6 @@ macro_rules! e { {} }
macro_rules! f {}
//~^ ERROR: macros must contain at least one rule
macro_rules! g { unsafe {} => {} }
//~^ ERROR: `unsafe` is only supported on `attr` rules

View file

@ -52,5 +52,11 @@ error: macros must contain at least one rule
LL | macro_rules! f {}
| ^^^^^^^^^^^^^^^^^
error: aborting due to 9 previous errors
error: `unsafe` is only supported on `attr` rules
--> $DIR/bad-macro-definition.rs:24:18
|
LL | macro_rules! g { unsafe {} => {} }
| ^^^^^^
error: aborting due to 10 previous errors

View file

@ -13,6 +13,12 @@ macro_rules! attr_incomplete_3 { attr() {} }
macro_rules! attr_incomplete_4 { attr() {} => }
//~^ ERROR macro definition ended unexpectedly
macro_rules! attr_incomplete_5 { unsafe }
//~^ ERROR macro definition ended unexpectedly
macro_rules! attr_incomplete_6 { unsafe attr }
//~^ ERROR macro definition ended unexpectedly
macro_rules! attr_noparens_1 { attr{} {} => {} }
//~^ ERROR `attr` rule argument matchers require parentheses

View file

@ -22,8 +22,20 @@ error: macro definition ended unexpectedly
LL | macro_rules! attr_incomplete_4 { attr() {} => }
| ^ expected right-hand side of macro rule
error: macro definition ended unexpectedly
--> $DIR/macro-attr-bad.rs:16:40
|
LL | macro_rules! attr_incomplete_5 { unsafe }
| ^ expected `attr`
error: macro definition ended unexpectedly
--> $DIR/macro-attr-bad.rs:19:45
|
LL | macro_rules! attr_incomplete_6 { unsafe attr }
| ^ expected macro attr args
error: `attr` rule argument matchers require parentheses
--> $DIR/macro-attr-bad.rs:16:36
--> $DIR/macro-attr-bad.rs:22:36
|
LL | macro_rules! attr_noparens_1 { attr{} {} => {} }
| ^^
@ -35,7 +47,7 @@ LL + macro_rules! attr_noparens_1 { attr() {} => {} }
|
error: `attr` rule argument matchers require parentheses
--> $DIR/macro-attr-bad.rs:19:36
--> $DIR/macro-attr-bad.rs:25:36
|
LL | macro_rules! attr_noparens_2 { attr[] {} => {} }
| ^^
@ -47,13 +59,13 @@ LL + macro_rules! attr_noparens_2 { attr() {} => {} }
|
error: invalid macro matcher; matchers must be contained in balanced delimiters
--> $DIR/macro-attr-bad.rs:22:37
--> $DIR/macro-attr-bad.rs:28:37
|
LL | macro_rules! attr_noparens_3 { attr _ {} => {} }
| ^
error: duplicate matcher binding
--> $DIR/macro-attr-bad.rs:25:52
--> $DIR/macro-attr-bad.rs:31:52
|
LL | macro_rules! attr_dup_matcher_1 { attr() {$x:ident $x:ident} => {} }
| -------- ^^^^^^^^ duplicate binding
@ -61,7 +73,7 @@ LL | macro_rules! attr_dup_matcher_1 { attr() {$x:ident $x:ident} => {} }
| previous binding
error: duplicate matcher binding
--> $DIR/macro-attr-bad.rs:28:49
--> $DIR/macro-attr-bad.rs:34:49
|
LL | macro_rules! attr_dup_matcher_2 { attr($x:ident $x:ident) {} => {} }
| -------- ^^^^^^^^ duplicate binding
@ -69,12 +81,12 @@ LL | macro_rules! attr_dup_matcher_2 { attr($x:ident $x:ident) {} => {} }
| previous binding
error: duplicate matcher binding
--> $DIR/macro-attr-bad.rs:31:51
--> $DIR/macro-attr-bad.rs:37:51
|
LL | macro_rules! attr_dup_matcher_3 { attr($x:ident) {$x:ident} => {} }
| -------- ^^^^^^^^ duplicate binding
| |
| previous binding
error: aborting due to 10 previous errors
error: aborting due to 12 previous errors

View file

@ -41,3 +41,6 @@ macro_rules! derive_dup_matcher { derive() {$x:ident $x:ident} => {} }
//~^ ERROR duplicate matcher binding
//~| NOTE duplicate binding
//~| NOTE previous binding
macro_rules! derive_unsafe { unsafe derive() {} => {} }
//~^ ERROR `unsafe` is only supported on `attr` rules

View file

@ -86,5 +86,11 @@ LL | macro_rules! derive_dup_matcher { derive() {$x:ident $x:ident} => {} }
| |
| previous binding
error: aborting due to 12 previous errors
error: `unsafe` is only supported on `attr` rules
--> $DIR/macro-derive-bad.rs:45:30
|
LL | macro_rules! derive_unsafe { unsafe derive() {} => {} }
| ^^^^^^
error: aborting due to 13 previous errors

View file

@ -1,15 +1,16 @@
fn foo(x: bool) -> i32 {
match x { //~ ERROR struct literals are not allowed here
x: i32 => x, //~ ERROR expected
true => 42., //~ ERROR expected identifier
false => 0.333, //~ ERROR expected identifier
match x {
x: i32 => x, //~ ERROR: expected
//~^ ERROR: mismatched types
true => 42.,
false => 0.333,
}
} //~ ERROR expected one of
}
fn main() {
match foo(true) {
42: i32 => (), //~ ERROR expected
_: f64 => (), //~ ERROR expected
x: i32 => (), //~ ERROR expected
42: i32 => (), //~ ERROR: expected
_: f64 => (), //~ ERROR: expected
x: i32 => (), //~ ERROR: expected
}
}

View file

@ -1,64 +1,18 @@
error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, `}`, or an operator, found `=>`
--> $DIR/type-ascription-in-pattern.rs:3:16
error: expected one of `@` or `|`, found `:`
--> $DIR/type-ascription-in-pattern.rs:3:10
|
LL | match x {
| - while parsing this struct
LL | x: i32 => x,
| -^^ expected one of 8 possible tokens
| |
| help: try adding a comma: `,`
error: expected identifier, found keyword `true`
--> $DIR/type-ascription-in-pattern.rs:4:9
| ^ --- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
|
LL | match x {
| - while parsing this struct
LL | x: i32 => x,
LL | true => 42.,
| ^^^^ expected identifier, found keyword
error: expected identifier, found keyword `false`
--> $DIR/type-ascription-in-pattern.rs:5:9
help: maybe write a path separator here
|
LL | match x {
| - while parsing this struct
...
LL | false => 0.333,
| ^^^^^ expected identifier, found keyword
error: struct literals are not allowed here
--> $DIR/type-ascription-in-pattern.rs:2:11
|
LL | match x {
| ___________^
LL | | x: i32 => x,
LL | | true => 42.,
LL | | false => 0.333,
LL | | }
| |_____^
|
help: surround the struct literal with parentheses
|
LL ~ match (x {
LL | x: i32 => x,
LL | true => 42.,
LL | false => 0.333,
LL ~ })
|
error: expected one of `.`, `?`, `{`, or an operator, found `}`
--> $DIR/type-ascription-in-pattern.rs:7:1
|
LL | match x {
| ----- while parsing this `match` expression
...
LL | }
| - expected one of `.`, `?`, `{`, or an operator
LL | }
| ^ unexpected token
LL | x::i32 => x,
| ~~
error: expected one of `...`, `..=`, `..`, or `|`, found `:`
--> $DIR/type-ascription-in-pattern.rs:11:11
--> $DIR/type-ascription-in-pattern.rs:12:11
|
LL | 42: i32 => (),
| ^ --- specifying the type of a pattern isn't supported
@ -66,7 +20,7 @@ LL | 42: i32 => (),
| expected one of `...`, `..=`, `..`, or `|`
error: expected `|`, found `:`
--> $DIR/type-ascription-in-pattern.rs:12:10
--> $DIR/type-ascription-in-pattern.rs:13:10
|
LL | _: f64 => (),
| ^ --- specifying the type of a pattern isn't supported
@ -74,7 +28,7 @@ LL | _: f64 => (),
| expected `|`
error: expected one of `@` or `|`, found `:`
--> $DIR/type-ascription-in-pattern.rs:13:10
--> $DIR/type-ascription-in-pattern.rs:14:10
|
LL | x: i32 => (),
| ^ --- specifying the type of a pattern isn't supported
@ -86,5 +40,15 @@ help: maybe write a path separator here
LL | x::i32 => (),
| ~~
error: aborting due to 8 previous errors
error[E0308]: mismatched types
--> $DIR/type-ascription-in-pattern.rs:3:19
|
LL | fn foo(x: bool) -> i32 {
| --- expected `i32` because of return type
LL | match x {
LL | x: i32 => x,
| ^ expected `i32`, found `bool`
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0308`.

View file

@ -2,7 +2,9 @@ error[E0608]: cannot index into a value of type `({integer},)`
--> $DIR/suggestion-non-ascii.rs:3:24
|
LL | println!("☃{}", tup[0]);
| ^^^ help: to access tuple elements, use: `.0`
| ^^^ help: to access tuple element `0`, use: `.0`
|
= help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.
error: aborting due to 1 previous error

View file

@ -1,10 +1,14 @@
//@ run-pass
//@ compile-flags: --cfg FOURTY_TWO="42" --cfg TRUE --check-cfg=cfg(FOURTY_TWO,values("42")) --check-cfg=cfg(TRUE)
#![feature(static_align)]
#![deny(non_upper_case_globals)]
use std::cell::Cell;
#[rustc_align_static(64)]
static A: u8 = 0;
#[rustc_align_static(64)]
#[rustc_align_static(4096)]
static B: u8 = 0;
#[rustc_align_static(128)]
@ -17,10 +21,86 @@ unsafe extern "C" {
static C: u64;
}
struct HasDrop(*const HasDrop);
impl Drop for HasDrop {
fn drop(&mut self) {
assert_eq!(core::ptr::from_mut(self).cast_const(), self.0);
}
}
thread_local! {
#[rustc_align_static(4096)]
static LOCAL: u64 = 0;
#[allow(unused_mut, reason = "test attribute handling")]
#[cfg_attr(true, rustc_align_static(4096))]
static CONST_LOCAL: u64 = const { 0 };
#[cfg_attr(any(true), cfg_attr(true, rustc_align_static(4096)))]
#[allow(unused_mut, reason = "test attribute handling")]
static HASDROP_LOCAL: Cell<HasDrop> = Cell::new(HasDrop(core::ptr::null()));
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
#[allow(unused_mut, reason = "test attribute handling")]
#[cfg_attr(TRUE,
cfg_attr(FOURTY_TWO = "42",
cfg_attr(all(),
cfg_attr(any(true),
cfg_attr(true, rustc_align_static(4096))))))]
#[allow(unused_mut, reason = "test attribute handling")]
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
static HASDROP_CONST_LOCAL: Cell<HasDrop> = const { Cell::new(HasDrop(core::ptr::null())) };
#[cfg_attr(TRUE,)]
#[cfg_attr(true,)]
#[cfg_attr(false,)]
#[cfg_attr(
TRUE,
rustc_align_static(32),
cfg_attr(true, allow(non_upper_case_globals, reason = "test attribute handling")),
cfg_attr(false,)
)]
#[cfg_attr(false, rustc_align_static(0))]
static more_attr_testing: u64 = 0;
}
fn thread_local_ptr<T>(key: &'static std::thread::LocalKey<T>) -> *const T {
key.with(|local| core::ptr::from_ref::<T>(local))
}
fn main() {
assert!(core::ptr::from_ref(&A).addr().is_multiple_of(64));
assert!(core::ptr::from_ref(&B).addr().is_multiple_of(64));
assert!(core::ptr::from_ref(&B).addr().is_multiple_of(4096));
assert!(core::ptr::from_ref(&EXPORTED).addr().is_multiple_of(128));
unsafe { assert!(core::ptr::from_ref(&C).addr().is_multiple_of(128)) };
assert!(thread_local_ptr(&LOCAL).addr().is_multiple_of(4096));
assert!(thread_local_ptr(&CONST_LOCAL).addr().is_multiple_of(4096));
assert!(thread_local_ptr(&HASDROP_LOCAL).addr().is_multiple_of(4096));
assert!(thread_local_ptr(&HASDROP_CONST_LOCAL).addr().is_multiple_of(4096));
assert!(thread_local_ptr(&more_attr_testing).addr().is_multiple_of(32));
// Test that address (and therefore alignment) is maintained during drop
let hasdrop_ptr = thread_local_ptr(&HASDROP_LOCAL);
core::mem::forget(HASDROP_LOCAL.replace(HasDrop(hasdrop_ptr.cast())));
let hasdrop_const_ptr = thread_local_ptr(&HASDROP_CONST_LOCAL);
core::mem::forget(HASDROP_CONST_LOCAL.replace(HasDrop(hasdrop_const_ptr.cast())));
}

View file

@ -0,0 +1,266 @@
//@ check-pass
thread_local! {
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
pub static LONG_DOCS: () = ();
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
/// I love doc comments.
#[allow(unused_mut, reason = "testing")]
pub static LONG_DOCS_2: () = ();
}
fn main() {}

View file

@ -0,0 +1,17 @@
thread_local! {
//~^ ERROR: use of an internal attribute [E0658]
//~| ERROR: use of an internal attribute [E0658]
//~| ERROR: `#[used(linker)]` is currently unstable [E0658]
//~| ERROR: `#[used]` attribute cannot be used on constants
#[rustc_dummy = 17]
pub static FOO: () = ();
#[cfg_attr(true, rustc_dummy = 17)]
pub static BAR: () = ();
#[used(linker)]
pub static BAZ: () = ();
}
fn main() {}

View file

@ -0,0 +1,57 @@
error[E0658]: use of an internal attribute
--> $DIR/no-unstable.rs:1:1
|
LL | / thread_local! {
... |
LL | | pub static BAZ: () = ();
LL | | }
| |_^
|
= help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
= note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable
= note: the `#[rustc_dummy]` attribute is used for rustc unit tests
= note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0658]: use of an internal attribute
--> $DIR/no-unstable.rs:1:1
|
LL | / thread_local! {
... |
LL | | pub static BAZ: () = ();
LL | | }
| |_^
|
= help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
= note: the `#[rustc_dummy]` attribute is an internal implementation detail that will never be stable
= note: the `#[rustc_dummy]` attribute is used for rustc unit tests
= note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0658]: `#[used(linker)]` is currently unstable
--> $DIR/no-unstable.rs:1:1
|
LL | / thread_local! {
... |
LL | | pub static BAZ: () = ();
LL | | }
| |_^
|
= note: see issue #93798 <https://github.com/rust-lang/rust/issues/93798> for more information
= help: add `#![feature(used_with_arg)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
error: `#[used]` attribute cannot be used on constants
--> $DIR/no-unstable.rs:1:1
|
LL | / thread_local! {
... |
LL | | pub static BAZ: () = ();
LL | | }
| |_^
|
= help: `#[used]` can only be applied to statics
= note: this error originates in the macro `$crate::thread::local_impl::thread_local_process_attrs` which comes from the expansion of the macro `thread_local` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0658`.

View file

@ -4,7 +4,7 @@ error[E0608]: cannot index into a value of type `()`
LL | ()[f(&[1.0])];
| ^^^^^^^^^^^
|
= help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`)
= help: tuples are indexed with a dot and a literal index: `tuple.0`, `tuple.1`, etc.
error: aborting due to 1 previous error