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:
commit
94ecb52bbe
59 changed files with 1300 additions and 314 deletions
|
|
@ -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)
|
||||
}
|
||||
|
||||
(
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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, ...)
|
||||
```
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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`)",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
],
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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]> {
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)) };
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -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::*;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 { () };
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "citool"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())));
|
||||
}
|
||||
|
|
|
|||
8
src/tools/miri/tests/pass/thread_local-panic.rs
Normal file
8
src/tools/miri/tests/pass/thread_local-panic.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
thread_local! {
|
||||
static LOCAL: u64 = panic!();
|
||||
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = std::panic::catch_unwind(|| LOCAL.with(|_| {}));
|
||||
}
|
||||
5
src/tools/miri/tests/pass/thread_local-panic.stderr
Normal file
5
src/tools/miri/tests/pass/thread_local-panic.stderr
Normal 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
|
||||
|
|
@ -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() {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
17
tests/ui/compiletest-self-test/compile-flags-incremental.rs
Normal file
17
tests/ui/compiletest-self-test/compile-flags-incremental.rs
Normal 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.
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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`.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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())));
|
||||
}
|
||||
|
|
|
|||
266
tests/ui/thread-local/long-docs.rs
Normal file
266
tests/ui/thread-local/long-docs.rs
Normal 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() {}
|
||||
17
tests/ui/thread-local/no-unstable.rs
Normal file
17
tests/ui/thread-local/no-unstable.rs
Normal 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() {}
|
||||
57
tests/ui/thread-local/no-unstable.stderr
Normal file
57
tests/ui/thread-local/no-unstable.stderr
Normal 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`.
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue