Merge pull request #4284 from rust-lang/rustup-2025-04-22

Automatic Rustup
This commit is contained in:
Oli Scherer 2025-04-22 05:26:48 +00:00 committed by GitHub
commit f456b40240
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
422 changed files with 5540 additions and 3653 deletions

View file

@ -225,7 +225,7 @@ dependencies = [
"memchr",
"serde",
"serde_derive",
"winnow 0.7.4",
"winnow 0.7.6",
]
[[package]]
@ -303,9 +303,9 @@ dependencies = [
[[package]]
name = "bstr"
version = "1.11.3"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"regex-automata 0.4.9",
@ -496,9 +496,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.35"
version = "4.5.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04"
dependencies = [
"clap_builder",
"clap_derive",
@ -516,9 +516,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.35"
version = "4.5.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
dependencies = [
"anstream",
"anstyle",
@ -798,9 +798,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.14"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
@ -1910,9 +1910,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jiff"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260"
checksum = "1f33145a5cbea837164362c7bd596106eb7c5198f97d1ba6f6ebb3223952e488"
dependencies = [
"jiff-static",
"log",
@ -1923,9 +1923,9 @@ dependencies = [
[[package]]
name = "jiff-static"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c"
checksum = "43ce13c40ec6956157a3635d97a1ee2df323b263f09ea14165131289cb0f5c19"
dependencies = [
"proc-macro2",
"quote",
@ -1979,9 +1979,9 @@ dependencies = [
[[package]]
name = "jsonpath-rust"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b0231bb404a6cd6c8f0ab41b907049063a089fc02aa7636cc5cd9a4d87364c9"
checksum = "6a37c2c87b8d16e788ce359660fead0ea5f4ed29ff400d55be74a4e01d1817d9"
dependencies = [
"pest",
"pest_derive",
@ -2113,9 +2113,9 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "litemap"
@ -4807,14 +4807,14 @@ version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d"
dependencies = [
"self_cell 1.1.0",
"self_cell 1.2.0",
]
[[package]]
name = "self_cell"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
[[package]]
name = "semver"
@ -6415,9 +6415,9 @@ dependencies = [
[[package]]
name = "winnow"
version = "0.7.4"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
dependencies = [
"memchr",
]

View file

@ -19,6 +19,14 @@
# Note that this has no default value (x.py uses the defaults in `bootstrap.example.toml`).
#profile = <none>
# Inherits configuration values from different configuration files (a.k.a. config extensions).
# Supports absolute paths, and uses the current directory (where the bootstrap was invoked)
# as the base if the given path is not absolute.
#
# The overriding logic follows a right-to-left order. For example, in `include = ["a.toml", "b.toml"]`,
# extension `b.toml` overrides `a.toml`. Also, parent extensions always overrides the inner ones.
#include = []
# Keeps track of major changes made to this configuration.
#
# This value also represents ID of the PR that caused major changes. Meaning,

View file

@ -416,10 +416,7 @@ impl MetaItem {
// This path is currently unreachable in the test suite.
unreachable!()
}
Some(TokenTree::Token(
Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. },
_,
)) => {
Some(TokenTree::Token(Token { kind, .. }, _)) if kind.is_delim() => {
panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tt);
}
_ => return None,

View file

@ -32,6 +32,18 @@ pub enum InvisibleOrigin {
ProcMacro,
}
impl InvisibleOrigin {
// Should the parser skip these invisible delimiters? Ideally this function
// will eventually disappear and no invisible delimiters will be skipped.
#[inline]
pub fn skip(&self) -> bool {
match self {
InvisibleOrigin::MetaVar(_) => false,
InvisibleOrigin::ProcMacro => true,
}
}
}
impl PartialEq for InvisibleOrigin {
#[inline]
fn eq(&self, _other: &InvisibleOrigin) -> bool {
@ -125,8 +137,7 @@ impl Delimiter {
pub fn skip(&self) -> bool {
match self {
Delimiter::Parenthesis | Delimiter::Bracket | Delimiter::Brace => false,
Delimiter::Invisible(InvisibleOrigin::MetaVar(_)) => false,
Delimiter::Invisible(InvisibleOrigin::ProcMacro) => true,
Delimiter::Invisible(origin) => origin.skip(),
}
}
@ -140,6 +151,24 @@ impl Delimiter {
_ => false,
}
}
pub fn as_open_token_kind(&self) -> TokenKind {
match *self {
Delimiter::Parenthesis => OpenParen,
Delimiter::Brace => OpenBrace,
Delimiter::Bracket => OpenBracket,
Delimiter::Invisible(origin) => OpenInvisible(origin),
}
}
pub fn as_close_token_kind(&self) -> TokenKind {
match *self {
Delimiter::Parenthesis => CloseParen,
Delimiter::Brace => CloseBrace,
Delimiter::Bracket => CloseBracket,
Delimiter::Invisible(origin) => CloseInvisible(origin),
}
}
}
// Note that the suffix is *not* considered when deciding the `LitKind` in this
@ -194,9 +223,9 @@ impl Lit {
match token.uninterpolate().kind {
Ident(name, IdentIsRaw::No) if name.is_bool_lit() => Some(Lit::new(Bool, name, None)),
Literal(token_lit) => Some(token_lit),
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
OpenInvisible(InvisibleOrigin::MetaVar(
MetaVarKind::Literal | MetaVarKind::Expr { .. },
))) => {
)) => {
// Unreachable with the current test suite.
panic!("from_token metavar");
}
@ -426,10 +455,22 @@ pub enum TokenKind {
Question,
/// Used by proc macros for representing lifetimes, not generated by lexer right now.
SingleQuote,
/// An opening delimiter (e.g., `{`).
OpenDelim(Delimiter),
/// A closing delimiter (e.g., `}`).
CloseDelim(Delimiter),
/// `(`
OpenParen,
/// `)`
CloseParen,
/// `{`
OpenBrace,
/// `}`
CloseBrace,
/// `[`
OpenBracket,
/// `]`
CloseBracket,
/// Invisible opening delimiter, produced by a macro.
OpenInvisible(InvisibleOrigin),
/// Invisible closing delimiter, produced by a macro.
CloseInvisible(InvisibleOrigin),
/* Literals */
Literal(Lit),
@ -530,6 +571,37 @@ impl TokenKind {
pub fn should_end_const_arg(&self) -> bool {
matches!(self, Gt | Ge | Shr | ShrEq)
}
pub fn is_delim(&self) -> bool {
self.open_delim().is_some() || self.close_delim().is_some()
}
pub fn open_delim(&self) -> Option<Delimiter> {
match *self {
OpenParen => Some(Delimiter::Parenthesis),
OpenBrace => Some(Delimiter::Brace),
OpenBracket => Some(Delimiter::Bracket),
OpenInvisible(origin) => Some(Delimiter::Invisible(origin)),
_ => None,
}
}
pub fn close_delim(&self) -> Option<Delimiter> {
match *self {
CloseParen => Some(Delimiter::Parenthesis),
CloseBrace => Some(Delimiter::Brace),
CloseBracket => Some(Delimiter::Bracket),
CloseInvisible(origin) => Some(Delimiter::Invisible(origin)),
_ => None,
}
}
pub fn is_close_delim_or_eof(&self) -> bool {
match self {
CloseParen | CloseBrace | CloseBracket | CloseInvisible(_) | Eof => true,
_ => false,
}
}
}
impl Token {
@ -559,7 +631,8 @@ impl Token {
| DotDotDot | DotDotEq | Comma | Semi | Colon | PathSep | RArrow | LArrow
| FatArrow | Pound | Dollar | Question | SingleQuote => true,
OpenDelim(..) | CloseDelim(..) | Literal(..) | DocComment(..) | Ident(..)
OpenParen | CloseParen | OpenBrace | CloseBrace | OpenBracket | CloseBracket
| OpenInvisible(_) | CloseInvisible(_) | Literal(..) | DocComment(..) | Ident(..)
| NtIdent(..) | Lifetime(..) | NtLifetime(..) | Eof => false,
}
}
@ -573,11 +646,12 @@ impl Token {
/// **NB**: Take care when modifying this function, since it will change
/// the stable set of tokens that are allowed to match an expr nonterminal.
pub fn can_begin_expr(&self) -> bool {
use Delimiter::*;
match self.uninterpolate().kind {
Ident(name, is_raw) =>
ident_can_begin_expr(name, self.span, is_raw), // value name or keyword
OpenDelim(Parenthesis | Brace | Bracket) | // tuple, array or block
OpenParen | // tuple
OpenBrace | // block
OpenBracket | // array
Literal(..) | // literal
Bang | // operator not
Minus | // unary minus
@ -591,12 +665,12 @@ impl Token {
PathSep | // global path
Lifetime(..) | // labeled loop
Pound => true, // expression attributes
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
OpenInvisible(InvisibleOrigin::MetaVar(
MetaVarKind::Block |
MetaVarKind::Expr { .. } |
MetaVarKind::Literal |
MetaVarKind::Path
))) => true,
)) => true,
_ => false,
}
}
@ -608,8 +682,8 @@ impl Token {
match &self.uninterpolate().kind {
// box, ref, mut, and other identifiers (can stricten)
Ident(..) | NtIdent(..) |
OpenDelim(Delimiter::Parenthesis) | // tuple pattern
OpenDelim(Delimiter::Bracket) | // slice pattern
OpenParen | // tuple pattern
OpenBracket | // slice pattern
And | // reference
Minus | // negative literal
AndAnd | // double reference
@ -620,14 +694,14 @@ impl Token {
Lt | // path (UFCS constant)
Shl => true, // path (double UFCS)
Or => matches!(pat_kind, PatWithOr), // leading vert `|` or-pattern
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
OpenInvisible(InvisibleOrigin::MetaVar(
MetaVarKind::Expr { .. } |
MetaVarKind::Literal |
MetaVarKind::Meta { .. } |
MetaVarKind::Pat(_) |
MetaVarKind::Path |
MetaVarKind::Ty { .. }
))) => true,
)) => true,
_ => false,
}
}
@ -637,8 +711,8 @@ impl Token {
match self.uninterpolate().kind {
Ident(name, is_raw) =>
ident_can_begin_type(name, self.span, is_raw), // type name or keyword
OpenDelim(Delimiter::Parenthesis) | // tuple
OpenDelim(Delimiter::Bracket) | // array
OpenParen | // tuple
OpenBracket | // array
Bang | // never
Star | // raw pointer
And | // reference
@ -647,10 +721,10 @@ impl Token {
Lifetime(..) | // lifetime bound in trait object
Lt | Shl | // associated path
PathSep => true, // global path
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
OpenInvisible(InvisibleOrigin::MetaVar(
MetaVarKind::Ty { .. } |
MetaVarKind::Path
))) => true,
)) => true,
// For anonymous structs or unions, which only appear in specific positions
// (type of struct fields or union fields), we don't consider them as regular types
_ => false,
@ -660,11 +734,11 @@ impl Token {
/// Returns `true` if the token can appear at the start of a const param.
pub fn can_begin_const_arg(&self) -> bool {
match self.kind {
OpenDelim(Delimiter::Brace) | Literal(..) | Minus => true,
OpenBrace | Literal(..) | Minus => true,
Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true,
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
OpenInvisible(InvisibleOrigin::MetaVar(
MetaVarKind::Expr { .. } | MetaVarKind::Block | MetaVarKind::Literal,
))) => true,
)) => true,
_ => false,
}
}
@ -711,7 +785,7 @@ impl Token {
match self.uninterpolate().kind {
Literal(..) | Minus => true,
Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true,
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind {
OpenInvisible(InvisibleOrigin::MetaVar(mv_kind)) => match mv_kind {
MetaVarKind::Literal => true,
MetaVarKind::Expr { can_begin_literal_maybe_minus, .. } => {
can_begin_literal_maybe_minus
@ -725,7 +799,7 @@ impl Token {
pub fn can_begin_string_literal(&self) -> bool {
match self.uninterpolate().kind {
Literal(..) => true,
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind {
OpenInvisible(InvisibleOrigin::MetaVar(mv_kind)) => match mv_kind {
MetaVarKind::Literal => true,
MetaVarKind::Expr { can_begin_string_literal, .. } => can_begin_string_literal,
_ => false,
@ -892,7 +966,7 @@ impl Token {
/// from an expanded metavar?
pub fn is_metavar_seq(&self) -> Option<MetaVarKind> {
match self.kind {
OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => Some(kind),
OpenInvisible(InvisibleOrigin::MetaVar(kind)) => Some(kind),
_ => None,
}
}
@ -970,7 +1044,8 @@ impl Token {
Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | PlusEq | MinusEq | StarEq | SlashEq
| PercentEq | CaretEq | AndEq | OrEq | ShlEq | ShrEq | At | DotDotDot | DotDotEq
| Comma | Semi | PathSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question
| OpenDelim(..) | CloseDelim(..) | Literal(..) | Ident(..) | NtIdent(..)
| OpenParen | CloseParen | OpenBrace | CloseBrace | OpenBracket | CloseBracket
| OpenInvisible(_) | CloseInvisible(_) | Literal(..) | Ident(..) | NtIdent(..)
| Lifetime(..) | NtLifetime(..) | DocComment(..) | Eof,
_,
) => {

View file

@ -770,12 +770,12 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
self.bclose(span, empty);
}
delim => {
let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
let token_str = self.token_kind_to_string(&delim.as_open_token_kind());
self.word(token_str);
self.ibox(0);
self.print_tts(tts, convert_dollar_crate);
self.end();
let token_str = self.token_kind_to_string(&token::CloseDelim(delim));
let token_str = self.token_kind_to_string(&delim.as_close_token_kind());
self.word(token_str);
}
}
@ -932,14 +932,13 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
token::RArrow => "->".into(),
token::LArrow => "<-".into(),
token::FatArrow => "=>".into(),
token::OpenDelim(Delimiter::Parenthesis) => "(".into(),
token::CloseDelim(Delimiter::Parenthesis) => ")".into(),
token::OpenDelim(Delimiter::Bracket) => "[".into(),
token::CloseDelim(Delimiter::Bracket) => "]".into(),
token::OpenDelim(Delimiter::Brace) => "{".into(),
token::CloseDelim(Delimiter::Brace) => "}".into(),
token::OpenDelim(Delimiter::Invisible(_))
| token::CloseDelim(Delimiter::Invisible(_)) => "".into(),
token::OpenParen => "(".into(),
token::CloseParen => ")".into(),
token::OpenBracket => "[".into(),
token::CloseBracket => "]".into(),
token::OpenBrace => "{".into(),
token::CloseBrace => "}".into(),
token::OpenInvisible(_) | token::CloseInvisible(_) => "".into(),
token::Pound => "#".into(),
token::Dollar => "$".into(),
token::Question => "?".into(),

View file

@ -191,7 +191,6 @@ pub enum AttributeKind {
},
MacroTransparency(Transparency),
Repr(ThinVec<(ReprAttr, Span)>),
RustcMacroEdition2021,
Stability {
stability: Stability,
/// Span of the `#[stable(...)]` or `#[unstable(...)]` attribute

View file

@ -28,7 +28,6 @@ pub(crate) mod cfg;
pub(crate) mod confusables;
pub(crate) mod deprecation;
pub(crate) mod repr;
pub(crate) mod rustc;
pub(crate) mod stability;
pub(crate) mod transparency;
pub(crate) mod util;

View file

@ -1,19 +0,0 @@
use rustc_attr_data_structures::AttributeKind;
use rustc_span::sym;
use super::{AcceptContext, SingleAttributeParser};
use crate::parser::ArgParser;
pub(crate) struct RustcMacroEdition2021Parser;
// FIXME(jdonszelmann): make these proper diagnostics
impl SingleAttributeParser for RustcMacroEdition2021Parser {
const PATH: &'static [rustc_span::Symbol] = &[sym::rustc_macro_edition_2021];
fn on_duplicate(_cx: &crate::context::AcceptContext<'_>, _first_span: rustc_span::Span) {}
fn convert(_cx: &AcceptContext<'_>, args: &ArgParser<'_>) -> Option<AttributeKind> {
assert!(args.no_args());
Some(AttributeKind::RustcMacroEdition2021)
}
}

View file

@ -15,7 +15,6 @@ use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInterna
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::repr::ReprParser;
use crate::attributes::rustc::RustcMacroEdition2021Parser;
use crate::attributes::stability::{
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
};
@ -77,7 +76,6 @@ attribute_groups!(
// tidy-alphabetical-start
Single<ConstStabilityIndirectParser>,
Single<DeprecationParser>,
Single<RustcMacroEdition2021Parser>,
Single<TransparencyParser>,
// tidy-alphabetical-end
];

View file

@ -430,9 +430,7 @@ impl<'a> MetaItemListParserContext<'a> {
let span = span.with_hi(segments.last().unwrap().span.hi());
Some(AttrPath { segments: segments.into_boxed_slice(), span })
}
TokenTree::Token(Token { kind: token::OpenDelim(_) | token::CloseDelim(_), .. }, _) => {
None
}
TokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => None,
_ => {
// malformed attributes can get here. We can't crash, but somewhere else should've
// already warned for this.

View file

@ -247,9 +247,9 @@ builtin_macros_multiple_defaults = multiple declared defaults
.suggestion = make `{$ident}` default
builtin_macros_naked_functions_testing_attribute =
cannot use `#[naked]` with testing attributes
cannot use `#[unsafe(naked)]` with testing attributes
.label = function marked with testing attribute here
.naked_attribute = `#[naked]` is incompatible with testing attributes
.naked_attribute = `#[unsafe(naked)]` is incompatible with testing attributes
builtin_macros_no_default_variant = `#[derive(Default)]` on enum with no `#[default]`
.label = this enum needs a unit variant marked with `#[default]`

View file

@ -596,15 +596,14 @@ mod llvm_enzyme {
}
};
let arg = ty.kind.is_simple_path().unwrap();
let sl: Vec<Symbol> = vec![arg, kw::Default];
let tmp = ecx.def_site_path(&sl);
let tmp = ecx.def_site_path(&[arg, kw::Default]);
let default_call_expr = ecx.expr_path(ecx.path(span, tmp));
let default_call_expr = ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]);
body.stmts.push(ecx.stmt_expr(default_call_expr));
return body;
}
let mut exprs: P<ast::Expr> = primal_call.clone();
let mut exprs: P<ast::Expr> = primal_call;
let d_ret_ty = match d_sig.decl.output {
FnRetTy::Ty(ref ty) => ty.clone(),
FnRetTy::Default(span) => {
@ -622,7 +621,7 @@ mod llvm_enzyme {
// type due to the Const return activity.
exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]);
} else {
let q = QSelf { ty: d_ret_ty.clone(), path_span: span, position: 0 };
let q = QSelf { ty: d_ret_ty, path_span: span, position: 0 };
let y =
ExprKind::Path(Some(P(q)), ecx.path_ident(span, Ident::from_str("default")));
let default_call_expr = ecx.expr(span, y);
@ -640,8 +639,7 @@ mod llvm_enzyme {
let mut exprs2 = thin_vec![exprs];
for arg in args.iter().skip(1) {
let arg = arg.kind.is_simple_path().unwrap();
let sl: Vec<Symbol> = vec![arg, kw::Default];
let tmp = ecx.def_site_path(&sl);
let tmp = ecx.def_site_path(&[arg, kw::Default]);
let default_call_expr = ecx.expr_path(ecx.path(span, tmp));
let default_call_expr =
ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]);

View file

@ -1,13 +1,4 @@
#![feature(
no_core,
lang_items,
never_type,
linkage,
extern_types,
naked_functions,
thread_local,
repr_simd
)]
#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local, repr_simd)]
#![no_core]
#![allow(dead_code, non_camel_case_types, internal_features)]
@ -387,11 +378,9 @@ global_asm! {
}
#[cfg(all(not(jit), target_arch = "x86_64"))]
#[naked]
#[unsafe(naked)]
extern "C" fn naked_test() {
unsafe {
naked_asm!("ret");
}
naked_asm!("ret")
}
#[repr(C)]

View file

@ -447,9 +447,14 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
m_len == v_len,
InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len }
);
// TODO: also support unsigned integers.
match *m_elem_ty.kind() {
ty::Int(_) => {}
_ => return_error!(InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }),
_ => return_error!(InvalidMonomorphization::MaskWrongElementType {
span,
name,
ty: m_elem_ty
}),
}
return Ok(bx.vector_select(args[0].immediate(), args[1].immediate(), args[2].immediate()));
}
@ -991,19 +996,15 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
assert_eq!(pointer_count - 1, ptr_count(element_ty0));
assert_eq!(underlying_ty, non_ptr(element_ty0));
// The element type of the third argument must be a signed integer type of any width:
// The element type of the third argument must be an integer type of any width:
// TODO: also support unsigned integers.
let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx());
match *element_ty2.kind() {
ty::Int(_) => (),
_ => {
require!(
false,
InvalidMonomorphization::ThirdArgElementType {
span,
name,
expected_element: element_ty2,
third_arg: arg_tys[2]
}
InvalidMonomorphization::MaskWrongElementType { span, name, ty: element_ty2 }
);
}
}
@ -1109,17 +1110,13 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
assert_eq!(underlying_ty, non_ptr(element_ty0));
// The element type of the third argument must be a signed integer type of any width:
// TODO: also support unsigned integers.
match *element_ty2.kind() {
ty::Int(_) => (),
_ => {
require!(
false,
InvalidMonomorphization::ThirdArgElementType {
span,
name,
expected_element: element_ty2,
third_arg: arg_tys[2]
}
InvalidMonomorphization::MaskWrongElementType { span, name, ty: element_ty2 }
);
}
}

View file

@ -1184,18 +1184,6 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
}};
}
/// Returns the bitwidth of the `$ty` argument if it is an `Int` type.
macro_rules! require_int_ty {
($ty: expr, $diag: expr) => {
match $ty {
ty::Int(i) => i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()),
_ => {
return_error!($diag);
}
}
};
}
/// Returns the bitwidth of the `$ty` argument if it is an `Int` or `Uint` type.
macro_rules! require_int_or_uint_ty {
($ty: expr, $diag: expr) => {
@ -1485,9 +1473,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
m_len == v_len,
InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len }
);
let in_elem_bitwidth = require_int_ty!(
let in_elem_bitwidth = require_int_or_uint_ty!(
m_elem_ty.kind(),
InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }
InvalidMonomorphization::MaskWrongElementType { span, name, ty: m_elem_ty }
);
let m_i1s = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, m_len);
return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate()));
@ -1508,7 +1496,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
// Integer vector <i{in_bitwidth} x in_len>:
let in_elem_bitwidth = require_int_or_uint_ty!(
in_elem.kind(),
InvalidMonomorphization::VectorArgument { span, name, in_ty, in_elem }
InvalidMonomorphization::MaskWrongElementType { span, name, ty: in_elem }
);
let i1xn = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, in_len);
@ -1732,14 +1720,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
}
);
let mask_elem_bitwidth = require_int_ty!(
let mask_elem_bitwidth = require_int_or_uint_ty!(
element_ty2.kind(),
InvalidMonomorphization::ThirdArgElementType {
span,
name,
expected_element: element_ty2,
third_arg: arg_tys[2]
}
InvalidMonomorphization::MaskWrongElementType { span, name, ty: element_ty2 }
);
// Alignment of T, must be a constant integer value:
@ -1834,14 +1817,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
}
);
let m_elem_bitwidth = require_int_ty!(
let m_elem_bitwidth = require_int_or_uint_ty!(
mask_elem.kind(),
InvalidMonomorphization::ThirdArgElementType {
span,
name,
expected_element: values_elem,
third_arg: mask_ty,
}
InvalidMonomorphization::MaskWrongElementType { span, name, ty: mask_elem }
);
let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
@ -1924,14 +1902,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
}
);
let m_elem_bitwidth = require_int_ty!(
let m_elem_bitwidth = require_int_or_uint_ty!(
mask_elem.kind(),
InvalidMonomorphization::ThirdArgElementType {
span,
name,
expected_element: values_elem,
third_arg: mask_ty,
}
InvalidMonomorphization::MaskWrongElementType { span, name, ty: mask_elem }
);
let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len);
@ -2019,15 +1992,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
}
);
// The element type of the third argument must be a signed integer type of any width:
let mask_elem_bitwidth = require_int_ty!(
// The element type of the third argument must be an integer type of any width:
let mask_elem_bitwidth = require_int_or_uint_ty!(
element_ty2.kind(),
InvalidMonomorphization::ThirdArgElementType {
span,
name,
expected_element: element_ty2,
third_arg: arg_tys[2]
}
InvalidMonomorphization::MaskWrongElementType { span, name, ty: element_ty2 }
);
// Alignment of T, must be a constant integer value:

View file

@ -125,8 +125,7 @@ codegen_ssa_invalid_monomorphization_inserted_type = invalid monomorphization of
codegen_ssa_invalid_monomorphization_invalid_bitmask = invalid monomorphization of `{$name}` intrinsic: invalid bitmask `{$mask_ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]`
codegen_ssa_invalid_monomorphization_mask_type = invalid monomorphization of `{$name}` intrinsic: found mask element type is `{$ty}`, expected a signed integer type
.note = the mask may be widened, which only has the correct behavior for signed integers
codegen_ssa_invalid_monomorphization_mask_wrong_element_type = invalid monomorphization of `{$name}` intrinsic: expected mask element type to be an integer, found `{$ty}`
codegen_ssa_invalid_monomorphization_mismatched_lengths = invalid monomorphization of `{$name}` intrinsic: mismatched lengths: mask length `{$m_len}` != other vector length `{$v_len}`
@ -158,8 +157,6 @@ codegen_ssa_invalid_monomorphization_simd_shuffle = invalid monomorphization of
codegen_ssa_invalid_monomorphization_simd_third = invalid monomorphization of `{$name}` intrinsic: expected SIMD third type, found non-SIMD `{$ty}`
codegen_ssa_invalid_monomorphization_third_arg_element_type = invalid monomorphization of `{$name}` intrinsic: expected element type `{$expected_element}` of third argument `{$third_arg}` to be a signed integer type
codegen_ssa_invalid_monomorphization_third_argument_length = invalid monomorphization of `{$name}` intrinsic: expected third argument with length {$in_len} (same as input type `{$in_ty}`), found `{$arg_ty}` with length {$out_len}
codegen_ssa_invalid_monomorphization_unrecognized_intrinsic = invalid monomorphization of `{$name}` intrinsic: unrecognized intrinsic `{$name}`
@ -172,8 +169,6 @@ codegen_ssa_invalid_monomorphization_unsupported_symbol = invalid monomorphizati
codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size = invalid monomorphization of `{$name}` intrinsic: unsupported {$symbol} from `{$in_ty}` with element `{$in_elem}` of size `{$size}` to `{$ret_ty}`
codegen_ssa_invalid_monomorphization_vector_argument = invalid monomorphization of `{$name}` intrinsic: vector argument `{$in_ty}`'s element type `{$in_elem}`, expected integer element type
codegen_ssa_invalid_no_sanitize = invalid argument for `no_sanitize`
.note = expected one of: `address`, `cfi`, `hwaddress`, `kcfi`, `memory`, `memtag`, `shadow-call-stack`, or `thread`

View file

@ -1037,24 +1037,14 @@ pub enum InvalidMonomorphization<'tcx> {
v_len: u64,
},
#[diag(codegen_ssa_invalid_monomorphization_mask_type, code = E0511)]
#[note]
MaskType {
#[diag(codegen_ssa_invalid_monomorphization_mask_wrong_element_type, code = E0511)]
MaskWrongElementType {
#[primary_span]
span: Span,
name: Symbol,
ty: Ty<'tcx>,
},
#[diag(codegen_ssa_invalid_monomorphization_vector_argument, code = E0511)]
VectorArgument {
#[primary_span]
span: Span,
name: Symbol,
in_ty: Ty<'tcx>,
in_elem: Ty<'tcx>,
},
#[diag(codegen_ssa_invalid_monomorphization_cannot_return, code = E0511)]
CannotReturn {
#[primary_span]
@ -1077,15 +1067,6 @@ pub enum InvalidMonomorphization<'tcx> {
mutability: ExpectedPointerMutability,
},
#[diag(codegen_ssa_invalid_monomorphization_third_arg_element_type, code = E0511)]
ThirdArgElementType {
#[primary_span]
span: Span,
name: Symbol,
expected_element: Ty<'tcx>,
third_arg: Ty<'tcx>,
},
#[diag(codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size, code = E0511)]
UnsupportedSymbolOfSize {
#[primary_span]

View file

@ -11,7 +11,7 @@ Erroneous code example:
```compile_fail,E0736
#[inline]
#[naked]
#[unsafe(naked)]
fn foo() {}
```

View file

@ -3,9 +3,7 @@ An unsupported naked function definition.
Erroneous code example:
```compile_fail,E0787
#![feature(naked_functions)]
#[naked]
#[unsafe(naked)]
pub extern "C" fn f() -> u32 {
42
}

View file

@ -237,10 +237,7 @@ impl<'a> StripUnconfigured<'a> {
inner = self.configure_tokens(&inner);
Some(AttrTokenTree::Delimited(sp, spacing, delim, inner))
}
AttrTokenTree::Token(
Token { kind: TokenKind::OpenDelim(_) | TokenKind::CloseDelim(_), .. },
_,
) => {
AttrTokenTree::Token(Token { kind, .. }, _) if kind.is_delim() => {
panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tree);
}
AttrTokenTree::Token(token, spacing) => Some(AttrTokenTree::Token(token, spacing)),

View file

@ -7,13 +7,12 @@ use std::{iter, mem};
use rustc_ast as ast;
use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list};
use rustc_ast::{
AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, ExprKind, ForeignItemKind,
HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind,
NodeId, PatKind, StmtKind, TyKind,
NodeId, PatKind, StmtKind, TyKind, token,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
@ -1004,7 +1003,7 @@ pub fn parse_ast_fragment<'a>(
AstFragmentKind::Stmts => {
let mut stmts = SmallVec::new();
// Won't make progress on a `}`.
while this.token != token::Eof && this.token != token::CloseDelim(Delimiter::Brace) {
while this.token != token::Eof && this.token != token::CloseBrace {
if let Some(stmt) = this.parse_full_stmt(AttemptLocalParseRecovery::Yes)? {
stmts.push(stmt);
}

View file

@ -1,6 +1,6 @@
use std::borrow::Cow;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::token::{self, Token};
use rustc_ast::tokenstream::TokenStream;
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage};
use rustc_macros::Subdiagnostic;
@ -66,8 +66,8 @@ pub(super) fn failed_to_match_macro(
}
if let MatcherLoc::Token { token: expected_token } = &remaining_matcher
&& (matches!(expected_token.kind, TokenKind::OpenDelim(Delimiter::Invisible(_)))
|| matches!(token.kind, TokenKind::OpenDelim(Delimiter::Invisible(_))))
&& (matches!(expected_token.kind, token::OpenInvisible(_))
|| matches!(token.kind, token::OpenInvisible(_)))
{
err.note("captured metavariables except for `:tt`, `:ident` and `:lifetime` cannot be compared to other tokens");
err.note("see <https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment> for more information");

View file

@ -182,8 +182,8 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec<MatcherLoc> {
locs.push(MatcherLoc::Token { token: *token });
}
TokenTree::Delimited(span, _, delimited) => {
let open_token = Token::new(token::OpenDelim(delimited.delim), span.open);
let close_token = Token::new(token::CloseDelim(delimited.delim), span.close);
let open_token = Token::new(delimited.delim.as_open_token_kind(), span.open);
let close_token = Token::new(delimited.delim.as_close_token_kind(), span.close);
locs.push(MatcherLoc::Delimited);
locs.push(MatcherLoc::Token { token: open_token });

View file

@ -6,7 +6,7 @@ use std::{mem, slice};
use ast::token::IdentIsRaw;
use rustc_ast::token::NtPatKind::*;
use rustc_ast::token::TokenKind::*;
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind};
use rustc_ast::token::{self, NonterminalKind, Token, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{self as ast, DUMMY_NODE_ID, NodeId};
use rustc_ast_pretty::pprust;
@ -784,7 +784,7 @@ impl<'tt> FirstSets<'tt> {
TokenTree::Delimited(span, _, delimited) => {
build_recur(sets, &delimited.tts);
first.replace_with(TtHandle::from_token_kind(
token::OpenDelim(delimited.delim),
delimited.delim.as_open_token_kind(),
span.open,
));
}
@ -852,7 +852,7 @@ impl<'tt> FirstSets<'tt> {
}
TokenTree::Delimited(span, _, delimited) => {
first.add_one(TtHandle::from_token_kind(
token::OpenDelim(delimited.delim),
delimited.delim.as_open_token_kind(),
span.open,
));
return first;
@ -1099,7 +1099,7 @@ fn check_matcher_core<'tt>(
}
TokenTree::Delimited(span, _, d) => {
let my_suffix = TokenSet::singleton(TtHandle::from_token_kind(
token::CloseDelim(d.delim),
d.delim.as_close_token_kind(),
span.close,
));
check_matcher_core(sess, node_id, first_sets, &d.tts, &my_suffix)?;
@ -1299,7 +1299,9 @@ enum IsInFollow {
fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
use mbe::TokenTree;
if let TokenTree::Token(Token { kind: token::CloseDelim(_), .. }) = *tok {
if let TokenTree::Token(Token { kind, .. }) = tok
&& kind.close_delim().is_some()
{
// closing a token tree can never be matched by any fragment;
// iow, we always require that `(` and `)` match, etc.
IsInFollow::Yes
@ -1358,16 +1360,8 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
];
match tok {
TokenTree::Token(token) => match token.kind {
OpenDelim(Delimiter::Brace)
| OpenDelim(Delimiter::Bracket)
| Comma
| FatArrow
| Colon
| Eq
| Gt
| Shr
| Semi
| Or => IsInFollow::Yes,
OpenBrace | OpenBracket | Comma | FatArrow | Colon | Eq | Gt | Shr
| Semi | Or => IsInFollow::Yes,
Ident(name, IdentIsRaw::No) if name == kw::As || name == kw::Where => {
IsInFollow::Yes
}

View file

@ -181,7 +181,10 @@ fn parse_tree<'a>(
if delim != Delimiter::Parenthesis {
span_dollar_dollar_or_metavar_in_the_lhs_err(
sess,
&Token { kind: token::OpenDelim(delim), span: delim_span.entire() },
&Token {
kind: delim.as_open_token_kind(),
span: delim_span.entire(),
},
);
}
} else {
@ -217,7 +220,8 @@ fn parse_tree<'a>(
}
Delimiter::Parenthesis => {}
_ => {
let token = pprust::token_kind_to_string(&token::OpenDelim(delim));
let token =
pprust::token_kind_to_string(&delim.as_open_token_kind());
sess.dcx().emit_err(errors::ExpectedParenOrBrace {
span: delim_span.entire(),
token,

View file

@ -308,8 +308,8 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
}));
}
OpenDelim(..) | CloseDelim(..) => unreachable!(),
Eof => unreachable!(),
OpenParen | CloseParen | OpenBrace | CloseBrace | OpenBracket | CloseBracket
| OpenInvisible(_) | CloseInvisible(_) | Eof => unreachable!(),
}
}
trees

View file

@ -300,6 +300,8 @@ declare_features! (
/// Allows patterns with concurrent by-move and by-ref bindings.
/// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref.
(accepted, move_ref_pattern, "1.49.0", Some(68354)),
/// Allows using `#[naked]` on functions.
(accepted, naked_functions, "CURRENT_RUSTC_VERSION", Some(90957)),
/// Allows specifying modifiers in the link attribute: `#[link(modifiers = "...")]`
(accepted, native_link_modifiers, "1.61.0", Some(81490)),
/// Allows specifying the bundle link modifier

View file

@ -443,6 +443,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
ungated!(unsafe(Edition2024) no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, EncodeCrossCrate::No),
ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, EncodeCrossCrate::Yes),
ungated!(unsafe naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
// Limits:
ungated!(
@ -515,12 +516,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
// Unstable attributes:
// ==========================================================================
// Linking:
gated!(
naked, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No,
naked_functions, experimental!(naked)
),
// Testing:
gated!(
test_runner, CrateLevel, template!(List: "path"), ErrorFollowing,
@ -676,14 +671,6 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
"`rustc_never_type_options` is used to experiment with never type fallback and work on \
never type stabilization, and will never be stable"
),
rustc_attr!(
rustc_macro_edition_2021,
Normal,
template!(Word),
ErrorFollowing,
EncodeCrossCrate::No,
"makes spans in this macro edition 2021"
),
// ==========================================================================
// Internal attributes: Runtime related:

View file

@ -563,8 +563,6 @@ declare_features! (
(unstable, must_not_suspend, "1.57.0", Some(83310)),
/// Allows `mut ref` and `mut ref mut` identifier patterns.
(incomplete, mut_ref, "1.79.0", Some(123076)),
/// Allows using `#[naked]` on functions.
(unstable, naked_functions, "1.9.0", Some(90957)),
/// Allows using `#[naked]` on `extern "Rust"` functions.
(unstable, naked_functions_rustic_abi, "CURRENT_RUSTC_VERSION", Some(138997)),
/// Allows using `#[target_feature(enable = "...")]` on `#[naked]` on functions.

View file

@ -486,15 +486,15 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
let items: &AssocItems = self.tcx.associated_items(self.def_id);
items
.in_definition_order()
.filter(|item| item.is_type())
.filter(|item| {
!self
.gen_args
.constraints
.iter()
.any(|constraint| constraint.ident.name == item.name())
item.is_type()
&& !item.is_impl_trait_in_trait()
&& !self
.gen_args
.constraints
.iter()
.any(|constraint| constraint.ident.name == item.name())
})
.filter(|item| !item.is_impl_trait_in_trait())
.map(|item| self.tcx.item_ident(item.def_id).to_string())
.collect()
} else {

View file

@ -29,7 +29,7 @@ use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err,
};
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Namespace, Res};
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@ -1731,9 +1731,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
tcx.associated_items(*trait_def_id)
.in_definition_order()
.any(|i| {
i.namespace() == Namespace::TypeNS
i.is_type()
&& !i.is_impl_trait_in_trait()
&& i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
&& i.is_type()
})
// Consider only accessible traits
&& tcx.visibility(*trait_def_id)

View file

@ -234,7 +234,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// us do better coercions than we would be able to do otherwise,
// particularly for things like `String + &String`.
let rhs_ty_var = self.next_ty_var(rhs_expr.span);
let result = self.lookup_op_method(
(lhs_expr, lhs_ty),
Some((rhs_expr, rhs_ty_var)),
@ -698,6 +697,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
let lhs_name_str = match lhs_expr.kind {
hir::ExprKind::Path(hir::QPath::Resolved(_, path)) => {
path.segments.last().map_or("_".to_string(), |s| s.ident.to_string())
}
_ => self
.tcx
.sess
.source_map()
.span_to_snippet(lhs_expr.span)
.unwrap_or("_".to_string()),
};
if op.span().can_be_used_for_suggestions() {
match op {
Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. })
if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
{
err.multipart_suggestion(
"consider using `add` or `wrapping_add` to do pointer arithmetic",
vec![
(lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)),
(
lhs_expr.span.between(rhs_expr.span),
".wrapping_add(".to_owned(),
),
(rhs_expr.span.shrink_to_hi(), ")".to_owned()),
],
Applicability::MaybeIncorrect,
);
}
Op::AssignOp(Spanned { node: hir::AssignOpKind::SubAssign, .. }) => {
if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
err.multipart_suggestion(
"consider using `sub` or `wrapping_sub` to do pointer arithmetic",
vec![
(lhs_expr.span.shrink_to_lo(), format!("{} = ", lhs_name_str)),
(
lhs_expr.span.between(rhs_expr.span),
".wrapping_sub(".to_owned(),
),
(rhs_expr.span.shrink_to_hi(), ")".to_owned()),
],
Applicability::MaybeIncorrect,
);
}
}
_ => {}
}
}
let reported = err.emit();
Ty::new_error(self.tcx, reported)
}

View file

@ -427,12 +427,21 @@ impl<'a> CrateLocator<'a> {
let (rlibs, rmetas, dylibs) =
candidates.entry(hash.to_string()).or_default();
let path =
try_canonicalize(&spf.path).unwrap_or_else(|_| spf.path.to_path_buf());
if seen_paths.contains(&path) {
continue;
};
seen_paths.insert(path.clone());
{
// As a perforamnce optimisation we canonicalize the path and skip
// ones we've already seeen. This allows us to ignore crates
// we know are exactual equal to ones we've already found.
// Going to the same crate through different symlinks does not change the result.
let path = try_canonicalize(&spf.path)
.unwrap_or_else(|_| spf.path.to_path_buf());
if seen_paths.contains(&path) {
continue;
};
seen_paths.insert(path);
}
// Use the original path (potentially with unresolved symlinks),
// filesystem code should not care, but this is nicer for diagnostics.
let path = spf.path.to_path_buf();
match kind {
CrateFlavor::Rlib => rlibs.insert(path, search_path.kind),
CrateFlavor::Rmeta => rmetas.insert(path, search_path.kind),

View file

@ -246,6 +246,8 @@ impl AssocItems {
}
/// Returns an iterator over all associated items with the given name, ignoring hygiene.
///
/// Panics if `name.is_empty()` returns `true`.
pub fn filter_by_name_unhygienic(
&self,
name: Symbol,

View file

@ -1530,7 +1530,7 @@ fn build_scope_drops<'tcx>(
// path, then don't generate the drop. (We only take this into
// account for non-unwind paths so as not to disturb the
// caching mechanism.)
if scope.moved_locals.iter().any(|&o| o == local) {
if scope.moved_locals.contains(&local) {
continue;
}

View file

@ -564,13 +564,17 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
}
}
ExprKind::InlineAsm(box InlineAsmExpr {
asm_macro: AsmMacro::Asm | AsmMacro::NakedAsm,
asm_macro: asm_macro @ (AsmMacro::Asm | AsmMacro::NakedAsm),
ref operands,
template: _,
options: _,
line_spans: _,
}) => {
self.requires_unsafe(expr.span, UseOfInlineAssembly);
// The `naked` attribute and the `naked_asm!` block form one atomic unit of
// unsafety, and `naked_asm!` does not itself need to be wrapped in an unsafe block.
if let AsmMacro::Asm = asm_macro {
self.requires_unsafe(expr.span, UseOfInlineAssembly);
}
// For inline asm, do not use `walk_expr`, since we want to handle the label block
// specially.

View file

@ -254,8 +254,9 @@ where
always_export_generics,
);
// We can't differentiate something that got inlined.
// We can't differentiate a function that got inlined.
let autodiff_active = cfg!(llvm_enzyme)
&& matches!(mono_item, MonoItem::Fn(_))
&& cx
.tcx
.codegen_fn_attrs(mono_item.def_id())

View file

@ -288,6 +288,21 @@ where
) -> Vec<Candidate<I>>;
}
/// Allows callers of `assemble_and_evaluate_candidates` to choose whether to limit
/// candidate assembly to param-env and alias-bound candidates.
///
/// On top of being a micro-optimization, as it avoids doing unnecessary work when
/// a param-env trait bound candidate shadows impls for normalization, this is also
/// required to prevent query cycles due to RPITIT inference. See the issue at:
/// <https://github.com/rust-lang/trait-system-refactor-initiative/issues/173>.
pub(super) enum AssembleCandidatesFrom {
All,
/// Only assemble candidates from the environment and alias bounds, ignoring
/// user-written and built-in impls. We only expect `ParamEnv` and `AliasBound`
/// candidates to be assembled.
EnvAndBounds,
}
impl<D, I> EvalCtxt<'_, D>
where
D: SolverDelegate<Interner = I>,
@ -296,6 +311,7 @@ where
pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<D>>(
&mut self,
goal: Goal<I, G>,
assemble_from: AssembleCandidatesFrom,
) -> Vec<Candidate<I>> {
let Ok(normalized_self_ty) =
self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
@ -322,16 +338,18 @@ where
}
}
self.assemble_impl_candidates(goal, &mut candidates);
self.assemble_builtin_impl_candidates(goal, &mut candidates);
self.assemble_alias_bound_candidates(goal, &mut candidates);
self.assemble_object_bound_candidates(goal, &mut candidates);
self.assemble_param_env_candidates(goal, &mut candidates);
match assemble_from {
AssembleCandidatesFrom::All => {
self.assemble_impl_candidates(goal, &mut candidates);
self.assemble_builtin_impl_candidates(goal, &mut candidates);
self.assemble_object_bound_candidates(goal, &mut candidates);
}
AssembleCandidatesFrom::EnvAndBounds => {}
}
candidates
}
@ -754,6 +772,9 @@ where
})
}
/// Assemble and merge candidates for goals which are related to an underlying trait
/// goal. Right now, this is normalizes-to and host effect goals.
///
/// We sadly can't simply take all possible candidates for normalization goals
/// and check whether they result in the same constraints. We want to make sure
/// that trying to normalize an alias doesn't result in constraints which aren't
@ -782,47 +803,44 @@ where
///
/// See trait-system-refactor-initiative#124 for more details.
#[instrument(level = "debug", skip(self, inject_normalize_to_rigid_candidate), ret)]
pub(super) fn merge_candidates(
pub(super) fn assemble_and_merge_candidates<G: GoalKind<D>>(
&mut self,
proven_via: Option<TraitGoalProvenVia>,
candidates: Vec<Candidate<I>>,
goal: Goal<I, G>,
inject_normalize_to_rigid_candidate: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
) -> QueryResult<I> {
let Some(proven_via) = proven_via else {
// We don't care about overflow. If proving the trait goal overflowed, then
// it's enough to report an overflow error for that, we don't also have to
// overflow during normalization.
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Ambiguity));
//
// We use `forced_ambiguity` here over `make_ambiguous_response_no_constraints`
// because the former will also record a built-in candidate in the inspector.
return self.forced_ambiguity(MaybeCause::Ambiguity).map(|cand| cand.result);
};
match proven_via {
TraitGoalProvenVia::ParamEnv | TraitGoalProvenVia::AliasBound => {
let mut considered_candidates = Vec::new();
considered_candidates.extend(
candidates
.iter()
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
.map(|c| c.result),
);
// Even when a trait bound has been proven using a where-bound, we
// still need to consider alias-bounds for normalization, see
// tests/ui/next-solver/alias-bound-shadowed-by-env.rs.
//
// `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`.
let candidates_from_env_and_bounds: Vec<_> = self
.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds);
// We still need to prefer where-bounds over alias-bounds however.
// See tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs.
//
// FIXME(const_trait_impl): should this behavior also be used by
// constness checking. Doing so is *at least theoretically* breaking,
// see github.com/rust-lang/rust/issues/133044#issuecomment-2500709754
if considered_candidates.is_empty() {
considered_candidates.extend(
candidates
.iter()
.filter(|c| matches!(c.source, CandidateSource::AliasBound))
.map(|c| c.result),
);
}
// See `tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs`.
let mut considered_candidates: Vec<_> = if candidates_from_env_and_bounds
.iter()
.any(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
{
candidates_from_env_and_bounds
.into_iter()
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
.map(|c| c.result)
.collect()
} else {
candidates_from_env_and_bounds.into_iter().map(|c| c.result).collect()
};
// If the trait goal has been proven by using the environment, we want to treat
// aliases as rigid if there are no applicable projection bounds in the environment.
@ -839,6 +857,9 @@ where
}
}
TraitGoalProvenVia::Misc => {
let candidates =
self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
// Prefer "orphaned" param-env normalization predicates, which are used
// (for example, and ideally only) when proving item bounds for an impl.
let candidates_from_env: Vec<_> = candidates

View file

@ -399,12 +399,11 @@ where
&mut self,
goal: Goal<I, ty::HostEffectPredicate<I>>,
) -> QueryResult<I> {
let candidates = self.assemble_and_evaluate_candidates(goal);
let (_, proven_via) = self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
let trait_goal: Goal<I, ty::TraitPredicate<I>> =
goal.with(ecx.cx(), goal.predicate.trait_ref);
ecx.compute_trait_goal(trait_goal)
})?;
self.merge_candidates(proven_via, candidates, |_ecx| Err(NoSolution))
self.assemble_and_merge_candidates(proven_via, goal, |_ecx| Err(NoSolution))
}
}

View file

@ -32,14 +32,13 @@ where
let cx = self.cx();
match goal.predicate.alias.kind(cx) {
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
let candidates = self.assemble_and_evaluate_candidates(goal);
let trait_ref = goal.predicate.alias.trait_ref(cx);
let (_, proven_via) =
self.probe(|_| ProbeKind::ShadowedEnvProbing).enter(|ecx| {
let trait_goal: Goal<I, ty::TraitPredicate<I>> = goal.with(cx, trait_ref);
ecx.compute_trait_goal(trait_goal)
})?;
self.merge_candidates(proven_via, candidates, |ecx| {
self.assemble_and_merge_candidates(proven_via, goal, |ecx| {
ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| {
this.structurally_instantiate_normalizes_to_term(
goal,

View file

@ -13,7 +13,7 @@ use tracing::{instrument, trace};
use crate::delegate::SolverDelegate;
use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
use crate::solve::assembly::{self, Candidate};
use crate::solve::assembly::{self, AssembleCandidatesFrom, Candidate};
use crate::solve::inspect::ProbeKind;
use crate::solve::{
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
@ -1365,7 +1365,7 @@ where
&mut self,
goal: Goal<I, TraitPredicate<I>>,
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
let candidates = self.assemble_and_evaluate_candidates(goal);
let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
self.merge_trait_candidates(goal, candidates)
}
}

View file

@ -371,12 +371,12 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
rustc_lexer::TokenKind::Semi => token::Semi,
rustc_lexer::TokenKind::Comma => token::Comma,
rustc_lexer::TokenKind::Dot => token::Dot,
rustc_lexer::TokenKind::OpenParen => token::OpenDelim(Delimiter::Parenthesis),
rustc_lexer::TokenKind::CloseParen => token::CloseDelim(Delimiter::Parenthesis),
rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(Delimiter::Brace),
rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(Delimiter::Brace),
rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(Delimiter::Bracket),
rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(Delimiter::Bracket),
rustc_lexer::TokenKind::OpenParen => token::OpenParen,
rustc_lexer::TokenKind::CloseParen => token::CloseParen,
rustc_lexer::TokenKind::OpenBrace => token::OpenBrace,
rustc_lexer::TokenKind::CloseBrace => token::CloseBrace,
rustc_lexer::TokenKind::OpenBracket => token::OpenBracket,
rustc_lexer::TokenKind::CloseBracket => token::CloseBracket,
rustc_lexer::TokenKind::At => token::At,
rustc_lexer::TokenKind::Pound => token::Pound,
rustc_lexer::TokenKind::Tilde => token::Tilde,

View file

@ -18,38 +18,33 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
let mut buf = Vec::new();
loop {
match self.token.kind {
token::OpenDelim(delim) => {
// Invisible delimiters cannot occur here because `TokenTreesReader` parses
// code directly from strings, with no macro expansion involved.
debug_assert!(!matches!(delim, Delimiter::Invisible(_)));
buf.push(match self.lex_token_tree_open_delim(delim) {
Ok(val) => val,
Err(errs) => return Err(errs),
})
}
token::CloseDelim(delim) => {
// Invisible delimiters cannot occur here because `TokenTreesReader` parses
// code directly from strings, with no macro expansion involved.
debug_assert!(!matches!(delim, Delimiter::Invisible(_)));
return if is_delimited {
Ok((open_spacing, TokenStream::new(buf)))
} else {
Err(vec![self.close_delim_err(delim)])
};
}
token::Eof => {
return if is_delimited {
Err(vec![self.eof_err()])
} else {
Ok((open_spacing, TokenStream::new(buf)))
};
}
_ => {
// Get the next normal token.
let (this_tok, this_spacing) = self.bump();
buf.push(TokenTree::Token(this_tok, this_spacing));
}
if let Some(delim) = self.token.kind.open_delim() {
// Invisible delimiters cannot occur here because `TokenTreesReader` parses
// code directly from strings, with no macro expansion involved.
debug_assert!(!matches!(delim, Delimiter::Invisible(_)));
buf.push(match self.lex_token_tree_open_delim(delim) {
Ok(val) => val,
Err(errs) => return Err(errs),
})
} else if let Some(delim) = self.token.kind.close_delim() {
// Invisible delimiters cannot occur here because `TokenTreesReader` parses
// code directly from strings, with no macro expansion involved.
debug_assert!(!matches!(delim, Delimiter::Invisible(_)));
return if is_delimited {
Ok((open_spacing, TokenStream::new(buf)))
} else {
Err(vec![self.close_delim_err(delim)])
};
} else if self.token.kind == token::Eof {
return if is_delimited {
Err(vec![self.eof_err()])
} else {
Ok((open_spacing, TokenStream::new(buf)))
};
} else {
// Get the next normal token.
let (this_tok, this_spacing) = self.bump();
buf.push(TokenTree::Token(this_tok, this_spacing));
}
}
}
@ -111,9 +106,9 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
let delim_span = DelimSpan::from_pair(pre_span, self.token.span);
let sm = self.psess.source_map();
let close_spacing = match self.token.kind {
// Correct delimiter.
token::CloseDelim(close_delim) if close_delim == open_delim => {
let close_spacing = if let Some(close_delim) = self.token.kind.close_delim() {
if close_delim == open_delim {
// Correct delimiter.
let (open_brace, open_brace_span) = self.diag_info.open_braces.pop().unwrap();
let close_brace_span = self.token.span;
@ -134,9 +129,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
// Move past the closing delimiter.
self.bump_minimal()
}
// Incorrect delimiter.
token::CloseDelim(close_delim) => {
} else {
// Incorrect delimiter.
let mut unclosed_delimiter = None;
let mut candidate = None;
@ -182,14 +176,13 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
Spacing::Alone
}
}
token::Eof => {
// Silently recover, the EOF token will be seen again
// and an error emitted then. Thus we don't pop from
// self.open_braces here. The choice of spacing value here
// doesn't matter.
Spacing::Alone
}
_ => unreachable!(),
} else {
assert_eq!(self.token.kind, token::Eof);
// Silently recover, the EOF token will be seen again
// and an error emitted then. Thus we don't pop from
// self.open_braces here. The choice of spacing value here
// doesn't matter.
Spacing::Alone
};
let spacing = DelimSpacing::new(open_spacing, close_spacing);

View file

@ -5,7 +5,7 @@ use rustc_span::{BytePos, Pos, Span, kw};
use super::Lexer;
use crate::errors::TokenSubstitution;
use crate::token::{self, Delimiter};
use crate::token;
#[rustfmt::skip] // for line breaks
pub(super) static UNICODE_ARRAY: &[(char, &str, &str)] = &[
@ -315,12 +315,12 @@ const ASCII_ARRAY: &[(&str, &str, Option<token::TokenKind>)] = &[
("!", "Exclamation Mark", Some(token::Bang)),
("?", "Question Mark", Some(token::Question)),
(".", "Period", Some(token::Dot)),
("(", "Left Parenthesis", Some(token::OpenDelim(Delimiter::Parenthesis))),
(")", "Right Parenthesis", Some(token::CloseDelim(Delimiter::Parenthesis))),
("[", "Left Square Bracket", Some(token::OpenDelim(Delimiter::Bracket))),
("]", "Right Square Bracket", Some(token::CloseDelim(Delimiter::Bracket))),
("{", "Left Curly Brace", Some(token::OpenDelim(Delimiter::Brace))),
("}", "Right Curly Brace", Some(token::CloseDelim(Delimiter::Brace))),
("(", "Left Parenthesis", Some(token::OpenParen)),
(")", "Right Parenthesis", Some(token::CloseParen)),
("[", "Left Square Bracket", Some(token::OpenBracket)),
("]", "Right Square Bracket", Some(token::CloseBracket)),
("{", "Left Curly Brace", Some(token::OpenBrace)),
("}", "Right Curly Brace", Some(token::CloseBrace)),
("*", "Asterisk", Some(token::Star)),
("/", "Slash", Some(token::Slash)),
("\\", "Backslash", None),

View file

@ -1,7 +1,7 @@
use std::borrow::Cow;
use std::{iter, mem};
use rustc_ast::token::{Delimiter, Token, TokenKind};
use rustc_ast::token::{Delimiter, Token};
use rustc_ast::tokenstream::{
AttrTokenStream, AttrTokenTree, AttrsTarget, DelimSpacing, DelimSpan, LazyAttrTokenStream,
Spacing, ToAttrTokenStream,
@ -501,27 +501,27 @@ fn make_attr_token_stream(
let mut stack_rest = vec![];
for flat_token in iter {
match flat_token {
FlatToken::Token((Token { kind: TokenKind::OpenDelim(delim), span }, spacing)) => {
stack_rest.push(mem::replace(
&mut stack_top,
FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] },
));
}
FlatToken::Token((Token { kind: TokenKind::CloseDelim(delim), span }, spacing)) => {
let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap());
let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap();
assert!(
open_delim.eq_ignoring_invisible_origin(&delim),
"Mismatched open/close delims: open={open_delim:?} close={span:?}"
);
let dspan = DelimSpan::from_pair(open_sp, span);
let dspacing = DelimSpacing::new(open_spacing, spacing);
let stream = AttrTokenStream::new(frame_data.inner);
let delimited = AttrTokenTree::Delimited(dspan, dspacing, delim, stream);
stack_top.inner.push(delimited);
}
FlatToken::Token((token, spacing)) => {
stack_top.inner.push(AttrTokenTree::Token(token, spacing))
FlatToken::Token((token @ Token { kind, span }, spacing)) => {
if let Some(delim) = kind.open_delim() {
stack_rest.push(mem::replace(
&mut stack_top,
FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] },
));
} else if let Some(delim) = kind.close_delim() {
let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap());
let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap();
assert!(
open_delim.eq_ignoring_invisible_origin(&delim),
"Mismatched open/close delims: open={open_delim:?} close={span:?}"
);
let dspan = DelimSpan::from_pair(open_sp, span);
let dspacing = DelimSpacing::new(open_spacing, spacing);
let stream = AttrTokenStream::new(frame_data.inner);
let delimited = AttrTokenTree::Delimited(dspan, dspacing, delim, stream);
stack_top.inner.push(delimited);
} else {
stack_top.inner.push(AttrTokenTree::Token(token, spacing))
}
}
FlatToken::AttrsTarget(target) => {
stack_top.inner.push(AttrTokenTree::AttrsTarget(target))

View file

@ -4,7 +4,7 @@ use std::ops::{Deref, DerefMut};
use ast::token::IdentIsRaw;
use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Lit, LitKind, Token, TokenKind};
use rustc_ast::token::{self, Lit, LitKind, Token, TokenKind};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{
AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block,
@ -304,10 +304,10 @@ impl<'a> Parser<'a> {
TokenKind::Comma,
TokenKind::Semi,
TokenKind::PathSep,
TokenKind::OpenDelim(Delimiter::Brace),
TokenKind::OpenDelim(Delimiter::Parenthesis),
TokenKind::CloseDelim(Delimiter::Brace),
TokenKind::CloseDelim(Delimiter::Parenthesis),
TokenKind::OpenBrace,
TokenKind::OpenParen,
TokenKind::CloseBrace,
TokenKind::CloseParen,
];
if let TokenKind::DocComment(..) = self.prev_token.kind
&& valid_follow.contains(&self.token.kind)
@ -507,7 +507,7 @@ impl<'a> Parser<'a> {
} else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {
// The current token is in the same line as the prior token, not recoverable.
} else if [token::Comma, token::Colon].contains(&self.token.kind)
&& self.prev_token == token::CloseDelim(Delimiter::Parenthesis)
&& self.prev_token == token::CloseParen
{
// Likely typo: The current token is on a new line and is expected to be
// `.`, `;`, `?`, or an operator after a close delimiter token.
@ -518,8 +518,7 @@ impl<'a> Parser<'a> {
// ^
// https://github.com/rust-lang/rust/issues/72253
} else if self.look_ahead(1, |t| {
t == &token::CloseDelim(Delimiter::Brace)
|| t.can_begin_expr() && *t != token::Colon
t == &token::CloseBrace || t.can_begin_expr() && *t != token::Colon
}) && [token::Comma, token::Colon].contains(&self.token.kind)
{
// Likely typo: `,` → `;` or `:` → `;`. This is triggered if the current token is
@ -537,7 +536,7 @@ impl<'a> Parser<'a> {
self.bump();
return Ok(guar);
} else if self.look_ahead(0, |t| {
t == &token::CloseDelim(Delimiter::Brace)
t == &token::CloseBrace
|| ((t.can_begin_expr() || t.can_begin_item())
&& t != &token::Semi
&& t != &token::Pound)
@ -675,8 +674,7 @@ impl<'a> Parser<'a> {
// `pub` may be used for an item or `pub(crate)`
if self.prev_token.is_ident_named(sym::public)
&& (self.token.can_begin_item()
|| self.token == TokenKind::OpenDelim(Delimiter::Parenthesis))
&& (self.token.can_begin_item() || self.token == TokenKind::OpenParen)
{
err.span_suggestion_short(
self.prev_token.span,
@ -843,9 +841,7 @@ impl<'a> Parser<'a> {
if expr.attrs.len() == 1 { "this attribute" } else { "these attributes" },
),
);
if self.token == token::Pound
&& self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket))
{
if self.token == token::Pound && self.look_ahead(1, |t| *t == token::OpenBracket) {
// We have
// #[attr]
// expr
@ -1037,9 +1033,7 @@ impl<'a> Parser<'a> {
) -> PResult<'a, P<Expr>> {
err.span_label(lo.to(decl_hi), "while parsing the body of this closure");
let guar = match before.kind {
token::OpenDelim(Delimiter::Brace)
if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
{
token::OpenBrace if token.kind != token::OpenBrace => {
// `{ || () }` should have been `|| { () }`
err.multipart_suggestion(
"you might have meant to open the body of the closure, instead of enclosing \
@ -1054,9 +1048,7 @@ impl<'a> Parser<'a> {
self.eat_to_tokens(&[exp!(CloseBrace)]);
guar
}
token::OpenDelim(Delimiter::Parenthesis)
if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) =>
{
token::OpenParen if token.kind != token::OpenBrace => {
// We are within a function call or tuple, we can emit the error
// and recover.
self.eat_to_tokens(&[exp!(CloseParen), exp!(Comma)]);
@ -1071,7 +1063,7 @@ impl<'a> Parser<'a> {
);
err.emit()
}
_ if !matches!(token.kind, token::OpenDelim(Delimiter::Brace)) => {
_ if token.kind != token::OpenBrace => {
// We don't have a heuristic to correctly identify where the block
// should be closed.
err.multipart_suggestion_verbose(
@ -1225,7 +1217,7 @@ impl<'a> Parser<'a> {
trailing_span = trailing_span.to(self.token.span);
self.bump();
}
if self.token == token::OpenDelim(Delimiter::Parenthesis) {
if self.token == token::OpenParen {
// Recover from bad turbofish: `foo.collect::Vec<_>()`.
segment.args = Some(AngleBracketedArgs { args, span }.into());
@ -1470,9 +1462,7 @@ impl<'a> Parser<'a> {
let modifiers = [(token::Lt, 1), (token::Gt, -1), (token::Shr, -2)];
self.consume_tts(1, &modifiers);
if !&[token::OpenDelim(Delimiter::Parenthesis), token::PathSep]
.contains(&self.token.kind)
{
if !matches!(self.token.kind, token::OpenParen | token::PathSep) {
// We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the
// parser and bail out.
self.restore_snapshot(snapshot);
@ -1510,7 +1500,7 @@ impl<'a> Parser<'a> {
Err(self.dcx().create_err(err))
}
}
} else if self.token == token::OpenDelim(Delimiter::Parenthesis) {
} else if self.token == token::OpenParen {
// We have high certainty that this was a bad turbofish at this point.
// `foo< bar >(`
if let ExprKind::Binary(o, ..) = inner_op.kind
@ -1570,10 +1560,7 @@ impl<'a> Parser<'a> {
self.bump(); // `(`
// Consume the fn call arguments.
let modifiers = [
(token::OpenDelim(Delimiter::Parenthesis), 1),
(token::CloseDelim(Delimiter::Parenthesis), -1),
];
let modifiers = [(token::OpenParen, 1), (token::CloseParen, -1)];
self.consume_tts(1, &modifiers);
if self.token == token::Eof {
@ -1978,7 +1965,7 @@ impl<'a> Parser<'a> {
fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P<Expr>, bool)> {
let is_question = self.eat(exp!(Question)); // Handle `await? <expr>`.
let expr = if self.token == token::OpenDelim(Delimiter::Brace) {
let expr = if self.token == token::OpenBrace {
// Handle `await { <expr> }`.
// This needs to be handled separately from the next arm to avoid
// interpreting `await { <expr> }?` as `<expr>?.await`.
@ -2014,9 +2001,7 @@ impl<'a> Parser<'a> {
/// If encountering `future.await()`, consumes and emits an error.
pub(super) fn recover_from_await_method_call(&mut self) {
if self.token == token::OpenDelim(Delimiter::Parenthesis)
&& self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
{
if self.token == token::OpenParen && self.look_ahead(1, |t| t == &token::CloseParen) {
// future.await()
let lo = self.token.span;
self.bump(); // (
@ -2029,9 +2014,7 @@ impl<'a> Parser<'a> {
///
/// If encountering `x.use()`, consumes and emits an error.
pub(super) fn recover_from_use(&mut self) {
if self.token == token::OpenDelim(Delimiter::Parenthesis)
&& self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
{
if self.token == token::OpenParen && self.look_ahead(1, |t| t == &token::CloseParen) {
// var.use()
let lo = self.token.span;
self.bump(); // (
@ -2045,7 +2028,7 @@ impl<'a> Parser<'a> {
pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P<Expr>> {
let is_try = self.token.is_keyword(kw::Try);
let is_questionmark = self.look_ahead(1, |t| t == &token::Bang); //check for !
let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(Delimiter::Parenthesis)); //check for (
let is_open = self.look_ahead(2, |t| t == &token::OpenParen); //check for (
if is_try && is_questionmark && is_open {
let lo = self.token.span;
@ -2053,7 +2036,7 @@ impl<'a> Parser<'a> {
self.bump(); //remove !
let try_span = lo.to(self.token.span); //we take the try!( span
self.bump(); //remove (
let is_empty = self.token == token::CloseDelim(Delimiter::Parenthesis); //check if the block is empty
let is_empty = self.token == token::CloseParen; //check if the block is empty
self.consume_block(exp!(OpenParen), exp!(CloseParen), ConsumeClosingDelim::No); //eat the block
let hi = self.token.span;
self.bump(); //remove )
@ -2148,7 +2131,7 @@ impl<'a> Parser<'a> {
loop {
debug!("recover_stmt_ loop {:?}", self.token);
match self.token.kind {
token::OpenDelim(Delimiter::Brace) => {
token::OpenBrace => {
brace_depth += 1;
self.bump();
if break_on_block == BlockMode::Break && brace_depth == 1 && bracket_depth == 0
@ -2156,11 +2139,11 @@ impl<'a> Parser<'a> {
in_block = true;
}
}
token::OpenDelim(Delimiter::Bracket) => {
token::OpenBracket => {
bracket_depth += 1;
self.bump();
}
token::CloseDelim(Delimiter::Brace) => {
token::CloseBrace => {
if brace_depth == 0 {
debug!("recover_stmt_ return - close delim {:?}", self.token);
break;
@ -2172,7 +2155,7 @@ impl<'a> Parser<'a> {
break;
}
}
token::CloseDelim(Delimiter::Bracket) => {
token::CloseBracket => {
bracket_depth -= 1;
if bracket_depth < 0 {
bracket_depth = 0;
@ -2219,12 +2202,10 @@ impl<'a> Parser<'a> {
if let token::DocComment(..) = self.token.kind {
self.dcx().emit_err(DocCommentOnParamType { span: self.token.span });
self.bump();
} else if self.token == token::Pound
&& self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket))
{
} else if self.token == token::Pound && self.look_ahead(1, |t| *t == token::OpenBracket) {
let lo = self.token.span;
// Skip every token until next possible arg.
while self.token != token::CloseDelim(Delimiter::Bracket) {
while self.token != token::CloseBracket {
self.bump();
}
let sp = lo.to(self.token.span);
@ -2243,9 +2224,7 @@ impl<'a> Parser<'a> {
// If we find a pattern followed by an identifier, it could be an (incorrect)
// C-style parameter declaration.
if self.check_ident()
&& self.look_ahead(1, |t| {
*t == token::Comma || *t == token::CloseDelim(Delimiter::Parenthesis)
})
&& self.look_ahead(1, |t| *t == token::Comma || *t == token::CloseParen)
{
// `fn foo(String s) {}`
let ident = self.parse_ident().unwrap();
@ -2261,7 +2240,7 @@ impl<'a> Parser<'a> {
} else if require_name
&& (self.token == token::Comma
|| self.token == token::Lt
|| self.token == token::CloseDelim(Delimiter::Parenthesis))
|| self.token == token::CloseParen)
{
let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)";
@ -2872,7 +2851,7 @@ impl<'a> Parser<'a> {
// Check for `'a : {`
if !(self.check_lifetime()
&& self.look_ahead(1, |t| *t == token::Colon)
&& self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace)))
&& self.look_ahead(2, |t| *t == token::OpenBrace))
{
return false;
}

View file

@ -436,7 +436,7 @@ impl<'a> Parser<'a> {
fn is_at_start_of_range_notation_rhs(&self) -> bool {
if self.token.can_begin_expr() {
// Parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`.
if self.token == token::OpenDelim(Delimiter::Brace) {
if self.token == token::OpenBrace {
return !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
}
true
@ -542,8 +542,8 @@ impl<'a> Parser<'a> {
}
// Recover from `++x`:
token::Plus if this.look_ahead(1, |t| *t == token::Plus) => {
let starts_stmt = this.prev_token == token::Semi
|| this.prev_token == token::CloseDelim(Delimiter::Brace);
let starts_stmt =
this.prev_token == token::Semi || this.prev_token == token::CloseBrace;
let pre_span = this.token.span.to(this.look_ahead(1, |t| t.span));
// Eat both `+`s.
this.bump();
@ -637,8 +637,8 @@ impl<'a> Parser<'a> {
/// Returns the span of expr if it was not interpolated, or the span of the interpolated token.
fn interpolated_or_expr_span(&self, expr: &Expr) -> Span {
match self.prev_token.kind {
TokenKind::NtIdent(..) | TokenKind::NtLifetime(..) => self.prev_token.span,
TokenKind::CloseDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => {
token::NtIdent(..) | token::NtLifetime(..) => self.prev_token.span,
token::CloseInvisible(InvisibleOrigin::MetaVar(_)) => {
// `expr.span` is the interpolated span, because invisible open
// and close delims both get marked with the same span, one
// that covers the entire thing between them. (See
@ -912,8 +912,8 @@ impl<'a> Parser<'a> {
return Ok(e);
}
e = match self.token.kind {
token::OpenDelim(Delimiter::Parenthesis) => self.parse_expr_fn_call(lo, e),
token::OpenDelim(Delimiter::Bracket) => self.parse_expr_index(lo, e)?,
token::OpenParen => self.parse_expr_fn_call(lo, e),
token::OpenBracket => self.parse_expr_index(lo, e)?,
_ => return Ok(e),
}
}
@ -1002,7 +1002,7 @@ impl<'a> Parser<'a> {
(token::Eof, Some(_)) if let Ok(snippet) = sm.span_to_snippet(sm.next_point(span)) => {
(span.shrink_to_hi(), format!("`{}`", snippet))
}
(token::CloseDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))), _) => {
(token::CloseInvisible(InvisibleOrigin::MetaVar(_)), _) => {
// No need to report an error. This case will only occur when parsing a pasted
// metavariable, and we should have emitted an error when parsing the macro call in
// the first place. E.g. in this code:
@ -1202,7 +1202,7 @@ impl<'a> Parser<'a> {
}
}
if matches!(self.token.kind, token::CloseDelim(..) | token::Comma) {
if self.token.kind.close_delim().is_some() || self.token.kind == token::Comma {
break;
} else if trailing_dot.is_none() {
// This loop should only repeat if there is a trailing dot.
@ -1232,7 +1232,7 @@ impl<'a> Parser<'a> {
/// Parse a function call expression, `expr(...)`.
fn parse_expr_fn_call(&mut self, lo: Span, fun: P<Expr>) -> P<Expr> {
let snapshot = if self.token == token::OpenDelim(Delimiter::Parenthesis) {
let snapshot = if self.token == token::OpenParen {
Some((self.create_snapshot_for_diagnostic(), fun.kind.clone()))
} else {
None
@ -1676,14 +1676,11 @@ impl<'a> Parser<'a> {
self.parse_expr_for(label, lo)
} else if self.eat_keyword(exp!(Loop)) {
self.parse_expr_loop(label, lo)
} else if self.check_noexpect(&token::OpenDelim(Delimiter::Brace))
|| self.token.is_metavar_block()
{
} else if self.check_noexpect(&token::OpenBrace) || self.token.is_metavar_block() {
self.parse_expr_block(label, lo, BlockCheckMode::Default)
} else if !ate_colon
&& self.may_recover()
&& (matches!(self.token.kind, token::CloseDelim(_) | token::Comma)
|| self.token.is_punct())
&& (self.token.kind.close_delim().is_some() || self.token.is_punct())
&& could_be_unclosed_char_literal(label_.ident)
{
let (lit, _) =
@ -1878,7 +1875,7 @@ impl<'a> Parser<'a> {
},
});
Some(lexpr)
} else if self.token != token::OpenDelim(Delimiter::Brace)
} else if self.token != token::OpenBrace
|| !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
{
let mut expr = self.parse_expr_opt()?;
@ -2016,7 +2013,7 @@ impl<'a> Parser<'a> {
// Eat tokens until the macro call ends.
if self.may_recover() {
while !matches!(self.token.kind, token::CloseDelim(..) | token::Eof) {
while !self.token.kind.is_close_delim_or_eof() {
self.bump();
}
}
@ -2157,9 +2154,7 @@ impl<'a> Parser<'a> {
self.bump();
Some(token_lit)
}
token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
MetaVarKind::Literal,
))) => {
token::OpenInvisible(InvisibleOrigin::MetaVar(MetaVarKind::Literal)) => {
let lit = self
.eat_metavar_seq(MetaVarKind::Literal, |this| this.parse_literal_maybe_minus())
.expect("metavar seq literal");
@ -2168,9 +2163,9 @@ impl<'a> Parser<'a> {
};
Some(token_lit)
}
token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
token::OpenInvisible(InvisibleOrigin::MetaVar(
mv_kind @ MetaVarKind::Expr { can_begin_literal_maybe_minus: true, .. },
))) => {
)) => {
let expr = self
.eat_metavar_seq(mv_kind, |this| this.parse_expr())
.expect("metavar seq expr");
@ -2275,7 +2270,7 @@ impl<'a> Parser<'a> {
}
fn is_array_like_block(&mut self) -> bool {
matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace))
self.token.kind == TokenKind::OpenBrace
&& self
.look_ahead(1, |t| matches!(t.kind, TokenKind::Ident(..) | TokenKind::Literal(_)))
&& self.look_ahead(2, |t| t == &token::Comma)
@ -2328,8 +2323,8 @@ impl<'a> Parser<'a> {
|p| p.parse_expr(),
) {
Ok(_)
// When the close delim is `)`, `token.kind` is expected to be `token::CloseDelim(Delimiter::Parenthesis)`,
// but the actual `token.kind` is `token::CloseDelim(Delimiter::Bracket)`.
// When the close delim is `)`, `token.kind` is expected to be `token::CloseParen`,
// but the actual `token.kind` is `token::CloseBracket`.
// This is because the `token.kind` of the close delim is treated as the same as
// that of the open delim in `TokenTreesReader::parse_token_tree`, even if the delimiters of them are different.
// Therefore, `token.kind` should not be compared here.
@ -2484,7 +2479,7 @@ impl<'a> Parser<'a> {
fn parse_closure_block_body(&mut self, ret_span: Span) -> PResult<'a, P<Expr>> {
if self.may_recover()
&& self.token.can_begin_expr()
&& !matches!(self.token.kind, TokenKind::OpenDelim(Delimiter::Brace))
&& self.token.kind != TokenKind::OpenBrace
&& !self.token.is_metavar_block()
{
let snapshot = self.create_snapshot_for_diagnostic();
@ -2887,7 +2882,7 @@ impl<'a> Parser<'a> {
}
fn parse_for_head(&mut self) -> PResult<'a, (P<Pat>, P<Expr>)> {
let begin_paren = if self.token == token::OpenDelim(Delimiter::Parenthesis) {
let begin_paren = if self.token == token::OpenParen {
// Record whether we are about to parse `for (`.
// This is used below for recovery in case of `for ( $stuff ) $block`
// in which case we will suggest `for $stuff $block`.
@ -2921,7 +2916,7 @@ impl<'a> Parser<'a> {
return Err(err);
}
};
return if self.token == token::CloseDelim(Delimiter::Parenthesis) {
return if self.token == token::CloseParen {
// We know for sure we have seen `for ($SOMETHING in $EXPR)`, so we recover the
// parser state and emit a targeted suggestion.
let span = vec![start_span, self.token.span];
@ -2965,7 +2960,7 @@ impl<'a> Parser<'a> {
let (pat, expr) = self.parse_for_head()?;
// Recover from missing expression in `for` loop
if matches!(expr.kind, ExprKind::Block(..))
&& !matches!(self.token.kind, token::OpenDelim(Delimiter::Brace))
&& self.token.kind != token::OpenBrace
&& self.may_recover()
{
let guar = self
@ -3114,7 +3109,7 @@ impl<'a> Parser<'a> {
let attrs = self.parse_inner_attributes()?;
let mut arms = ThinVec::new();
while self.token != token::CloseDelim(Delimiter::Brace) {
while self.token != token::CloseBrace {
match self.parse_arm() {
Ok(arm) => arms.push(arm),
Err(e) => {
@ -3122,7 +3117,7 @@ impl<'a> Parser<'a> {
let guar = e.emit();
self.recover_stmt();
let span = lo.to(self.token.span);
if self.token == token::CloseDelim(Delimiter::Brace) {
if self.token == token::CloseBrace {
self.bump();
}
// Always push at least one arm to make the match non-empty
@ -3183,7 +3178,7 @@ impl<'a> Parser<'a> {
// We might have either a `,` -> `;` typo, or a block without braces. We need
// a more subtle parsing strategy.
loop {
if self.token == token::CloseDelim(Delimiter::Brace) {
if self.token == token::CloseBrace {
// We have reached the closing brace of the `match` expression.
return Some(err(self, stmts));
}
@ -3242,7 +3237,7 @@ impl<'a> Parser<'a> {
// this avoids the compiler saying that a `,` or `}` was expected even though
// the pattern isn't a never pattern (and thus an arm body is required)
let armless = (!is_fat_arrow && !is_almost_fat_arrow && pat.could_be_never_pattern())
|| matches!(this.token.kind, token::Comma | token::CloseDelim(Delimiter::Brace));
|| matches!(this.token.kind, token::Comma | token::CloseBrace);
let mut result = if armless {
// A pattern without a body, allowed for never patterns.
@ -3290,8 +3285,8 @@ impl<'a> Parser<'a> {
err
})?;
let require_comma = !classify::expr_is_complete(&expr)
&& this.token != token::CloseDelim(Delimiter::Brace);
let require_comma =
!classify::expr_is_complete(&expr) && this.token != token::CloseBrace;
if !require_comma {
arm_body = Some(expr);
@ -3442,7 +3437,7 @@ impl<'a> Parser<'a> {
}
fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P<Pat>, Option<P<Expr>>)> {
if self.token == token::OpenDelim(Delimiter::Parenthesis) {
if self.token == token::OpenParen {
let left = self.token.span;
let pat = self.parse_pat_no_top_guard(
None,
@ -3488,7 +3483,7 @@ impl<'a> Parser<'a> {
match self.parse_expr_res(Restrictions::ALLOW_LET | Restrictions::IN_IF_GUARD, attrs) {
Ok((expr, _)) => Ok(expr),
Err(mut err) => {
if self.prev_token == token::OpenDelim(Delimiter::Brace) {
if self.prev_token == token::OpenBrace {
let sugg_sp = self.prev_token.span.shrink_to_lo();
// Consume everything within the braces, let's avoid further parse
// errors.
@ -3531,8 +3526,7 @@ impl<'a> Parser<'a> {
fn is_do_catch_block(&self) -> bool {
self.token.is_keyword(kw::Do)
&& self.is_keyword_ahead(1, &[kw::Catch])
&& self
.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_metavar_block())
&& self.look_ahead(2, |t| *t == token::OpenBrace || t.is_metavar_block())
&& !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
}
@ -3542,8 +3536,7 @@ impl<'a> Parser<'a> {
fn is_try_block(&self) -> bool {
self.token.is_keyword(kw::Try)
&& self
.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_metavar_block())
&& self.look_ahead(1, |t| *t == token::OpenBrace || t.is_metavar_block())
&& self.token_uninterpolated_span().at_least_rust_2018()
}
@ -3577,13 +3570,11 @@ impl<'a> Parser<'a> {
// `async move {`
self.is_keyword_ahead(lookahead + 1, &[kw::Move, kw::Use])
&& self.look_ahead(lookahead + 2, |t| {
*t == token::OpenDelim(Delimiter::Brace) || t.is_metavar_block()
*t == token::OpenBrace || t.is_metavar_block()
})
) || (
// `async {`
self.look_ahead(lookahead + 1, |t| {
*t == token::OpenDelim(Delimiter::Brace) || t.is_metavar_block()
})
self.look_ahead(lookahead + 1, |t| *t == token::OpenBrace || t.is_metavar_block())
))
}
@ -3707,11 +3698,7 @@ impl<'a> Parser<'a> {
AssocOp::from_token(t).is_some()
|| matches!(
t.kind,
token::OpenDelim(
Delimiter::Parenthesis
| Delimiter::Bracket
| Delimiter::Brace
)
token::OpenParen | token::OpenBracket | token::OpenBrace
)
|| *t == token::Dot
})
@ -3868,8 +3855,8 @@ impl<'a> Parser<'a> {
t == &token::Colon
|| t == &token::Eq
|| t == &token::Comma
|| t == &token::CloseDelim(Delimiter::Brace)
|| t == &token::CloseDelim(Delimiter::Parenthesis)
|| t == &token::CloseBrace
|| t == &token::CloseParen
});
if is_wrong {
return Err(this.dcx().create_err(errors::ExpectedStructField {

View file

@ -1,4 +1,3 @@
use ast::token::Delimiter;
use rustc_ast::{
self as ast, AttrVec, DUMMY_NODE_ID, GenericBounds, GenericParam, GenericParamKind, TyKind,
WhereClause, token,
@ -437,7 +436,7 @@ impl<'a> Parser<'a> {
if let Some(struct_) = struct_
&& self.may_recover()
&& self.token == token::OpenDelim(Delimiter::Parenthesis)
&& self.token == token::OpenParen
{
snapshot = Some((struct_, self.create_snapshot_for_diagnostic()));
};
@ -548,7 +547,7 @@ impl<'a> Parser<'a> {
matches!(t.kind, token::Gt | token::Comma | token::Colon | token::Eq)
// Recovery-only branch -- this could be removed,
// since it only affects diagnostics currently.
|| matches!(t.kind, token::Question)
|| t.kind == token::Question
})
|| self.is_keyword_ahead(start + 1, &[kw::Const]))
}

View file

@ -399,14 +399,9 @@ impl<'a> Parser<'a> {
let insert_span = ident_span.shrink_to_lo();
let ident = if self.token.is_ident()
&& (!is_const || self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Parenthesis)))
&& (!is_const || self.look_ahead(1, |t| *t == token::OpenParen))
&& self.look_ahead(1, |t| {
[
token::Lt,
token::OpenDelim(Delimiter::Brace),
token::OpenDelim(Delimiter::Parenthesis),
]
.contains(&t.kind)
matches!(t.kind, token::Lt | token::OpenBrace | token::OpenParen)
}) {
self.parse_ident().unwrap()
} else {
@ -422,7 +417,7 @@ impl<'a> Parser<'a> {
let err = if self.check(exp!(OpenBrace)) {
// possible struct or enum definition where `struct` or `enum` was forgotten
if self.look_ahead(1, |t| *t == token::CloseDelim(Delimiter::Brace)) {
if self.look_ahead(1, |t| *t == token::CloseBrace) {
// `S {}` could be unit enum or struct
Some(errors::MissingKeywordForItemDefinition::EnumOrStruct { span })
} else if self.look_ahead(2, |t| *t == token::Colon)
@ -764,11 +759,12 @@ impl<'a> Parser<'a> {
match parse_item(self) {
Ok(None) => {
let mut is_unnecessary_semicolon = !items.is_empty()
// When the close delim is `)` in a case like the following, `token.kind` is expected to be `token::CloseDelim(Delimiter::Parenthesis)`,
// but the actual `token.kind` is `token::CloseDelim(Delimiter::Brace)`.
// This is because the `token.kind` of the close delim is treated as the same as
// that of the open delim in `TokenTreesReader::parse_token_tree`, even if the delimiters of them are different.
// Therefore, `token.kind` should not be compared here.
// When the close delim is `)` in a case like the following, `token.kind`
// is expected to be `token::CloseParen`, but the actual `token.kind` is
// `token::CloseBrace`. This is because the `token.kind` of the close delim
// is treated as the same as that of the open delim in
// `TokenTreesReader::parse_token_tree`, even if the delimiters of them are
// different. Therefore, `token.kind` should not be compared here.
//
// issue-60075.rs
// ```
@ -787,8 +783,8 @@ impl<'a> Parser<'a> {
let mut semicolon_span = self.token.span;
if !is_unnecessary_semicolon {
// #105369, Detect spurious `;` before assoc fn body
is_unnecessary_semicolon = self.token == token::OpenDelim(Delimiter::Brace)
&& self.prev_token == token::Semi;
is_unnecessary_semicolon =
self.token == token::OpenBrace && self.prev_token == token::Semi;
semicolon_span = self.prev_token.span;
}
// We have to bail or we'll potentially never make progress.
@ -840,7 +836,7 @@ impl<'a> Parser<'a> {
/// Recover on a doc comment before `}`.
fn recover_doc_comment_before_brace(&mut self) -> bool {
if let token::DocComment(..) = self.token.kind {
if self.look_ahead(1, |tok| tok == &token::CloseDelim(Delimiter::Brace)) {
if self.look_ahead(1, |tok| tok == &token::CloseBrace) {
// FIXME: merge with `DocCommentDoesNotDocumentAnything` (E0585)
struct_span_code_err!(
self.dcx(),
@ -1206,7 +1202,7 @@ impl<'a> Parser<'a> {
// FIXME: This recovery should be tested better.
if safety == Safety::Default
&& self.token.is_keyword(kw::Unsafe)
&& self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
&& self.look_ahead(1, |t| *t == token::OpenBrace)
{
self.expect(exp!(OpenBrace)).unwrap_err().emit();
safety = Safety::Unsafe(self.token.span);
@ -1718,7 +1714,7 @@ impl<'a> Parser<'a> {
} else if self.eat(exp!(Semi)) {
VariantData::Unit(DUMMY_NODE_ID)
// Record-style struct definition
} else if self.token == token::OpenDelim(Delimiter::Brace) {
} else if self.token == token::OpenBrace {
let (fields, recovered) = self.parse_record_struct_body(
"struct",
ident.span,
@ -1726,7 +1722,7 @@ impl<'a> Parser<'a> {
)?;
VariantData::Struct { fields, recovered }
// Tuple-style struct definition with optional where-clause.
} else if self.token == token::OpenDelim(Delimiter::Parenthesis) {
} else if self.token == token::OpenParen {
let body = VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID);
generics.where_clause = self.parse_where_clause()?;
self.expect_semi()?;
@ -1753,7 +1749,7 @@ impl<'a> Parser<'a> {
generics.where_clause.has_where_token,
)?;
VariantData::Struct { fields, recovered }
} else if self.token == token::OpenDelim(Delimiter::Brace) {
} else if self.token == token::OpenBrace {
let (fields, recovered) = self.parse_record_struct_body(
"union",
ident.span,
@ -1784,7 +1780,7 @@ impl<'a> Parser<'a> {
let mut fields = ThinVec::new();
let mut recovered = Recovered::No;
if self.eat(exp!(OpenBrace)) {
while self.token != token::CloseDelim(Delimiter::Brace) {
while self.token != token::CloseBrace {
match self.parse_field_def(adt_ty) {
Ok(field) => {
fields.push(field);
@ -1941,7 +1937,7 @@ impl<'a> Parser<'a> {
token::Comma => {
self.bump();
}
token::CloseDelim(Delimiter::Brace) => {}
token::CloseBrace => {}
token::DocComment(..) => {
let previous_span = self.prev_token.span;
let mut err = errors::DocCommentDoesNotDocumentAnything {
@ -1955,7 +1951,7 @@ impl<'a> Parser<'a> {
if !seen_comma && comma_after_doc_seen {
seen_comma = true;
}
if comma_after_doc_seen || self.token == token::CloseDelim(Delimiter::Brace) {
if comma_after_doc_seen || self.token == token::CloseBrace {
self.dcx().emit_err(err);
} else {
if !seen_comma {
@ -1993,7 +1989,7 @@ impl<'a> Parser<'a> {
if self.token.is_ident()
|| (self.token == TokenKind::Pound
&& (self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Bracket))))
&& (self.look_ahead(1, |t| t == &token::OpenBracket)))
{
// This is likely another field, TokenKind::Pound is used for `#[..]`
// attribute for next field. Emit the diagnostic and continue parsing.
@ -2447,7 +2443,7 @@ impl<'a> Parser<'a> {
match self.expected_one_of_not_found(&[], expected) {
Ok(error_guaranteed) => Ok(error_guaranteed),
Err(mut err) => {
if self.token == token::CloseDelim(Delimiter::Brace) {
if self.token == token::CloseBrace {
// The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in
// the AST for typechecking.
err.span_label(ident_span, "while parsing this `fn`");
@ -2874,7 +2870,7 @@ impl<'a> Parser<'a> {
pub(super) fn parse_fn_params(&mut self, req_name: ReqName) -> PResult<'a, ThinVec<Param>> {
let mut first_param = true;
// Parse the arguments, starting out with `self` being allowed...
if self.token != TokenKind::OpenDelim(Delimiter::Parenthesis)
if self.token != TokenKind::OpenParen
// might be typo'd trait impl, handled elsewhere
&& !self.token.is_keyword(kw::For)
{
@ -2892,7 +2888,7 @@ impl<'a> Parser<'a> {
// When parsing a param failed, we should check to make the span of the param
// not contain '(' before it.
// For example when parsing `*mut Self` in function `fn oof(*mut Self)`.
let lo = if let TokenKind::OpenDelim(Delimiter::Parenthesis) = p.prev_token.kind {
let lo = if let TokenKind::OpenParen = p.prev_token.kind {
p.prev_token.span.shrink_to_hi()
} else {
p.prev_token.span
@ -2969,9 +2965,7 @@ impl<'a> Parser<'a> {
}
}
if this.token != token::Comma
&& this.token != token::CloseDelim(Delimiter::Parenthesis)
{
if this.token != token::Comma && this.token != token::CloseParen {
// This wasn't actually a type, but a pattern looking like a type,
// so we are going to rollback and re-parse for recovery.
ty = this.unexpected_any();
@ -3153,7 +3147,7 @@ impl<'a> Parser<'a> {
fn is_named_param(&self) -> bool {
let offset = match &self.token.kind {
token::OpenDelim(Delimiter::Invisible(origin)) => match origin {
token::OpenInvisible(origin) => match origin {
InvisibleOrigin::MetaVar(MetaVarKind::Pat(_)) => {
return self.check_noexpect_past_close_delim(&token::Colon);
}

View file

@ -23,8 +23,7 @@ pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
use path::PathStyle;
use rustc_ast::ptr::P;
use rustc_ast::token::{
self, Delimiter, IdentIsRaw, InvisibleOrigin, MetaVarKind, NtExprKind, NtPatKind, Token,
TokenKind,
self, IdentIsRaw, InvisibleOrigin, MetaVarKind, NtExprKind, NtPatKind, Token, TokenKind,
};
use rustc_ast::tokenstream::{AttrsTarget, Spacing, TokenStream, TokenTree};
use rustc_ast::util::case::Case;
@ -327,10 +326,7 @@ impl TokenCursor {
if let Some(tree) = self.curr.curr() {
match tree {
&TokenTree::Token(token, spacing) => {
debug_assert!(!matches!(
token.kind,
token::OpenDelim(_) | token::CloseDelim(_)
));
debug_assert!(!token.kind.is_delim());
let res = (token, spacing);
self.curr.bump();
return res;
@ -339,7 +335,7 @@ impl TokenCursor {
let trees = TokenTreeCursor::new(tts.clone());
self.stack.push(mem::replace(&mut self.curr, trees));
if !delim.skip() {
return (Token::new(token::OpenDelim(delim), sp.open), spacing.open);
return (Token::new(delim.as_open_token_kind(), sp.open), spacing.open);
}
// No open delimiter to return; continue on to the next iteration.
}
@ -352,7 +348,7 @@ impl TokenCursor {
self.curr = parent;
self.curr.bump(); // move past the `Delimited`
if !delim.skip() {
return (Token::new(token::CloseDelim(delim), span.close), spacing.close);
return (Token::new(delim.as_close_token_kind(), span.close), spacing.close);
}
// No close delimiter to return; continue on to the next iteration.
} else {
@ -423,7 +419,7 @@ impl TokenDescription {
_ if token.is_used_keyword() => Some(TokenDescription::Keyword),
_ if token.is_unused_keyword() => Some(TokenDescription::ReservedKeyword),
token::DocComment(..) => Some(TokenDescription::DocComment),
token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => {
token::OpenInvisible(InvisibleOrigin::MetaVar(kind)) => {
Some(TokenDescription::MetaVar(kind))
}
_ => None,
@ -620,9 +616,8 @@ impl<'a> Parser<'a> {
// past the entire `TokenTree::Delimited` in a single step, avoiding the
// need for unbounded token lookahead.
//
// Primarily used when `self.token` matches
// `OpenDelim(Delimiter::Invisible(_))`, to look ahead through the current
// metavar expansion.
// Primarily used when `self.token` matches `OpenInvisible(_))`, to look
// ahead through the current metavar expansion.
fn check_noexpect_past_close_delim(&self, tok: &TokenKind) -> bool {
let mut tree_cursor = self.token_cursor.stack.last().unwrap().clone();
tree_cursor.bump();
@ -756,8 +751,7 @@ impl<'a> Parser<'a> {
match_mv_kind: impl Fn(MetaVarKind) -> bool,
mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
) -> Option<T> {
if let token::OpenDelim(delim) = self.token.kind
&& let Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)) = delim
if let token::OpenInvisible(InvisibleOrigin::MetaVar(mv_kind)) = self.token.kind
&& match_mv_kind(mv_kind)
{
self.bump();
@ -776,8 +770,7 @@ impl<'a> Parser<'a> {
}
};
if let token::CloseDelim(delim) = self.token.kind
&& let Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind)) = delim
if let token::CloseInvisible(InvisibleOrigin::MetaVar(mv_kind)) = self.token.kind
&& match_mv_kind(mv_kind)
{
self.bump();
@ -838,10 +831,8 @@ impl<'a> Parser<'a> {
fn check_inline_const(&self, dist: usize) -> bool {
self.is_keyword_ahead(dist, &[kw::Const])
&& self.look_ahead(dist + 1, |t| match &t.kind {
token::OpenDelim(Delimiter::Brace) => true,
token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
MetaVarKind::Block,
))) => true,
token::OpenBrace => true,
token::OpenInvisible(InvisibleOrigin::MetaVar(MetaVarKind::Block)) => true,
_ => false,
})
}
@ -960,7 +951,7 @@ impl<'a> Parser<'a> {
let mut v = ThinVec::new();
while !self.expect_any_with_type(closes_expected, closes_not_expected) {
if let token::CloseDelim(..) | token::Eof = self.token.kind {
if self.token.kind.is_close_delim_or_eof() {
break;
}
if let Some(exp) = sep.sep {
@ -1244,7 +1235,7 @@ impl<'a> Parser<'a> {
}
debug_assert!(!matches!(
next.0.kind,
token::OpenDelim(delim) | token::CloseDelim(delim) if delim.skip()
token::OpenInvisible(origin) | token::CloseInvisible(origin) if origin.skip()
));
self.inlined_bump_with(next)
}
@ -1269,7 +1260,7 @@ impl<'a> Parser<'a> {
TokenTree::Token(token, _) => return looker(token),
&TokenTree::Delimited(dspan, _, delim, _) => {
if !delim.skip() {
return looker(&Token::new(token::OpenDelim(delim), dspan.open));
return looker(&Token::new(delim.as_open_token_kind(), dspan.open));
}
}
}
@ -1283,7 +1274,7 @@ impl<'a> Parser<'a> {
{
// We are not in the outermost token stream, so we have
// delimiters. Also, those delimiters are not skipped.
return looker(&Token::new(token::CloseDelim(delim), span.close));
return looker(&Token::new(delim.as_close_token_kind(), span.close));
}
}
}
@ -1298,7 +1289,7 @@ impl<'a> Parser<'a> {
token = cursor.next().0;
if matches!(
token.kind,
token::OpenDelim(delim) | token::CloseDelim(delim) if delim.skip()
token::OpenInvisible(origin) | token::CloseInvisible(origin) if origin.skip()
) {
continue;
}
@ -1386,8 +1377,7 @@ impl<'a> Parser<'a> {
fn parse_constness_(&mut self, case: Case, is_closure: bool) -> Const {
// Avoid const blocks and const closures to be parsed as const items
if (self.check_const_closure() == is_closure)
&& !self
.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace) || t.is_metavar_block())
&& !self.look_ahead(1, |t| *t == token::OpenBrace || t.is_metavar_block())
&& self.eat_keyword_case(exp!(Const), case)
{
Const::Yes(self.prev_token_uninterpolated_span())
@ -1486,48 +1476,46 @@ impl<'a> Parser<'a> {
/// Parses a single token tree from the input.
pub fn parse_token_tree(&mut self) -> TokenTree {
match self.token.kind {
token::OpenDelim(..) => {
// Clone the `TokenTree::Delimited` that we are currently
// within. That's what we are going to return.
let tree = self.token_cursor.stack.last().unwrap().curr().unwrap().clone();
debug_assert_matches!(tree, TokenTree::Delimited(..));
if self.token.kind.open_delim().is_some() {
// Clone the `TokenTree::Delimited` that we are currently
// within. That's what we are going to return.
let tree = self.token_cursor.stack.last().unwrap().curr().unwrap().clone();
debug_assert_matches!(tree, TokenTree::Delimited(..));
// Advance the token cursor through the entire delimited
// sequence. After getting the `OpenDelim` we are *within* the
// delimited sequence, i.e. at depth `d`. After getting the
// matching `CloseDelim` we are *after* the delimited sequence,
// i.e. at depth `d - 1`.
let target_depth = self.token_cursor.stack.len() - 1;
loop {
// Advance one token at a time, so `TokenCursor::next()`
// can capture these tokens if necessary.
self.bump();
if self.token_cursor.stack.len() == target_depth {
debug_assert_matches!(self.token.kind, token::CloseDelim(_));
break;
}
// Advance the token cursor through the entire delimited
// sequence. After getting the `OpenDelim` we are *within* the
// delimited sequence, i.e. at depth `d`. After getting the
// matching `CloseDelim` we are *after* the delimited sequence,
// i.e. at depth `d - 1`.
let target_depth = self.token_cursor.stack.len() - 1;
loop {
// Advance one token at a time, so `TokenCursor::next()`
// can capture these tokens if necessary.
self.bump();
if self.token_cursor.stack.len() == target_depth {
debug_assert!(self.token.kind.close_delim().is_some());
break;
}
}
// Consume close delimiter
self.bump();
tree
}
token::CloseDelim(_) | token::Eof => unreachable!(),
_ => {
let prev_spacing = self.token_spacing;
self.bump();
TokenTree::Token(self.prev_token, prev_spacing)
}
// Consume close delimiter
self.bump();
tree
} else {
assert!(!self.token.kind.is_close_delim_or_eof());
let prev_spacing = self.token_spacing;
self.bump();
TokenTree::Token(self.prev_token, prev_spacing)
}
}
pub fn parse_tokens(&mut self) -> TokenStream {
let mut result = Vec::new();
loop {
match self.token.kind {
token::Eof | token::CloseDelim(..) => break,
_ => result.push(self.parse_token_tree()),
if self.token.kind.is_close_delim_or_eof() {
break;
} else {
result.push(self.parse_token_tree());
}
}
TokenStream::new(result)
@ -1590,7 +1578,7 @@ impl<'a> Parser<'a> {
kind: vis,
tokens: None,
});
} else if self.look_ahead(2, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
} else if self.look_ahead(2, |t| t == &token::CloseParen)
&& self.is_keyword_ahead(1, &[kw::Crate, kw::Super, kw::SelfLower])
{
// Parse `pub(crate)`, `pub(self)`, or `pub(super)`.
@ -1687,9 +1675,7 @@ impl<'a> Parser<'a> {
/// `::{` or `::*`
fn is_import_coupler(&mut self) -> bool {
self.check_path_sep_and_look_ahead(|t| {
matches!(t.kind, token::OpenDelim(Delimiter::Brace) | token::Star)
})
self.check_path_sep_and_look_ahead(|t| matches!(t.kind, token::OpenBrace | token::Star))
}
// Debug view of the parser's token stream, up to `{lookahead}` tokens.
@ -1744,9 +1730,7 @@ impl<'a> Parser<'a> {
pub fn token_uninterpolated_span(&self) -> Span {
match &self.token.kind {
token::NtIdent(ident, _) | token::NtLifetime(ident, _) => ident.span,
token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => {
self.look_ahead(1, |t| t.span)
}
token::OpenInvisible(InvisibleOrigin::MetaVar(_)) => self.look_ahead(1, |t| t.span),
_ => self.token.span,
}
}
@ -1755,9 +1739,7 @@ impl<'a> Parser<'a> {
pub fn prev_token_uninterpolated_span(&self) -> Span {
match &self.prev_token.kind {
token::NtIdent(ident, _) | token::NtLifetime(ident, _) => ident.span,
token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => {
self.look_ahead(0, |t| t.span)
}
token::OpenInvisible(InvisibleOrigin::MetaVar(_)) => self.look_ahead(0, |t| t.span),
_ => self.prev_token.span,
}
}
@ -1776,7 +1758,7 @@ pub(crate) fn make_unclosed_delims_error(
};
let err = psess.dcx().create_err(MismatchedClosingDelimiter {
spans,
delimiter: pprust::token_kind_to_string(&token::CloseDelim(found_delim)).to_string(),
delimiter: pprust::token_kind_to_string(&found_delim.as_close_token_kind()).to_string(),
unmatched: unmatched.found_span,
opening_candidate: unmatched.candidate_span,
unclosed: unmatched.unclosed_span,

View file

@ -1,7 +1,7 @@
use rustc_ast::ptr::P;
use rustc_ast::token::NtExprKind::*;
use rustc_ast::token::NtPatKind::*;
use rustc_ast::token::{self, Delimiter, InvisibleOrigin, MetaVarKind, NonterminalKind, Token};
use rustc_ast::token::{self, InvisibleOrigin, MetaVarKind, NonterminalKind, Token};
use rustc_errors::PResult;
use rustc_span::{Ident, kw};
@ -69,13 +69,13 @@ impl<'a> Parser<'a> {
| token::Ident(..)
| token::NtIdent(..)
| token::NtLifetime(..)
| token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => true,
| token::OpenInvisible(InvisibleOrigin::MetaVar(_)) => true,
_ => token.can_begin_type(),
},
NonterminalKind::Block => match &token.kind {
token::OpenDelim(Delimiter::Brace) => true,
token::OpenBrace => true,
token::NtLifetime(..) => true,
token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(k))) => match k {
token::OpenInvisible(InvisibleOrigin::MetaVar(k)) => match k {
MetaVarKind::Block
| MetaVarKind::Stmt
| MetaVarKind::Expr { .. }
@ -94,9 +94,7 @@ impl<'a> Parser<'a> {
},
NonterminalKind::Path | NonterminalKind::Meta => match &token.kind {
token::PathSep | token::Ident(..) | token::NtIdent(..) => true,
token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => {
may_be_ident(*kind)
}
token::OpenInvisible(InvisibleOrigin::MetaVar(kind)) => may_be_ident(*kind),
_ => false,
},
NonterminalKind::Pat(pat_kind) => token.can_begin_pattern(pat_kind),
@ -105,7 +103,7 @@ impl<'a> Parser<'a> {
_ => false,
},
NonterminalKind::TT | NonterminalKind::Item | NonterminalKind::Stmt => {
!matches!(token.kind, token::CloseDelim(_))
token.kind.close_delim().is_none()
}
}
}

View file

@ -3,7 +3,7 @@ use std::ops::Bound;
use rustc_ast::mut_visit::{self, MutVisitor};
use rustc_ast::ptr::P;
use rustc_ast::token::NtPatKind::*;
use rustc_ast::token::{self, Delimiter, IdentIsRaw, MetaVarKind, Token};
use rustc_ast::token::{self, IdentIsRaw, MetaVarKind, Token};
use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast::visit::{self, Visitor};
use rustc_ast::{
@ -323,7 +323,7 @@ impl<'a> Parser<'a> {
fn eat_or_separator(&mut self, lo: Option<Span>) -> EatOrResult {
if self.recover_trailing_vert(lo) {
EatOrResult::TrailingVert
} else if matches!(self.token.kind, token::OrOr) {
} else if self.token.kind == token::OrOr {
// Found `||`; Recover and pretend we parsed `|`.
self.dcx().emit_err(UnexpectedVertVertInPattern { span: self.token.span, start: lo });
self.bump();
@ -352,9 +352,9 @@ impl<'a> Parser<'a> {
| token::Semi // e.g. `let a |;`.
| token::Colon // e.g. `let a | :`.
| token::Comma // e.g. `let (a |,)`.
| token::CloseDelim(Delimiter::Bracket) // e.g. `let [a | ]`.
| token::CloseDelim(Delimiter::Parenthesis) // e.g. `let (a | )`.
| token::CloseDelim(Delimiter::Brace) // e.g. `let A { f: a | }`.
| token::CloseBracket // e.g. `let [a | ]`.
| token::CloseParen // e.g. `let (a | )`.
| token::CloseBrace // e.g. `let A { f: a | }`.
)
});
match (is_end_ahead, &self.token.kind) {
@ -364,7 +364,7 @@ impl<'a> Parser<'a> {
span: self.token.span,
start: lo,
token: self.token,
note_double_vert: matches!(self.token.kind, token::OrOr),
note_double_vert: self.token.kind == token::OrOr,
});
self.bump();
true
@ -438,8 +438,8 @@ impl<'a> Parser<'a> {
| token::Caret | token::And | token::Shl | token::Shr // excludes `Or`
)
|| self.token == token::Question
|| (self.token == token::OpenDelim(Delimiter::Bracket)
&& self.look_ahead(1, |t| *t != token::CloseDelim(Delimiter::Bracket))) // excludes `[]`
|| (self.token == token::OpenBracket
&& self.look_ahead(1, |t| *t != token::CloseBracket)) // excludes `[]`
|| self.token.is_keyword(kw::As);
if !has_dot_expr && !has_trailing_operator {
@ -481,7 +481,7 @@ impl<'a> Parser<'a> {
let is_bound = is_end_bound
// is_start_bound: either `..` or `)..`
|| self.token.is_range_separator()
|| self.token == token::CloseDelim(Delimiter::Parenthesis)
|| self.token == token::CloseParen
&& self.look_ahead(1, Token::is_range_separator);
let span = expr.span;
@ -835,7 +835,7 @@ impl<'a> Parser<'a> {
// because we never have `'a: label {}` in a pattern position anyways, but it does
// keep us from suggesting something like `let 'a: Ty = ..` => `let 'a': Ty = ..`
&& could_be_unclosed_char_literal(lt)
&& !self.look_ahead(1, |token| matches!(token.kind, token::Colon))
&& !self.look_ahead(1, |token| token.kind == token::Colon)
{
// Recover a `'a` as a `'a'` literal
let lt = self.expect_lifetime();
@ -1255,8 +1255,8 @@ impl<'a> Parser<'a> {
|| t.is_metavar_expr()
|| t.is_lifetime() // recover `'a` instead of `'a'`
|| (self.may_recover() // recover leading `(`
&& *t == token::OpenDelim(Delimiter::Parenthesis)
&& self.look_ahead(dist + 1, |t| *t != token::OpenDelim(Delimiter::Parenthesis))
&& *t == token::OpenParen
&& self.look_ahead(dist + 1, |t| *t != token::OpenParen)
&& self.is_pat_range_end_start(dist + 1))
})
}
@ -1264,9 +1264,8 @@ impl<'a> Parser<'a> {
/// Parse a range pattern end bound
fn parse_pat_range_end(&mut self) -> PResult<'a, P<Expr>> {
// recover leading `(`
let open_paren = (self.may_recover()
&& self.eat_noexpect(&token::OpenDelim(Delimiter::Parenthesis)))
.then_some(self.prev_token.span);
let open_paren = (self.may_recover() && self.eat_noexpect(&token::OpenParen))
.then_some(self.prev_token.span);
let bound = if self.check_inline_const(0) {
self.parse_const_block(self.token.span, true)
@ -1322,8 +1321,8 @@ impl<'a> Parser<'a> {
// Avoid `in`. Due to recovery in the list parser this messes with `for ( $pat in $expr )`.
&& !self.token.is_keyword(kw::In)
// Try to do something more complex?
&& self.look_ahead(1, |t| !matches!(t.kind, token::OpenDelim(Delimiter::Parenthesis) // A tuple struct pattern.
| token::OpenDelim(Delimiter::Brace) // A struct pattern.
&& self.look_ahead(1, |t| !matches!(t.kind, token::OpenParen // A tuple struct pattern.
| token::OpenBrace // A struct pattern.
| token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern.
| token::PathSep // A tuple / struct variant pattern.
| token::Bang)) // A macro expanding to a pattern.
@ -1361,7 +1360,7 @@ impl<'a> Parser<'a> {
// This shortly leads to a parse error. Note that if there is no explicit
// binding mode then we do not end up here, because the lookahead
// will direct us over to `parse_enum_variant()`.
if self.token == token::OpenDelim(Delimiter::Parenthesis) {
if self.token == token::OpenParen {
return Err(self
.dcx()
.create_err(EnumPatternInsteadOfIdentifier { span: self.prev_token.span }));
@ -1429,9 +1428,9 @@ impl<'a> Parser<'a> {
token::Comma,
token::Semi,
token::At,
token::OpenDelim(Delimiter::Brace),
token::CloseDelim(Delimiter::Brace),
token::CloseDelim(Delimiter::Parenthesis),
token::OpenBrace,
token::CloseBrace,
token::CloseParen,
]
.contains(&self.token.kind)
}
@ -1489,7 +1488,7 @@ impl<'a> Parser<'a> {
let mut first_etc_and_maybe_comma_span = None;
let mut last_non_comma_dotdot_span = None;
while self.token != token::CloseDelim(Delimiter::Brace) {
while self.token != token::CloseBrace {
// check that a comma comes after every field
if !ate_comma {
let err = if self.token == token::At {
@ -1538,7 +1537,7 @@ impl<'a> Parser<'a> {
self.recover_bad_dot_dot();
self.bump(); // `..` || `...` || `_`
if self.token == token::CloseDelim(Delimiter::Brace) {
if self.token == token::CloseBrace {
break;
}
let token_str = super::token_descr(&self.token);
@ -1561,7 +1560,7 @@ impl<'a> Parser<'a> {
ate_comma = true;
}
if self.token == token::CloseDelim(Delimiter::Brace) {
if self.token == token::CloseBrace {
// If the struct looks otherwise well formed, recover and continue.
if let Some(sp) = comma_sp {
err.span_suggestion_short(
@ -1681,7 +1680,7 @@ impl<'a> Parser<'a> {
// We found `ref mut? ident:`, try to parse a `name,` or `name }`.
&& let Some(name_span) = self.look_ahead(1, |t| t.is_ident().then(|| t.span))
&& self.look_ahead(2, |t| {
t == &token::Comma || t == &token::CloseDelim(Delimiter::Brace)
t == &token::Comma || t == &token::CloseBrace
})
{
let span = last.pat.span.with_hi(ident.span.lo());

View file

@ -2,7 +2,7 @@ use std::mem;
use ast::token::IdentIsRaw;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, MetaVarKind, Token, TokenKind};
use rustc_ast::token::{self, MetaVarKind, Token, TokenKind};
use rustc_ast::{
self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemConstraint,
AssocItemConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs,
@ -302,10 +302,7 @@ impl<'a> Parser<'a> {
) -> PResult<'a, PathSegment> {
let ident = self.parse_path_segment_ident()?;
let is_args_start = |token: &Token| {
matches!(
token.kind,
token::Lt | token::Shl | token::OpenDelim(Delimiter::Parenthesis) | token::LArrow
)
matches!(token.kind, token::Lt | token::Shl | token::OpenParen | token::LArrow)
};
let check_args_start = |this: &mut Self| {
this.expected_token_types.insert(TokenType::Lt);
@ -366,7 +363,7 @@ impl<'a> Parser<'a> {
})?;
let span = lo.to(self.prev_token.span);
AngleBracketedArgs { args, span }.into()
} else if self.token == token::OpenDelim(Delimiter::Parenthesis)
} else if self.token == token::OpenParen
// FIXME(return_type_notation): Could also recover `...` here.
&& self.look_ahead(1, |t| *t == token::DotDot)
{
@ -852,7 +849,7 @@ impl<'a> Parser<'a> {
/// the caller.
pub(super) fn parse_const_arg(&mut self) -> PResult<'a, AnonConst> {
// Parse const argument.
let value = if let token::OpenDelim(Delimiter::Brace) = self.token.kind {
let value = if self.token.kind == token::OpenBrace {
self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)?
} else {
self.handle_unambiguous_unbraced_const_arg()?

View file

@ -162,7 +162,7 @@ impl<'a> Parser<'a> {
// Do not attempt to parse an expression if we're done here.
self.error_outer_attrs(attrs);
self.mk_stmt(lo, StmtKind::Empty)
} else if self.token != token::CloseDelim(Delimiter::Brace) {
} else if self.token != token::CloseBrace {
// Remainder are line-expr stmts. This is similar to the `parse_stmt_path_start` case
// above.
let restrictions =
@ -254,9 +254,7 @@ impl<'a> Parser<'a> {
self.token.kind,
token::Semi
| token::Eof
| token::CloseDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(
MetaVarKind::Stmt
)))
| token::CloseInvisible(InvisibleOrigin::MetaVar(MetaVarKind::Stmt))
) {
StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
} else {
@ -547,7 +545,7 @@ impl<'a> Parser<'a> {
// + +
Ok(Some(_))
if (!self.token.is_keyword(kw::Else)
&& self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)))
&& self.look_ahead(1, |t| t == &token::OpenBrace))
|| do_not_suggest_help => {}
// Do not suggest `if foo println!("") {;}` (as would be seen in test for #46836).
Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {}
@ -584,9 +582,7 @@ impl<'a> Parser<'a> {
stmt_kind: &StmtKind,
) {
match (&self.token.kind, &stmt_kind) {
(token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
if let ExprKind::Call(..) = expr.kind =>
{
(token::OpenBrace, StmtKind::Expr(expr)) if let ExprKind::Call(..) = expr.kind => {
// for _ in x y() {}
e.span_suggestion_verbose(
between,
@ -595,9 +591,7 @@ impl<'a> Parser<'a> {
Applicability::MaybeIncorrect,
);
}
(token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
if let ExprKind::Field(..) = expr.kind =>
{
(token::OpenBrace, StmtKind::Expr(expr)) if let ExprKind::Field(..) = expr.kind => {
// for _ in x y.z {}
e.span_suggestion_verbose(
between,
@ -606,7 +600,7 @@ impl<'a> Parser<'a> {
Applicability::MaybeIncorrect,
);
}
(token::CloseDelim(Delimiter::Brace), StmtKind::Expr(expr))
(token::CloseBrace, StmtKind::Expr(expr))
if let ExprKind::Struct(expr) = &expr.kind
&& let None = expr.qself
&& expr.path.segments.len() == 1 =>
@ -621,7 +615,7 @@ impl<'a> Parser<'a> {
Applicability::MaybeIncorrect,
);
}
(token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
(token::OpenBrace, StmtKind::Expr(expr))
if let ExprKind::Lit(lit) = expr.kind
&& let None = lit.suffix
&& let token::LitKind::Integer | token::LitKind::Float = lit.kind =>
@ -635,7 +629,7 @@ impl<'a> Parser<'a> {
Applicability::MaybeIncorrect,
);
}
(token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr))
(token::OpenBrace, StmtKind::Expr(expr))
if let ExprKind::Loop(..)
| ExprKind::If(..)
| ExprKind::While(..)
@ -658,7 +652,7 @@ impl<'a> Parser<'a> {
Applicability::MaybeIncorrect,
);
}
(token::OpenDelim(Delimiter::Brace), _) => {}
(token::OpenBrace, _) => {}
(_, _) => {
e.multipart_suggestion(
"you might have meant to write this as part of a block",
@ -809,7 +803,7 @@ impl<'a> Parser<'a> {
// Likely `foo bar`
} else if self.prev_token.kind == token::Question {
// `foo? bar`
} else if self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis) {
} else if self.prev_token.kind == token::CloseParen {
// `foo() bar`
} else {
return;
@ -826,7 +820,7 @@ impl<'a> Parser<'a> {
Applicability::MaybeIncorrect,
);
}
if self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis)) {
if self.look_ahead(1, |t| t.kind == token::OpenParen) {
err.span_suggestion_verbose(
self.prev_token.span.between(self.token.span),
"you might have meant to write a method call",
@ -870,8 +864,7 @@ impl<'a> Parser<'a> {
StmtKind::Expr(expr)
if classify::expr_requires_semi_to_be_stmt(expr)
&& !expr.attrs.is_empty()
&& ![token::Eof, token::Semi, token::CloseDelim(Delimiter::Brace)]
.contains(&self.token.kind) =>
&& !matches!(self.token.kind, token::Eof | token::Semi | token::CloseBrace) =>
{
// The user has written `#[attr] expr` which is unsupported. (#106020)
let guar = self.attr_on_non_tail_expr(&expr);
@ -919,7 +912,7 @@ impl<'a> Parser<'a> {
token::Ident(
kw::For | kw::Loop | kw::While,
token::IdentIsRaw::No
) | token::OpenDelim(Delimiter::Brace)
) | token::OpenBrace
)
})
{

View file

@ -2573,14 +2573,14 @@ fn look_ahead() {
// Current position is the `fn`.
look(&p, 0, token::Ident(kw::Fn, raw_no));
look(&p, 1, token::Ident(sym_f, raw_no));
look(&p, 2, token::OpenDelim(Delimiter::Parenthesis));
look(&p, 2, token::OpenParen);
look(&p, 3, token::Ident(sym_x, raw_no));
look(&p, 4, token::Colon);
look(&p, 5, token::Ident(sym::u32, raw_no));
look(&p, 6, token::CloseDelim(Delimiter::Parenthesis));
look(&p, 7, token::OpenDelim(Delimiter::Brace));
look(&p, 6, token::CloseParen);
look(&p, 7, token::OpenBrace);
look(&p, 8, token::Ident(sym_x, raw_no));
look(&p, 9, token::CloseDelim(Delimiter::Brace));
look(&p, 9, token::CloseBrace);
look(&p, 10, token::Ident(kw::Struct, raw_no));
look(&p, 11, token::Ident(sym_S, raw_no));
look(&p, 12, token::Semi);
@ -2597,10 +2597,10 @@ fn look_ahead() {
look(&p, 0, token::Ident(sym_x, raw_no));
look(&p, 1, token::Colon);
look(&p, 2, token::Ident(sym::u32, raw_no));
look(&p, 3, token::CloseDelim(Delimiter::Parenthesis));
look(&p, 4, token::OpenDelim(Delimiter::Brace));
look(&p, 3, token::CloseParen);
look(&p, 4, token::OpenBrace);
look(&p, 5, token::Ident(sym_x, raw_no));
look(&p, 6, token::CloseDelim(Delimiter::Brace));
look(&p, 6, token::CloseBrace);
look(&p, 7, token::Ident(kw::Struct, raw_no));
look(&p, 8, token::Ident(sym_S, raw_no));
look(&p, 9, token::Semi);
@ -2652,18 +2652,18 @@ fn look_ahead_non_outermost_stream() {
}
look(&p, 0, token::Ident(kw::Fn, raw_no));
look(&p, 1, token::Ident(sym_f, raw_no));
look(&p, 2, token::OpenDelim(Delimiter::Parenthesis));
look(&p, 2, token::OpenParen);
look(&p, 3, token::Ident(sym_x, raw_no));
look(&p, 4, token::Colon);
look(&p, 5, token::Ident(sym::u32, raw_no));
look(&p, 6, token::CloseDelim(Delimiter::Parenthesis));
look(&p, 7, token::OpenDelim(Delimiter::Brace));
look(&p, 6, token::CloseParen);
look(&p, 7, token::OpenBrace);
look(&p, 8, token::Ident(sym_x, raw_no));
look(&p, 9, token::CloseDelim(Delimiter::Brace));
look(&p, 9, token::CloseBrace);
look(&p, 10, token::Ident(kw::Struct, raw_no));
look(&p, 11, token::Ident(sym_S, raw_no));
look(&p, 12, token::Semi);
look(&p, 13, token::CloseDelim(Delimiter::Brace));
look(&p, 13, token::CloseBrace);
// Any lookahead past the end of the token stream returns `Eof`.
look(&p, 14, token::Eof);
look(&p, 15, token::Eof);
@ -2723,9 +2723,7 @@ fn debug_lookahead() {
\"f\",
No,
),
OpenDelim(
Parenthesis,
),
OpenParen,
Ident(
\"x\",
No,
@ -2735,9 +2733,7 @@ fn debug_lookahead() {
\"u32\",
No,
),
CloseDelim(
Parenthesis,
),
CloseParen,
],
approx_token_stream_pos: 0,
..
@ -2768,9 +2764,7 @@ fn debug_lookahead() {
\"f\",
No,
),
OpenDelim(
Parenthesis,
),
OpenParen,
Ident(
\"x\",
No,
@ -2780,19 +2774,13 @@ fn debug_lookahead() {
\"u32\",
No,
),
CloseDelim(
Parenthesis,
),
OpenDelim(
Brace,
),
CloseParen,
OpenBrace,
Ident(
\"x\",
No,
),
CloseDelim(
Brace,
),
CloseBrace,
Ident(
\"struct\",
No,
@ -2817,9 +2805,7 @@ fn debug_lookahead() {
&format!("{:#?}", p.debug_lookahead(1)),
"Parser {
prev_token: Token {
kind: OpenDelim(
Brace,
),
kind: OpenBrace,
span: Span {
lo: BytePos(
13,
@ -2844,9 +2830,7 @@ fn debug_lookahead() {
&format!("{:#?}", p.debug_lookahead(4)),
"Parser {
prev_token: Token {
kind: OpenDelim(
Brace,
),
kind: OpenBrace,
span: Span {
lo: BytePos(
13,
@ -2862,9 +2846,7 @@ fn debug_lookahead() {
\"x\",
No,
),
CloseDelim(
Brace,
),
CloseBrace,
Ident(
\"struct\",
No,

View file

@ -448,18 +448,6 @@ macro_rules! exp {
token_type: $crate::parser::token_type::TokenType::$tok
}
};
(@open, $delim:ident, $token_type:ident) => {
$crate::parser::token_type::ExpTokenPair {
tok: &rustc_ast::token::OpenDelim(rustc_ast::token::Delimiter::$delim),
token_type: $crate::parser::token_type::TokenType::$token_type,
}
};
(@close, $delim:ident, $token_type:ident) => {
$crate::parser::token_type::ExpTokenPair {
tok: &rustc_ast::token::CloseDelim(rustc_ast::token::Delimiter::$delim),
token_type: $crate::parser::token_type::TokenType::$token_type,
}
};
// `ExpKeywordPair` helper rules.
(@kw, $kw:ident, $token_type:ident) => {
@ -504,12 +492,12 @@ macro_rules! exp {
(Question) => { exp!(@tok, Question) };
(Eof) => { exp!(@tok, Eof) };
(OpenParen) => { exp!(@open, Parenthesis, OpenParen) };
(OpenBrace) => { exp!(@open, Brace, OpenBrace) };
(OpenBracket) => { exp!(@open, Bracket, OpenBracket) };
(CloseParen) => { exp!(@close, Parenthesis, CloseParen) };
(CloseBrace) => { exp!(@close, Brace, CloseBrace) };
(CloseBracket) => { exp!(@close, Bracket, CloseBracket) };
(OpenParen) => { exp!(@tok, OpenParen) };
(OpenBrace) => { exp!(@tok, OpenBrace) };
(OpenBracket) => { exp!(@tok, OpenBracket) };
(CloseParen) => { exp!(@tok, CloseParen) };
(CloseBrace) => { exp!(@tok, CloseBrace) };
(CloseBracket) => { exp!(@tok, CloseBracket) };
(As) => { exp!(@kw, As, KwAs) };
(Async) => { exp!(@kw, Async, KwAsync) };

View file

@ -1,5 +1,5 @@
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, IdentIsRaw, MetaVarKind, Token, TokenKind};
use rustc_ast::token::{self, IdentIsRaw, MetaVarKind, Token, TokenKind};
use rustc_ast::util::case::Case;
use rustc_ast::{
self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnRetTy,
@ -98,7 +98,7 @@ fn can_begin_dyn_bound_in_edition_2015(t: &Token) -> bool {
|| t.is_lifetime()
|| t == &TokenKind::Question
|| t.is_keyword(kw::For)
|| t == &TokenKind::OpenDelim(Delimiter::Parenthesis)
|| t == &TokenKind::OpenParen
}
impl<'a> Parser<'a> {
@ -355,7 +355,7 @@ impl<'a> Parser<'a> {
}
}
} else if self.check_keyword(exp!(Unsafe))
&& self.look_ahead(1, |tok| matches!(tok.kind, token::Lt))
&& self.look_ahead(1, |tok| tok.kind == token::Lt)
{
self.parse_unsafe_binder_ty()?
} else {
@ -534,7 +534,7 @@ impl<'a> Parser<'a> {
let elt_ty = match self.parse_ty() {
Ok(ty) => ty,
Err(err)
if self.look_ahead(1, |t| *t == token::CloseDelim(Delimiter::Bracket))
if self.look_ahead(1, |t| *t == token::CloseBracket)
| self.look_ahead(1, |t| *t == token::Semi) =>
{
// Recover from `[LIT; EXPR]` and `[LIT]`
@ -1154,7 +1154,7 @@ impl<'a> Parser<'a> {
}
let mut path = if self.token.is_keyword(kw::Fn)
&& self.look_ahead(1, |t| *t == TokenKind::OpenDelim(Delimiter::Parenthesis))
&& self.look_ahead(1, |t| *t == TokenKind::OpenParen)
&& let Some(path) = self.recover_path_from_fn()
{
path
@ -1208,7 +1208,7 @@ impl<'a> Parser<'a> {
self.parse_path(PathStyle::Type)?
};
if self.may_recover() && self.token == TokenKind::OpenDelim(Delimiter::Parenthesis) {
if self.may_recover() && self.token == TokenKind::OpenParen {
self.recover_fn_trait_with_lifetime_params(&mut path, &mut lifetime_defs)?;
}

View file

@ -194,12 +194,6 @@ pub fn check_attribute_safety(psess: &ParseSess, safety: AttributeSafety, attr:
}
}
} else if let Safety::Unsafe(unsafe_span) = attr_item.unsafety {
// Allow (but don't require) `#[unsafe(naked)]` so that compiler-builtins can upgrade to it.
// FIXME(#139797): remove this special case when compiler-builtins has upgraded.
if attr.has_name(sym::naked) {
return;
}
psess.dcx().emit_err(errors::InvalidAttrUnsafe {
span: unsafe_span,
name: attr_item.path.clone(),

View file

@ -508,7 +508,7 @@ passes_must_use_no_effect =
`#[must_use]` has no effect when applied to {$article} {$target}
passes_naked_asm_outside_naked_fn =
the `naked_asm!` macro can only be used in functions marked with `#[naked]`
the `naked_asm!` macro can only be used in functions marked with `#[unsafe(naked)]`
passes_naked_functions_asm_block =
naked functions must contain a single `naked_asm!` invocation
@ -516,9 +516,9 @@ passes_naked_functions_asm_block =
.label_non_asm = not allowed in naked functions
passes_naked_functions_incompatible_attribute =
attribute incompatible with `#[naked]`
.label = the `{$attr}` attribute is incompatible with `#[naked]`
.naked_attribute = function marked with `#[naked]` here
attribute incompatible with `#[unsafe(naked)]`
.label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`
.naked_attribute = function marked with `#[unsafe(naked)]` here
passes_naked_functions_must_naked_asm =
the `asm!` macro is not allowed in naked functions

View file

@ -693,13 +693,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
}
}
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
// `#[naked]` attribute with just a lint, because we previously
// erroneously allowed it and some crates used it accidentally, to be compatible
// with crates depending on them, we can't throw an error here.
Target::Field | Target::Arm | Target::MacroDef => {
self.inline_attr_str_error_with_macro_def(hir_id, attr, "naked")
}
_ => {
self.dcx().emit_err(errors::AttrShouldBeAppliedToFn {
attr_span: attr.span(),

View file

@ -8,7 +8,7 @@ use std::sync::Arc;
use rustc_ast::expand::StrippedCfgItem;
use rustc_ast::{self as ast, Crate, NodeId, attr};
use rustc_ast_pretty::pprust;
use rustc_attr_parsing::{AttributeKind, StabilityLevel, find_attr};
use rustc_attr_parsing::StabilityLevel;
use rustc_data_structures::intern::Interned;
use rustc_errors::{Applicability, DiagCtxtHandle, StashKey};
use rustc_expand::base::{
@ -1128,13 +1128,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
edition,
);
// The #[rustc_macro_edition_2021] attribute is used by the pin!() macro
// as a temporary workaround for a regression in expressiveness in Rust 2024.
// See https://github.com/rust-lang/rust/issues/138718.
if find_attr!(attrs.iter(), AttributeKind::RustcMacroEdition2021) {
ext.edition = Edition::Edition2021;
}
if let Some(builtin_name) = ext.builtin_name {
// The macro was marked with `#[rustc_builtin_macro]`.
if let Some(builtin_ext_kind) = self.builtin_macros.get(&builtin_name) {

View file

@ -72,6 +72,8 @@ pub struct TypeSizeInfo {
#[derive(Default)]
pub struct CodeStats {
/// The hash set that actually holds all the type size information.
/// The field is public for use in external tools. See #139876.
pub type_sizes: Lock<FxHashSet<TypeSizeInfo>>,
}

View file

@ -568,122 +568,203 @@ impl FromStr for SplitDwarfKind {
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, HashStable_Generic)]
#[derive(Encodable, Decodable)]
pub enum OutputType {
/// This is the optimized bitcode, which could be either pre-LTO or non-LTO bitcode,
/// depending on the specific request type.
Bitcode,
/// This is the summary or index data part of the ThinLTO bitcode.
ThinLinkBitcode,
Assembly,
LlvmAssembly,
Mir,
Metadata,
Object,
Exe,
DepInfo,
}
macro_rules! define_output_types {
(
$(
$(#[doc = $doc:expr])*
$Variant:ident => {
shorthand: $shorthand:expr,
extension: $extension:expr,
description: $description:expr,
default_filename: $default_filename:expr,
is_text: $is_text:expr,
compatible_with_cgus_and_single_output: $compatible:expr
}
),* $(,)?
) => {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, HashStable_Generic)]
#[derive(Encodable, Decodable)]
pub enum OutputType {
$(
$(#[doc = $doc])*
$Variant,
)*
}
impl StableOrd for OutputType {
const CAN_USE_UNSTABLE_SORT: bool = true;
// Trivial C-Style enums have a stable sort order across compilation sessions.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl StableOrd for OutputType {
const CAN_USE_UNSTABLE_SORT: bool = true;
impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType {
type KeyType = Self;
// Trivial C-Style enums have a stable sort order across compilation sessions.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
*self
impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType {
type KeyType = Self;
fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
*self
}
}
impl OutputType {
pub fn iter_all() -> impl Iterator<Item = OutputType> {
static ALL_VARIANTS: &[OutputType] = &[
$(
OutputType::$Variant,
)*
];
ALL_VARIANTS.iter().copied()
}
fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
match *self {
$(
OutputType::$Variant => $compatible,
)*
}
}
pub fn shorthand(&self) -> &'static str {
match *self {
$(
OutputType::$Variant => $shorthand,
)*
}
}
fn from_shorthand(shorthand: &str) -> Option<Self> {
match shorthand {
$(
s if s == $shorthand => Some(OutputType::$Variant),
)*
_ => None,
}
}
fn shorthands_display() -> String {
let shorthands = vec![
$(
format!("`{}`", $shorthand),
)*
];
shorthands.join(", ")
}
pub fn extension(&self) -> &'static str {
match *self {
$(
OutputType::$Variant => $extension,
)*
}
}
pub fn is_text_output(&self) -> bool {
match *self {
$(
OutputType::$Variant => $is_text,
)*
}
}
pub fn description(&self) -> &'static str {
match *self {
$(
OutputType::$Variant => $description,
)*
}
}
pub fn default_filename(&self) -> &'static str {
match *self {
$(
OutputType::$Variant => $default_filename,
)*
}
}
}
}
}
impl OutputType {
fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
match *self {
OutputType::Exe | OutputType::DepInfo | OutputType::Metadata => true,
OutputType::Bitcode
| OutputType::ThinLinkBitcode
| OutputType::Assembly
| OutputType::LlvmAssembly
| OutputType::Mir
| OutputType::Object => false,
}
}
pub fn shorthand(&self) -> &'static str {
match *self {
OutputType::Bitcode => "llvm-bc",
OutputType::ThinLinkBitcode => "thin-link-bitcode",
OutputType::Assembly => "asm",
OutputType::LlvmAssembly => "llvm-ir",
OutputType::Mir => "mir",
OutputType::Object => "obj",
OutputType::Metadata => "metadata",
OutputType::Exe => "link",
OutputType::DepInfo => "dep-info",
}
}
fn from_shorthand(shorthand: &str) -> Option<Self> {
Some(match shorthand {
"asm" => OutputType::Assembly,
"llvm-ir" => OutputType::LlvmAssembly,
"mir" => OutputType::Mir,
"llvm-bc" => OutputType::Bitcode,
"thin-link-bitcode" => OutputType::ThinLinkBitcode,
"obj" => OutputType::Object,
"metadata" => OutputType::Metadata,
"link" => OutputType::Exe,
"dep-info" => OutputType::DepInfo,
_ => return None,
})
}
fn shorthands_display() -> String {
format!(
"`{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`, `{}`",
OutputType::Bitcode.shorthand(),
OutputType::ThinLinkBitcode.shorthand(),
OutputType::Assembly.shorthand(),
OutputType::LlvmAssembly.shorthand(),
OutputType::Mir.shorthand(),
OutputType::Object.shorthand(),
OutputType::Metadata.shorthand(),
OutputType::Exe.shorthand(),
OutputType::DepInfo.shorthand(),
)
}
pub fn extension(&self) -> &'static str {
match *self {
OutputType::Bitcode => "bc",
OutputType::ThinLinkBitcode => "indexing.o",
OutputType::Assembly => "s",
OutputType::LlvmAssembly => "ll",
OutputType::Mir => "mir",
OutputType::Object => "o",
OutputType::Metadata => "rmeta",
OutputType::DepInfo => "d",
OutputType::Exe => "",
}
}
pub fn is_text_output(&self) -> bool {
match *self {
OutputType::Assembly
| OutputType::LlvmAssembly
| OutputType::Mir
| OutputType::DepInfo => true,
OutputType::Bitcode
| OutputType::ThinLinkBitcode
| OutputType::Object
| OutputType::Metadata
| OutputType::Exe => false,
}
}
define_output_types! {
Assembly => {
shorthand: "asm",
extension: "s",
description: "Generates a file with the crate's assembly code",
default_filename: "CRATE_NAME.s",
is_text: true,
compatible_with_cgus_and_single_output: false
},
#[doc = "This is the optimized bitcode, which could be either pre-LTO or non-LTO bitcode,"]
#[doc = "depending on the specific request type."]
Bitcode => {
shorthand: "llvm-bc",
extension: "bc",
description: "Generates a binary file containing the LLVM bitcode",
default_filename: "CRATE_NAME.bc",
is_text: false,
compatible_with_cgus_and_single_output: false
},
DepInfo => {
shorthand: "dep-info",
extension: "d",
description: "Generates a file with Makefile syntax that indicates all the source files that were loaded to generate the crate",
default_filename: "CRATE_NAME.d",
is_text: true,
compatible_with_cgus_and_single_output: true
},
Exe => {
shorthand: "link",
extension: "",
description: "Generates the crates specified by --crate-type. This is the default if --emit is not specified",
default_filename: "(platform and crate-type dependent)",
is_text: false,
compatible_with_cgus_and_single_output: true
},
LlvmAssembly => {
shorthand: "llvm-ir",
extension: "ll",
description: "Generates a file containing LLVM IR",
default_filename: "CRATE_NAME.ll",
is_text: true,
compatible_with_cgus_and_single_output: false
},
Metadata => {
shorthand: "metadata",
extension: "rmeta",
description: "Generates a file containing metadata about the crate",
default_filename: "libCRATE_NAME.rmeta",
is_text: false,
compatible_with_cgus_and_single_output: true
},
Mir => {
shorthand: "mir",
extension: "mir",
description: "Generates a file containing rustc's mid-level intermediate representation",
default_filename: "CRATE_NAME.mir",
is_text: true,
compatible_with_cgus_and_single_output: false
},
Object => {
shorthand: "obj",
extension: "o",
description: "Generates a native object file",
default_filename: "CRATE_NAME.o",
is_text: false,
compatible_with_cgus_and_single_output: false
},
#[doc = "This is the summary or index data part of the ThinLTO bitcode."]
ThinLinkBitcode => {
shorthand: "thin-link-bitcode",
extension: "indexing.o",
description: "Generates the ThinLTO summary as bitcode",
default_filename: "CRATE_NAME.indexing.o",
is_text: false,
compatible_with_cgus_and_single_output: false
},
}
/// The type of diagnostics output to generate.
@ -1565,13 +1646,32 @@ The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE
static PRINT_HELP: LazyLock<String> = LazyLock::new(|| {
format!(
"Compiler information to print on stdout (or to a file)\n\
INFO may be one of ({}).",
INFO may be one of <{}>.",
PRINT_KINDS.iter().map(|(name, _)| format!("{name}")).collect::<Vec<_>>().join("|")
)
});
static EMIT_HELP: LazyLock<String> = LazyLock::new(|| {
let mut result =
String::from("Comma separated list of types of output for the compiler to emit.\n");
result.push_str("Each TYPE has the default FILE name:\n");
for output in OutputType::iter_all() {
result.push_str(&format!("* {} - {}\n", output.shorthand(), output.default_filename()));
}
result
});
/// Returns all rustc command line options, including metadata for
/// each option, such as whether the option is stable.
///
/// # Option style guidelines
///
/// - `<param>`: Indicates a required parameter
/// - `[param]`: Indicates an optional parameter
/// - `|`: Indicates a mutually exclusive option
/// - `*`: a list element with description
pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
use OptionKind::{Flag, FlagMulti, Multi, Opt};
use OptionStability::{Stable, Unstable};
@ -1586,18 +1686,18 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
"",
"cfg",
"Configure the compilation environment.\n\
SPEC supports the syntax `NAME[=\"VALUE\"]`.",
"SPEC",
SPEC supports the syntax `<NAME>[=\"<VALUE>\"]`.",
"<SPEC>",
),
opt(Stable, Multi, "", "check-cfg", "Provide list of expected cfgs for checking", "SPEC"),
opt(Stable, Multi, "", "check-cfg", "Provide list of expected cfgs for checking", "<SPEC>"),
opt(
Stable,
Multi,
"L",
"",
"Add a directory to the library search path. \
The optional KIND can be one of dependency, crate, native, framework, or all (the default).",
"[KIND=]PATH",
The optional KIND can be one of <dependency|crate|native|framework|all> (default: all).",
"[<KIND>=]<PATH>",
),
opt(
Stable,
@ -1606,53 +1706,46 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
"",
"Link the generated crate(s) to the specified native\n\
library NAME. The optional KIND can be one of\n\
static, framework, or dylib (the default).\n\
<static|framework|dylib> (default: dylib).\n\
Optional comma separated MODIFIERS\n\
(bundle|verbatim|whole-archive|as-needed)\n\
<bundle|verbatim|whole-archive|as-needed>\n\
may be specified each with a prefix of either '+' to\n\
enable or '-' to disable.",
"[KIND[:MODIFIERS]=]NAME[:RENAME]",
"[<KIND>[:<MODIFIERS>]=]<NAME>[:<RENAME>]",
),
make_crate_type_option(),
opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", "NAME"),
opt(Stable, Opt, "", "crate-name", "Specify the name of the crate being built", "<NAME>"),
opt(Stable, Opt, "", "edition", &EDITION_STRING, EDITION_NAME_LIST),
opt(
Stable,
Multi,
"",
"emit",
"Comma separated list of types of output for the compiler to emit",
"[asm|llvm-bc|llvm-ir|obj|metadata|link|dep-info|mir]",
),
opt(Stable, Multi, "", "print", &PRINT_HELP, "INFO[=FILE]"),
opt(Stable, Multi, "", "emit", &EMIT_HELP, "<TYPE>[=<FILE>]"),
opt(Stable, Multi, "", "print", &PRINT_HELP, "<INFO>[=<FILE>]"),
opt(Stable, FlagMulti, "g", "", "Equivalent to -C debuginfo=2", ""),
opt(Stable, FlagMulti, "O", "", "Equivalent to -C opt-level=3", ""),
opt(Stable, Opt, "o", "", "Write output to <filename>", "FILENAME"),
opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
opt(Stable, Opt, "o", "", "Write output to FILENAME", "<FILENAME>"),
opt(Stable, Opt, "", "out-dir", "Write output to compiler-chosen filename in DIR", "<DIR>"),
opt(
Stable,
Opt,
"",
"explain",
"Provide a detailed explanation of an error message",
"OPT",
"<OPT>",
),
opt(Stable, Flag, "", "test", "Build a test harness", ""),
opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", "TARGET"),
opt(Stable, Multi, "A", "allow", "Set lint allowed", "LINT"),
opt(Stable, Multi, "W", "warn", "Set lint warnings", "LINT"),
opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "LINT"),
opt(Stable, Multi, "D", "deny", "Set lint denied", "LINT"),
opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "LINT"),
opt(Stable, Opt, "", "target", "Target triple for which the code is compiled", "<TARGET>"),
opt(Stable, Multi, "A", "allow", "Set lint allowed", "<LINT>"),
opt(Stable, Multi, "W", "warn", "Set lint warnings", "<LINT>"),
opt(Stable, Multi, "", "force-warn", "Set lint force-warn", "<LINT>"),
opt(Stable, Multi, "D", "deny", "Set lint denied", "<LINT>"),
opt(Stable, Multi, "F", "forbid", "Set lint forbidden", "<LINT>"),
opt(
Stable,
Multi,
"",
"cap-lints",
"Set the most restrictive lint level. More restrictive lints are capped at this level",
"LEVEL",
"<LEVEL>",
),
opt(Stable, Multi, "C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
opt(Stable, Multi, "C", "codegen", "Set a codegen option", "<OPT>[=<VALUE>]"),
opt(Stable, Flag, "V", "version", "Print version info and exit", ""),
opt(Stable, Flag, "v", "verbose", "Use verbose output", ""),
];
@ -1666,29 +1759,29 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
"",
"extern",
"Specify where an external rust library is located",
"NAME[=PATH]",
"<NAME>[=<PATH>]",
),
opt(Stable, Opt, "", "sysroot", "Override the system root", "PATH"),
opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", "FLAG"),
opt(Stable, Opt, "", "sysroot", "Override the system root", "<PATH>"),
opt(Unstable, Multi, "Z", "", "Set unstable / perma-unstable options", "<FLAG>"),
opt(
Stable,
Opt,
"",
"error-format",
"How errors and other messages are produced",
"human|json|short",
"<human|json|short>",
),
opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", "CONFIG"),
opt(Stable, Multi, "", "json", "Configure the JSON output of the compiler", "<CONFIG>"),
opt(
Stable,
Opt,
"",
"color",
"Configure coloring of output:
auto = colorize, if output goes to a tty (default);
always = always colorize output;
never = never colorize output",
"auto|always|never",
* auto = colorize, if output goes to a tty (default);
* always = always colorize output;
* never = never colorize output",
"<auto|always|never>",
),
opt(
Stable,
@ -1696,7 +1789,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
"",
"diagnostic-width",
"Inform rustc of the width of the output so that diagnostics can be truncated to fit",
"WIDTH",
"<WIDTH>",
),
opt(
Stable,
@ -1704,9 +1797,9 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
"",
"remap-path-prefix",
"Remap source names in all output (compiler messages and output files)",
"FROM=TO",
"<FROM>=<TO>",
),
opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "VAR=VALUE"),
opt(Unstable, Multi, "", "env-set", "Inject an environment variable", "<VAR>=<VALUE>"),
];
options.extend(verbose_only.into_iter().map(|mut opt| {
opt.is_verbose_help_only = true;
@ -2706,7 +2799,7 @@ pub fn make_crate_type_option() -> RustcOptGroup {
"crate-type",
"Comma separated list of types of crates
for the compiler to emit",
"[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]",
"<bin|lib|rlib|dylib|cdylib|staticlib|proc-macro>",
)
}

View file

@ -45,7 +45,7 @@ pub const ALL_EDITIONS: &[Edition] = &[
Edition::EditionFuture,
];
pub const EDITION_NAME_LIST: &str = "2015|2018|2021|2024";
pub const EDITION_NAME_LIST: &str = "<2015|2018|2021|2024|future>";
pub const DEFAULT_EDITION: Edition = Edition::Edition2015;

View file

@ -1232,6 +1232,25 @@ impl DesugaringKind {
DesugaringKind::PatTyRange => "pattern type",
}
}
/// For use with `rustc_unimplemented` to support conditions
/// like `from_desugaring = "QuestionMark"`
pub fn matches(&self, value: &str) -> bool {
match self {
DesugaringKind::CondTemporary => value == "CondTemporary",
DesugaringKind::Async => value == "Async",
DesugaringKind::Await => value == "Await",
DesugaringKind::QuestionMark => value == "QuestionMark",
DesugaringKind::TryBlock => value == "TryBlock",
DesugaringKind::YeetExpr => value == "YeetExpr",
DesugaringKind::OpaqueTy => value == "OpaqueTy",
DesugaringKind::ForLoop => value == "ForLoop",
DesugaringKind::WhileLoop => value == "WhileLoop",
DesugaringKind::BoundModifier => value == "BoundModifier",
DesugaringKind::Contract => value == "Contract",
DesugaringKind::PatTyRange => value == "PatTyRange",
}
}
}
#[derive(Default)]

View file

@ -372,6 +372,7 @@ symbols! {
SyncUnsafeCell,
T,
Target,
This,
ToOwned,
ToString,
TokenStream,
@ -1823,7 +1824,6 @@ symbols! {
rustc_lint_opt_ty,
rustc_lint_query_instability,
rustc_lint_untracked_query_information,
rustc_macro_edition_2021,
rustc_macro_transparency,
rustc_main,
rustc_mir,

View file

@ -1,12 +1,11 @@
use crate::spec::{LinkSelfContainedDefault, TargetOptions, base, crt_objects};
pub(crate) fn opts() -> TargetOptions {
let mut base = base::linux::opts();
base.env = "musl".into();
base.pre_link_objects_self_contained = crt_objects::pre_musl_self_contained();
base.post_link_objects_self_contained = crt_objects::post_musl_self_contained();
base.link_self_contained = LinkSelfContainedDefault::InferredForMusl;
base
TargetOptions {
env: "musl".into(),
pre_link_objects_self_contained: crt_objects::pre_musl_self_contained(),
post_link_objects_self_contained: crt_objects::post_musl_self_contained(),
link_self_contained: LinkSelfContainedDefault::InferredForMusl,
..base::linux::opts()
}
}

View file

@ -1,12 +1,11 @@
use crate::spec::{TargetOptions, TlsModel, base};
pub(crate) fn opts() -> TargetOptions {
let mut base = base::linux::opts();
base.env = "ohos".into();
base.crt_static_default = false;
base.tls_model = TlsModel::Emulated;
base.has_thread_local = false;
base
TargetOptions {
env: "ohos".into(),
crt_static_default: false,
tls_model: TlsModel::Emulated,
has_thread_local: false,
..base::linux::opts()
}
}

View file

@ -2,6 +2,8 @@ pub mod ambiguity;
pub mod call_kind;
mod fulfillment_errors;
pub mod on_unimplemented;
pub mod on_unimplemented_condition;
pub mod on_unimplemented_format;
mod overflow;
pub mod suggestions;

View file

@ -1,44 +1,31 @@
use std::iter;
use std::path::PathBuf;
use rustc_ast::MetaItemInner;
use rustc_data_structures::fx::FxHashMap;
use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit};
use rustc_errors::codes::*;
use rustc_errors::{ErrorGuaranteed, struct_span_code_err};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{AttrArgs, Attribute};
use rustc_macros::LintDiagnostic;
use rustc_middle::bug;
use rustc_middle::ty::print::PrintTraitRefExt as _;
use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt};
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
use rustc_middle::ty::print::PrintTraitRefExt;
use rustc_middle::ty::{self, GenericArgsRef, GenericParamDef, GenericParamDefKind, TyCtxt};
use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
use rustc_span::{Ident, Span, Symbol, kw, sym};
use rustc_span::{Span, Symbol, sym};
use tracing::{debug, info};
use {rustc_attr_parsing as attr, rustc_hir as hir};
use super::{ObligationCauseCode, PredicateObligation};
use crate::error_reporting::TypeErrCtxt;
use crate::error_reporting::traits::on_unimplemented_condition::{Condition, ConditionOptions};
use crate::error_reporting::traits::on_unimplemented_format::{
Ctx, FormatArgs, FormatString, FormatWarning,
};
use crate::errors::{
EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
};
use crate::infer::InferCtxtExt;
/// The symbols which are always allowed in a format string
static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
kw::SelfUpper,
sym::ItemContext,
sym::from_desugaring,
sym::direct,
sym::cause,
sym::integral,
sym::integer_,
sym::float,
sym::_Self,
sym::crate_local,
sym::Trait,
];
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
fn impl_similar_to(
&self,
@ -121,86 +108,78 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
.unwrap_or_else(|| (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args));
let trait_pred = trait_pred.skip_binder();
let mut flags = vec![];
let mut self_types = vec![];
let mut generic_args: Vec<(Symbol, String)> = vec![];
let mut crate_local = false;
// FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs,
// but I guess we could synthesize one here. We don't see any errors that rely on
// that yet, though.
let enclosure = self.describe_enclosure(obligation.cause.body_id).map(|t| t.to_owned());
flags.push((sym::ItemContext, enclosure));
let item_context = self.describe_enclosure(obligation.cause.body_id).unwrap_or("");
match obligation.cause.code() {
let direct = match obligation.cause.code() {
ObligationCauseCode::BuiltinDerived(..)
| ObligationCauseCode::ImplDerived(..)
| ObligationCauseCode::WellFormedDerived(..) => {}
| ObligationCauseCode::WellFormedDerived(..) => false,
_ => {
// this is a "direct", user-specified, rather than derived,
// obligation.
flags.push((sym::direct, None));
true
}
}
};
if let Some(k) = obligation.cause.span.desugaring_kind() {
flags.push((sym::from_desugaring, None));
flags.push((sym::from_desugaring, Some(format!("{k:?}"))));
}
let from_desugaring = obligation.cause.span.desugaring_kind();
if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
flags.push((sym::cause, Some("MainFunctionType".to_string())));
}
flags.push((sym::Trait, Some(trait_pred.trait_ref.print_trait_sugared().to_string())));
let cause = if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
Some("MainFunctionType".to_string())
} else {
None
};
// Add all types without trimmed paths or visible paths, ensuring they end up with
// their "canonical" def path.
ty::print::with_no_trimmed_paths!(ty::print::with_no_visible_paths!({
let generics = self.tcx.generics_of(def_id);
let self_ty = trait_pred.self_ty();
// This is also included through the generics list as `Self`,
// but the parser won't allow you to use it
flags.push((sym::_Self, Some(self_ty.to_string())));
self_types.push(self_ty.to_string());
if let Some(def) = self_ty.ty_adt_def() {
// We also want to be able to select self's original
// signature with no type arguments resolved
flags.push((
sym::_Self,
Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
));
self_types.push(self.tcx.type_of(def.did()).instantiate_identity().to_string());
}
for param in generics.own_params.iter() {
let value = match param.kind {
for GenericParamDef { name, kind, index, .. } in generics.own_params.iter() {
let value = match kind {
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
args[param.index as usize].to_string()
args[*index as usize].to_string()
}
GenericParamDefKind::Lifetime => continue,
};
let name = param.name;
flags.push((name, Some(value)));
generic_args.push((*name, value));
if let GenericParamDefKind::Type { .. } = param.kind {
let param_ty = args[param.index as usize].expect_ty();
if let GenericParamDefKind::Type { .. } = kind {
let param_ty = args[*index as usize].expect_ty();
if let Some(def) = param_ty.ty_adt_def() {
// We also want to be able to select the parameter's
// original signature with no type arguments resolved
flags.push((
name,
Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()),
generic_args.push((
*name,
self.tcx.type_of(def.did()).instantiate_identity().to_string(),
));
}
}
}
if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) {
flags.push((sym::crate_local, None));
crate_local = true;
}
// Allow targeting all integers using `{integral}`, even if the exact type was resolved
if self_ty.is_integral() {
flags.push((sym::_Self, Some("{integral}".to_owned())));
self_types.push("{integral}".to_owned());
}
if self_ty.is_array_slice() {
flags.push((sym::_Self, Some("&[]".to_owned())));
self_types.push("&[]".to_owned());
}
if self_ty.is_fn() {
@ -215,53 +194,51 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
hir::Safety::Unsafe => "unsafe fn",
}
};
flags.push((sym::_Self, Some(shortname.to_owned())));
self_types.push(shortname.to_owned());
}
// Slices give us `[]`, `[{ty}]`
if let ty::Slice(aty) = self_ty.kind() {
flags.push((sym::_Self, Some("[]".to_string())));
self_types.push("[]".to_owned());
if let Some(def) = aty.ty_adt_def() {
// We also want to be able to select the slice's type's original
// signature with no type arguments resolved
flags.push((
sym::_Self,
Some(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity())),
));
self_types
.push(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity()));
}
if aty.is_integral() {
flags.push((sym::_Self, Some("[{integral}]".to_string())));
self_types.push("[{integral}]".to_string());
}
}
// Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
if let ty::Array(aty, len) = self_ty.kind() {
flags.push((sym::_Self, Some("[]".to_string())));
self_types.push("[]".to_string());
let len = len.try_to_target_usize(self.tcx);
flags.push((sym::_Self, Some(format!("[{aty}; _]"))));
self_types.push(format!("[{aty}; _]"));
if let Some(n) = len {
flags.push((sym::_Self, Some(format!("[{aty}; {n}]"))));
self_types.push(format!("[{aty}; {n}]"));
}
if let Some(def) = aty.ty_adt_def() {
// We also want to be able to select the array's type's original
// signature with no type arguments resolved
let def_ty = self.tcx.type_of(def.did()).instantiate_identity();
flags.push((sym::_Self, Some(format!("[{def_ty}; _]"))));
self_types.push(format!("[{def_ty}; _]"));
if let Some(n) = len {
flags.push((sym::_Self, Some(format!("[{def_ty}; {n}]"))));
self_types.push(format!("[{def_ty}; {n}]"));
}
}
if aty.is_integral() {
flags.push((sym::_Self, Some("[{integral}; _]".to_string())));
self_types.push("[{integral}; _]".to_string());
if let Some(n) = len {
flags.push((sym::_Self, Some(format!("[{{integral}}; {n}]"))));
self_types.push(format!("[{{integral}}; {n}]"));
}
}
}
if let ty::Dynamic(traits, _, _) = self_ty.kind() {
for t in traits.iter() {
if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() {
flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id))))
self_types.push(self.tcx.def_path_str(trait_ref.def_id));
}
}
}
@ -271,31 +248,76 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
&& let ty::Slice(sty) = ref_ty.kind()
&& sty.is_integral()
{
flags.push((sym::_Self, Some("&[{integral}]".to_owned())));
self_types.push("&[{integral}]".to_owned());
}
}));
let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id);
let trait_sugared = trait_pred.trait_ref.print_trait_sugared();
let condition_options = ConditionOptions {
self_types,
from_desugaring,
cause,
crate_local,
direct,
generic_args,
};
// Unlike the generic_args earlier,
// this one is *not* collected under `with_no_trimmed_paths!`
// for printing the type to the user
//
// This includes `Self`, as it is the first parameter in `own_params`.
let generic_args = self
.tcx
.generics_of(trait_pred.trait_ref.def_id)
.own_params
.iter()
.filter_map(|param| {
let value = match param.kind {
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
if let Some(ty) = trait_pred.trait_ref.args[param.index as usize].as_type()
{
self.tcx.short_string(ty, long_ty_file)
} else {
trait_pred.trait_ref.args[param.index as usize].to_string()
}
}
GenericParamDefKind::Lifetime => return None,
};
let name = param.name;
Some((name, value))
})
.collect();
let format_args = FormatArgs { this, trait_sugared, generic_args, item_context };
if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) {
command.evaluate(self.tcx, trait_pred.trait_ref, &flags, long_ty_file)
command.evaluate(self.tcx, trait_pred.trait_ref, &condition_options, &format_args)
} else {
OnUnimplementedNote::default()
}
}
}
/// Represents a format string in a on_unimplemented attribute,
/// like the "content" in `#[diagnostic::on_unimplemented(message = "content")]`
#[derive(Clone, Debug)]
pub struct OnUnimplementedFormatString {
symbol: Symbol,
span: Span,
is_diagnostic_namespace_variant: bool,
/// Symbol of the format string, i.e. `"content"`
pub symbol: Symbol,
///The span of the format string, i.e. `"content"`
pub span: Span,
pub is_diagnostic_namespace_variant: bool,
}
#[derive(Debug)]
pub struct OnUnimplementedDirective {
pub condition: Option<MetaItemInner>,
pub condition: Option<Condition>,
pub subcommands: Vec<OnUnimplementedDirective>,
pub message: Option<OnUnimplementedFormatString>,
pub label: Option<OnUnimplementedFormatString>,
pub message: Option<(Span, OnUnimplementedFormatString)>,
pub label: Option<(Span, OnUnimplementedFormatString)>,
pub notes: Vec<OnUnimplementedFormatString>,
pub parent_label: Option<OnUnimplementedFormatString>,
pub append_const_msg: Option<AppendConstMessage>,
@ -329,7 +351,7 @@ pub struct MalformedOnUnimplementedAttrLint {
}
impl MalformedOnUnimplementedAttrLint {
fn new(span: Span) -> Self {
pub fn new(span: Span) -> Self {
Self { span }
}
}
@ -350,7 +372,7 @@ pub struct IgnoredDiagnosticOption {
}
impl IgnoredDiagnosticOption {
fn maybe_emit_warning<'tcx>(
pub fn maybe_emit_warning<'tcx>(
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
new: Option<Span>,
@ -370,29 +392,11 @@ impl IgnoredDiagnosticOption {
}
}
#[derive(LintDiagnostic)]
#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
#[help]
pub struct UnknownFormatParameterForOnUnimplementedAttr {
argument_name: Symbol,
trait_name: Ident,
}
#[derive(LintDiagnostic)]
#[diag(trait_selection_disallowed_positional_argument)]
#[help]
pub struct DisallowedPositionalArgument;
#[derive(LintDiagnostic)]
#[diag(trait_selection_invalid_format_specifier)]
#[help]
pub struct InvalidFormatSpecifier;
#[derive(LintDiagnostic)]
#[diag(trait_selection_wrapped_parser_error)]
pub struct WrappedParserError {
description: String,
label: String,
pub description: String,
pub label: String,
}
impl<'tcx> OnUnimplementedDirective {
@ -407,12 +411,12 @@ impl<'tcx> OnUnimplementedDirective {
let mut errored = None;
let mut item_iter = items.iter();
let parse_value = |value_str, value_span| {
let parse_value = |value_str, span| {
OnUnimplementedFormatString::try_parse(
tcx,
item_def_id,
value_str,
value_span,
span,
is_diagnostic_namespace_variant,
)
.map(Some)
@ -434,7 +438,7 @@ impl<'tcx> OnUnimplementedDirective {
}
true
});
Some(cond.clone())
Some(Condition { inner: cond.clone() })
};
let mut message = None;
@ -444,24 +448,36 @@ impl<'tcx> OnUnimplementedDirective {
let mut subcommands = vec![];
let mut append_const_msg = None;
let get_value_and_span = |item: &_, key| {
if let MetaItemInner::MetaItem(MetaItem {
path,
kind: MetaItemKind::NameValue(MetaItemLit { span, kind: LitKind::Str(s, _), .. }),
..
}) = item
&& *path == key
{
Some((*s, *span))
} else {
None
}
};
for item in item_iter {
if item.has_name(sym::message) && message.is_none() {
if let Some(message_) = item.value_str() {
message = parse_value(message_, item.span())?;
if let Some((message_, span)) = get_value_and_span(item, sym::message)
&& message.is_none()
{
message = parse_value(message_, span)?.map(|l| (item.span(), l));
continue;
} else if let Some((label_, span)) = get_value_and_span(item, sym::label)
&& label.is_none()
{
label = parse_value(label_, span)?.map(|l| (item.span(), l));
continue;
} else if let Some((note_, span)) = get_value_and_span(item, sym::note) {
if let Some(note) = parse_value(note_, span)? {
notes.push(note);
continue;
}
} else if item.has_name(sym::label) && label.is_none() {
if let Some(label_) = item.value_str() {
label = parse_value(label_, item.span())?;
continue;
}
} else if item.has_name(sym::note) {
if let Some(note_) = item.value_str() {
if let Some(note) = parse_value(note_, item.span())? {
notes.push(note);
continue;
}
}
} else if item.has_name(sym::parent_label)
&& parent_label.is_none()
&& !is_diagnostic_namespace_variant
@ -539,6 +555,13 @@ impl<'tcx> OnUnimplementedDirective {
}
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
if !tcx.is_trait(item_def_id) {
// It could be a trait_alias (`trait MyTrait = SomeOtherTrait`)
// or an implementation (`impl MyTrait for Foo {}`)
//
// We don't support those.
return Ok(None);
}
if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
return Self::parse_attribute(attr, false, tcx, item_def_id);
} else {
@ -554,15 +577,15 @@ impl<'tcx> OnUnimplementedDirective {
IgnoredDiagnosticOption::maybe_emit_warning(
tcx,
item_def_id,
directive.message.as_ref().map(|f| f.span),
aggr.message.as_ref().map(|f| f.span),
directive.message.as_ref().map(|f| f.0),
aggr.message.as_ref().map(|f| f.0),
"message",
);
IgnoredDiagnosticOption::maybe_emit_warning(
tcx,
item_def_id,
directive.label.as_ref().map(|f| f.span),
aggr.label.as_ref().map(|f| f.span),
directive.label.as_ref().map(|f| f.0),
aggr.label.as_ref().map(|f| f.0),
"label",
);
IgnoredDiagnosticOption::maybe_emit_warning(
@ -636,13 +659,16 @@ impl<'tcx> OnUnimplementedDirective {
condition: None,
message: None,
subcommands: vec![],
label: Some(OnUnimplementedFormatString::try_parse(
tcx,
item_def_id,
value,
label: Some((
attr.span(),
is_diagnostic_namespace_variant,
)?),
OnUnimplementedFormatString::try_parse(
tcx,
item_def_id,
value,
attr.value_span().unwrap_or(attr.span()),
is_diagnostic_namespace_variant,
)?,
)),
notes: Vec::new(),
parent_label: None,
append_const_msg: None,
@ -702,43 +728,23 @@ impl<'tcx> OnUnimplementedDirective {
&self,
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
options: &[(Symbol, Option<String>)],
long_ty_file: &mut Option<PathBuf>,
condition_options: &ConditionOptions,
args: &FormatArgs<'tcx>,
) -> OnUnimplementedNote {
let mut message = None;
let mut label = None;
let mut notes = Vec::new();
let mut parent_label = None;
let mut append_const_msg = None;
info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
let options_map: FxHashMap<Symbol, String> =
options.iter().filter_map(|(k, v)| v.clone().map(|v| (*k, v))).collect();
info!(
"evaluate({:?}, trait_ref={:?}, options={:?}, args ={:?})",
self, trait_ref, condition_options, args
);
for command in self.subcommands.iter().chain(Some(self)).rev() {
debug!(?command);
if let Some(ref condition) = command.condition
&& !attr::eval_condition(condition, &tcx.sess, Some(tcx.features()), &mut |cfg| {
let value = cfg.value.map(|v| {
// `with_no_visible_paths` is also used when generating the options,
// so we need to match it here.
ty::print::with_no_visible_paths!(
OnUnimplementedFormatString {
symbol: v,
span: cfg.span,
is_diagnostic_namespace_variant: false
}
.format(
tcx,
trait_ref,
&options_map,
long_ty_file
)
)
});
options.contains(&(cfg.name, value))
})
&& !condition.matches_predicate(tcx, condition_options)
{
debug!("evaluate: skipping {:?} due to condition", command);
continue;
@ -762,14 +768,10 @@ impl<'tcx> OnUnimplementedDirective {
}
OnUnimplementedNote {
label: label.map(|l| l.format(tcx, trait_ref, &options_map, long_ty_file)),
message: message.map(|m| m.format(tcx, trait_ref, &options_map, long_ty_file)),
notes: notes
.into_iter()
.map(|n| n.format(tcx, trait_ref, &options_map, long_ty_file))
.collect(),
parent_label: parent_label
.map(|e_s| e_s.format(tcx, trait_ref, &options_map, long_ty_file)),
label: label.map(|l| l.1.format(tcx, trait_ref, args)),
message: message.map(|m| m.1.format(tcx, trait_ref, args)),
notes: notes.into_iter().map(|n| n.format(tcx, trait_ref, args)).collect(),
parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, args)),
append_const_msg,
}
}
@ -780,142 +782,95 @@ impl<'tcx> OnUnimplementedFormatString {
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
from: Symbol,
value_span: Span,
span: Span,
is_diagnostic_namespace_variant: bool,
) -> Result<Self, ErrorGuaranteed> {
let result = OnUnimplementedFormatString {
symbol: from,
span: value_span,
is_diagnostic_namespace_variant,
};
let result =
OnUnimplementedFormatString { symbol: from, span, is_diagnostic_namespace_variant };
result.verify(tcx, item_def_id)?;
Ok(result)
}
fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> {
let trait_def_id = if tcx.is_trait(item_def_id) {
item_def_id
} else {
tcx.trait_id_of_impl(item_def_id)
.expect("expected `on_unimplemented` to correspond to a trait")
fn verify(&self, tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> {
if !tcx.is_trait(trait_def_id) {
return Ok(());
};
let trait_name = tcx.item_ident(trait_def_id);
let generics = tcx.generics_of(item_def_id);
let s = self.symbol.as_str();
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
let ctx = if self.is_diagnostic_namespace_variant {
Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }
} else {
Ctx::RustcOnUnimplemented { tcx, trait_def_id }
};
let mut result = Ok(());
for token in &mut parser {
match token {
Piece::Lit(_) => (), // Normal string, no need to check it
Piece::NextArgument(a) => {
let format_spec = a.format;
if self.is_diagnostic_namespace_variant
&& (format_spec.ty_span.is_some()
|| format_spec.width_span.is_some()
|| format_spec.precision_span.is_some()
|| format_spec.fill_span.is_some())
{
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
self.span,
InvalidFormatSpecifier,
);
}
match FormatString::parse(self.symbol, self.span, &ctx) {
// Warnings about format specifiers, deprecated parameters, wrong parameters etc.
// In other words we'd like to let the author know, but we can still try to format the string later
Ok(FormatString { warnings, .. }) => {
if self.is_diagnostic_namespace_variant {
for w in warnings {
w.emit_warning(tcx, trait_def_id)
}
match a.position {
Position::ArgumentNamed(s) => {
match Symbol::intern(s) {
// `{ThisTraitsName}` is allowed
s if s == trait_name.name
&& !self.is_diagnostic_namespace_variant =>
{
()
}
s if ALLOWED_FORMAT_SYMBOLS.contains(&s)
&& !self.is_diagnostic_namespace_variant =>
{
()
}
// So is `{A}` if A is a type parameter
s if generics.own_params.iter().any(|param| param.name == s) => (),
s => {
if self.is_diagnostic_namespace_variant {
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
self.span,
UnknownFormatParameterForOnUnimplementedAttr {
argument_name: s,
trait_name,
},
);
}
} else {
result = Err(struct_span_code_err!(
tcx.dcx(),
self.span,
E0230,
"there is no parameter `{}` on {}",
s,
if trait_def_id == item_def_id {
format!("trait `{trait_name}`")
} else {
"impl".to_string()
}
)
.emit());
}
}
}
}
// `{:1}` and `{}` are not to be used
Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
if self.is_diagnostic_namespace_variant {
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
self.span,
DisallowedPositionalArgument,
);
}
} else {
} else {
for w in warnings {
match w {
FormatWarning::UnknownParam { argument_name, span } => {
let reported = struct_span_code_err!(
tcx.dcx(),
self.span,
E0231,
"only named generic parameters are allowed"
span,
E0230,
"cannot find parameter {} on this trait",
argument_name,
)
.emit();
result = Err(reported);
}
FormatWarning::PositionalArgument { span, .. } => {
let reported = struct_span_code_err!(
tcx.dcx(),
span,
E0231,
"positional format arguments are not allowed here"
)
.emit();
result = Err(reported);
}
FormatWarning::InvalidSpecifier { .. }
| FormatWarning::FutureIncompat { .. } => {}
}
}
}
}
}
// we cannot return errors from processing the format string as hard error here
// as the diagnostic namespace guarantees that malformed input cannot cause an error
//
// if we encounter any error while processing we nevertheless want to show it as warning
// so that users are aware that something is not correct
for e in parser.errors {
if self.is_diagnostic_namespace_variant {
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
self.span,
WrappedParserError { description: e.description, label: e.label },
);
// Errors from the underlying `rustc_parse_format::Parser`
Err(errors) => {
// we cannot return errors from processing the format string as hard error here
// as the diagnostic namespace guarantees that malformed input cannot cause an error
//
// if we encounter any error while processing we nevertheless want to show it as warning
// so that users are aware that something is not correct
for e in errors {
if self.is_diagnostic_namespace_variant {
if let Some(trait_def_id) = trait_def_id.as_local() {
tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(trait_def_id),
self.span,
WrappedParserError { description: e.description, label: e.label },
);
}
} else {
let reported = struct_span_code_err!(
tcx.dcx(),
self.span,
E0231,
"{}",
e.description,
)
.emit();
result = Err(reported);
}
}
} else {
let reported =
struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,).emit();
result = Err(reported);
}
}
@ -926,98 +881,28 @@ impl<'tcx> OnUnimplementedFormatString {
&self,
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
options: &FxHashMap<Symbol, String>,
long_ty_file: &mut Option<PathBuf>,
args: &FormatArgs<'tcx>,
) -> String {
let name = tcx.item_name(trait_ref.def_id);
let trait_str = tcx.def_path_str(trait_ref.def_id);
let generics = tcx.generics_of(trait_ref.def_id);
let generic_map = generics
.own_params
.iter()
.filter_map(|param| {
let value = match param.kind {
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
if let Some(ty) = trait_ref.args[param.index as usize].as_type() {
tcx.short_string(ty, long_ty_file)
} else {
trait_ref.args[param.index as usize].to_string()
}
}
GenericParamDefKind::Lifetime => return None,
};
let name = param.name;
Some((name, value))
})
.collect::<FxHashMap<Symbol, String>>();
let empty_string = String::new();
let s = self.symbol.as_str();
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
let constructed_message = (&mut parser)
.map(|p| match p {
Piece::Lit(s) => s.to_owned(),
Piece::NextArgument(a) => match a.position {
Position::ArgumentNamed(arg) => {
let s = Symbol::intern(arg);
match generic_map.get(&s) {
Some(val) => val.to_string(),
None if self.is_diagnostic_namespace_variant => {
format!("{{{arg}}}")
}
None if s == name => trait_str.clone(),
None => {
if let Some(val) = options.get(&s) {
val.clone()
} else if s == sym::from_desugaring {
// don't break messages using these two arguments incorrectly
String::new()
} else if s == sym::ItemContext
&& !self.is_diagnostic_namespace_variant
{
item_context.clone()
} else if s == sym::integral {
String::from("{integral}")
} else if s == sym::integer_ {
String::from("{integer}")
} else if s == sym::float {
String::from("{float}")
} else {
bug!(
"broken on_unimplemented {:?} for {:?}: \
no argument matching {:?}",
self.symbol,
trait_ref,
s
)
}
}
}
}
Position::ArgumentImplicitlyIs(_) if self.is_diagnostic_namespace_variant => {
String::from("{}")
}
Position::ArgumentIs(idx) if self.is_diagnostic_namespace_variant => {
format!("{{{idx}}}")
}
_ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol),
},
})
.collect();
// we cannot return errors from processing the format string as hard error here
// as the diagnostic namespace guarantees that malformed input cannot cause an error
//
// if we encounter any error while processing the format string
// we don't want to show the potentially half assembled formatted string,
// therefore we fall back to just showing the input string in this case
//
// The actual parser errors are emitted earlier
// as lint warnings in OnUnimplementedFormatString::verify
if self.is_diagnostic_namespace_variant && !parser.errors.is_empty() {
String::from(s)
let trait_def_id = trait_ref.def_id;
let ctx = if self.is_diagnostic_namespace_variant {
Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }
} else {
constructed_message
Ctx::RustcOnUnimplemented { tcx, trait_def_id }
};
if let Ok(s) = FormatString::parse(self.symbol, self.span, &ctx) {
s.format(args)
} else {
// we cannot return errors from processing the format string as hard error here
// as the diagnostic namespace guarantees that malformed input cannot cause an error
//
// if we encounter any error while processing the format string
// we don't want to show the potentially half assembled formatted string,
// therefore we fall back to just showing the input string in this case
//
// The actual parser errors are emitted earlier
// as lint warnings in OnUnimplementedFormatString::verify
self.symbol.as_str().into()
}
}
}

View file

@ -0,0 +1,120 @@
use rustc_ast::MetaItemInner;
use rustc_attr_parsing as attr;
use rustc_middle::ty::{self, TyCtxt};
use rustc_parse_format::{ParseMode, Parser, Piece, Position};
use rustc_span::{DesugaringKind, Span, Symbol, kw, sym};
/// A predicate in an attribute using on, all, any,
/// similar to a cfg predicate.
#[derive(Debug)]
pub struct Condition {
pub inner: MetaItemInner,
}
impl Condition {
pub fn span(&self) -> Span {
self.inner.span()
}
pub fn matches_predicate<'tcx>(&self, tcx: TyCtxt<'tcx>, options: &ConditionOptions) -> bool {
attr::eval_condition(&self.inner, tcx.sess, Some(tcx.features()), &mut |cfg| {
let value = cfg.value.map(|v| {
// `with_no_visible_paths` is also used when generating the options,
// so we need to match it here.
ty::print::with_no_visible_paths!({
Parser::new(v.as_str(), None, None, false, ParseMode::Format)
.map(|p| match p {
Piece::Lit(s) => s.to_owned(),
Piece::NextArgument(a) => match a.position {
Position::ArgumentNamed(arg) => {
let s = Symbol::intern(arg);
match options.generic_args.iter().find(|(k, _)| *k == s) {
Some((_, val)) => val.to_string(),
None => format!("{{{arg}}}"),
}
}
Position::ArgumentImplicitlyIs(_) => String::from("{}"),
Position::ArgumentIs(idx) => format!("{{{idx}}}"),
},
})
.collect()
})
});
options.contains(cfg.name, &value)
})
}
}
/// Used with `Condition::matches_predicate` to test whether the condition applies
///
/// For example, given a
/// ```rust,ignore (just an example)
/// #[rustc_on_unimplemented(
/// on(all(from_desugaring = "QuestionMark"),
/// message = "the `?` operator can only be used in {ItemContext} \
/// that returns `Result` or `Option` \
/// (or another type that implements `{FromResidual}`)",
/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`",
/// parent_label = "this function should return `Result` or `Option` to accept `?`"
/// ),
/// )]
/// pub trait FromResidual<R = <Self as Try>::Residual> {
/// ...
/// }
///
/// async fn an_async_function() -> u32 {
/// let x: Option<u32> = None;
/// x?; //~ ERROR the `?` operator
/// 22
/// }
/// ```
/// it will look like this:
///
/// ```rust,ignore (just an example)
/// ConditionOptions {
/// self_types: ["u32", "{integral}"],
/// from_desugaring: Some("QuestionMark"),
/// cause: None,
/// crate_local: false,
/// direct: true,
/// generic_args: [("Self","u32"),
/// ("R", "core::option::Option<core::convert::Infallible>"),
/// ("R", "core::option::Option<T>" ),
/// ],
/// }
/// ```
#[derive(Debug)]
pub struct ConditionOptions {
/// All the self types that may apply.
/// for example
pub self_types: Vec<String>,
// The kind of compiler desugaring.
pub from_desugaring: Option<DesugaringKind>,
/// Match on a variant of [rustc_infer::traits::ObligationCauseCode]
pub cause: Option<String>,
pub crate_local: bool,
/// Is the obligation "directly" user-specified, rather than derived?
pub direct: bool,
// A list of the generic arguments and their reified types
pub generic_args: Vec<(Symbol, String)>,
}
impl ConditionOptions {
pub fn contains(&self, key: Symbol, value: &Option<String>) -> bool {
match (key, value) {
(sym::_Self | kw::SelfUpper, Some(value)) => self.self_types.contains(&value),
// from_desugaring as a flag
(sym::from_desugaring, None) => self.from_desugaring.is_some(),
// from_desugaring as key == value
(sym::from_desugaring, Some(v)) if let Some(ds) = self.from_desugaring => ds.matches(v),
(sym::cause, Some(value)) => self.cause.as_deref() == Some(value),
(sym::crate_local, None) => self.crate_local,
(sym::direct, None) => self.direct,
(other, Some(value)) => {
self.generic_args.iter().any(|(k, v)| *k == other && v == value)
}
_ => false,
}
}
}

View file

@ -0,0 +1,414 @@
use std::fmt;
use errors::*;
use rustc_middle::ty::TyCtxt;
use rustc_middle::ty::print::TraitRefPrintSugared;
use rustc_parse_format::{
Alignment, Argument, Count, FormatSpec, InnerSpan, ParseError, ParseMode, Parser,
Piece as RpfPiece, Position,
};
use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
use rustc_span::def_id::DefId;
use rustc_span::{BytePos, Pos, Span, Symbol, kw, sym};
/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces",
/// either as string pieces or dynamic arguments.
#[derive(Debug)]
pub struct FormatString {
#[allow(dead_code, reason = "Debug impl")]
input: Symbol,
span: Span,
pieces: Vec<Piece>,
/// The formatting string was parsed succesfully but with warnings
pub warnings: Vec<FormatWarning>,
}
#[derive(Debug)]
enum Piece {
Lit(String),
Arg(FormatArg),
}
#[derive(Debug)]
enum FormatArg {
// A generic parameter, like `{T}` if we're on the `From<T>` trait.
GenericParam {
generic_param: Symbol,
},
// `{Self}`
SelfUpper,
/// `{This}` or `{TraitName}`
This,
/// The sugared form of the trait
Trait,
/// what we're in, like a function, method, closure etc.
ItemContext,
/// What the user typed, if it doesn't match anything we can use.
AsIs(String),
}
pub enum Ctx<'tcx> {
// `#[rustc_on_unimplemented]`
RustcOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId },
// `#[diagnostic::...]`
DiagnosticOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId },
}
#[derive(Debug)]
pub enum FormatWarning {
UnknownParam { argument_name: Symbol, span: Span },
PositionalArgument { span: Span, help: String },
InvalidSpecifier { name: String, span: Span },
FutureIncompat { span: Span, help: String },
}
impl FormatWarning {
pub fn emit_warning<'tcx>(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) {
match *self {
FormatWarning::UnknownParam { argument_name, span } => {
let this = tcx.item_ident(item_def_id);
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
span,
UnknownFormatParameterForOnUnimplementedAttr {
argument_name,
trait_name: this,
},
);
}
}
FormatWarning::PositionalArgument { span, .. } => {
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
span,
DisallowedPositionalArgument,
);
}
}
FormatWarning::InvalidSpecifier { span, .. } => {
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
span,
InvalidFormatSpecifier,
);
}
}
FormatWarning::FutureIncompat { .. } => {
// We've never deprecated anything in diagnostic namespace format strings
// but if we do we will emit a warning here
// FIXME(mejrs) in a couple releases, start emitting warnings for
// #[rustc_on_unimplemented] deprecated args
}
}
}
}
/// Arguments to fill a [FormatString] with.
///
/// For example, given a
/// ```rust,ignore (just an example)
///
/// #[rustc_on_unimplemented(
/// on(all(from_desugaring = "QuestionMark"),
/// message = "the `?` operator can only be used in {ItemContext} \
/// that returns `Result` or `Option` \
/// (or another type that implements `{FromResidual}`)",
/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`",
/// parent_label = "this function should return `Result` or `Option` to accept `?`"
/// ),
/// )]
/// pub trait FromResidual<R = <Self as Try>::Residual> {
/// ...
/// }
///
/// async fn an_async_function() -> u32 {
/// let x: Option<u32> = None;
/// x?; //~ ERROR the `?` operator
/// 22
/// }
/// ```
/// it will look like this:
///
/// ```rust,ignore (just an example)
/// FormatArgs {
/// this: "FromResidual",
/// trait_sugared: "FromResidual<Option<Infallible>>",
/// item_context: "an async function",
/// generic_args: [("Self", "u32"), ("R", "Option<Infallible>")],
/// }
/// ```
#[derive(Debug)]
pub struct FormatArgs<'tcx> {
pub this: String,
pub trait_sugared: TraitRefPrintSugared<'tcx>,
pub item_context: &'static str,
pub generic_args: Vec<(Symbol, String)>,
}
impl FormatString {
pub fn span(&self) -> Span {
self.span
}
pub fn parse<'tcx>(
input: Symbol,
span: Span,
ctx: &Ctx<'tcx>,
) -> Result<Self, Vec<ParseError>> {
let s = input.as_str();
let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
let mut pieces = Vec::new();
let mut warnings = Vec::new();
for piece in &mut parser {
match piece {
RpfPiece::Lit(lit) => {
pieces.push(Piece::Lit(lit.into()));
}
RpfPiece::NextArgument(arg) => {
warn_on_format_spec(arg.format, &mut warnings, span);
let arg = parse_arg(&arg, ctx, &mut warnings, span);
pieces.push(Piece::Arg(arg));
}
}
}
if parser.errors.is_empty() {
Ok(FormatString { input, pieces, span, warnings })
} else {
Err(parser.errors)
}
}
pub fn format(&self, args: &FormatArgs<'_>) -> String {
let mut ret = String::new();
for piece in &self.pieces {
match piece {
Piece::Lit(s) | Piece::Arg(FormatArg::AsIs(s)) => ret.push_str(&s),
// `A` if we have `trait Trait<A> {}` and `note = "i'm the actual type of {A}"`
Piece::Arg(FormatArg::GenericParam { generic_param }) => {
// Should always be some but we can't raise errors here
let value = match args.generic_args.iter().find(|(p, _)| p == generic_param) {
Some((_, val)) => val.to_string(),
None => generic_param.to_string(),
};
ret.push_str(&value);
}
// `{Self}`
Piece::Arg(FormatArg::SelfUpper) => {
let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) {
Some((_, val)) => val.to_string(),
None => "Self".to_string(),
};
ret.push_str(&slf);
}
// It's only `rustc_onunimplemented` from here
Piece::Arg(FormatArg::This) => ret.push_str(&args.this),
Piece::Arg(FormatArg::Trait) => {
let _ = fmt::write(&mut ret, format_args!("{}", &args.trait_sugared));
}
Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context),
}
}
ret
}
}
fn parse_arg<'tcx>(
arg: &Argument<'_>,
ctx: &Ctx<'tcx>,
warnings: &mut Vec<FormatWarning>,
input_span: Span,
) -> FormatArg {
let (Ctx::RustcOnUnimplemented { tcx, trait_def_id }
| Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = ctx;
let trait_name = tcx.item_ident(*trait_def_id);
let generics = tcx.generics_of(trait_def_id);
let span = slice_span(input_span, arg.position_span);
match arg.position {
// Something like "hello {name}"
Position::ArgumentNamed(name) => match (ctx, Symbol::intern(name)) {
// accepted, but deprecated
(Ctx::RustcOnUnimplemented { .. }, sym::_Self) => {
warnings
.push(FormatWarning::FutureIncompat { span, help: String::from("use {Self}") });
FormatArg::SelfUpper
}
(
Ctx::RustcOnUnimplemented { .. },
sym::from_desugaring
| sym::crate_local
| sym::direct
| sym::cause
| sym::float
| sym::integer_
| sym::integral,
) => {
warnings.push(FormatWarning::FutureIncompat {
span,
help: String::from("don't use this in a format string"),
});
FormatArg::AsIs(String::new())
}
// Only `#[rustc_on_unimplemented]` can use these
(Ctx::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext,
(Ctx::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This,
(Ctx::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait,
// `{ThisTraitsName}`. Some attrs in std use this, but I'd like to change it to the more general `{This}`
// because that'll be simpler to parse and extend in the future
(Ctx::RustcOnUnimplemented { .. }, name) if name == trait_name.name => {
warnings
.push(FormatWarning::FutureIncompat { span, help: String::from("use {This}") });
FormatArg::This
}
// Any attribute can use these
(
Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
kw::SelfUpper,
) => FormatArg::SelfUpper,
(
Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
generic_param,
) if generics.own_params.iter().any(|param| param.name == generic_param) => {
FormatArg::GenericParam { generic_param }
}
(_, argument_name) => {
warnings.push(FormatWarning::UnknownParam { argument_name, span });
FormatArg::AsIs(format!("{{{}}}", argument_name.as_str()))
}
},
// `{:1}` and `{}` are ignored
Position::ArgumentIs(idx) => {
warnings.push(FormatWarning::PositionalArgument {
span,
help: format!("use `{{{idx}}}` to print a number in braces"),
});
FormatArg::AsIs(format!("{{{idx}}}"))
}
Position::ArgumentImplicitlyIs(_) => {
warnings.push(FormatWarning::PositionalArgument {
span,
help: String::from("use `{{}}` to print empty braces"),
});
FormatArg::AsIs(String::from("{}"))
}
}
}
/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything
/// with specifiers, so emit a warning if they are used.
fn warn_on_format_spec(spec: FormatSpec<'_>, warnings: &mut Vec<FormatWarning>, input_span: Span) {
if !matches!(
spec,
FormatSpec {
fill: None,
fill_span: None,
align: Alignment::AlignUnknown,
sign: None,
alternate: false,
zero_pad: false,
debug_hex: None,
precision: Count::CountImplied,
precision_span: None,
width: Count::CountImplied,
width_span: None,
ty: _,
ty_span: _,
},
) {
let span = spec.ty_span.map(|inner| slice_span(input_span, inner)).unwrap_or(input_span);
warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() })
}
}
/// Helper function because `Span` and `rustc_parse_format::InnerSpan` don't know about each other
fn slice_span(input: Span, inner: InnerSpan) -> Span {
let InnerSpan { start, end } = inner;
let span = input.data();
Span::new(
span.lo + BytePos::from_usize(start),
span.lo + BytePos::from_usize(end),
span.ctxt,
span.parent,
)
}
pub mod errors {
use rustc_macros::LintDiagnostic;
use rustc_span::Ident;
use super::*;
#[derive(LintDiagnostic)]
#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
#[help]
pub struct UnknownFormatParameterForOnUnimplementedAttr {
pub argument_name: Symbol,
pub trait_name: Ident,
}
#[derive(LintDiagnostic)]
#[diag(trait_selection_disallowed_positional_argument)]
#[help]
pub struct DisallowedPositionalArgument;
#[derive(LintDiagnostic)]
#[diag(trait_selection_invalid_format_specifier)]
#[help]
pub struct InvalidFormatSpecifier;
#[derive(LintDiagnostic)]
#[diag(trait_selection_missing_options_for_on_unimplemented_attr)]
#[help]
pub struct MissingOptionsForOnUnimplementedAttr;
#[derive(LintDiagnostic)]
#[diag(trait_selection_ignored_diagnostic_option)]
pub struct IgnoredDiagnosticOption {
pub option_name: &'static str,
#[label]
pub span: Span,
#[label(trait_selection_other_label)]
pub prev_span: Span,
}
impl IgnoredDiagnosticOption {
pub fn maybe_emit_warning<'tcx>(
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
new: Option<Span>,
old: Option<Span>,
option_name: &'static str,
) {
if let (Some(new_item), Some(old_item)) = (new, old) {
if let Some(item_def_id) = item_def_id.as_local() {
tcx.emit_node_span_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.local_def_id_to_hir_id(item_def_id),
new_item,
IgnoredDiagnosticOption {
span: new_item,
prev_span: old_item,
option_name,
},
);
}
}
}
}
}

View file

@ -77,7 +77,15 @@ impl<'tcx> At<'_, 'tcx> {
.into_value_registering_obligations(self.infcx, &mut *fulfill_cx);
let errors = fulfill_cx.select_all_or_error(self.infcx);
let value = self.infcx.resolve_vars_if_possible(value);
if errors.is_empty() { Ok(value) } else { Err(errors) }
if errors.is_empty() {
Ok(value)
} else {
// Drop pending obligations, since deep normalization may happen
// in a loop and we don't want to trigger the assertion on the next
// iteration due to pending ambiguous obligations we've left over.
let _ = fulfill_cx.collect_remaining_errors(self.infcx);
Err(errors)
}
}
}
}

View file

@ -1,19 +1,18 @@
use std::fmt;
use std::sync::atomic::{AtomicU32, Ordering};
use tracing::instrument;
use super::{Byte, Nfa, Ref, nfa};
use super::{Byte, Ref, Tree, Uninhabited};
use crate::Map;
#[derive(PartialEq, Clone, Debug)]
#[derive(PartialEq)]
#[cfg_attr(test, derive(Clone))]
pub(crate) struct Dfa<R>
where
R: Ref,
{
pub(crate) transitions: Map<State, Transitions<R>>,
pub(crate) start: State,
pub(crate) accepting: State,
pub(crate) accept: State,
}
#[derive(PartialEq, Clone, Debug)]
@ -34,35 +33,15 @@ where
}
}
impl<R> Transitions<R>
where
R: Ref,
{
#[cfg(test)]
fn insert(&mut self, transition: Transition<R>, state: State) {
match transition {
Transition::Byte(b) => {
self.byte_transitions.insert(b, state);
}
Transition::Ref(r) => {
self.ref_transitions.insert(r, state);
}
}
}
}
/// The states in a `Nfa` represent byte offsets.
/// The states in a [`Dfa`] represent byte offsets.
#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Copy, Clone)]
pub(crate) struct State(u32);
pub(crate) struct State(pub(crate) u32);
#[cfg(test)]
#[derive(Hash, Eq, PartialEq, Clone, Copy)]
pub(crate) enum Transition<R>
where
R: Ref,
{
Byte(Byte),
Ref(R),
impl State {
pub(crate) fn new() -> Self {
static COUNTER: AtomicU32 = AtomicU32::new(0);
Self(COUNTER.fetch_add(1, Ordering::SeqCst))
}
}
impl fmt::Debug for State {
@ -71,19 +50,6 @@ impl fmt::Debug for State {
}
}
#[cfg(test)]
impl<R> fmt::Debug for Transition<R>
where
R: Ref,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Self::Byte(b) => b.fmt(f),
Self::Ref(r) => r.fmt(f),
}
}
}
impl<R> Dfa<R>
where
R: Ref,
@ -92,60 +58,167 @@ where
pub(crate) fn bool() -> Self {
let mut transitions: Map<State, Transitions<R>> = Map::default();
let start = State::new();
let accepting = State::new();
let accept = State::new();
transitions.entry(start).or_default().insert(Transition::Byte(Byte::Init(0x00)), accepting);
transitions.entry(start).or_default().byte_transitions.insert(Byte::Init(0x00), accept);
transitions.entry(start).or_default().insert(Transition::Byte(Byte::Init(0x01)), accepting);
transitions.entry(start).or_default().byte_transitions.insert(Byte::Init(0x01), accept);
Self { transitions, start, accepting }
Self { transitions, start, accept }
}
#[instrument(level = "debug")]
pub(crate) fn from_nfa(nfa: Nfa<R>) -> Self {
let Nfa { transitions: nfa_transitions, start: nfa_start, accepting: nfa_accepting } = nfa;
pub(crate) fn unit() -> Self {
let transitions: Map<State, Transitions<R>> = Map::default();
let start = State::new();
let accept = start;
let mut dfa_transitions: Map<State, Transitions<R>> = Map::default();
let mut nfa_to_dfa: Map<nfa::State, State> = Map::default();
let dfa_start = State::new();
nfa_to_dfa.insert(nfa_start, dfa_start);
Self { transitions, start, accept }
}
let mut queue = vec![(nfa_start, dfa_start)];
pub(crate) fn from_byte(byte: Byte) -> Self {
let mut transitions: Map<State, Transitions<R>> = Map::default();
let start = State::new();
let accept = State::new();
while let Some((nfa_state, dfa_state)) = queue.pop() {
if nfa_state == nfa_accepting {
continue;
transitions.entry(start).or_default().byte_transitions.insert(byte, accept);
Self { transitions, start, accept }
}
pub(crate) fn from_ref(r: R) -> Self {
let mut transitions: Map<State, Transitions<R>> = Map::default();
let start = State::new();
let accept = State::new();
transitions.entry(start).or_default().ref_transitions.insert(r, accept);
Self { transitions, start, accept }
}
pub(crate) fn from_tree(tree: Tree<!, R>) -> Result<Self, Uninhabited> {
Ok(match tree {
Tree::Byte(b) => Self::from_byte(b),
Tree::Ref(r) => Self::from_ref(r),
Tree::Alt(alts) => {
// Convert and filter the inhabited alternatives.
let mut alts = alts.into_iter().map(Self::from_tree).filter_map(Result::ok);
// If there are no alternatives, return `Uninhabited`.
let dfa = alts.next().ok_or(Uninhabited)?;
// Combine the remaining alternatives with `dfa`.
alts.fold(dfa, |dfa, alt| dfa.union(alt, State::new))
}
Tree::Seq(elts) => {
let mut dfa = Self::unit();
for elt in elts.into_iter().map(Self::from_tree) {
dfa = dfa.concat(elt?);
}
dfa
}
})
}
/// Concatenate two `Dfa`s.
pub(crate) fn concat(self, other: Self) -> Self {
if self.start == self.accept {
return other;
} else if other.start == other.accept {
return self;
}
let start = self.start;
let accept = other.accept;
let mut transitions: Map<State, Transitions<R>> = self.transitions;
for (source, transition) in other.transitions {
let fix_state = |state| if state == other.start { self.accept } else { state };
let entry = transitions.entry(fix_state(source)).or_default();
for (edge, destination) in transition.byte_transitions {
entry.byte_transitions.insert(edge, fix_state(destination));
}
for (edge, destination) in transition.ref_transitions {
entry.ref_transitions.insert(edge, fix_state(destination));
}
}
Self { transitions, start, accept }
}
/// Compute the union of two `Dfa`s.
pub(crate) fn union(self, other: Self, mut new_state: impl FnMut() -> State) -> Self {
// We implement `union` by lazily initializing a set of states
// corresponding to the product of states in `self` and `other`, and
// then add transitions between these states that correspond to where
// they exist between `self` and `other`.
let a = self;
let b = other;
let accept = new_state();
let mut mapping: Map<(Option<State>, Option<State>), State> = Map::default();
let mut mapped = |(a_state, b_state)| {
if Some(a.accept) == a_state || Some(b.accept) == b_state {
// If either `a_state` or `b_state` are accepting, map to a
// common `accept` state.
accept
} else {
*mapping.entry((a_state, b_state)).or_insert_with(&mut new_state)
}
};
let start = mapped((Some(a.start), Some(b.start)));
let mut transitions: Map<State, Transitions<R>> = Map::default();
let mut queue = vec![(Some(a.start), Some(b.start))];
let empty_transitions = Transitions::default();
while let Some((a_src, b_src)) = queue.pop() {
let a_transitions =
a_src.and_then(|a_src| a.transitions.get(&a_src)).unwrap_or(&empty_transitions);
let b_transitions =
b_src.and_then(|b_src| b.transitions.get(&b_src)).unwrap_or(&empty_transitions);
let byte_transitions =
a_transitions.byte_transitions.keys().chain(b_transitions.byte_transitions.keys());
for byte_transition in byte_transitions {
let a_dst = a_transitions.byte_transitions.get(byte_transition).copied();
let b_dst = b_transitions.byte_transitions.get(byte_transition).copied();
assert!(a_dst.is_some() || b_dst.is_some());
let src = mapped((a_src, b_src));
let dst = mapped((a_dst, b_dst));
transitions.entry(src).or_default().byte_transitions.insert(*byte_transition, dst);
if !transitions.contains_key(&dst) {
queue.push((a_dst, b_dst))
}
}
for (nfa_transition, next_nfa_states) in nfa_transitions[&nfa_state].iter() {
let dfa_transitions =
dfa_transitions.entry(dfa_state).or_insert_with(Default::default);
let ref_transitions =
a_transitions.ref_transitions.keys().chain(b_transitions.ref_transitions.keys());
let mapped_state = next_nfa_states.iter().find_map(|x| nfa_to_dfa.get(x).copied());
for ref_transition in ref_transitions {
let a_dst = a_transitions.ref_transitions.get(ref_transition).copied();
let b_dst = b_transitions.ref_transitions.get(ref_transition).copied();
let next_dfa_state = match nfa_transition {
&nfa::Transition::Byte(b) => *dfa_transitions
.byte_transitions
.entry(b)
.or_insert_with(|| mapped_state.unwrap_or_else(State::new)),
&nfa::Transition::Ref(r) => *dfa_transitions
.ref_transitions
.entry(r)
.or_insert_with(|| mapped_state.unwrap_or_else(State::new)),
};
assert!(a_dst.is_some() || b_dst.is_some());
for &next_nfa_state in next_nfa_states {
nfa_to_dfa.entry(next_nfa_state).or_insert_with(|| {
queue.push((next_nfa_state, next_dfa_state));
next_dfa_state
});
let src = mapped((a_src, b_src));
let dst = mapped((a_dst, b_dst));
transitions.entry(src).or_default().ref_transitions.insert(*ref_transition, dst);
if !transitions.contains_key(&dst) {
queue.push((a_dst, b_dst))
}
}
}
let dfa_accepting = nfa_to_dfa[&nfa_accepting];
Self { transitions: dfa_transitions, start: dfa_start, accepting: dfa_accepting }
Self { transitions, start, accept }
}
pub(crate) fn bytes_from(&self, start: State) -> Option<&Map<Byte, State>> {
@ -159,24 +232,48 @@ where
pub(crate) fn refs_from(&self, start: State) -> Option<&Map<R, State>> {
Some(&self.transitions.get(&start)?.ref_transitions)
}
}
impl State {
pub(crate) fn new() -> Self {
static COUNTER: AtomicU32 = AtomicU32::new(0);
Self(COUNTER.fetch_add(1, Ordering::SeqCst))
#[cfg(test)]
pub(crate) fn from_edges<B: Copy + Into<Byte>>(
start: u32,
accept: u32,
edges: &[(u32, B, u32)],
) -> Self {
let start = State(start);
let accept = State(accept);
let mut transitions: Map<State, Transitions<R>> = Map::default();
for &(src, edge, dst) in edges {
let src = State(src);
let dst = State(dst);
let old = transitions.entry(src).or_default().byte_transitions.insert(edge.into(), dst);
assert!(old.is_none());
}
Self { start, accept, transitions }
}
}
#[cfg(test)]
impl<R> From<nfa::Transition<R>> for Transition<R>
/// Serialize the DFA using the Graphviz DOT format.
impl<R> fmt::Debug for Dfa<R>
where
R: Ref,
{
fn from(nfa_transition: nfa::Transition<R>) -> Self {
match nfa_transition {
nfa::Transition::Byte(byte) => Transition::Byte(byte),
nfa::Transition::Ref(r) => Transition::Ref(r),
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "digraph {{")?;
writeln!(f, " {:?} [shape = doublecircle]", self.start)?;
writeln!(f, " {:?} [shape = doublecircle]", self.accept)?;
for (src, transitions) in self.transitions.iter() {
for (t, dst) in transitions.byte_transitions.iter() {
writeln!(f, " {src:?} -> {dst:?} [label=\"{t:?}\"]")?;
}
for (t, dst) in transitions.ref_transitions.iter() {
writeln!(f, " {src:?} -> {dst:?} [label=\"{t:?}\"]")?;
}
}
writeln!(f, "}}")
}
}

View file

@ -4,9 +4,6 @@ use std::hash::Hash;
pub(crate) mod tree;
pub(crate) use tree::Tree;
pub(crate) mod nfa;
pub(crate) use nfa::Nfa;
pub(crate) mod dfa;
pub(crate) use dfa::Dfa;
@ -29,6 +26,13 @@ impl fmt::Debug for Byte {
}
}
#[cfg(test)]
impl From<u8> for Byte {
fn from(src: u8) -> Self {
Self::Init(src)
}
}
pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {
fn has_safety_invariants(&self) -> bool;
}

View file

@ -1,169 +0,0 @@
use std::fmt;
use std::sync::atomic::{AtomicU32, Ordering};
use super::{Byte, Ref, Tree, Uninhabited};
use crate::{Map, Set};
/// A non-deterministic finite automaton (NFA) that represents the layout of a type.
/// The transmutability of two given types is computed by comparing their `Nfa`s.
#[derive(PartialEq, Debug)]
pub(crate) struct Nfa<R>
where
R: Ref,
{
pub(crate) transitions: Map<State, Map<Transition<R>, Set<State>>>,
pub(crate) start: State,
pub(crate) accepting: State,
}
/// The states in a `Nfa` represent byte offsets.
#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Copy, Clone)]
pub(crate) struct State(u32);
/// The transitions between states in a `Nfa` reflect bit validity.
#[derive(Hash, Eq, PartialEq, Clone, Copy)]
pub(crate) enum Transition<R>
where
R: Ref,
{
Byte(Byte),
Ref(R),
}
impl fmt::Debug for State {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "S_{}", self.0)
}
}
impl<R> fmt::Debug for Transition<R>
where
R: Ref,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Self::Byte(b) => b.fmt(f),
Self::Ref(r) => r.fmt(f),
}
}
}
impl<R> Nfa<R>
where
R: Ref,
{
pub(crate) fn unit() -> Self {
let transitions: Map<State, Map<Transition<R>, Set<State>>> = Map::default();
let start = State::new();
let accepting = start;
Nfa { transitions, start, accepting }
}
pub(crate) fn from_byte(byte: Byte) -> Self {
let mut transitions: Map<State, Map<Transition<R>, Set<State>>> = Map::default();
let start = State::new();
let accepting = State::new();
let source = transitions.entry(start).or_default();
let edge = source.entry(Transition::Byte(byte)).or_default();
edge.insert(accepting);
Nfa { transitions, start, accepting }
}
pub(crate) fn from_ref(r: R) -> Self {
let mut transitions: Map<State, Map<Transition<R>, Set<State>>> = Map::default();
let start = State::new();
let accepting = State::new();
let source = transitions.entry(start).or_default();
let edge = source.entry(Transition::Ref(r)).or_default();
edge.insert(accepting);
Nfa { transitions, start, accepting }
}
pub(crate) fn from_tree(tree: Tree<!, R>) -> Result<Self, Uninhabited> {
Ok(match tree {
Tree::Byte(b) => Self::from_byte(b),
Tree::Ref(r) => Self::from_ref(r),
Tree::Alt(alts) => {
let mut alts = alts.into_iter().map(Self::from_tree);
let mut nfa = alts.next().ok_or(Uninhabited)??;
for alt in alts {
nfa = nfa.union(alt?);
}
nfa
}
Tree::Seq(elts) => {
let mut nfa = Self::unit();
for elt in elts.into_iter().map(Self::from_tree) {
nfa = nfa.concat(elt?);
}
nfa
}
})
}
/// Concatenate two `Nfa`s.
pub(crate) fn concat(self, other: Self) -> Self {
if self.start == self.accepting {
return other;
} else if other.start == other.accepting {
return self;
}
let start = self.start;
let accepting = other.accepting;
let mut transitions: Map<State, Map<Transition<R>, Set<State>>> = self.transitions;
for (source, transition) in other.transitions {
let fix_state = |state| if state == other.start { self.accepting } else { state };
let entry = transitions.entry(fix_state(source)).or_default();
for (edge, destinations) in transition {
let entry = entry.entry(edge).or_default();
for destination in destinations {
entry.insert(fix_state(destination));
}
}
}
Self { transitions, start, accepting }
}
/// Compute the union of two `Nfa`s.
pub(crate) fn union(self, other: Self) -> Self {
let start = self.start;
let accepting = self.accepting;
let mut transitions: Map<State, Map<Transition<R>, Set<State>>> = self.transitions.clone();
for (&(mut source), transition) in other.transitions.iter() {
// if source is starting state of `other`, replace with starting state of `self`
if source == other.start {
source = self.start;
}
let entry = transitions.entry(source).or_default();
for (edge, destinations) in transition {
let entry = entry.entry(*edge).or_default();
for &(mut destination) in destinations {
// if dest is accepting state of `other`, replace with accepting state of `self`
if destination == other.accepting {
destination = self.accepting;
}
entry.insert(destination);
}
}
}
Self { transitions, start, accepting }
}
}
impl State {
pub(crate) fn new() -> Self {
static COUNTER: AtomicU32 = AtomicU32::new(0);
Self(COUNTER.fetch_add(1, Ordering::SeqCst))
}
}

View file

@ -514,7 +514,7 @@ pub(crate) mod rustc {
}
}
ty::Tuple(fields) => fields[i.as_usize()],
kind @ _ => unimplemented!(
kind => unimplemented!(
"only a subset of `Ty::ty_and_layout_field`'s functionality is implemented. implementation needed for {:?}",
kind
),

View file

@ -2,7 +2,7 @@
#![feature(never_type)]
// tidy-alphabetical-end
pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set};
pub(crate) use rustc_data_structures::fx::FxIndexMap as Map;
pub mod layout;
mod maybe_transmutable;

View file

@ -4,7 +4,7 @@ pub(crate) mod query_context;
#[cfg(test)]
mod tests;
use crate::layout::{self, Byte, Def, Dfa, Nfa, Ref, Tree, Uninhabited, dfa};
use crate::layout::{self, Byte, Def, Dfa, Ref, Tree, Uninhabited, dfa};
use crate::maybe_transmutable::query_context::QueryContext;
use crate::{Answer, Condition, Map, Reason};
@ -73,7 +73,7 @@ where
/// Answers whether a `Tree` is transmutable into another `Tree`.
///
/// This method begins by de-def'ing `src` and `dst`, and prunes private paths from `dst`,
/// then converts `src` and `dst` to `Nfa`s, and computes an answer using those NFAs.
/// then converts `src` and `dst` to `Dfa`s, and computes an answer using those DFAs.
#[inline(always)]
#[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
@ -105,22 +105,22 @@ where
trace!(?dst, "pruned dst");
// Convert `src` from a tree-based representation to an NFA-based
// Convert `src` from a tree-based representation to an DFA-based
// representation. If the conversion fails because `src` is uninhabited,
// conclude that the transmutation is acceptable, because instances of
// the `src` type do not exist.
let src = match Nfa::from_tree(src) {
let src = match Dfa::from_tree(src) {
Ok(src) => src,
Err(Uninhabited) => return Answer::Yes,
};
// Convert `dst` from a tree-based representation to an NFA-based
// Convert `dst` from a tree-based representation to an DFA-based
// representation. If the conversion fails because `src` is uninhabited,
// conclude that the transmutation is unacceptable. Valid instances of
// the `dst` type do not exist, either because it's genuinely
// uninhabited, or because there are no branches of the tree that are
// free of safety invariants.
let dst = match Nfa::from_tree(dst) {
let dst = match Dfa::from_tree(dst) {
Ok(dst) => dst,
Err(Uninhabited) => return Answer::No(Reason::DstMayHaveSafetyInvariants),
};
@ -129,23 +129,6 @@ where
}
}
impl<C> MaybeTransmutableQuery<Nfa<<C as QueryContext>::Ref>, C>
where
C: QueryContext,
{
/// Answers whether a `Nfa` is transmutable into another `Nfa`.
///
/// This method converts `src` and `dst` to DFAs, then computes an answer using those DFAs.
#[inline(always)]
#[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
let Self { src, dst, assume, context } = self;
let src = Dfa::from_nfa(src);
let dst = Dfa::from_nfa(dst);
MaybeTransmutableQuery { src, dst, assume, context }.answer()
}
}
impl<C> MaybeTransmutableQuery<Dfa<<C as QueryContext>::Ref>, C>
where
C: QueryContext,
@ -173,7 +156,7 @@ where
src_transitions_len = self.src.transitions.len(),
dst_transitions_len = self.dst.transitions.len()
);
let answer = if dst_state == self.dst.accepting {
let answer = if dst_state == self.dst.accept {
// truncation: `size_of(Src) >= size_of(Dst)`
//
// Why is truncation OK to do? Because even though the Src is bigger, all we care about
@ -190,7 +173,7 @@ where
// that none of the actually-used data can introduce an invalid state for Dst's type, we
// are able to safely transmute, even with truncation.
Answer::Yes
} else if src_state == self.src.accepting {
} else if src_state == self.src.accept {
// extension: `size_of(Src) >= size_of(Dst)`
if let Some(dst_state_prime) = self.dst.byte_from(dst_state, Byte::Uninit) {
self.answer_memo(cache, src_state, dst_state_prime)

View file

@ -126,7 +126,7 @@ mod bool {
let into_set = |alts: Vec<_>| {
#[cfg(feature = "rustc")]
let mut set = crate::Set::default();
let mut set = rustc_data_structures::fx::FxIndexSet::default();
#[cfg(not(feature = "rustc"))]
let mut set = std::collections::HashSet::new();
set.extend(alts);
@ -174,3 +174,32 @@ mod bool {
}
}
}
mod union {
use super::*;
#[test]
fn union() {
let [a, b, c, d] = [0, 1, 2, 3];
let s = Dfa::from_edges(a, d, &[(a, 0, b), (b, 0, d), (a, 1, c), (c, 1, d)]);
let t = Dfa::from_edges(a, c, &[(a, 1, b), (b, 0, c)]);
let mut ctr = 0;
let new_state = || {
let state = crate::layout::dfa::State(ctr);
ctr += 1;
state
};
let u = s.clone().union(t.clone(), new_state);
let expected_u =
Dfa::from_edges(b, a, &[(b, 0, c), (b, 1, d), (d, 1, a), (d, 0, a), (c, 0, a)]);
assert_eq!(u, expected_u);
assert_eq!(is_transmutable(&s, &u, Assume::default()), Answer::Yes);
assert_eq!(is_transmutable(&t, &u, Assume::default()), Answer::Yes);
}
}

View file

@ -157,9 +157,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.171"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
dependencies = [
"rustc-std-workspace-core",
]

View file

@ -574,7 +574,7 @@ impl CString {
#[stable(feature = "as_c_str", since = "1.20.0")]
#[rustc_diagnostic_item = "cstring_as_c_str"]
pub fn as_c_str(&self) -> &CStr {
&*self
unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) }
}
/// Converts this `CString` into a boxed [`CStr`].
@ -705,14 +705,14 @@ impl ops::Deref for CString {
#[inline]
fn deref(&self) -> &CStr {
unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) }
self.as_c_str()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Debug for CString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
fmt::Debug::fmt(self.as_c_str(), f)
}
}

View file

@ -33,12 +33,6 @@ fn build_with_zero2() {
assert!(CString::new(vec![0]).is_err());
}
#[test]
fn formatted() {
let s = CString::new(&b"abc\x01\x02\n\xE2\x80\xA6\xFF"[..]).unwrap();
assert_eq!(format!("{s:?}"), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#);
}
#[test]
fn borrowed() {
unsafe {

View file

@ -772,8 +772,8 @@ impl hash::Hash for TypeId {
// (especially given the previous point about the lower 64 bits being
// high quality on their own).
// - It is correct to do so -- only hashing a subset of `self` is still
// with an `Eq` implementation that considers the entire value, as
// ours does.
// compatible with an `Eq` implementation that considers the entire
// value, as ours does.
self.t.1.hash(state);
}
}

View file

@ -32,7 +32,7 @@ pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) {
///
/// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html
/// [reference]: https://doc.rust-lang.org/nightly/reference/inline-assembly.html
#[unstable(feature = "naked_functions", issue = "90957")]
#[stable(feature = "naked_functions", since = "CURRENT_RUSTC_VERSION")]
#[rustc_builtin_macro]
pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) {
/* compiler built-in */

View file

@ -36,8 +36,7 @@ use crate::ops::{Deref, DerefMut, DerefPure};
/// presented as hex escape sequences.
///
/// The `Display` implementation behaves as if the `ByteStr` were first lossily converted to a
/// `str`, with invalid UTF-8 presented as the Unicode replacement character: <20>
///
/// `str`, with invalid UTF-8 presented as the Unicode replacement character (<28>).
#[unstable(feature = "bstr", issue = "134915")]
#[repr(transparent)]
#[doc(alias = "BStr")]

View file

@ -150,7 +150,6 @@ impl Error for FromBytesWithNulError {
/// within the slice.
///
/// This error is created by the [`CStr::from_bytes_until_nul`] method.
///
#[derive(Clone, PartialEq, Eq, Debug)]
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
pub struct FromBytesUntilNulError(());

View file

@ -304,7 +304,7 @@ pub unsafe fn simd_shuffle<T, U, V>(x: T, y: T, idx: U) -> V;
///
/// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`.
///
/// `V` must be a vector of signed integers with the same length as `T` (but any element size).
/// `V` must be a vector of integers with the same length as `T` (but any element size).
///
/// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, read the pointer.
/// Otherwise if the corresponding value in `mask` is `0`, return the corresponding value from
@ -325,7 +325,7 @@ pub unsafe fn simd_gather<T, U, V>(val: T, ptr: U, mask: V) -> T;
///
/// `U` must be a vector of pointers to the element type of `T`, with the same length as `T`.
///
/// `V` must be a vector of signed integers with the same length as `T` (but any element size).
/// `V` must be a vector of integers with the same length as `T` (but any element size).
///
/// For each pointer in `ptr`, if the corresponding value in `mask` is `!0`, write the
/// corresponding value in `val` to the pointer.
@ -349,7 +349,7 @@ pub unsafe fn simd_scatter<T, U, V>(val: T, ptr: U, mask: V);
///
/// `U` must be a pointer to the element type of `T`
///
/// `V` must be a vector of signed integers with the same length as `T` (but any element size).
/// `V` must be a vector of integers with the same length as `T` (but any element size).
///
/// For each element, if the corresponding value in `mask` is `!0`, read the corresponding
/// pointer offset from `ptr`.
@ -372,7 +372,7 @@ pub unsafe fn simd_masked_load<V, U, T>(mask: V, ptr: U, val: T) -> T;
///
/// `U` must be a pointer to the element type of `T`
///
/// `V` must be a vector of signed integers with the same length as `T` (but any element size).
/// `V` must be a vector of integers with the same length as `T` (but any element size).
///
/// For each element, if the corresponding value in `mask` is `!0`, write the corresponding
/// value in `val` to the pointer offset from `ptr`.
@ -556,7 +556,7 @@ pub unsafe fn simd_bitmask<T, U>(x: T) -> U;
///
/// `T` must be a vector.
///
/// `M` must be a signed integer vector with the same length as `T` (but any element size).
/// `M` must be an integer vector with the same length as `T` (but any element size).
///
/// For each element, if the corresponding value in `mask` is `!0`, select the element from
/// `if_true`. If the corresponding value in `mask` is `0`, select the element from

View file

@ -1307,10 +1307,12 @@ mod prim_f16 {}
// FIXME: Is there a better place to put this?
///
/// | `target_arch` | Extra payloads possible on this platform |
/// |---------------|---------|
/// | `x86`, `x86_64`, `arm`, `aarch64`, `riscv32`, `riscv64` | None |
/// |---------------|------------------------------------------|
// Sorted alphabetically
/// | `aarch64`, `arm`, `arm64ec`, `loongarch64`, `powerpc` (except when `target_abi = "spe"`), `powerpc64`, `riscv32`, `riscv64`, `s390x`, `x86`, `x86_64` | None |
/// | `nvptx64` | All payloads |
/// | `sparc`, `sparc64` | The all-one payload |
/// | `wasm32`, `wasm64` | If all input NaNs are quiet with all-zero payload: None.<br> Otherwise: all possible payloads. |
/// | `wasm32`, `wasm64` | If all input NaNs are quiet with all-zero payload: None.<br> Otherwise: all payloads. |
///
/// For targets not in this table, all payloads are possible.
///

View file

@ -2820,7 +2820,7 @@ impl<T> [T] {
let half = size / 2;
let mid = base + half;
// SAFETY: the call is made safe by the following inconstants:
// SAFETY: the call is made safe by the following invariants:
// - `mid >= 0`: by definition
// - `mid < size`: `mid = size / 2 + size / 4 + size / 8 ...`
let cmp = f(unsafe { self.get_unchecked(mid) });

View file

@ -13,3 +13,9 @@ fn compares_as_u8s() {
assert_eq!(Ord::cmp(a, b), Ord::cmp(a_bytes, b_bytes));
assert_eq!(PartialOrd::partial_cmp(a, b), PartialOrd::partial_cmp(a_bytes, b_bytes));
}
#[test]
fn debug() {
let s = c"abc\x01\x02\n\xE2\x80\xA6\xFF";
assert_eq!(format!("{s:?}"), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#);
}

View file

@ -35,7 +35,7 @@ miniz_oxide = { version = "0.8.0", optional = true, default-features = false }
addr2line = { version = "0.24.0", optional = true, default-features = false }
[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
libc = { version = "0.2.171", default-features = false, features = [
libc = { version = "0.2.172", default-features = false, features = [
'rustc-dep-of-std',
], public = true }

View file

@ -950,7 +950,7 @@ impl fmt::Debug for ArgsOs {
/// Constants associated with the current target
#[stable(feature = "env", since = "1.0.0")]
pub mod consts {
use crate::sys::env::os;
use crate::sys::env_consts::os;
/// A string describing the architecture of the CPU that is currently in use.
/// An example value may be: `"x86"`, `"arm"` or `"riscv64"`.

View file

@ -3265,7 +3265,7 @@ impl Hash for Path {
if !verbatim {
component_start += match tail {
[b'.'] => 1,
[b'.', sep @ _, ..] if is_sep_byte(*sep) => 1,
[b'.', sep, ..] if is_sep_byte(*sep) => 1,
_ => 0,
};
}

View file

@ -46,7 +46,7 @@ macro_rules! rtprintpanic {
macro_rules! rtabort {
($($t:tt)*) => {
{
rtprintpanic!("fatal runtime error: {}\n", format_args!($($t)*));
rtprintpanic!("fatal runtime error: {}, aborting\n", format_args!($($t)*));
crate::sys::abort_internal();
}
}

View file

@ -10,8 +10,10 @@ use crate::sys::pal::waitqueue::SpinMutex;
// The current allocator here is the `dlmalloc` crate which we've got included
// in the rust-lang/rust repository as a submodule. The crate is a port of
// dlmalloc.c from C to Rust.
//
// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests
#[cfg_attr(test, linkage = "available_externally")]
#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx5alloc8DLMALLOCE")]
#[unsafe(export_name = "_ZN16__rust_internals3std3sys5alloc3sgx8DLMALLOCE")]
static DLMALLOC: SpinMutex<dlmalloc::Dlmalloc<Sgx>> =
SpinMutex::new(dlmalloc::Dlmalloc::new_with_allocator(Sgx {}));

View file

@ -1,35 +0,0 @@
use crate::ffi::{CStr, OsString, c_char};
use crate::os::hermit::ffi::OsStringExt;
use crate::ptr;
use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release};
use crate::sync::atomic::{AtomicIsize, AtomicPtr};
#[path = "common.rs"]
mod common;
pub use common::Args;
static ARGC: AtomicIsize = AtomicIsize::new(0);
static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut());
/// One-time global initialization.
pub unsafe fn init(argc: isize, argv: *const *const u8) {
ARGC.store(argc, Relaxed);
// Use release ordering here to broadcast writes by the OS.
ARGV.store(argv as *mut *const u8, Release);
}
/// Returns the command line arguments
pub fn args() -> Args {
// Synchronize with the store above.
let argv = ARGV.load(Acquire);
// If argv has not been initialized yet, do not return any arguments.
let argc = if argv.is_null() { 0 } else { ARGC.load(Relaxed) };
let args: Vec<OsString> = (0..argc)
.map(|i| unsafe {
let cstr = CStr::from_ptr(*argv.offset(i) as *const c_char);
OsStringExt::from_vec(cstr.to_bytes().to_vec())
})
.collect();
Args::new(args)
}

View file

@ -3,15 +3,15 @@
#![forbid(unsafe_op_in_unsafe_fn)]
cfg_if::cfg_if! {
if #[cfg(all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))))] {
if #[cfg(any(
all(target_family = "unix", not(any(target_os = "espidf", target_os = "vita"))),
target_os = "hermit",
))] {
mod unix;
pub use unix::*;
} else if #[cfg(target_family = "windows")] {
mod windows;
pub use windows::*;
} else if #[cfg(target_os = "hermit")] {
mod hermit;
pub use hermit::*;
} else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
mod sgx;
pub use sgx::*;

View file

@ -8,6 +8,7 @@ use crate::sys::pal::abi::usercalls::raw::ByteBuffer;
use crate::sys_common::FromInner;
use crate::{fmt, slice};
// Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests
#[cfg_attr(test, linkage = "available_externally")]
#[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx4args4ARGSE")]
static ARGS: AtomicUsize = AtomicUsize::new(0);

View file

@ -6,6 +6,9 @@
#![allow(dead_code)] // runtime init functions not used during testing
use crate::ffi::CStr;
#[cfg(target_os = "hermit")]
use crate::os::hermit::ffi::OsStringExt;
#[cfg(not(target_os = "hermit"))]
use crate::os::unix::ffi::OsStringExt;
#[path = "common.rs"]
@ -73,6 +76,7 @@ pub fn args() -> Args {
target_os = "illumos",
target_os = "emscripten",
target_os = "haiku",
target_os = "hermit",
target_os = "l4re",
target_os = "fuchsia",
target_os = "redox",
@ -100,7 +104,7 @@ mod imp {
unsafe fn really_init(argc: isize, argv: *const *const u8) {
// These don't need to be ordered with each other or other stores,
// because they only hold the unmodified system-provide argv/argc.
// because they only hold the unmodified system-provided argv/argc.
ARGC.store(argc, Ordering::Relaxed);
ARGV.store(argv as *mut _, Ordering::Relaxed);
}

Some files were not shown because too many files have changed in this diff Show more