diff options
Diffstat (limited to 'compiler')
175 files changed, 3718 insertions, 3231 deletions
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index d656d9b0b8a..f165c4ddcdd 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -305,8 +305,8 @@ impl MetaItem { if let [PathSegment { ident, .. }] = self.path.segments[..] { Some(ident) } else { None } } - pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or_else(Ident::empty).name + pub fn name(&self) -> Option<Symbol> { + self.ident().map(|ident| ident.name) } pub fn has_name(&self, name: Symbol) -> bool { @@ -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, @@ -511,13 +508,14 @@ impl MetaItemInner { } } - /// For a single-segment meta item, returns its name; otherwise, returns `None`. + /// For a single-segment meta item, returns its identifier; otherwise, returns `None`. pub fn ident(&self) -> Option<Ident> { self.meta_item().and_then(|meta_item| meta_item.ident()) } - pub fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or_else(Ident::empty).name + /// For a single-segment meta item, returns its name; otherwise, returns `None`. + pub fn name(&self) -> Option<Symbol> { + self.ident().map(|ident| ident.name) } /// Returns `true` if this list item is a MetaItem with a name of `name`. @@ -738,9 +736,9 @@ pub trait AttributeExt: Debug { fn id(&self) -> AttrId; /// For a single-segment attribute (i.e., `#[attr]` and not `#[path::atrr]`), - /// return the name of the attribute, else return the empty identifier. - fn name_or_empty(&self) -> Symbol { - self.ident().unwrap_or_else(Ident::empty).name + /// return the name of the attribute; otherwise, returns `None`. + fn name(&self) -> Option<Symbol> { + self.ident().map(|ident| ident.name) } /// Get the meta item list, `#[attr(meta item list)]` @@ -752,7 +750,7 @@ pub trait AttributeExt: Debug { /// Gets the span of the value literal, as string, when using `#[attr = value]` fn value_span(&self) -> Option<Span>; - /// For a single-segment attribute, returns its name; otherwise, returns `None`. + /// For a single-segment attribute, returns its ident; otherwise, returns `None`. fn ident(&self) -> Option<Ident>; /// Checks whether the path of this attribute matches the name. @@ -770,6 +768,11 @@ pub trait AttributeExt: Debug { self.ident().map(|x| x.name == name).unwrap_or(false) } + #[inline] + fn has_any_name(&self, names: &[Symbol]) -> bool { + names.iter().any(|&name| self.has_name(name)) + } + /// get the span of the entire attribute fn span(&self) -> Span; @@ -813,8 +816,8 @@ impl Attribute { AttributeExt::id(self) } - pub fn name_or_empty(&self) -> Symbol { - AttributeExt::name_or_empty(self) + pub fn name(&self) -> Option<Symbol> { + AttributeExt::name(self) } pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> { @@ -846,6 +849,11 @@ impl Attribute { AttributeExt::has_name(self, name) } + #[inline] + pub fn has_any_name(&self, names: &[Symbol]) -> bool { + AttributeExt::has_any_name(self, names) + } + pub fn span(&self) -> Span { AttributeExt::span(self) } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 055481f5d87..54781e8235e 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -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, _, ) => { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index d8b55bea3d7..fc32c4efce5 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1310,7 +1310,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // create a fake body so that the entire rest of the compiler doesn't have to deal with // this as a special case. return self.lower_fn_body(decl, contract, |this| { - if attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic) { + if attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)) { let span = this.lower_span(span); let empty_block = hir::Block { hir_id: this.next_id(), diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 9a7b7daabbf..1feb3e9bf9b 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -347,7 +347,7 @@ impl<'a> AstValidator<'a> { sym::forbid, sym::warn, ]; - !arr.contains(&attr.name_or_empty()) && rustc_attr_parsing::is_builtin_attr(*attr) + !attr.has_any_name(&arr) && rustc_attr_parsing::is_builtin_attr(*attr) }) .for_each(|attr| { if attr.is_doc_comment() { @@ -947,8 +947,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident); self.check_defaultness(item.span, *defaultness); - let is_intrinsic = - item.attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic); + let is_intrinsic = item.attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic)); if body.is_none() && !is_intrinsic { self.dcx().emit_err(errors::FnWithoutBody { span: item.span, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 0985ebf945b..6959cbd87f1 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -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(), diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 969bce7ae20..d2d1285b075 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -191,7 +191,6 @@ pub enum AttributeKind { }, MacroTransparency(Transparency), Repr(ThinVec<(ReprAttr, Span)>), - RustcMacroEdition2021, Stability { stability: Stability, /// Span of the `#[stable(...)]` or `#[unstable(...)]` attribute diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 48297b2ebd8..7cb1fede174 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -102,7 +102,7 @@ pub fn eval_condition( }; match &cfg.kind { - MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => { + MetaItemKind::List(mis) if cfg.has_name(sym::version) => { try_gate_cfg(sym::version, cfg.span, sess, features); let (min_version, span) = match &mis[..] { [MetaItemInner::Lit(MetaItemLit { kind: LitKind::Str(sym, ..), span, .. })] => { @@ -149,18 +149,18 @@ pub fn eval_condition( // The unwraps below may look dangerous, but we've already asserted // that they won't fail with the loop above. - match cfg.name_or_empty() { - sym::any => mis + match cfg.name() { + Some(sym::any) => mis .iter() // We don't use any() here, because we want to evaluate all cfg condition // as eval_condition can (and does) extra checks .fold(false, |res, mi| res | eval_condition(mi, sess, features, eval)), - sym::all => mis + Some(sym::all) => mis .iter() // We don't use all() here, because we want to evaluate all cfg condition // as eval_condition can (and does) extra checks .fold(true, |res, mi| res & eval_condition(mi, sess, features, eval)), - sym::not => { + Some(sym::not) => { let [mi] = mis.as_slice() else { dcx.emit_err(session_diagnostics::ExpectedOneCfgPattern { span: cfg.span }); return false; @@ -168,7 +168,7 @@ pub fn eval_condition( !eval_condition(mi, sess, features, eval) } - sym::target => { + Some(sym::target) => { if let Some(features) = features && !features.cfg_target_compact() { diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index bac111159db..6ecd6b4d7db 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -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; diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc.rs b/compiler/rustc_attr_parsing/src/attributes/rustc.rs deleted file mode 100644 index bdd3bef2834..00000000000 --- a/compiler/rustc_attr_parsing/src/attributes/rustc.rs +++ /dev/null @@ -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) - } -} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 3bf03f84ce8..63597b37cb5 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -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 ]; @@ -222,7 +220,7 @@ impl<'sess> AttributeParser<'sess> { // if we're only looking for a single attribute, // skip all the ones we don't care about if let Some(expected) = self.parse_only { - if attr.name_or_empty() != expected { + if !attr.has_name(expected) { continue; } } @@ -232,7 +230,7 @@ impl<'sess> AttributeParser<'sess> { // that's expanded right? But no, sometimes, when parsing attributes on macros, // we already use the lowering logic and these are still there. So, when `omit_doc` // is set we *also* want to ignore these - if omit_doc == OmitDoc::Skip && attr.name_or_empty() == sym::doc { + if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) { continue; } @@ -250,7 +248,7 @@ impl<'sess> AttributeParser<'sess> { })) } // // FIXME: make doc attributes go through a proper attribute parser - // ast::AttrKind::Normal(n) if n.name_or_empty() == sym::doc => { + // ast::AttrKind::Normal(n) if n.has_name(sym::doc) => { // let p = GenericMetaItemParser::from_attr(&n, self.dcx()); // // attributes.push(Attribute::Parsed(AttributeKind::DocComment { diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 384fae59873..40aa39711d3 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -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. diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 959cf9fa513..cf735815fd2 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2959,21 +2959,27 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - let mut err = self.path_does_not_live_long_enough(borrow_span, &format!("`{name}`")); + let name = if borrow_span.in_external_macro(self.infcx.tcx.sess.source_map()) { + // Don't name local variables in external macros. + "value".to_string() + } else { + format!("`{name}`") + }; + + let mut err = self.path_does_not_live_long_enough(borrow_span, &name); if let Some(annotation) = self.annotate_argument_and_return_for_borrow(borrow) { let region_name = annotation.emit(self, &mut err); err.span_label( borrow_span, - format!("`{name}` would have to be valid for `{region_name}`..."), + format!("{name} would have to be valid for `{region_name}`..."), ); err.span_label( drop_span, format!( - "...but `{}` will be dropped here, when the {} returns", - name, + "...but {name} will be dropped here, when the {} returns", self.infcx .tcx .opt_item_name(self.mir_def_id().to_def_id()) @@ -3011,7 +3017,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } else { err.span_label(borrow_span, "borrowed value does not live long enough"); - err.span_label(drop_span, format!("`{name}` dropped here while still borrowed")); + err.span_label(drop_span, format!("{name} dropped here while still borrowed")); borrow_spans.args_subdiag(&mut err, |args_span| { crate::session_diagnostics::CaptureArgLabel::Capture { diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index f77dda0d386..a845431faca 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -95,7 +95,9 @@ impl<'tcx> BorrowExplanation<'tcx> { && let hir::def::Res::Local(hir_id) = p.res && let hir::Node::Pat(pat) = tcx.hir_node(hir_id) { - err.span_label(pat.span, format!("binding `{ident}` declared here")); + if !ident.span.in_external_macro(tcx.sess.source_map()) { + err.span_label(pat.span, format!("binding `{ident}` declared here")); + } } } } diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 5316e90847a..73be954cefd 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -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]` diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index daebd516499..e60efdbefd9 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -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![]); diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index b9197be4442..d9aac54ee73 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -527,15 +527,14 @@ impl<'a> TraitDef<'a> { item.attrs .iter() .filter(|a| { - [ + a.has_any_name(&[ sym::allow, sym::warn, sym::deny, sym::forbid, sym::stable, sym::unstable, - ] - .contains(&a.name_or_empty()) + ]) }) .cloned(), ); diff --git a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs index 09d5b73fd3d..93ca2e0e421 100644 --- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs @@ -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)] diff --git a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml index f96912e6b7a..ef024258ffc 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml @@ -1,8 +1,10 @@ name: CI on: - - push - - pull_request + push: + branches: + - master + pull_request: permissions: contents: read @@ -121,3 +123,22 @@ jobs: run: | cd build_system cargo test + + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + success: + needs: [build, duplicates, build_system] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml index d080bbfe91f..bc42eb1468e 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml @@ -2,7 +2,10 @@ name: Failures on: - - pull_request + push: + branches: + - master + pull_request: permissions: contents: read @@ -108,3 +111,22 @@ jobs: echo "Error: 'the compiler unexpectedly panicked' found in output logs. CI Error!!" exit 1 fi + + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + success_failures: + needs: [build] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml b/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml index bb9e020dc6a..da9a1506855 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml @@ -1,8 +1,10 @@ name: CI libgccjit 12 on: - - push - - pull_request + push: + branches: + - master + pull_request: permissions: contents: read @@ -85,3 +87,22 @@ jobs: #- name: Run tests #run: | #./y.sh test --release --clean --build-sysroot ${{ matrix.commands }} --no-default-features + + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + success_gcc12: + needs: [build] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml b/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml index ed1fc02bd91..21731f7087e 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml @@ -3,8 +3,10 @@ name: m68k CI on: - - push - - pull_request + push: + branches: + - master + pull_request: permissions: contents: read @@ -105,3 +107,22 @@ jobs: - name: Run tests run: | ./y.sh test --release --clean --build-sysroot --sysroot-features compiler_builtins/no-f16-f128 ${{ matrix.commands }} + + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + success_m68k: + needs: [build] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/compiler/rustc_codegen_gcc/.github/workflows/release.yml b/compiler/rustc_codegen_gcc/.github/workflows/release.yml index 886ce90b471..47a40286554 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/release.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/release.yml @@ -1,8 +1,10 @@ name: CI with sysroot compiled in release mode on: - - push - - pull_request + push: + branches: + - master + pull_request: permissions: contents: read @@ -82,3 +84,22 @@ jobs: echo "Test is done with LTO enabled, hence inlining should occur across crates" exit 1 fi + + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + success_release: + needs: [build] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml index d5ae6144496..4b9f48e7b18 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/stdarch.yml @@ -1,8 +1,10 @@ name: stdarch tests with sysroot compiled in release mode on: - - push - - pull_request + push: + branches: + - master + pull_request: permissions: contents: read @@ -102,3 +104,22 @@ jobs: # TODO: remove --skip test_mm512_stream_ps when stdarch is updated in rustc. # TODO: remove --skip test_tile_ when it's implemented. STDARCH_TEST_EVERYTHING=1 CHANNEL=release CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="${{ matrix.cargo_runner }}" TARGET=x86_64-unknown-linux-gnu CG_RUSTFLAGS="-Ainternal_features --cfg stdarch_intel_sde" ./y.sh cargo test --manifest-path build/build_sysroot/sysroot_src/library/stdarch/Cargo.toml -- --skip rtm --skip tbm --skip sse4a --skip test_mm512_stream_ps --skip test_tile_ + + # Summary job for the merge queue. + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + success_stdarch: + needs: [build] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock index 636e75b94a3..832603aa792 100644 --- a/compiler/rustc_codegen_gcc/Cargo.lock +++ b/compiler/rustc_codegen_gcc/Cargo.lock @@ -56,18 +56,18 @@ dependencies = [ [[package]] name = "gccjit" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72fd91f4adbf02b53cfc73c97bc33c5f253009043f30c56a5ec08dd5c8094dc8" +checksum = "2895ddec764de7ac76fe6c056050c4801a80109c066f177a00a9cc8dee02b29b" dependencies = [ "gccjit_sys", ] [[package]] name = "gccjit_sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fb7b8f48a75e2cfe78c3d9a980b32771c34ffd12d196021ab3f98c49fbd2f0d" +checksum = "ac133db68db8a6a8b2c51ef4b18d8ea16682d5814c4641272fe37bbbc223d5f3" dependencies = [ "libc", ] diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml index 63d37358561..b50f2a626d5 100644 --- a/compiler/rustc_codegen_gcc/Cargo.toml +++ b/compiler/rustc_codegen_gcc/Cargo.toml @@ -22,7 +22,7 @@ master = ["gccjit/master"] default = ["master"] [dependencies] -gccjit = "2.4" +gccjit = "2.5" #gccjit = { git = "https://github.com/rust-lang/gccjit.rs" } # Local copy. diff --git a/compiler/rustc_codegen_gcc/Readme.md b/compiler/rustc_codegen_gcc/Readme.md index e92c16ece2f..d0e4dbba6d3 100644 --- a/compiler/rustc_codegen_gcc/Readme.md +++ b/compiler/rustc_codegen_gcc/Readme.md @@ -23,7 +23,7 @@ A secondary goal is to check if using the gcc backend will provide any run-time ## Building **This requires a patched libgccjit in order to work. -You need to use my [fork of gcc](https://github.com/antoyo/gcc) which already includes these patches.** +You need to use my [fork of gcc](https://github.com/rust-lang/gcc) which already includes these patches.** ```bash $ cp config.example.toml config.toml @@ -40,7 +40,7 @@ to do a few more things. To build it (most of these instructions come from [here](https://gcc.gnu.org/onlinedocs/jit/internals/index.html), so don't hesitate to take a look there if you encounter an issue): ```bash -$ git clone https://github.com/antoyo/gcc +$ git clone https://github.com/rust-lang/gcc $ sudo apt install flex libmpfr-dev libgmp-dev libmpc3 libmpc-dev $ mkdir gcc-build gcc-install $ cd gcc-build diff --git a/compiler/rustc_codegen_gcc/build_system/src/clone_gcc.rs b/compiler/rustc_codegen_gcc/build_system/src/clone_gcc.rs index e28ee873eb6..b49dd47f352 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/clone_gcc.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/clone_gcc.rs @@ -61,7 +61,7 @@ pub fn run() -> Result<(), String> { return Ok(()); }; - let result = git_clone("https://github.com/antoyo/gcc", Some(&args.out_path), false)?; + let result = git_clone("https://github.com/rust-lang/gcc", Some(&args.out_path), false)?; if result.ran_clone { let gcc_commit = args.config_info.get_gcc_commit()?; println!("Checking out GCC commit `{}`...", gcc_commit); diff --git a/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs index 6c29c7d1825..df4ac85233b 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/test.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/test.rs @@ -529,20 +529,21 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> { env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string()); - let extra = - if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" }; - - let rustc_args = &format!( - r#"-Zpanic-abort-tests \ - -Zcodegen-backend="{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}" \ - --sysroot "{sysroot_dir}" -Cpanic=abort{extra}"#, + let codegen_backend_path = format!( + "{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}", pwd = std::env::current_dir() .map_err(|error| format!("`current_dir` failed: {:?}", error))? .display(), channel = args.config_info.channel.as_str(), dylib_ext = args.config_info.dylib_ext, - sysroot_dir = args.config_info.sysroot_path, - extra = extra, + ); + + let extra = + if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" }; + + let rustc_args = format!( + "-Zpanic-abort-tests -Zcodegen-backend={codegen_backend_path} --sysroot {} -Cpanic=abort{extra}", + args.config_info.sysroot_path ); run_command_with_env( @@ -677,7 +678,7 @@ fn test_projects(env: &Env, args: &TestArg) -> Result<(), String> { fn test_libcore(env: &Env, args: &TestArg) -> Result<(), String> { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] libcore"); - let path = get_sysroot_dir().join("sysroot_src/library/core/tests"); + let path = get_sysroot_dir().join("sysroot_src/library/coretests"); let _ = remove_dir_all(path.join("target")); run_cargo_command(&[&"test"], Some(&path), env, args)?; Ok(()) diff --git a/compiler/rustc_codegen_gcc/doc/add-attribute.md b/compiler/rustc_codegen_gcc/doc/add-attribute.md index ae3bcc5e2eb..267c1819525 100644 --- a/compiler/rustc_codegen_gcc/doc/add-attribute.md +++ b/compiler/rustc_codegen_gcc/doc/add-attribute.md @@ -14,4 +14,4 @@ Finally, you need to update this repository by calling the relevant API you adde To test it, build `gcc`, run `cargo update -p gccjit` and then you can test the generated output for a given Rust crate. -[gccjit.rs]: https://github.com/antoyo/gccjit.rs +[gccjit.rs]: https://github.com/rust-lang/gccjit.rs diff --git a/compiler/rustc_codegen_gcc/example/mini_core.rs b/compiler/rustc_codegen_gcc/example/mini_core.rs index 5544aee9eaf..c554a87b825 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core.rs @@ -51,6 +51,10 @@ impl<T: ?Sized> LegacyReceiver for &T {} impl<T: ?Sized> LegacyReceiver for &mut T {} impl<T: ?Sized, A: Allocator> LegacyReceiver for Box<T, A> {} +#[lang = "receiver"] +trait Receiver { +} + #[lang = "copy"] pub trait Copy {} @@ -134,6 +138,14 @@ impl Mul for u8 { } } +impl Mul for i32 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + self * rhs + } +} + impl Mul for usize { type Output = Self; @@ -142,6 +154,14 @@ impl Mul for usize { } } +impl Mul for isize { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + self * rhs + } +} + #[lang = "add"] pub trait Add<RHS = Self> { type Output; @@ -165,6 +185,14 @@ impl Add for i8 { } } +impl Add for i32 { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + impl Add for usize { type Output = Self; @@ -196,6 +224,14 @@ impl Sub for usize { } } +impl Sub for isize { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + self - rhs + } +} + impl Sub for u8 { type Output = Self; @@ -220,6 +256,14 @@ impl Sub for i16 { } } +impl Sub for i32 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self { + self - rhs + } +} + #[lang = "rem"] pub trait Rem<RHS = Self> { type Output; @@ -628,6 +672,10 @@ pub mod libc { pub fn memcpy(dst: *mut u8, src: *const u8, size: usize); pub fn memmove(dst: *mut u8, src: *const u8, size: usize); pub fn strncpy(dst: *mut u8, src: *const u8, size: usize); + pub fn fflush(stream: *mut i32) -> i32; + pub fn exit(status: i32); + + pub static stdout: *mut i32; } } diff --git a/compiler/rustc_codegen_gcc/libgccjit.version b/compiler/rustc_codegen_gcc/libgccjit.version index 417fd5b0393..125b04004b0 100644 --- a/compiler/rustc_codegen_gcc/libgccjit.version +++ b/compiler/rustc_codegen_gcc/libgccjit.version @@ -1 +1 @@ -e607be166673a8de9fc07f6f02c60426e556c5f2 +0ea98a1365b81f7488073512c850e8ee951a4afd diff --git a/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch deleted file mode 100644 index 70e3e2ba7fe..00000000000 --- a/compiler/rustc_codegen_gcc/patches/0022-core-Disable-not-compiling-tests.patch +++ /dev/null @@ -1,44 +0,0 @@ -From af0e237f056fa838c77463381a19b0dc993c0a35 Mon Sep 17 00:00:00 2001 -From: None <none@example.com> -Date: Sun, 1 Sep 2024 11:42:17 -0400 -Subject: [PATCH] Disable not compiling tests - ---- - library/core/tests/Cargo.toml | 14 ++++++++++++++ - library/core/tests/lib.rs | 1 + - 2 files changed, 15 insertions(+) - create mode 100644 library/core/tests/Cargo.toml - -diff --git a/library/core/tests/Cargo.toml b/library/core/tests/Cargo.toml -new file mode 100644 -index 0000000..ca326ac ---- /dev/null -+++ b/library/core/tests/Cargo.toml -@@ -0,0 +1,14 @@ -+[workspace] -+ -+[package] -+name = "coretests" -+version = "0.0.0" -+edition = "2021" -+ -+[lib] -+name = "coretests" -+path = "lib.rs" -+ -+[dependencies] -+rand = { version = "0.8.5", default-features = false } -+rand_xorshift = { version = "0.3.0", default-features = false } -diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs -index a4a7946..ecfe43f 100644 ---- a/library/core/tests/lib.rs -+++ b/library/core/tests/lib.rs -@@ -1,4 +1,5 @@ - // tidy-alphabetical-start -+#![cfg(test)] - #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] - #![cfg_attr(test, feature(cfg_match))] - #![feature(alloc_layout_extra)] --- -2.47.1 - diff --git a/compiler/rustc_codegen_gcc/patches/0028-core-Disable-long-running-tests.patch b/compiler/rustc_codegen_gcc/patches/0028-core-Disable-long-running-tests.patch index dc1beae6d2e..20df4245cfd 100644 --- a/compiler/rustc_codegen_gcc/patches/0028-core-Disable-long-running-tests.patch +++ b/compiler/rustc_codegen_gcc/patches/0028-core-Disable-long-running-tests.patch @@ -1,17 +1,17 @@ -From eb703e627e7a84f1cd8d0d87f0f69da1f0acf765 Mon Sep 17 00:00:00 2001 -From: bjorn3 <bjorn3@users.noreply.github.com> -Date: Fri, 3 Dec 2021 12:16:30 +0100 +From ec2d0dc77fb484d926b45bb626b0db6a4bb0ab5c Mon Sep 17 00:00:00 2001 +From: None <none@example.com> +Date: Thu, 27 Mar 2025 09:20:41 -0400 Subject: [PATCH] Disable long running tests --- - library/core/tests/slice.rs | 2 ++ + library/coretests/tests/slice.rs | 2 ++ 1 file changed, 2 insertions(+) -diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs -index 8402833..84592e0 100644 ---- a/library/core/tests/slice.rs -+++ b/library/core/tests/slice.rs -@@ -2462,6 +2462,7 @@ take_tests! { +diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs +index d17e681..fba5cd6 100644 +--- a/library/coretests/tests/slice.rs ++++ b/library/coretests/tests/slice.rs +@@ -2486,6 +2486,7 @@ split_off_tests! { #[cfg(not(miri))] // unused in Miri const EMPTY_MAX: &'static [()] = &[(); usize::MAX]; @@ -19,14 +19,14 @@ index 8402833..84592e0 100644 // can't be a constant due to const mutability rules #[cfg(not(miri))] // unused in Miri macro_rules! empty_max_mut { -@@ -2485,6 +2486,7 @@ take_tests! { - (take_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), - (take_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), +@@ -2509,6 +2510,7 @@ split_off_tests! { + (split_off_mut_oob_max_range_to_inclusive, (..=usize::MAX), None, empty_max_mut!()), + (split_off_mut_in_bounds_max_range_from, (usize::MAX..), Some(&mut [] as _), empty_max_mut!()), } +*/ #[test] fn test_slice_from_ptr_range() { -- -2.26.2.7.g19db9cfb68 +2.49.0 diff --git a/compiler/rustc_codegen_gcc/patches/cross_patches/0001-Disable-libstd-and-libtest-dylib.patch b/compiler/rustc_codegen_gcc/patches/cross_patches/0001-Disable-libstd-and-libtest-dylib.patch index c220f53040f..fa360fe9e74 100644 --- a/compiler/rustc_codegen_gcc/patches/cross_patches/0001-Disable-libstd-and-libtest-dylib.patch +++ b/compiler/rustc_codegen_gcc/patches/cross_patches/0001-Disable-libstd-and-libtest-dylib.patch @@ -1,19 +1,18 @@ -From 966beefe08be6045bfcca26079b76a7a80413080 Mon Sep 17 00:00:00 2001 +From b2911e732d1bf0e28872495c4c47af1dad3c7911 Mon Sep 17 00:00:00 2001 From: None <none@example.com> -Date: Thu, 28 Sep 2023 17:37:38 -0400 +Date: Thu, 27 Mar 2025 14:30:10 -0400 Subject: [PATCH] Disable libstd and libtest dylib --- - library/std/Cargo.toml | 2 +- - library/test/Cargo.toml | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) + library/std/Cargo.toml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml -index 5b21355..cb0c49b 100644 +index 176da60..c183cdb 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml -@@ -9,7 +9,7 @@ description = "The Rust Standard Library" - edition = "2021" +@@ -10,7 +10,7 @@ edition = "2024" + autobenches = false [lib] -crate-type = ["dylib", "rlib"] @@ -21,3 +20,6 @@ index 5b21355..cb0c49b 100644 [dependencies] alloc = { path = "../alloc", public = true } +-- +2.49.0 + diff --git a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch index 9ef5e0e4f46..9d5b2dc537d 100644 --- a/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch +++ b/compiler/rustc_codegen_gcc/patches/libgccjit12/0001-core-Disable-portable-simd-test.patch @@ -1,25 +1,17 @@ -From 124a11ce086952a5794d5cfbaa45175809497b81 Mon Sep 17 00:00:00 2001 +From 1a8f6b8e39f343959d4d2e6b6957a6d780ac3fc0 Mon Sep 17 00:00:00 2001 From: None <none@example.com> -Date: Sat, 18 Nov 2023 10:50:36 -0500 -Subject: [PATCH] [core] Disable portable-simd test +Date: Thu, 27 Mar 2025 14:32:14 -0400 +Subject: [PATCH] Disable portable-simd test --- - library/core/tests/lib.rs | 2 -- - 1 file changed, 2 deletions(-) + library/coretests/tests/lib.rs | 1 - + 1 file changed, 1 deletion(-) -diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs -index b71786c..cf484d5 100644 ---- a/library/core/tests/lib.rs -+++ b/library/core/tests/lib.rs -@@ -87,7 +87,6 @@ - #![feature(numfmt)] - #![feature(pattern)] - #![feature(pointer_is_aligned_to)] --#![feature(portable_simd)] - #![feature(ptr_metadata)] - #![feature(slice_from_ptr_range)] - #![feature(slice_internals)] -@@ -155,7 +154,6 @@ mod pin; +diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs +index 79022fe..9223b2f 100644 +--- a/library/coretests/tests/lib.rs ++++ b/library/coretests/tests/lib.rs +@@ -165,7 +165,6 @@ mod pin; mod pin_macro; mod ptr; mod result; @@ -27,4 +19,6 @@ index b71786c..cf484d5 100644 mod slice; mod str; mod str_lossy; --- 2.45.2 +-- +2.49.0 + diff --git a/compiler/rustc_codegen_gcc/rust-toolchain b/compiler/rustc_codegen_gcc/rust-toolchain index 940b3de9f74..fd898c59707 100644 --- a/compiler/rustc_codegen_gcc/rust-toolchain +++ b/compiler/rustc_codegen_gcc/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2025-01-12" +channel = "nightly-2025-04-17" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs index 9fe6baa3d25..a96b18e01c0 100644 --- a/compiler/rustc_codegen_gcc/src/abi.rs +++ b/compiler/rustc_codegen_gcc/src/abi.rs @@ -9,6 +9,8 @@ use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; #[cfg(feature = "master")] use rustc_session::config; +#[cfg(feature = "master")] +use rustc_target::callconv::Conv; use rustc_target::callconv::{ArgAttributes, CastTarget, FnAbi, PassMode}; use crate::builder::Builder; @@ -105,6 +107,8 @@ pub trait FnAbiGccExt<'gcc, 'tcx> { // TODO(antoyo): return a function pointer type instead? fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> FnAbiGcc<'gcc>; fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>; + #[cfg(feature = "master")] + fn gcc_cconv(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Option<FnAttribute<'gcc>>; } impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { @@ -227,4 +231,47 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { ); pointer_type } + + #[cfg(feature = "master")] + fn gcc_cconv(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Option<FnAttribute<'gcc>> { + conv_to_fn_attribute(self.conv, &cx.tcx.sess.target.arch) + } +} + +#[cfg(feature = "master")] +pub fn conv_to_fn_attribute<'gcc>(conv: Conv, arch: &str) -> Option<FnAttribute<'gcc>> { + // TODO: handle the calling conventions returning None. + let attribute = match conv { + Conv::C + | Conv::Rust + | Conv::CCmseNonSecureCall + | Conv::CCmseNonSecureEntry + | Conv::RiscvInterrupt { .. } => return None, + Conv::Cold => return None, + Conv::PreserveMost => return None, + Conv::PreserveAll => return None, + Conv::GpuKernel => { + // TODO(antoyo): remove clippy allow attribute when this is implemented. + #[allow(clippy::if_same_then_else)] + if arch == "amdgpu" { + return None; + } else if arch == "nvptx64" { + return None; + } else { + panic!("Architecture {} does not support GpuKernel calling convention", arch); + } + } + Conv::AvrInterrupt => return None, + Conv::AvrNonBlockingInterrupt => return None, + Conv::ArmAapcs => return None, + Conv::Msp430Intr => return None, + Conv::X86Fastcall => return None, + Conv::X86Intr => return None, + Conv::X86Stdcall => return None, + Conv::X86ThisCall => return None, + Conv::X86VectorCall => return None, + Conv::X86_64SysV => FnAttribute::SysvAbi, + Conv::X86_64Win64 => FnAttribute::MsAbi, + }; + Some(attribute) } diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 415f8affab9..dbdf37ee6c9 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -36,7 +36,8 @@ use crate::type_of::LayoutGccExt; // // 3. Clobbers. GCC has a separate list of clobbers, and clobbers don't have indexes. // Contrary, Rust expresses clobbers through "out" operands that aren't tied to -// a variable (`_`), and such "clobbers" do have index. +// a variable (`_`), and such "clobbers" do have index. Input operands cannot also +// be clobbered. // // 4. Furthermore, GCC Extended Asm does not support explicit register constraints // (like `out("eax")`) directly, offering so-called "local register variables" @@ -161,6 +162,16 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { // Also, we don't emit any asm operands immediately; we save them to // the one of the buffers to be emitted later. + let mut input_registers = vec![]; + + for op in rust_operands { + if let InlineAsmOperandRef::In { reg, .. } = *op { + if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) { + input_registers.push(reg_name); + } + } + } + // 1. Normal variables (and saving operands to buffers). for (rust_idx, op) in rust_operands.iter().enumerate() { match *op { @@ -183,25 +194,39 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { continue; } (Register(reg_name), None) => { - // `clobber_abi` can add lots of clobbers that are not supported by the target, - // such as AVX-512 registers, so we just ignore unsupported registers - let is_target_supported = - reg.reg_class().supported_types(asm_arch, true).iter().any( - |&(_, feature)| { - if let Some(feature) = feature { - self.tcx - .asm_target_features(instance.def_id()) - .contains(&feature) - } else { - true // Register class is unconditionally supported - } - }, - ); - - if is_target_supported && !clobbers.contains(®_name) { - clobbers.push(reg_name); + if input_registers.contains(®_name) { + // the `clobber_abi` operand is converted into a series of + // `lateout("reg") _` operands. Of course, a user could also + // explicitly define such an output operand. + // + // GCC does not allow input registers to be clobbered, so if this out register + // is also used as an in register, do not add it to the clobbers list. + // it will be treated as a lateout register with `out_place: None` + if !late { + bug!("input registers can only be used as lateout regisers"); + } + ("r", dummy_output_type(self.cx, reg.reg_class())) + } else { + // `clobber_abi` can add lots of clobbers that are not supported by the target, + // such as AVX-512 registers, so we just ignore unsupported registers + let is_target_supported = + reg.reg_class().supported_types(asm_arch, true).iter().any( + |&(_, feature)| { + if let Some(feature) = feature { + self.tcx + .asm_target_features(instance.def_id()) + .contains(&feature) + } else { + true // Register class is unconditionally supported + } + }, + ); + + if is_target_supported && !clobbers.contains(®_name) { + clobbers.push(reg_name); + } + continue; } - continue; } }; @@ -230,13 +255,10 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { } InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => { - let constraint = - if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) { - constraint - } else { - // left for the next pass - continue; - }; + let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) else { + // left for the next pass + continue; + }; // Rustc frontend guarantees that input and output types are "compatible", // so we can just use input var's type for the output variable. @@ -589,114 +611,127 @@ fn estimate_template_length( } /// Converts a register class to a GCC constraint code. -fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister { - let constraint = match reg { - // For vector registers LLVM wants the register name to match the type size. +fn reg_to_gcc(reg_or_reg_class: InlineAsmRegOrRegClass) -> ConstraintOrRegister { + match reg_or_reg_class { InlineAsmRegOrRegClass::Reg(reg) => { - match reg { - InlineAsmReg::X86(_) => { - // TODO(antoyo): add support for vector register. - // - // // For explicit registers, we have to create a register variable: https://stackoverflow.com/a/31774784/389119 - return ConstraintOrRegister::Register(match reg.name() { - // Some of registers' names does not map 1-1 from rust to gcc - "st(0)" => "st", + ConstraintOrRegister::Register(explicit_reg_to_gcc(reg)) + } + InlineAsmRegOrRegClass::RegClass(reg_class) => { + ConstraintOrRegister::Constraint(reg_class_to_gcc(reg_class)) + } + } +} - name => name, - }); +fn explicit_reg_to_gcc(reg: InlineAsmReg) -> &'static str { + // For explicit registers, we have to create a register variable: https://stackoverflow.com/a/31774784/389119 + match reg { + InlineAsmReg::X86(reg) => { + // TODO(antoyo): add support for vector register. + match reg.reg_class() { + X86InlineAsmRegClass::reg_byte => { + // GCC does not support the `b` suffix, so we just strip it + // see https://github.com/rust-lang/rustc_codegen_gcc/issues/485 + reg.name().trim_end_matches('b') } + _ => match reg.name() { + // Some of registers' names does not map 1-1 from rust to gcc + "st(0)" => "st", - _ => unimplemented!(), + name => name, + }, } } - // They can be retrieved from https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html - InlineAsmRegOrRegClass::RegClass(reg) => match reg { - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => "r", - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => "w", - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x", - InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) - | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => "t", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_upper) => "d", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => "r", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => "w", - InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => "e", - InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w", - InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::preg) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => "a", - InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_data) => "d", - InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => "d", // more specific than "r" - InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => "r", - // https://github.com/gcc-mirror/gcc/blob/master/gcc/config/nvptx/nvptx.md -> look for - // "define_constraint". - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => "h", - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => "r", - InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => "l", - - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => "v", - InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) - | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r", - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q", - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q", - InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x", - InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v", - InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "Yk", - InlineAsmRegClass::X86( - X86InlineAsmRegClass::kreg0 - | X86InlineAsmRegClass::x87_reg - | X86InlineAsmRegClass::mmx_reg - | X86InlineAsmRegClass::tmm_reg, - ) => unreachable!("clobber-only"), - InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { - bug!("GCC backend does not support SPIR-V") - } - InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg_addr) => "a", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::vreg) => "v", - InlineAsmRegClass::S390x(S390xInlineAsmRegClass::areg) => { - unreachable!("clobber-only") - } - InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::reg) => "r", - InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::yreg) => unreachable!("clobber-only"), - InlineAsmRegClass::Err => unreachable!(), - }, - }; - ConstraintOrRegister::Constraint(constraint) + _ => unimplemented!(), + } +} + +/// They can be retrieved from https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html +fn reg_class_to_gcc(reg_class: InlineAsmRegClass) -> &'static str { + match reg_class { + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => "r", + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => "w", + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => "x", + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => { + unreachable!("clobber-only") + } + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => "t", + InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_upper) => "d", + InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => "r", + InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => "w", + InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => "e", + InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w", + InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::preg) => { + unreachable!("clobber-only") + } + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::LoongArch(LoongArchInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => "a", + InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_data) => "d", + InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::CSKY(CSKYInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => "d", // more specific than "r" + InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => "r", + // https://github.com/gcc-mirror/gcc/blob/master/gcc/config/nvptx/nvptx.md -> look for + // "define_constraint". + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => "h", + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => "r", + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => "l", + + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => "b", + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::vreg) => "v", + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::cr) + | InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::xer) => { + unreachable!("clobber-only") + } + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => { + unreachable!("clobber-only") + } + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r", + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q", + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q", + InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x", + InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v", + InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "Yk", + InlineAsmRegClass::X86( + X86InlineAsmRegClass::kreg0 + | X86InlineAsmRegClass::x87_reg + | X86InlineAsmRegClass::mmx_reg + | X86InlineAsmRegClass::tmm_reg, + ) => unreachable!("clobber-only"), + InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { + bug!("GCC backend does not support SPIR-V") + } + InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r", + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg_addr) => "a", + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::vreg) => "v", + InlineAsmRegClass::S390x(S390xInlineAsmRegClass::areg) => { + unreachable!("clobber-only") + } + InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::yreg) => unreachable!("clobber-only"), + InlineAsmRegClass::Err => unreachable!(), + } } /// Type to use for outputs that are discarded. It doesn't really matter what diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index 6573b5b165e..5c70f4a7df9 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -368,16 +368,8 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let previous_arg_count = args.len(); let orig_args = args; let args = { - let function_address_names = self.function_address_names.borrow(); - let original_function_name = function_address_names.get(&func_ptr); func_ptr = llvm::adjust_function(self.context, &func_name, func_ptr, args); - llvm::adjust_intrinsic_arguments( - self, - gcc_func, - args.into(), - &func_name, - original_function_name, - ) + llvm::adjust_intrinsic_arguments(self, gcc_func, args.into(), &func_name) }; let args_adjusted = args.len() != previous_arg_count; let args = self.check_ptr_call("call", func_ptr, &args); @@ -1271,7 +1263,50 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn fcmp(&mut self, op: RealPredicate, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { - self.context.new_comparison(self.location, op.to_gcc_comparison(), lhs, rhs) + // LLVM has a concept of "unordered compares", where eg ULT returns true if either the two + // arguments are unordered (i.e. either is NaN), or the lhs is less than the rhs. GCC does + // not natively have this concept, so in some cases we must manually handle NaNs + let must_handle_nan = match op { + RealPredicate::RealPredicateFalse => unreachable!(), + RealPredicate::RealOEQ => false, + RealPredicate::RealOGT => false, + RealPredicate::RealOGE => false, + RealPredicate::RealOLT => false, + RealPredicate::RealOLE => false, + RealPredicate::RealONE => false, + RealPredicate::RealORD => unreachable!(), + RealPredicate::RealUNO => unreachable!(), + RealPredicate::RealUEQ => false, + RealPredicate::RealUGT => true, + RealPredicate::RealUGE => true, + RealPredicate::RealULT => true, + RealPredicate::RealULE => true, + RealPredicate::RealUNE => false, + RealPredicate::RealPredicateTrue => unreachable!(), + }; + + let cmp = self.context.new_comparison(self.location, op.to_gcc_comparison(), lhs, rhs); + + if must_handle_nan { + let is_nan = self.context.new_binary_op( + self.location, + BinaryOp::LogicalOr, + self.cx.bool_type, + // compare a value to itself to check whether it is NaN + self.context.new_comparison(self.location, ComparisonOp::NotEquals, lhs, lhs), + self.context.new_comparison(self.location, ComparisonOp::NotEquals, rhs, rhs), + ); + + self.context.new_binary_op( + self.location, + BinaryOp::LogicalOr, + self.cx.bool_type, + is_nan, + cmp, + ) + } else { + cmp + } } /* Miscellaneous instructions */ diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 1e1f577bb3a..73718994e64 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -23,6 +23,8 @@ use rustc_target::spec::{ HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, Target, TlsModel, WasmCAbi, X86Abi, }; +#[cfg(feature = "master")] +use crate::abi::conv_to_fn_attribute; use crate::callee::get_fn; use crate::common::SignType; @@ -213,33 +215,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let bool_type = context.new_type::<bool>(); let mut functions = FxHashMap::default(); - let builtins = [ - "__builtin_unreachable", - "abort", - "__builtin_expect", /*"__builtin_expect_with_probability",*/ - "__builtin_constant_p", - "__builtin_add_overflow", - "__builtin_mul_overflow", - "__builtin_saddll_overflow", - /*"__builtin_sadd_overflow",*/ - "__builtin_smulll_overflow", /*"__builtin_smul_overflow",*/ - "__builtin_ssubll_overflow", - /*"__builtin_ssub_overflow",*/ "__builtin_sub_overflow", - "__builtin_uaddll_overflow", - "__builtin_uadd_overflow", - "__builtin_umulll_overflow", - "__builtin_umul_overflow", - "__builtin_usubll_overflow", - "__builtin_usub_overflow", - "__builtin_powif", - "__builtin_powi", - "fabsf", - "fabs", - "copysignf", - "copysign", - "nearbyintf", - "nearbyint", - ]; + let builtins = ["abort"]; for builtin in builtins.iter() { functions.insert(builtin.to_string(), context.get_builtin_function(builtin)); @@ -509,7 +485,11 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn declare_c_main(&self, fn_type: Self::Type) -> Option<Self::Function> { let entry_name = self.sess().target.entry_name.as_ref(); if !self.functions.borrow().contains_key(entry_name) { - Some(self.declare_entry_fn(entry_name, fn_type, ())) + #[cfg(feature = "master")] + let conv = conv_to_fn_attribute(self.sess().target.entry_abi, &self.sess().target.arch); + #[cfg(not(feature = "master"))] + let conv = None; + Some(self.declare_entry_fn(entry_name, fn_type, conv)) } else { // If the symbol already exists, it is an error: for example, the user wrote // #[no_mangle] extern "C" fn main(..) {..} @@ -605,7 +585,10 @@ impl<'b, 'tcx> CodegenCx<'b, 'tcx> { let mut name = String::with_capacity(prefix.len() + 6); name.push_str(prefix); name.push('.'); - name.push_str(&(idx as u64).to_base(ALPHANUMERIC_ONLY)); + // Offset the index by the base so that always at least two characters + // are generated. This avoids cases where the suffix is interpreted as + // size by the assembler (for m68k: .b, .w, .l). + name.push_str(&(idx as u64 + ALPHANUMERIC_ONLY as u64).to_base(ALPHANUMERIC_ONLY)); name } } diff --git a/compiler/rustc_codegen_gcc/src/declare.rs b/compiler/rustc_codegen_gcc/src/declare.rs index 7cdbe3c0c62..c1ca3eb849e 100644 --- a/compiler/rustc_codegen_gcc/src/declare.rs +++ b/compiler/rustc_codegen_gcc/src/declare.rs @@ -58,7 +58,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { variadic: bool, ) -> Function<'gcc> { self.linkage.set(FunctionType::Extern); - declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, params, variadic) + declare_raw_fn(self, name, None, return_type, params, variadic) } pub fn declare_global( @@ -92,7 +92,8 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { &self, name: &str, _fn_type: Type<'gcc>, - callconv: (), /*llvm::CCallConv*/ + #[cfg(feature = "master")] callconv: Option<FnAttribute<'gcc>>, + #[cfg(not(feature = "master"))] callconv: Option<()>, ) -> RValue<'gcc> { // TODO(antoyo): use the fn_type parameter. let const_string = self.context.new_type::<u8>().make_pointer().make_pointer(); @@ -123,14 +124,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { #[cfg(feature = "master")] fn_attributes, } = fn_abi.gcc_type(self); - let func = declare_raw_fn( - self, - name, - (), /*fn_abi.llvm_cconv()*/ - return_type, - &arguments_type, - is_c_variadic, - ); + #[cfg(feature = "master")] + let conv = fn_abi.gcc_cconv(self); + #[cfg(not(feature = "master"))] + let conv = None; + let func = declare_raw_fn(self, name, conv, return_type, &arguments_type, is_c_variadic); self.on_stack_function_params.borrow_mut().insert(func, on_stack_param_indices); #[cfg(feature = "master")] for fn_attr in fn_attributes { @@ -162,7 +160,8 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { fn declare_raw_fn<'gcc>( cx: &CodegenCx<'gcc, '_>, name: &str, - _callconv: (), /*llvm::CallConv*/ + #[cfg(feature = "master")] callconv: Option<FnAttribute<'gcc>>, + #[cfg(not(feature = "master"))] _callconv: Option<()>, return_type: Type<'gcc>, param_types: &[Type<'gcc>], variadic: bool, @@ -192,6 +191,10 @@ fn declare_raw_fn<'gcc>( let name = &mangle_name(name); let func = cx.context.new_function(None, cx.linkage.get(), return_type, ¶ms, name, variadic); + #[cfg(feature = "master")] + if let Some(attribute) = callconv { + func.add_attribute(attribute); + } cx.functions.borrow_mut().insert(name.to_string(), func); #[cfg(feature = "master")] diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 6eae0c24f48..202764d5649 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -194,6 +194,7 @@ pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> fn arch_to_gcc(name: &str) -> &str { match name { + "M68000" => "68000", "M68020" => "68020", _ => name, } diff --git a/compiler/rustc_codegen_gcc/src/int.rs b/compiler/rustc_codegen_gcc/src/int.rs index f3552d9b12f..9b5b0fde6e2 100644 --- a/compiler/rustc_codegen_gcc/src/int.rs +++ b/compiler/rustc_codegen_gcc/src/int.rs @@ -404,7 +404,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let ret_indirect = matches!(fn_abi.ret.mode, PassMode::Indirect { .. }); - let result = if ret_indirect { + let call = if ret_indirect { let res_value = self.current_func().new_local(self.location, res_type, "result_value"); let res_addr = res_value.get_address(self.location); let res_param_type = res_type.make_pointer(); @@ -432,8 +432,17 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { ); self.context.new_call(self.location, func, &[lhs, rhs, overflow_addr]) }; - - (result, self.context.new_cast(self.location, overflow_value, self.bool_type).to_rvalue()) + // NOTE: we must assign the result of the operation to a variable at this point to make + // sure it will be evaluated by libgccjit now. + // Otherwise, it will only be evaluated when the rvalue for the call is used somewhere else + // and overflow_value will not be initialized at the correct point in the program. + let result = self.current_func().new_local(self.location, res_type, "result"); + self.block.add_assignment(self.location, result, call); + + ( + result.to_rvalue(), + self.context.new_cast(self.location, overflow_value, self.bool_type).to_rvalue(), + ) } pub fn gcc_icmp( @@ -865,6 +874,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let value_type = value.get_type(); if self.is_native_int_type_or_bool(dest_typ) && self.is_native_int_type_or_bool(value_type) { + // TODO: use self.location. self.context.new_cast(None, value, dest_typ) } else if self.is_native_int_type_or_bool(dest_typ) { self.context.new_cast(None, self.low(value), dest_typ) @@ -905,6 +915,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let name_suffix = match self.type_kind(dest_typ) { TypeKind::Float => "tisf", TypeKind::Double => "tidf", + TypeKind::FP128 => "tixf", kind => panic!("cannot cast a non-native integer to type {:?}", kind), }; let sign = if signed { "" } else { "un" }; diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs index 2d731f88d7d..0eebd21001a 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/llvm.rs @@ -1,11 +1,90 @@ use std::borrow::Cow; -use gccjit::{CType, Context, Function, FunctionPtrType, RValue, ToRValue, UnaryOp}; +use gccjit::{CType, Context, Field, Function, FunctionPtrType, RValue, ToRValue, Type}; use rustc_codegen_ssa::traits::BuilderMethods; use crate::builder::Builder; use crate::context::CodegenCx; +fn encode_key_128_type<'a, 'gcc, 'tcx>( + builder: &Builder<'a, 'gcc, 'tcx>, +) -> (Type<'gcc>, Field<'gcc>, Field<'gcc>) { + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let field1 = builder.context.new_field(None, builder.u32_type, "field1"); + let field2 = builder.context.new_field(None, m128i, "field2"); + let field3 = builder.context.new_field(None, m128i, "field3"); + let field4 = builder.context.new_field(None, m128i, "field4"); + let field5 = builder.context.new_field(None, m128i, "field5"); + let field6 = builder.context.new_field(None, m128i, "field6"); + let field7 = builder.context.new_field(None, m128i, "field7"); + let encode_type = builder.context.new_struct_type( + None, + "EncodeKey128Output", + &[field1, field2, field3, field4, field5, field6, field7], + ); + #[cfg(feature = "master")] + encode_type.as_type().set_packed(); + (encode_type.as_type(), field1, field2) +} + +fn encode_key_256_type<'a, 'gcc, 'tcx>( + builder: &Builder<'a, 'gcc, 'tcx>, +) -> (Type<'gcc>, Field<'gcc>, Field<'gcc>) { + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let field1 = builder.context.new_field(None, builder.u32_type, "field1"); + let field2 = builder.context.new_field(None, m128i, "field2"); + let field3 = builder.context.new_field(None, m128i, "field3"); + let field4 = builder.context.new_field(None, m128i, "field4"); + let field5 = builder.context.new_field(None, m128i, "field5"); + let field6 = builder.context.new_field(None, m128i, "field6"); + let field7 = builder.context.new_field(None, m128i, "field7"); + let field8 = builder.context.new_field(None, m128i, "field8"); + let encode_type = builder.context.new_struct_type( + None, + "EncodeKey256Output", + &[field1, field2, field3, field4, field5, field6, field7, field8], + ); + #[cfg(feature = "master")] + encode_type.as_type().set_packed(); + (encode_type.as_type(), field1, field2) +} + +fn aes_output_type<'a, 'gcc, 'tcx>( + builder: &Builder<'a, 'gcc, 'tcx>, +) -> (Type<'gcc>, Field<'gcc>, Field<'gcc>) { + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let field1 = builder.context.new_field(None, builder.u8_type, "field1"); + let field2 = builder.context.new_field(None, m128i, "field2"); + let aes_output_type = builder.context.new_struct_type(None, "AesOutput", &[field1, field2]); + let typ = aes_output_type.as_type(); + #[cfg(feature = "master")] + typ.set_packed(); + (typ, field1, field2) +} + +fn wide_aes_output_type<'a, 'gcc, 'tcx>( + builder: &Builder<'a, 'gcc, 'tcx>, +) -> (Type<'gcc>, Field<'gcc>, Field<'gcc>) { + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let field1 = builder.context.new_field(None, builder.u8_type, "field1"); + let field2 = builder.context.new_field(None, m128i, "field2"); + let field3 = builder.context.new_field(None, m128i, "field3"); + let field4 = builder.context.new_field(None, m128i, "field4"); + let field5 = builder.context.new_field(None, m128i, "field5"); + let field6 = builder.context.new_field(None, m128i, "field6"); + let field7 = builder.context.new_field(None, m128i, "field7"); + let field8 = builder.context.new_field(None, m128i, "field8"); + let field9 = builder.context.new_field(None, m128i, "field9"); + let aes_output_type = builder.context.new_struct_type( + None, + "WideAesOutput", + &[field1, field2, field3, field4, field5, field6, field7, field8, field9], + ); + #[cfg(feature = "master")] + aes_output_type.as_type().set_packed(); + (aes_output_type.as_type(), field1, field2) +} + #[cfg_attr(not(feature = "master"), allow(unused_variables))] pub fn adjust_function<'gcc>( context: &'gcc Context<'gcc>, @@ -43,7 +122,6 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( gcc_func: FunctionPtrType<'gcc>, mut args: Cow<'b, [RValue<'gcc>]>, func_name: &str, - original_function_name: Option<&String>, ) -> Cow<'b, [RValue<'gcc>]> { // TODO: this might not be a good way to workaround the missing tile builtins. if func_name == "__builtin_trap" { @@ -504,6 +582,72 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( let arg4 = builder.context.new_rvalue_from_int(arg4_type, -1); args = vec![a, b, c, arg4, new_args[3]].into(); } + "__builtin_ia32_encodekey128_u32" => { + let mut new_args = args.to_vec(); + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let array_type = builder.context.new_array_type(None, m128i, 6); + let result = builder.current_func().new_local(None, array_type, "result"); + new_args.push(result.get_address(None)); + args = new_args.into(); + } + "__builtin_ia32_encodekey256_u32" => { + let mut new_args = args.to_vec(); + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let array_type = builder.context.new_array_type(None, m128i, 7); + let result = builder.current_func().new_local(None, array_type, "result"); + new_args.push(result.get_address(None)); + args = new_args.into(); + } + "__builtin_ia32_aesenc128kl_u8" + | "__builtin_ia32_aesdec128kl_u8" + | "__builtin_ia32_aesenc256kl_u8" + | "__builtin_ia32_aesdec256kl_u8" => { + let mut new_args = vec![]; + let m128i = builder.context.new_vector_type(builder.i64_type, 2); + let result = builder.current_func().new_local(None, m128i, "result"); + new_args.push(result.get_address(None)); + new_args.extend(args.to_vec()); + args = new_args.into(); + } + "__builtin_ia32_aesencwide128kl_u8" + | "__builtin_ia32_aesdecwide128kl_u8" + | "__builtin_ia32_aesencwide256kl_u8" + | "__builtin_ia32_aesdecwide256kl_u8" => { + let mut new_args = vec![]; + + let mut old_args = args.to_vec(); + let handle = old_args.swap_remove(0); // Called __P in GCC. + let first_value = old_args.swap_remove(0); + + let element_type = first_value.get_type(); + let array_type = builder.context.new_array_type(None, element_type, 8); + let result = builder.current_func().new_local(None, array_type, "result"); + new_args.push(result.get_address(None)); + + let array = builder.current_func().new_local(None, array_type, "array"); + let input = builder.context.new_array_constructor( + None, + array_type, + &[ + first_value, + old_args.swap_remove(0), + old_args.swap_remove(0), + old_args.swap_remove(0), + old_args.swap_remove(0), + old_args.swap_remove(0), + old_args.swap_remove(0), + old_args.swap_remove(0), + ], + ); + builder.llbb().add_assignment(None, array, input); + let input_ptr = array.get_address(None); + let arg2_type = gcc_func.get_param_type(1); + let input_ptr = builder.context.new_cast(None, input_ptr, arg2_type); + new_args.push(input_ptr); + + new_args.push(handle); + args = new_args.into(); + } _ => (), } } else { @@ -541,33 +685,6 @@ pub fn adjust_intrinsic_arguments<'a, 'b, 'gcc, 'tcx>( let c = builder.context.new_rvalue_from_vector(None, arg3_type, &[new_args[2]; 2]); args = vec![a, b, c, new_args[3]].into(); } - "__builtin_ia32_vfmaddsubpd256" - | "__builtin_ia32_vfmaddsubps" - | "__builtin_ia32_vfmaddsubps256" - | "__builtin_ia32_vfmaddsubpd" => { - if let Some(original_function_name) = original_function_name { - match &**original_function_name { - "llvm.x86.fma.vfmsubadd.pd.256" - | "llvm.x86.fma.vfmsubadd.ps" - | "llvm.x86.fma.vfmsubadd.ps.256" - | "llvm.x86.fma.vfmsubadd.pd" => { - // NOTE: since both llvm.x86.fma.vfmsubadd.ps and llvm.x86.fma.vfmaddsub.ps maps to - // __builtin_ia32_vfmaddsubps, only add minus if this comes from a - // subadd LLVM intrinsic, e.g. _mm256_fmsubadd_pd. - let mut new_args = args.to_vec(); - let arg3 = &mut new_args[2]; - *arg3 = builder.context.new_unary_op( - None, - UnaryOp::Minus, - arg3.get_type(), - *arg3, - ); - args = new_args.into(); - } - _ => (), - } - } - } "__builtin_ia32_ldmxcsr" => { // The builtin __builtin_ia32_ldmxcsr takes an integer value while llvm.x86.sse.ldmxcsr takes a pointer, // so dereference the pointer. @@ -728,6 +845,96 @@ pub fn adjust_intrinsic_return_value<'a, 'gcc, 'tcx>( let f16_type = builder.context.new_c_type(CType::Float16); return_value = builder.context.new_cast(None, return_value, f16_type); } + "__builtin_ia32_encodekey128_u32" => { + // The builtin __builtin_ia32_encodekey128_u32 writes the result in its pointer argument while + // llvm.x86.encodekey128 returns a value. + // We added a result pointer argument and now need to assign its value to the return_value expected by + // the LLVM intrinsic. + let (encode_type, field1, field2) = encode_key_128_type(builder); + let result = builder.current_func().new_local(None, encode_type, "result"); + let field1 = result.access_field(None, field1); + builder.llbb().add_assignment(None, field1, return_value); + let field2 = result.access_field(None, field2); + let field2_type = field2.to_rvalue().get_type(); + let array_type = builder.context.new_array_type(None, field2_type, 6); + let ptr = builder.context.new_cast(None, args[2], array_type.make_pointer()); + let field2_ptr = + builder.context.new_cast(None, field2.get_address(None), array_type.make_pointer()); + builder.llbb().add_assignment( + None, + field2_ptr.dereference(None), + ptr.dereference(None), + ); + return_value = result.to_rvalue(); + } + "__builtin_ia32_encodekey256_u32" => { + // The builtin __builtin_ia32_encodekey256_u32 writes the result in its pointer argument while + // llvm.x86.encodekey256 returns a value. + // We added a result pointer argument and now need to assign its value to the return_value expected by + // the LLVM intrinsic. + let (encode_type, field1, field2) = encode_key_256_type(builder); + let result = builder.current_func().new_local(None, encode_type, "result"); + let field1 = result.access_field(None, field1); + builder.llbb().add_assignment(None, field1, return_value); + let field2 = result.access_field(None, field2); + let field2_type = field2.to_rvalue().get_type(); + let array_type = builder.context.new_array_type(None, field2_type, 7); + let ptr = builder.context.new_cast(None, args[3], array_type.make_pointer()); + let field2_ptr = + builder.context.new_cast(None, field2.get_address(None), array_type.make_pointer()); + builder.llbb().add_assignment( + None, + field2_ptr.dereference(None), + ptr.dereference(None), + ); + return_value = result.to_rvalue(); + } + "__builtin_ia32_aesdec128kl_u8" + | "__builtin_ia32_aesenc128kl_u8" + | "__builtin_ia32_aesdec256kl_u8" + | "__builtin_ia32_aesenc256kl_u8" => { + // The builtin for aesdec/aesenc writes the result in its pointer argument while + // llvm.x86.aesdec128kl returns a value. + // We added a result pointer argument and now need to assign its value to the return_value expected by + // the LLVM intrinsic. + let (aes_output_type, field1, field2) = aes_output_type(builder); + let result = builder.current_func().new_local(None, aes_output_type, "result"); + let field1 = result.access_field(None, field1); + builder.llbb().add_assignment(None, field1, return_value); + let field2 = result.access_field(None, field2); + let ptr = builder.context.new_cast( + None, + args[0], + field2.to_rvalue().get_type().make_pointer(), + ); + builder.llbb().add_assignment(None, field2, ptr.dereference(None)); + return_value = result.to_rvalue(); + } + "__builtin_ia32_aesencwide128kl_u8" + | "__builtin_ia32_aesdecwide128kl_u8" + | "__builtin_ia32_aesencwide256kl_u8" + | "__builtin_ia32_aesdecwide256kl_u8" => { + // The builtin for aesdecwide/aesencwide writes the result in its pointer argument while + // llvm.x86.aesencwide128kl returns a value. + // We added a result pointer argument and now need to assign its value to the return_value expected by + // the LLVM intrinsic. + let (aes_output_type, field1, field2) = wide_aes_output_type(builder); + let result = builder.current_func().new_local(None, aes_output_type, "result"); + let field1 = result.access_field(None, field1); + builder.llbb().add_assignment(None, field1, return_value); + let field2 = result.access_field(None, field2); + let field2_type = field2.to_rvalue().get_type(); + let array_type = builder.context.new_array_type(None, field2_type, 8); + let ptr = builder.context.new_cast(None, args[0], array_type.make_pointer()); + let field2_ptr = + builder.context.new_cast(None, field2.get_address(None), array_type.make_pointer()); + builder.llbb().add_assignment( + None, + field2_ptr.dereference(None), + ptr.dereference(None), + ); + return_value = result.to_rvalue(); + } _ => (), } @@ -915,16 +1122,6 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.ctlz.v4i64" => "__builtin_ia32_vplzcntq_256_mask", "llvm.ctlz.v2i64" => "__builtin_ia32_vplzcntq_128_mask", "llvm.ctpop.v32i16" => "__builtin_ia32_vpopcountw_v32hi", - "llvm.x86.fma.vfmsub.sd" => "__builtin_ia32_vfmsubsd3", - "llvm.x86.fma.vfmsub.ss" => "__builtin_ia32_vfmsubss3", - "llvm.x86.fma.vfmsubadd.pd" => "__builtin_ia32_vfmaddsubpd", - "llvm.x86.fma.vfmsubadd.pd.256" => "__builtin_ia32_vfmaddsubpd256", - "llvm.x86.fma.vfmsubadd.ps" => "__builtin_ia32_vfmaddsubps", - "llvm.x86.fma.vfmsubadd.ps.256" => "__builtin_ia32_vfmaddsubps256", - "llvm.x86.fma.vfnmadd.sd" => "__builtin_ia32_vfnmaddsd3", - "llvm.x86.fma.vfnmadd.ss" => "__builtin_ia32_vfnmaddss3", - "llvm.x86.fma.vfnmsub.sd" => "__builtin_ia32_vfnmsubsd3", - "llvm.x86.fma.vfnmsub.ss" => "__builtin_ia32_vfnmsubss3", "llvm.x86.avx512.conflict.d.512" => "__builtin_ia32_vpconflictsi_512_mask", "llvm.x86.avx512.conflict.d.256" => "__builtin_ia32_vpconflictsi_256_mask", "llvm.x86.avx512.conflict.d.128" => "__builtin_ia32_vpconflictsi_128_mask", @@ -1002,8 +1199,6 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.fshr.v32i16" => "__builtin_ia32_vpshrdv_v32hi", "llvm.fshr.v16i16" => "__builtin_ia32_vpshrdv_v16hi", "llvm.fshr.v8i16" => "__builtin_ia32_vpshrdv_v8hi", - "llvm.x86.fma.vfmadd.sd" => "__builtin_ia32_vfmaddsd3", - "llvm.x86.fma.vfmadd.ss" => "__builtin_ia32_vfmaddss3", "llvm.x86.rdrand.64" => "__builtin_ia32_rdrand64_step", // The above doc points to unknown builtins for the following, so override them: @@ -1324,6 +1519,16 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function "llvm.x86.avx512fp16.mask.vfmadd.cph.256" => "__builtin_ia32_vfmaddcph256_mask3", "llvm.x86.avx512fp16.mask.vfcmadd.cph.128" => "__builtin_ia32_vfcmaddcph128_mask3", "llvm.x86.avx512fp16.mask.vfmadd.cph.128" => "__builtin_ia32_vfmaddcph128_mask3", + "llvm.x86.encodekey128" => "__builtin_ia32_encodekey128_u32", + "llvm.x86.encodekey256" => "__builtin_ia32_encodekey256_u32", + "llvm.x86.aesenc128kl" => "__builtin_ia32_aesenc128kl_u8", + "llvm.x86.aesdec128kl" => "__builtin_ia32_aesdec128kl_u8", + "llvm.x86.aesenc256kl" => "__builtin_ia32_aesenc256kl_u8", + "llvm.x86.aesdec256kl" => "__builtin_ia32_aesdec256kl_u8", + "llvm.x86.aesencwide128kl" => "__builtin_ia32_aesencwide128kl_u8", + "llvm.x86.aesdecwide128kl" => "__builtin_ia32_aesdecwide128kl_u8", + "llvm.x86.aesencwide256kl" => "__builtin_ia32_aesencwide256kl_u8", + "llvm.x86.aesdecwide256kl" => "__builtin_ia32_aesdecwide256kl_u8", // TODO: support the tile builtins: "llvm.x86.ldtilecfg" => "__builtin_trap", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index f38622074f1..d22f4229e23 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -78,6 +78,7 @@ fn get_simple_intrinsic<'gcc, 'tcx>( sym::maxnumf64 => "fmax", sym::copysignf32 => "copysignf", sym::copysignf64 => "copysign", + sym::copysignf128 => "copysignl", sym::floorf32 => "floorf", sym::floorf64 => "floor", sym::ceilf32 => "ceilf", diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index 8b454ab2a42..b897d079249 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -399,7 +399,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( } #[cfg(feature = "master")] - if name == sym::simd_insert { + if name == sym::simd_insert || name == sym::simd_insert_dyn { require!( in_elem == arg_tys[2], InvalidMonomorphization::InsertedType { @@ -410,6 +410,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( out_ty: arg_tys[2] } ); + + // TODO(antoyo): For simd_insert, check if the index is a constant of the correct size. let vector = args[0].immediate(); let index = args[1].immediate(); let value = args[2].immediate(); @@ -422,13 +424,15 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( } #[cfg(feature = "master")] - if name == sym::simd_extract { + if name == sym::simd_extract || name == sym::simd_extract_dyn { require!( ret_ty == in_elem, InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } ); + // TODO(antoyo): For simd_extract, check if the index is a constant of the correct size. let vector = args[0].immediate(); - return Ok(bx.context.new_vector_access(None, vector, args[1].immediate()).to_rvalue()); + let index = args[1].immediate(); + return Ok(bx.context.new_vector_access(None, vector, index).to_rvalue()); } if name == sym::simd_select { @@ -443,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())); } @@ -987,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 } ); } } @@ -1105,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 } ); } } diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index bfa23174a19..624fdb4043c 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -188,10 +188,10 @@ impl CodegenBackend for GccCodegenBackend { crate::DEFAULT_LOCALE_RESOURCE } - fn init(&self, sess: &Session) { + fn init(&self, _sess: &Session) { #[cfg(feature = "master")] { - let target_cpu = target_cpu(sess); + let target_cpu = target_cpu(_sess); // Get the second TargetInfo with the correct CPU features by setting the arch. let context = Context::default(); diff --git a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt index 082958bfe1f..499c1a96231 100644 --- a/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt +++ b/compiler/rustc_codegen_gcc/tests/failing-ui-tests.txt @@ -1,11 +1,9 @@ tests/ui/allocator/no_std-alloc-error-handler-custom.rs tests/ui/allocator/no_std-alloc-error-handler-default.rs tests/ui/asm/may_unwind.rs -tests/ui/asm/x86_64/multiple-clobber-abi.rs tests/ui/functions-closures/parallel-codegen-closures.rs tests/ui/linkage-attr/linkage1.rs tests/ui/lto/dylib-works.rs -tests/ui/numbers-arithmetic/saturating-float-casts.rs tests/ui/sepcomp/sepcomp-cci.rs tests/ui/sepcomp/sepcomp-extern.rs tests/ui/sepcomp/sepcomp-fns-backwards.rs @@ -33,7 +31,6 @@ tests/ui/unwind-no-uwtable.rs tests/ui/parser/unclosed-delimiter-in-dep.rs tests/ui/consts/missing_span_in_backtrace.rs tests/ui/drop/dynamic-drop.rs -tests/ui/issues/issue-40883.rs tests/ui/issues/issue-43853.rs tests/ui/issues/issue-47364.rs tests/ui/macros/rfc-2011-nicer-assert-messages/assert-without-captures-does-not-create-unnecessary-code.rs @@ -102,14 +99,12 @@ tests/ui/codegen/equal-pointers-unequal/as-cast/basic.rs tests/ui/codegen/equal-pointers-unequal/as-cast/inline1.rs tests/ui/codegen/equal-pointers-unequal/as-cast/print.rs tests/ui/codegen/equal-pointers-unequal/as-cast/inline2.rs -tests/ui/codegen/equal-pointers-unequal/as-cast/print3.rs tests/ui/codegen/equal-pointers-unequal/as-cast/segfault.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/function.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/basic.rs tests/ui/codegen/equal-pointers-unequal/as-cast/zero.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline1.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print.rs -tests/ui/codegen/equal-pointers-unequal/exposed-provenance/print3.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/inline2.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/segfault.rs tests/ui/codegen/equal-pointers-unequal/exposed-provenance/zero.rs @@ -117,8 +112,9 @@ tests/ui/codegen/equal-pointers-unequal/strict-provenance/basic.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/function.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/print.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline1.rs -tests/ui/codegen/equal-pointers-unequal/strict-provenance/print3.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/inline2.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/segfault.rs tests/ui/codegen/equal-pointers-unequal/strict-provenance/zero.rs tests/ui/simd/simd-bitmask-notpow2.rs +tests/ui/codegen/StackColoring-not-blowup-stack-issue-40883.rs +tests/ui/uninhabited/uninhabited-transparent-return-abi.rs diff --git a/compiler/rustc_codegen_gcc/tests/run/abort1.rs b/compiler/rustc_codegen_gcc/tests/run/abort1.rs index fe46d9ae418..ff2bb75ece2 100644 --- a/compiler/rustc_codegen_gcc/tests/run/abort1.rs +++ b/compiler/rustc_codegen_gcc/tests/run/abort1.rs @@ -3,45 +3,13 @@ // Run-time: // status: signal -#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -mod intrinsics { - use super::Sized; - - #[rustc_nounwind] - #[rustc_intrinsic] - pub fn abort() -> !; -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; fn test_fail() -> ! { unsafe { intrinsics::abort() }; diff --git a/compiler/rustc_codegen_gcc/tests/run/abort2.rs b/compiler/rustc_codegen_gcc/tests/run/abort2.rs index 4123f4f4bee..781f518e0b2 100644 --- a/compiler/rustc_codegen_gcc/tests/run/abort2.rs +++ b/compiler/rustc_codegen_gcc/tests/run/abort2.rs @@ -3,45 +3,13 @@ // Run-time: // status: signal -#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -mod intrinsics { - use super::Sized; - - #[rustc_nounwind] - #[rustc_intrinsic] - pub fn abort() -> !; -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; fn fail() -> i32 { unsafe { intrinsics::abort() }; diff --git a/compiler/rustc_codegen_gcc/tests/run/array.rs b/compiler/rustc_codegen_gcc/tests/run/array.rs index e18a4ced6bc..3ab0c309fde 100644 --- a/compiler/rustc_codegen_gcc/tests/run/array.rs +++ b/compiler/rustc_codegen_gcc/tests/run/array.rs @@ -8,20 +8,12 @@ // 10 #![feature(no_core)] - #![no_std] #![no_core] #![no_main] extern crate mini_core; - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - pub fn puts(s: *const u8) -> i32; - } -} +use mini_core::*; static mut ONE: usize = 1; diff --git a/compiler/rustc_codegen_gcc/tests/run/asm.rs b/compiler/rustc_codegen_gcc/tests/run/asm.rs index 4e05d026868..2dbf43be664 100644 --- a/compiler/rustc_codegen_gcc/tests/run/asm.rs +++ b/compiler/rustc_codegen_gcc/tests/run/asm.rs @@ -174,6 +174,59 @@ fn asm() { mem_cpy(array2.as_mut_ptr(), array1.as_ptr(), 3); } assert_eq!(array1, array2); + + // in and clobber registers cannot overlap. This tests that the lateout register without an + // output place (indicated by the `_`) is not added to the list of clobbered registers + let x = 8; + let y: i32; + unsafe { + asm!( + "mov rax, rdi", + in("rdi") x, + lateout("rdi") _, + out("rax") y, + ); + } + assert_eq!((x, y), (8, 8)); + + // sysv64 is the default calling convention on unix systems. The rdi register is + // used to pass arguments in the sysv64 calling convention, so this register will be clobbered + #[cfg(unix)] + { + let x = 16; + let y: i32; + unsafe { + asm!( + "mov rax, rdi", + in("rdi") x, + out("rax") y, + clobber_abi("sysv64"), + ); + } + assert_eq!((x, y), (16, 16)); + } + + // the `b` suffix for registers in the `reg_byte` register class is not supported in GCC + // and needs to be stripped in order to use these registers. + unsafe { + core::arch::asm!( + "", + out("al") _, + out("bl") _, + out("cl") _, + out("dl") _, + out("sil") _, + out("dil") _, + out("r8b") _, + out("r9b") _, + out("r10b") _, + out("r11b") _, + out("r12b") _, + out("r13b") _, + out("r14b") _, + out("r15b") _, + ); + } } #[cfg(not(target_arch = "x86_64"))] diff --git a/compiler/rustc_codegen_gcc/tests/run/assign.rs b/compiler/rustc_codegen_gcc/tests/run/assign.rs index 286155852d5..4535ab5778e 100644 --- a/compiler/rustc_codegen_gcc/tests/run/assign.rs +++ b/compiler/rustc_codegen_gcc/tests/run/assign.rs @@ -5,130 +5,13 @@ // 7 8 // 10 -#![allow(internal_features, unused_attributes)] -#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs, track_caller)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for *mut i32 {} -impl Copy for usize {} -impl Copy for u8 {} -impl Copy for i8 {} -impl Copy for i32 {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -#[lang = "panic_location"] -struct PanicLocation { - file: &'static str, - line: u32, - column: u32, -} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn puts(s: *const u8) -> i32; - pub fn fflush(stream: *mut i32) -> i32; - pub fn printf(format: *const i8, ...) -> i32; - - pub static stdout: *mut i32; - } -} - -mod intrinsics { - #[rustc_nounwind] - #[rustc_intrinsic] - pub fn abort() -> !; -} - -#[lang = "panic"] -#[track_caller] -#[no_mangle] -pub fn panic(_msg: &'static str) -> ! { - unsafe { - libc::puts("Panicking\0" as *const str as *const u8); - libc::fflush(libc::stdout); - intrinsics::abort(); - } -} - -#[lang = "add"] -trait Add<RHS = Self> { - type Output; - - fn add(self, rhs: RHS) -> Self::Output; -} - -impl Add for u8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i32 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for usize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for isize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -#[track_caller] -#[lang = "panic_const_add_overflow"] -pub fn panic_const_add_overflow() -> ! { - panic("attempt to add with overflow"); -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; fn inc_ref(num: &mut isize) -> isize { *num = *num + 5; @@ -139,9 +22,8 @@ fn inc(num: isize) -> isize { num + 1 } - #[no_mangle] -extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { +extern "C" fn main(mut argc: isize, _argv: *const *const u8) -> i32 { argc = inc(argc); unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc); diff --git a/compiler/rustc_codegen_gcc/tests/run/closure.rs b/compiler/rustc_codegen_gcc/tests/run/closure.rs index c7a236f74f9..a8a3fadfed4 100644 --- a/compiler/rustc_codegen_gcc/tests/run/closure.rs +++ b/compiler/rustc_codegen_gcc/tests/run/closure.rs @@ -9,55 +9,38 @@ // Both args: 11 #![feature(no_core)] - #![no_std] #![no_core] #![no_main] extern crate mini_core; - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} +use mini_core::*; #[no_mangle] -extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { +extern "C" fn main(argc: isize, _argv: *const *const u8) -> i32 { let string = "Arg: %d\n\0"; - let mut closure = || { - unsafe { - libc::printf(string as *const str as *const i8, argc); - } + let mut closure = || unsafe { + libc::printf(string as *const str as *const i8, argc); }; closure(); - let mut closure = || { - unsafe { - libc::printf("Argument: %d\n\0" as *const str as *const i8, argc); - } + let mut closure = || unsafe { + libc::printf("Argument: %d\n\0" as *const str as *const i8, argc); }; closure(); - let mut closure = |string| { - unsafe { - libc::printf(string as *const str as *const i8, argc); - } + let mut closure = |string| unsafe { + libc::printf(string as *const str as *const i8, argc); }; closure("String arg: %d\n\0"); - let mut closure = |arg: isize| { - unsafe { - libc::printf("Int argument: %d\n\0" as *const str as *const i8, arg); - } + let mut closure = |arg: isize| unsafe { + libc::printf("Int argument: %d\n\0" as *const str as *const i8, arg); }; closure(argc + 1); - let mut closure = |string, arg: isize| { - unsafe { - libc::printf(string as *const str as *const i8, arg); - } + let mut closure = |string, arg: isize| unsafe { + libc::printf(string as *const str as *const i8, arg); }; closure("Both args: %d\n\0", argc + 10); diff --git a/compiler/rustc_codegen_gcc/tests/run/condition.rs b/compiler/rustc_codegen_gcc/tests/run/condition.rs index b02359702ed..bd3b6f7497f 100644 --- a/compiler/rustc_codegen_gcc/tests/run/condition.rs +++ b/compiler/rustc_codegen_gcc/tests/run/condition.rs @@ -6,19 +6,12 @@ // 1 #![feature(no_core)] - #![no_std] #![no_core] #![no_main] extern crate mini_core; - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} +use mini_core::*; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { @@ -27,15 +20,14 @@ extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { libc::printf(b"true\n\0" as *const u8 as *const i8); } - let string = - match argc { - 1 => b"1\n\0", - 2 => b"2\n\0", - 3 => b"3\n\0", - 4 => b"4\n\0", - 5 => b"5\n\0", - _ => b"_\n\0", - }; + let string = match argc { + 1 => b"1\n\0", + 2 => b"2\n\0", + 3 => b"3\n\0", + 4 => b"4\n\0", + 5 => b"5\n\0", + _ => b"_\n\0", + }; libc::printf(string as *const u8 as *const i8); } 0 diff --git a/compiler/rustc_codegen_gcc/tests/run/empty_main.rs b/compiler/rustc_codegen_gcc/tests/run/empty_main.rs index 042e44080c5..fe3df5a2389 100644 --- a/compiler/rustc_codegen_gcc/tests/run/empty_main.rs +++ b/compiler/rustc_codegen_gcc/tests/run/empty_main.rs @@ -3,37 +3,13 @@ // Run-time: // status: 0 -#![feature(auto_traits, lang_items, no_core)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { diff --git a/compiler/rustc_codegen_gcc/tests/run/exit.rs b/compiler/rustc_codegen_gcc/tests/run/exit.rs index 9a7c91c0adb..e0a59174bd3 100644 --- a/compiler/rustc_codegen_gcc/tests/run/exit.rs +++ b/compiler/rustc_codegen_gcc/tests/run/exit.rs @@ -3,44 +3,13 @@ // Run-time: // status: 2 -#![feature(auto_traits, lang_items, no_core, intrinsics)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -mod libc { - #[link(name = "c")] - extern "C" { - pub fn exit(status: i32); - } -} - -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { diff --git a/compiler/rustc_codegen_gcc/tests/run/exit_code.rs b/compiler/rustc_codegen_gcc/tests/run/exit_code.rs index c50d2b0d710..376824da845 100644 --- a/compiler/rustc_codegen_gcc/tests/run/exit_code.rs +++ b/compiler/rustc_codegen_gcc/tests/run/exit_code.rs @@ -3,37 +3,13 @@ // Run-time: // status: 1 -#![feature(auto_traits, lang_items, no_core)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { diff --git a/compiler/rustc_codegen_gcc/tests/run/float.rs b/compiler/rustc_codegen_gcc/tests/run/float.rs new file mode 100644 index 00000000000..424fa1cf4ad --- /dev/null +++ b/compiler/rustc_codegen_gcc/tests/run/float.rs @@ -0,0 +1,28 @@ +// Compiler: +// +// Run-time: +// status: 0 + +#![feature(const_black_box)] + +fn main() { + use std::hint::black_box; + + macro_rules! check { + ($ty:ty, $expr:expr) => {{ + const EXPECTED: $ty = $expr; + assert_eq!($expr, EXPECTED); + }}; + } + + check!(i32, (black_box(0.0f32) as i32)); + + check!(u64, (black_box(f32::NAN) as u64)); + check!(u128, (black_box(f32::NAN) as u128)); + + check!(i64, (black_box(f64::NAN) as i64)); + check!(u64, (black_box(f64::NAN) as u64)); + + check!(i16, (black_box(f32::MIN) as i16)); + check!(i16, (black_box(f32::MAX) as i16)); +} diff --git a/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs b/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs index 98b351e5044..93b9baee1b2 100644 --- a/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs +++ b/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs @@ -5,19 +5,12 @@ // stdout: 1 #![feature(no_core)] - #![no_std] #![no_core] #![no_main] extern crate mini_core; - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} +use mini_core::*; fn i16_as_i8(a: i16) -> i8 { a as i8 diff --git a/compiler/rustc_codegen_gcc/tests/run/int.rs b/compiler/rustc_codegen_gcc/tests/run/int.rs index 58a26801b67..47b5dea46f8 100644 --- a/compiler/rustc_codegen_gcc/tests/run/int.rs +++ b/compiler/rustc_codegen_gcc/tests/run/int.rs @@ -3,9 +3,7 @@ // Run-time: // status: 0 -/* - * Code - */ +#![feature(const_black_box)] fn main() { use std::hint::black_box; diff --git a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs index b0215860406..fa50d5bc5d3 100644 --- a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs +++ b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs @@ -1,4 +1,3 @@ - // Compiler: // // Run-time: @@ -7,139 +6,20 @@ // 6 // 11 -#![allow(internal_features, unused_attributes)] -#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs, track_caller)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for *mut i32 {} -impl Copy for usize {} -impl Copy for u8 {} -impl Copy for i8 {} -impl Copy for i32 {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -#[lang = "panic_location"] -struct PanicLocation { - file: &'static str, - line: u32, - column: u32, -} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn puts(s: *const u8) -> i32; - pub fn fflush(stream: *mut i32) -> i32; - pub fn printf(format: *const i8, ...) -> i32; - - pub static stdout: *mut i32; - } -} - -mod intrinsics { - #[rustc_nounwind] - #[rustc_intrinsic] - pub fn abort() -> !; -} - -#[lang = "panic"] -#[track_caller] -#[no_mangle] -pub fn panic(_msg: &'static str) -> ! { - unsafe { - libc::puts("Panicking\0" as *const str as *const u8); - libc::fflush(libc::stdout); - intrinsics::abort(); - } -} - -#[lang = "add"] -trait Add<RHS = Self> { - type Output; - - fn add(self, rhs: RHS) -> Self::Output; -} - -impl Add for u8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i32 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for usize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for isize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -#[track_caller] -#[lang = "panic_const_add_overflow"] -pub fn panic_const_add_overflow() -> ! { - panic("attempt to add with overflow"); -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; struct Test { field: isize, } fn test(num: isize) -> Test { - Test { - field: num + 1, - } + Test { field: num + 1 } } fn update_num(num: &mut isize) { @@ -147,7 +27,7 @@ fn update_num(num: &mut isize) { } #[no_mangle] -extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { +extern "C" fn main(mut argc: isize, _argv: *const *const u8) -> i32 { let mut test = test(argc); unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field); diff --git a/compiler/rustc_codegen_gcc/tests/run/operations.rs b/compiler/rustc_codegen_gcc/tests/run/operations.rs index 8ba7a4c5ed8..a1b0772f76b 100644 --- a/compiler/rustc_codegen_gcc/tests/run/operations.rs +++ b/compiler/rustc_codegen_gcc/tests/run/operations.rs @@ -5,229 +5,13 @@ // 39 // 10 -#![allow(internal_features, unused_attributes)] -#![feature(auto_traits, lang_items, no_core, intrinsics, arbitrary_self_types, rustc_attrs)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl Copy for *mut i32 {} -impl Copy for usize {} -impl Copy for u8 {} -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} - -#[lang = "deref"] -pub trait Deref { - type Target: ?Sized; - - fn deref(&self) -> &Self::Target; -} - -#[lang = "legacy_receiver"] -trait LegacyReceiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -#[lang = "panic_location"] -struct PanicLocation { - file: &'static str, - line: u32, - column: u32, -} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - pub fn puts(s: *const u8) -> i32; - pub fn fflush(stream: *mut i32) -> i32; - - pub static stdout: *mut i32; - } -} - -mod intrinsics { - #[rustc_nounwind] - #[rustc_intrinsic] - pub fn abort() -> !; -} - -#[lang = "panic"] -#[track_caller] -#[no_mangle] -pub fn panic(_msg: &'static str) -> ! { - unsafe { - libc::puts("Panicking\0" as *const str as *const u8); - libc::fflush(libc::stdout); - intrinsics::abort(); - } -} - -#[lang = "add"] -trait Add<RHS = Self> { - type Output; - - fn add(self, rhs: RHS) -> Self::Output; -} - -impl Add for u8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i8 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for i32 { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for usize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -impl Add for isize { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - self + rhs - } -} - -#[lang = "sub"] -pub trait Sub<RHS = Self> { - type Output; - - fn sub(self, rhs: RHS) -> Self::Output; -} - -impl Sub for usize { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for isize { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for u8 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for i8 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -impl Sub for i16 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self { - self - rhs - } -} - -#[lang = "mul"] -pub trait Mul<RHS = Self> { - type Output; - - #[must_use] - fn mul(self, rhs: RHS) -> Self::Output; -} - -impl Mul for u8 { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - self * rhs - } -} - -impl Mul for usize { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - self * rhs - } -} - -impl Mul for isize { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - self * rhs - } -} - -#[track_caller] -#[lang = "panic_const_add_overflow"] -pub fn panic_const_add_overflow() -> ! { - panic("attempt to add with overflow"); -} - -#[track_caller] -#[lang = "panic_const_sub_overflow"] -pub fn panic_const_sub_overflow() -> ! { - panic("attempt to subtract with overflow"); -} - -#[track_caller] -#[lang = "panic_const_mul_overflow"] -pub fn panic_const_mul_overflow() -> ! { - panic("attempt to multiply with overflow"); -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { diff --git a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs index 0ba49e7187f..c1254c51ce9 100644 --- a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs +++ b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs @@ -2,35 +2,32 @@ // // Run-time: // status: 0 -// stdout: 1 +// stdout: 10 +// 10 +// 42 #![feature(no_core)] - #![no_std] #![no_core] #![no_main] extern crate mini_core; +use mini_core::*; -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} - -static mut ONE: usize = 1; - -fn make_array() -> [u8; 3] { - [42, 10, 5] +fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u32) { + ( + a as u8, a as u16, a as u32, a as usize, a as i8, a as i16, a as i32, a as isize, b as u8, + b as u32, + ) } #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { + let (a, b, c, d, e, f, g, h, i, j) = int_cast(10, 42); unsafe { - let ptr = ONE as *mut usize; - let value = ptr as usize; - libc::printf(b"%ld\n\0" as *const u8 as *const i8, value); + libc::printf(b"%d\n\0" as *const u8 as *const i8, c); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, d); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, j); } 0 } diff --git a/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs b/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs index 3cc1e274001..c1254c51ce9 100644 --- a/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs +++ b/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs @@ -6,54 +6,13 @@ // 10 // 42 -#![feature(auto_traits, lang_items, no_core, intrinsics)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -#[lang = "copy"] -pub unsafe trait Copy {} - -impl Copy for bool {} -impl Copy for u8 {} -impl Copy for u16 {} -impl Copy for u32 {} -impl Copy for u64 {} -impl Copy for usize {} -impl Copy for i8 {} -impl Copy for i16 {} -impl Copy for i32 {} -impl Copy for isize {} -impl Copy for f32 {} -impl Copy for char {} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} - -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "legacy_receiver"] -trait LegacyReceiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u32) { ( diff --git a/compiler/rustc_codegen_gcc/tests/run/slice.rs b/compiler/rustc_codegen_gcc/tests/run/slice.rs index 825fcb8a081..449ccabef7f 100644 --- a/compiler/rustc_codegen_gcc/tests/run/slice.rs +++ b/compiler/rustc_codegen_gcc/tests/run/slice.rs @@ -5,26 +5,17 @@ // stdout: 5 #![feature(no_core)] - #![no_std] #![no_core] #![no_main] extern crate mini_core; - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} +use mini_core::*; static mut TWO: usize = 2; fn index_slice(s: &[u32]) -> u32 { - unsafe { - s[TWO] - } + unsafe { s[TWO] } } #[no_mangle] diff --git a/compiler/rustc_codegen_gcc/tests/run/static.rs b/compiler/rustc_codegen_gcc/tests/run/static.rs index c3c8121b1e1..1e36cf4f3d3 100644 --- a/compiler/rustc_codegen_gcc/tests/run/static.rs +++ b/compiler/rustc_codegen_gcc/tests/run/static.rs @@ -9,70 +9,13 @@ // 12 // 1 -#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "destruct"] -pub trait Destruct {} - -#[lang = "drop"] -pub trait Drop {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} -impl<T: ?Sized> Copy for *mut T {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -mod intrinsics { - use super::Sized; - - #[rustc_nounwind] - #[rustc_intrinsic] - pub fn abort() -> !; -} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} - -#[lang = "structural_peq"] -pub trait StructuralPartialEq {} - -#[lang = "drop_in_place"] -#[allow(unconditional_recursion)] -pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) { - // Code here does not matter - this is replaced by the - // real drop glue by the compiler. - drop_in_place(to_drop); -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; struct Test { field: isize, @@ -84,20 +27,14 @@ struct WithRef { static mut CONSTANT: isize = 10; -static mut TEST: Test = Test { - field: 12, -}; +static mut TEST: Test = Test { field: 12 }; -static mut TEST2: Test = Test { - field: 14, -}; +static mut TEST2: Test = Test { field: 14 }; -static mut WITH_REF: WithRef = WithRef { - refe: unsafe { &TEST }, -}; +static mut WITH_REF: WithRef = WithRef { refe: unsafe { &TEST } }; #[no_mangle] -extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { +extern "C" fn main(argc: isize, _argv: *const *const u8) -> i32 { unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, CONSTANT); libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST2.field); diff --git a/compiler/rustc_codegen_gcc/tests/run/structs.rs b/compiler/rustc_codegen_gcc/tests/run/structs.rs index 59b8f358863..da73cbed9ae 100644 --- a/compiler/rustc_codegen_gcc/tests/run/structs.rs +++ b/compiler/rustc_codegen_gcc/tests/run/structs.rs @@ -5,44 +5,13 @@ // stdout: 1 // 2 -#![feature(auto_traits, lang_items, no_core, intrinsics)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; struct Test { field: isize, diff --git a/compiler/rustc_codegen_gcc/tests/run/tuple.rs b/compiler/rustc_codegen_gcc/tests/run/tuple.rs index ed60a56a68c..e0f2e95f628 100644 --- a/compiler/rustc_codegen_gcc/tests/run/tuple.rs +++ b/compiler/rustc_codegen_gcc/tests/run/tuple.rs @@ -4,44 +4,13 @@ // status: 0 // stdout: 3 -#![feature(auto_traits, lang_items, no_core, intrinsics)] -#![allow(internal_features)] - +#![feature(no_core)] #![no_std] #![no_core] #![no_main] -/* - * Core - */ - -// Because we don't have core yet. -#[lang = "sized"] -pub trait Sized {} - -#[lang = "copy"] -trait Copy { -} - -impl Copy for isize {} - -#[lang = "receiver"] -trait Receiver { -} - -#[lang = "freeze"] -pub(crate) unsafe auto trait Freeze {} - -mod libc { - #[link(name = "c")] - extern "C" { - pub fn printf(format: *const i8, ...) -> i32; - } -} - -/* - * Code - */ +extern crate mini_core; +use mini_core::*; #[no_mangle] extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index ae2ab32ef53..56fb12d3c22 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -247,6 +247,16 @@ pub(super) fn stub<'ll, 'tcx>( StubInfo { metadata, unique_type_id } } +struct AdtStackPopGuard<'ll, 'tcx, 'a> { + cx: &'a CodegenCx<'ll, 'tcx>, +} + +impl<'ll, 'tcx, 'a> Drop for AdtStackPopGuard<'ll, 'tcx, 'a> { + fn drop(&mut self) { + debug_context(self.cx).adt_stack.borrow_mut().pop(); + } +} + /// This function enables creating debuginfo nodes that can recursively refer to themselves. /// It will first insert the given stub into the type map and only then execute the `members` /// and `generics` closures passed in. These closures have access to the stub so they can @@ -261,6 +271,44 @@ pub(super) fn build_type_with_children<'ll, 'tcx>( ) -> DINodeCreationResult<'ll> { assert_eq!(debug_context(cx).type_map.di_node_for_unique_id(stub_info.unique_type_id), None); + let mut _adt_stack_pop_guard = None; + if let UniqueTypeId::Ty(ty, ..) = stub_info.unique_type_id + && let ty::Adt(adt_def, args) = ty.kind() + { + let def_id = adt_def.did(); + // If any sub type reference the original type definition and the sub type has a type + // parameter that strictly contains the original parameter, the original type is a recursive + // type that can expanding indefinitely. Example, + // ``` + // enum Recursive<T> { + // Recurse(*const Recursive<Wrap<T>>), + // Item(T), + // } + // ``` + let is_expanding_recursive = + debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| { + if def_id == *parent_def_id { + args.iter().zip(parent_args.iter()).any(|(arg, parent_arg)| { + if let (Some(arg), Some(parent_arg)) = (arg.as_type(), parent_arg.as_type()) + { + arg != parent_arg && arg.contains(parent_arg) + } else { + false + } + }) + } else { + false + } + }); + if is_expanding_recursive { + // FIXME: indicate that this is an expanding recursive type in stub metadata? + return DINodeCreationResult::new(stub_info.metadata, false); + } else { + debug_context(cx).adt_stack.borrow_mut().push((def_id, args)); + _adt_stack_pop_guard = Some(AdtStackPopGuard { cx }); + } + } + debug_context(cx).type_map.insert(stub_info.unique_type_id, stub_info.metadata); let members: SmallVec<_> = diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 0f94a1dbb0d..c5085927923 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -66,6 +66,7 @@ pub(crate) struct CodegenUnitDebugContext<'ll, 'tcx> { created_files: RefCell<UnordMap<Option<(StableSourceFileId, SourceFileHash)>, &'ll DIFile>>, type_map: metadata::TypeMap<'ll, 'tcx>, + adt_stack: RefCell<Vec<(DefId, GenericArgsRef<'tcx>)>>, namespace_map: RefCell<DefIdMap<&'ll DIScope>>, recursion_marker_type: OnceCell<&'ll DIType>, } @@ -80,6 +81,7 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { builder, created_files: Default::default(), type_map: Default::default(), + adt_stack: Default::default(), namespace_map: RefCell::new(Default::default()), recursion_marker_type: OnceCell::new(), } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index d1d6bcebd33..ffeab59b05c 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -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: diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 1dabf01ffd6..2621935eecf 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -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` diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 8f23a5f21cd..b0c53ec93ce 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -346,20 +346,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { no_sanitize_span = Some(attr.span()); if let Some(list) = attr.meta_item_list() { for item in list.iter() { - match item.name_or_empty() { - sym::address => { + match item.name() { + Some(sym::address) => { codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS } - sym::cfi => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI, - sym::kcfi => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI, - sym::memory => codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY, - sym::memtag => codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG, - sym::shadow_call_stack => { + Some(sym::cfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI, + Some(sym::kcfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI, + Some(sym::memory) => { + codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY + } + Some(sym::memtag) => { + codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG + } + Some(sym::shadow_call_stack) => { codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK } - sym::thread => codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD, - sym::hwaddress => { + Some(sym::thread) => { + codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD + } + Some(sym::hwaddress) => { codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS } _ => { @@ -420,9 +426,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { continue; }; - let attrib_to_write = match meta_item.name_or_empty() { - sym::prefix_nops => &mut prefix, - sym::entry_nops => &mut entry, + let attrib_to_write = match meta_item.name() { + Some(sym::prefix_nops) => &mut prefix, + Some(sym::entry_nops) => &mut entry, _ => { tcx.dcx().emit_err(errors::UnexpectedParameterName { span: item.span(), @@ -786,8 +792,7 @@ impl<'a> MixedExportNameAndNoMangleState<'a> { fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option<AutoDiffAttrs> { let attrs = tcx.get_attrs(id, sym::rustc_autodiff); - let attrs = - attrs.filter(|attr| attr.name_or_empty() == sym::rustc_autodiff).collect::<Vec<_>>(); + let attrs = attrs.filter(|attr| attr.has_name(sym::rustc_autodiff)).collect::<Vec<_>>(); // check for exactly one autodiff attribute on placeholder functions. // There should only be one, since we generate a new placeholder per ad macro. diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 42cea5c86d4..c2064397855 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -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] diff --git a/compiler/rustc_error_codes/src/error_codes/E0736.md b/compiler/rustc_error_codes/src/error_codes/E0736.md index cb7633b7068..66d5fbb80cf 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0736.md +++ b/compiler/rustc_error_codes/src/error_codes/E0736.md @@ -11,7 +11,7 @@ Erroneous code example: ```compile_fail,E0736 #[inline] -#[naked] +#[unsafe(naked)] fn foo() {} ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0787.md b/compiler/rustc_error_codes/src/error_codes/E0787.md index f5c5faa066b..b7f92c8feb5 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0787.md +++ b/compiler/rustc_error_codes/src/error_codes/E0787.md @@ -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 } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 49f6d58172f..f5eaf7d616b 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -824,10 +824,10 @@ impl SyntaxExtension { return Err(item.span); } - match item.name_or_empty() { - sym::no => Ok(CollapseMacroDebuginfo::No), - sym::external => Ok(CollapseMacroDebuginfo::External), - sym::yes => Ok(CollapseMacroDebuginfo::Yes), + match item.name() { + Some(sym::no) => Ok(CollapseMacroDebuginfo::No), + Some(sym::external) => Ok(CollapseMacroDebuginfo::External), + Some(sym::yes) => Ok(CollapseMacroDebuginfo::Yes), _ => Err(item.path.span), } } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index c70e259b2cd..d2e45d717d9 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -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)), diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 1b539477d51..1f430b0018f 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -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); } @@ -2053,8 +2052,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { ) -> Node::OutputTy { loop { return match self.take_first_attr(&mut node) { - Some((attr, pos, derives)) => match attr.name_or_empty() { - sym::cfg => { + Some((attr, pos, derives)) => match attr.name() { + Some(sym::cfg) => { let (res, meta_item) = self.expand_cfg_true(&mut node, attr, pos); if res { continue; @@ -2071,7 +2070,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } Default::default() } - sym::cfg_attr => { + Some(sym::cfg_attr) => { self.expand_cfg_attr(&mut node, &attr, pos); continue; } @@ -2144,8 +2143,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { ) { loop { return match self.take_first_attr(node) { - Some((attr, pos, derives)) => match attr.name_or_empty() { - sym::cfg => { + Some((attr, pos, derives)) => match attr.name() { + Some(sym::cfg) => { let span = attr.span; if self.expand_cfg_true(node, attr, pos).0 { continue; @@ -2154,7 +2153,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { node.expand_cfg_false(self, pos, span); continue; } - sym::cfg_attr => { + Some(sym::cfg_attr) => { self.expand_cfg_attr(node, &attr, pos); continue; } diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index b663e959744..698492f42e2 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -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"); diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 0065f83eb4e..c78beb40688 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -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 }); diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index c138b090877..93604a149f1 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -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 } diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 3f037259956..0c2362f23bc 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -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, diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 4edaf68c89a..f00201ad202 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -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 diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index fcc11dd3c1f..e3e4eefe5e1 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -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 diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 3b441729d75..76270cad48f 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -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: diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index e09ae3c1239..cbc121e3632 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -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. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 3f5269eeb9b..d02c767ea67 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1237,7 +1237,7 @@ impl AttributeExt for Attribute { Attribute::Parsed(AttributeKind::DocComment { kind, comment, .. }) => { Some((*comment, *kind)) } - Attribute::Unparsed(_) if self.name_or_empty() == sym::doc => { + Attribute::Unparsed(_) if self.has_name(sym::doc) => { self.value_str().map(|s| (s, CommentKind::Line)) } _ => None, @@ -1262,8 +1262,8 @@ impl Attribute { } #[inline] - pub fn name_or_empty(&self) -> Symbol { - AttributeExt::name_or_empty(self) + pub fn name(&self) -> Option<Symbol> { + AttributeExt::name(self) } #[inline] @@ -1302,6 +1302,11 @@ impl Attribute { } #[inline] + pub fn has_any_name(&self, names: &[Symbol]) -> bool { + AttributeExt::has_any_name(self, names) + } + + #[inline] pub fn span(&self) -> Span { AttributeExt::span(self) } diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index b3eade8c8ae..99e495d9266 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -2,8 +2,8 @@ use rustc_infer::infer::InferCtxt; use rustc_infer::traits::PredicateObligations; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Limit; -use rustc_span::Span; use rustc_span::def_id::{LOCAL_CRATE, LocalDefId}; +use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::traits::ObligationCtxt; use tracing::{debug, instrument}; @@ -259,7 +259,11 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { } } -pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) { +pub fn report_autoderef_recursion_limit_error<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + ty: Ty<'tcx>, +) -> ErrorGuaranteed { // We've reached the recursion limit, error gracefully. let suggested_limit = match tcx.recursion_limit() { Limit(0) => Limit(2), @@ -270,5 +274,5 @@ pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Spa ty, suggested_limit, crate_name: tcx.crate_name(LOCAL_CRATE), - }); + }) } diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 526ee30209c..a3c8ce620b3 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -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 { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 7605c6c6a42..22162b8b364 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -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) diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 0511f4e25ad..f5e0f01e4c5 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -1000,6 +1000,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // determines whether to borrow *at the level of the deref pattern* rather than // borrowing the bound place (since that inner place is inside the temporary that // stores the result of calling `deref()`/`deref_mut()` so can't be captured). + // HACK: this could be a fake pattern corresponding to a deref inserted by match + // ergonomics, in which case `pat.hir_id` will be the id of the subpattern. let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpattern); let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; @@ -1227,9 +1229,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // actually this is somewhat "disjoint" from the code below // that aims to account for `ref x`. if let Some(vec) = self.cx.typeck_results().pat_adjustments().get(pat.hir_id) { - if let Some(first_ty) = vec.first() { - debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty); - return Ok(*first_ty); + if let Some(first_adjust) = vec.first() { + debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust); + return Ok(first_adjust.source); } } else if let PatKind::Ref(subpat, _) = pat.kind && self.cx.typeck_results().skipped_ref_pats().contains(pat.hir_id) @@ -1680,12 +1682,31 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // Then we see that to get the same result, we must start with // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. - for _ in - 0..self.cx.typeck_results().pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len()) - { + let typeck_results = self.cx.typeck_results(); + let adjustments: &[adjustment::PatAdjustment<'tcx>] = + typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); + let mut adjusts = adjustments.iter().peekable(); + while let Some(adjust) = adjusts.next() { debug!("applying adjustment to place_with_id={:?}", place_with_id); - place_with_id = self.cat_deref(pat.hir_id, place_with_id)?; + place_with_id = match adjust.kind { + adjustment::PatAdjust::BuiltinDeref => self.cat_deref(pat.hir_id, place_with_id)?, + adjustment::PatAdjust::OverloadedDeref => { + // This adjustment corresponds to an overloaded deref; it borrows the scrutinee to + // call `Deref::deref` or `DerefMut::deref_mut`. Invoke the callback before setting + // `place_with_id` to the temporary storing the result of the deref. + // HACK(dianne): giving the callback a fake deref pattern makes sure it behaves the + // same as it would if this were an explicit deref pattern. + op(&place_with_id, &hir::Pat { kind: PatKind::Deref(pat), ..*pat })?; + let target_ty = match adjusts.peek() { + Some(&&next_adjust) => next_adjust.source, + // At the end of the deref chain, we get `pat`'s scrutinee. + None => self.pat_ty_unadjusted(pat)?, + }; + self.pat_deref_temp(pat.hir_id, pat, target_ty)? + } + }; } + drop(typeck_results); // explicitly release borrow of typeck results, just in case. let place_with_id = place_with_id; // lose mutability debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id); @@ -1788,14 +1809,8 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.cat_pattern(subplace, subpat, op)?; } PatKind::Deref(subpat) => { - let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpat); - let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; - let re_erased = self.cx.tcx().lifetimes.re_erased; let ty = self.pat_ty_adjusted(subpat)?; - let ty = Ty::new_ref(self.cx.tcx(), re_erased, ty, mutability); - // A deref pattern generates a temporary. - let base = self.cat_rvalue(pat.hir_id, ty); - let place = self.cat_deref(pat.hir_id, base)?; + let place = self.pat_deref_temp(pat.hir_id, subpat, ty)?; self.cat_pattern(place, subpat, op)?; } @@ -1848,6 +1863,23 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx Ok(()) } + /// Represents the place of the temp that stores the scrutinee of a deref pattern's interior. + fn pat_deref_temp( + &self, + hir_id: HirId, + inner: &hir::Pat<'_>, + target_ty: Ty<'tcx>, + ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> { + let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(inner); + let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; + let re_erased = self.cx.tcx().lifetimes.re_erased; + let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability); + // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ... + let base = self.cat_rvalue(hir_id, ty); + // ... and the inner pattern matches on the place behind that reference. + self.cat_deref(hir_id, base) + } + fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool { if let ty::Adt(def, _) = self.cx.try_structurally_resolve_type(span, ty).kind() { // Note that if a non-exhaustive SingleVariant is defined in another crate, we need diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 74cc8181418..934820eb4da 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -488,7 +488,7 @@ fn parse_never_type_options_attr( item.span(), format!( "unknown or duplicate never type option: `{}` (supported: `fallback`, `diverging_block_default`)", - item.name_or_empty() + item.name().unwrap() ), ); } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index ba4396a5ab3..1d3a081cbb8 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -2334,8 +2334,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let hir_id = self.fcx.tcx.local_def_id_to_hir_id(local_def_id); let attrs = self.fcx.tcx.hir_attrs(hir_id); for attr in attrs { - if sym::doc == attr.name_or_empty() { - } else if sym::rustc_confusables == attr.name_or_empty() { + if attr.has_name(sym::doc) { + // do nothing + } else if attr.has_name(sym::rustc_confusables) { let Some(confusables) = attr.meta_item_list() else { continue; }; @@ -2355,7 +2356,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { continue; }; for v in values { - if v.name_or_empty() != sym::alias { + if !v.has_name(sym::alias) { continue; } if let Some(nested) = v.meta_item_list() { diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 0e42a84ca32..b86991f81ad 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -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) } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index fbc783c0509..e5e4fc7f8b7 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -9,11 +9,13 @@ use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err, }; use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def_id::DefId; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{ self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatExpr, PatExprKind, PatKind, expr_needs_parens, }; +use rustc_hir_analysis::autoderef::report_autoderef_recursion_limit_error; use rustc_infer::infer; use rustc_middle::traits::PatternOriginExpr; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -29,11 +31,12 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use tracing::{debug, instrument, trace}; use ty::VariantDef; +use ty::adjustment::{PatAdjust, PatAdjustment}; use super::report_unexpected_variant_res; use crate::expectation::Expectation; use crate::gather_locals::DeclOrigin; -use crate::{FnCtxt, LoweredTy, errors}; +use crate::{FnCtxt, errors}; const CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ @@ -161,12 +164,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Mode for adjusting the expected type and binding mode. #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum AdjustMode { - /// Peel off all immediate reference types. - Peel, + /// Peel off all immediate reference types. If the `deref_patterns` feature is enabled, this + /// also peels smart pointer ADTs. + Peel { kind: PeelKind }, /// Pass on the input binding mode and expected type. Pass, } +/// Restrictions on what types to peel when adjusting the expected type and binding mode. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum PeelKind { + /// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference + /// any number of `&`/`&mut` references, plus a single smart pointer. + ExplicitDerefPat, + /// Implicitly peel any number of references, and if `deref_patterns` is enabled, smart pointer + /// ADTs. In order to peel only as much as necessary for the pattern to match, the `until_adt` + /// field contains the ADT def that the pattern is a constructor for, if applicable, so that we + /// don't peel it. See [`ResolvedPat`] for more information. + Implicit { until_adt: Option<DefId> }, +} + +impl AdjustMode { + const fn peel_until_adt(opt_adt_def: Option<DefId>) -> AdjustMode { + AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def } } + } + const fn peel_all() -> AdjustMode { + AdjustMode::peel_until_adt(None) + } +} + /// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference. /// Normally, the borrow checker enforces this, but for (currently experimental) match ergonomics, /// we track this when typing patterns for two purposes: @@ -242,6 +268,47 @@ enum InheritedRefMatchRule { }, } +/// When checking patterns containing paths, we need to know the path's resolution to determine +/// whether to apply match ergonomics and implicitly dereference the scrutinee. For instance, when +/// the `deref_patterns` feature is enabled and we're matching against a scrutinee of type +/// `Cow<'a, Option<u8>>`, we insert an implicit dereference to allow the pattern `Some(_)` to type, +/// but we must not dereference it when checking the pattern `Cow::Borrowed(_)`. +/// +/// `ResolvedPat` contains the information from resolution needed to determine match ergonomics +/// adjustments, and to finish checking the pattern once we know its adjusted type. +#[derive(Clone, Copy, Debug)] +struct ResolvedPat<'tcx> { + /// The type of the pattern, to be checked against the type of the scrutinee after peeling. This + /// is also used to avoid peeling the scrutinee's constructors (see the `Cow` example above). + ty: Ty<'tcx>, + kind: ResolvedPatKind<'tcx>, +} + +#[derive(Clone, Copy, Debug)] +enum ResolvedPatKind<'tcx> { + Path { res: Res, pat_res: Res, segments: &'tcx [hir::PathSegment<'tcx>] }, + Struct { variant: &'tcx VariantDef }, + TupleStruct { res: Res, variant: &'tcx VariantDef }, +} + +impl<'tcx> ResolvedPat<'tcx> { + fn adjust_mode(&self) -> AdjustMode { + if let ResolvedPatKind::Path { res, .. } = self.kind + && matches!(res, Res::Def(DefKind::Const | DefKind::AssocConst, _)) + { + // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. + // Peeling the reference types too early will cause type checking failures. + // Although it would be possible to *also* peel the types of the constants too. + AdjustMode::Pass + } else { + // The remaining possible resolutions for path, struct, and tuple struct patterns are + // ADT constructors. As such, we may peel references freely, but we must not peel the + // ADT itself from the scrutinee if it's a smart pointer. + AdjustMode::peel_until_adt(self.ty.ty_adt_def().map(|adt| adt.did())) + } + } +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Experimental pattern feature: after matching against a shared reference, do we limit the /// default binding mode in subpatterns to be `ref` when it would otherwise be `ref mut`? @@ -318,16 +385,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Conversely, inside this module, `check_pat_top` should never be used. #[instrument(level = "debug", skip(self, pat_info))] fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx>) { + // For patterns containing paths, we need the path's resolution to determine whether to + // implicitly dereference the scrutinee before matching. let opt_path_res = match pat.kind { PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => { - Some(self.resolve_ty_and_res_fully_qualified_call(qpath, *hir_id, *span)) + Some(self.resolve_pat_path(*hir_id, *span, qpath)) } + PatKind::Struct(ref qpath, ..) => Some(self.resolve_pat_struct(pat, qpath)), + PatKind::TupleStruct(ref qpath, ..) => Some(self.resolve_pat_tuple_struct(pat, qpath)), _ => None, }; - let adjust_mode = self.calc_adjust_mode(pat, opt_path_res.map(|(res, ..)| res)); + let adjust_mode = self.calc_adjust_mode(pat, opt_path_res); let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info); self.write_ty(pat.hir_id, ty); + // If we implicitly inserted overloaded dereferences before matching, check the pattern to + // see if the dereferenced types need `DerefMut` bounds. + if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id) + && derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref) + { + self.register_deref_mut_bounds_if_needed( + pat.span, + pat, + derefed_tys.iter().filter_map(|adjust| match adjust.kind { + PatAdjust::OverloadedDeref => Some(adjust.source), + PatAdjust::BuiltinDeref => None, + }), + ); + } + // (note_1): In most of the cases where (note_1) is referenced // (literals and constants being the exception), we relate types // using strict equality, even though subtyping would be sufficient. @@ -375,7 +461,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_inner( &self, pat: &'tcx Pat<'tcx>, - opt_path_res: Option<(Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>])>, + opt_path_res: Option<Result<ResolvedPat<'tcx>, ErrorGuaranteed>>, adjust_mode: AdjustMode, expected: Ty<'tcx>, pat_info: PatInfo<'tcx>, @@ -389,7 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Resolve type if needed. - let expected = if let AdjustMode::Peel = adjust_mode + let expected = if let AdjustMode::Peel { .. } = adjust_mode && pat.default_binding_modes { self.try_structurally_resolve_type(pat.span, expected) @@ -402,7 +488,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match pat.kind { // Peel off a `&` or `&mut` from the scrutinee type. See the examples in // `tests/ui/rfcs/rfc-2005-default-binding-mode`. - _ if let AdjustMode::Peel = adjust_mode + _ if let AdjustMode::Peel { .. } = adjust_mode && pat.default_binding_modes && let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() => { @@ -415,7 +501,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .pat_adjustments_mut() .entry(pat.hir_id) .or_default() - .push(expected); + .push(PatAdjustment { kind: PatAdjust::BuiltinDeref, source: expected }); let mut binding_mode = ByRef::Yes(match pat_info.binding_mode { // If default binding mode is by value, make it `ref` or `ref mut` @@ -442,19 +528,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Recurse with the new expected type. self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info) } + // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the + // examples in `tests/ui/pattern/deref_patterns/`. + _ if self.tcx.features().deref_patterns() + && let AdjustMode::Peel { kind: PeelKind::Implicit { until_adt } } = adjust_mode + && pat.default_binding_modes + // For simplicity, only apply overloaded derefs if `expected` is a known ADT. + // FIXME(deref_patterns): we'll get better diagnostics for users trying to + // implicitly deref generics if we allow them here, but primitives, tuples, and + // inference vars definitely should be stopped. Figure out what makes most sense. + && let ty::Adt(scrutinee_adt, _) = *expected.kind() + // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if + // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern. + && until_adt != Some(scrutinee_adt.did()) + // At this point, the pattern isn't able to match `expected` without peeling. Check + // that it implements `Deref` before assuming it's a smart pointer, to get a normal + // type error instead of a missing impl error if not. This only checks for `Deref`, + // not `DerefPure`: we require that too, but we want a trait error if it's missing. + && let Some(deref_trait) = self.tcx.lang_items().deref_trait() + && self + .type_implements_trait(deref_trait, [expected], self.param_env) + .may_apply() => + { + debug!("scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref"); + // The scrutinee is a smart pointer; implicitly dereference it. This adds a + // requirement that `expected: DerefPure`. + let mut inner_ty = self.deref_pat_target(pat.span, expected); + // Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any + // `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`. + + let mut typeck_results = self.typeck_results.borrow_mut(); + let mut pat_adjustments_table = typeck_results.pat_adjustments_mut(); + let pat_adjustments = pat_adjustments_table.entry(pat.hir_id).or_default(); + // We may reach the recursion limit if a user matches on a type `T` satisfying + // `T: Deref<Target = T>`; error gracefully in this case. + // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move + // this check out of this branch. Alternatively, this loop could be implemented with + // autoderef and this check removed. For now though, don't break code compiling on + // stable with lots of `&`s and a low recursion limit, if anyone's done that. + if self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) { + // Preserve the smart pointer type for THIR lowering and closure upvar analysis. + pat_adjustments + .push(PatAdjustment { kind: PatAdjust::OverloadedDeref, source: expected }); + } else { + let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected); + inner_ty = Ty::new_error(self.tcx, guar); + } + drop(typeck_results); + + // Recurse, using the old pat info to keep `current_depth` to its old value. + // Peeling smart pointers does not update the default binding mode. + self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, old_pat_info) + } PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. PatKind::Never => expected, - PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => { - let ty = self.check_pat_path( - *hir_id, - pat.hir_id, - *span, - qpath, - opt_path_res.unwrap(), - expected, - &pat_info.top_info, - ); + PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), hir_id, .. }) => { + let ty = match opt_path_res.unwrap() { + Ok(ref pr) => { + self.check_pat_path(pat.hir_id, pat.span, pr, expected, &pat_info.top_info) + } + Err(guar) => Ty::new_error(self.tcx, guar), + }; self.write_ty(*hir_id, ty); ty } @@ -465,12 +600,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Binding(ba, var_id, ident, sub) => { self.check_pat_ident(pat, ba, var_id, ident, sub, expected, pat_info) } - PatKind::TupleStruct(ref qpath, subpats, ddpos) => { - self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info) - } - PatKind::Struct(ref qpath, fields, has_rest_pat) => { - self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info) - } + PatKind::TupleStruct(ref qpath, subpats, ddpos) => match opt_path_res.unwrap() { + Ok(ResolvedPat { ty, kind: ResolvedPatKind::TupleStruct { res, variant } }) => self + .check_pat_tuple_struct( + pat, qpath, subpats, ddpos, res, ty, variant, expected, pat_info, + ), + Err(guar) => { + let ty_err = Ty::new_error(self.tcx, guar); + for subpat in subpats { + self.check_pat(subpat, ty_err, pat_info); + } + ty_err + } + Ok(pr) => span_bug!(pat.span, "tuple struct pattern resolved to {pr:?}"), + }, + PatKind::Struct(_, fields, has_rest_pat) => match opt_path_res.unwrap() { + Ok(ResolvedPat { ty, kind: ResolvedPatKind::Struct { variant } }) => self + .check_pat_struct(pat, fields, has_rest_pat, ty, variant, expected, pat_info), + Err(guar) => { + let ty_err = Ty::new_error(self.tcx, guar); + for field in fields { + self.check_pat(field.pat, ty_err, pat_info); + } + ty_err + } + Ok(pr) => span_bug!(pat.span, "struct pattern resolved to {pr:?}"), + }, PatKind::Guard(pat, cond) => { self.check_pat(pat, expected, pat_info); self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {}); @@ -496,31 +651,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// How should the binding mode and expected type be adjusted? /// - /// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`. - fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode { + /// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`. + fn calc_adjust_mode( + &self, + pat: &'tcx Pat<'tcx>, + opt_path_res: Option<Result<ResolvedPat<'tcx>, ErrorGuaranteed>>, + ) -> AdjustMode { match &pat.kind { // Type checking these product-like types successfully always require // that the expected type be of those types and not reference types. - PatKind::Struct(..) - | PatKind::TupleStruct(..) - | PatKind::Tuple(..) - | PatKind::Box(_) - | PatKind::Deref(_) + PatKind::Tuple(..) | PatKind::Range(..) - | PatKind::Slice(..) => AdjustMode::Peel, + | PatKind::Slice(..) => AdjustMode::peel_all(), + // When checking an explicit deref pattern, only peel reference types. + // FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box + // patterns may want `PeelKind::Implicit`, stopping on encountering a box. + | PatKind::Box(_) + | PatKind::Deref(_) => AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat }, // A never pattern behaves somewhat like a literal or unit variant. - PatKind::Never => AdjustMode::Peel, - PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => match opt_path_res.unwrap() { - // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. - // Peeling the reference types too early will cause type checking failures. - // Although it would be possible to *also* peel the types of the constants too. - Res::Def(DefKind::Const | DefKind::AssocConst, _) => AdjustMode::Pass, - // In the `ValueNS`, we have `SelfCtor(..) | Ctor(_, Const), _)` remaining which - // could successfully compile. The former being `Self` requires a unit struct. - // In either case, and unlike constants, the pattern itself cannot be - // a reference type wherefore peeling doesn't give up any expressiveness. - _ => AdjustMode::Peel, - }, + PatKind::Never => AdjustMode::peel_all(), + // For patterns with paths, how we peel the scrutinee depends on the path's resolution. + PatKind::Struct(..) + | PatKind::TupleStruct(..) + | PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => { + // If there was an error resolving the path, default to peeling everything. + opt_path_res.unwrap().map_or(AdjustMode::peel_all(), |pr| pr.adjust_mode()) + } // String and byte-string literals result in types `&str` and `&[u8]` respectively. // All other literals result in non-reference types. @@ -529,7 +685,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Call `resolve_vars_if_possible` here for inline const blocks. PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() { ty::Ref(..) => AdjustMode::Pass, - _ => AdjustMode::Peel, + _ => { + // Path patterns have already been handled, and inline const blocks currently + // aren't possible to write, so any handling for them would be untested. + if cfg!(debug_assertions) + && self.tcx.features().deref_patterns() + && !matches!(lt.kind, PatExprKind::Lit { .. }) + { + span_bug!(lt.span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}", lt.kind); + } + AdjustMode::peel_all() + } }, // Ref patterns are complicated, we handle them in `check_pat_ref`. @@ -1112,27 +1278,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(()) } - fn check_pat_struct( + fn resolve_pat_struct( &self, pat: &'tcx Pat<'tcx>, qpath: &hir::QPath<'tcx>, + ) -> Result<ResolvedPat<'tcx>, ErrorGuaranteed> { + // Resolve the path and check the definition for errors. + let (variant, pat_ty) = self.check_struct_path(qpath, pat.hir_id)?; + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Struct { variant } }) + } + + fn check_pat_struct( + &self, + pat: &'tcx Pat<'tcx>, fields: &'tcx [hir::PatField<'tcx>], has_rest_pat: bool, + pat_ty: Ty<'tcx>, + variant: &'tcx VariantDef, expected: Ty<'tcx>, pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { - // Resolve the path and check the definition for errors. - let (variant, pat_ty) = match self.check_struct_path(qpath, pat.hir_id) { - Ok(data) => data, - Err(guar) => { - let err = Ty::new_error(self.tcx, guar); - for field in fields { - self.check_pat(field.pat, err, pat_info); - } - return err; - } - }; - // Type-check the path. let _ = self.demand_eqtype_pat(pat.span, expected, pat_ty, &pat_info.top_info); @@ -1143,31 +1308,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_pat_path( + fn resolve_pat_path( &self, path_id: HirId, - pat_id_for_diag: HirId, span: Span, - qpath: &hir::QPath<'_>, - path_resolution: (Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]), - expected: Ty<'tcx>, - ti: &TopInfo<'tcx>, - ) -> Ty<'tcx> { + qpath: &'tcx hir::QPath<'_>, + ) -> Result<ResolvedPat<'tcx>, ErrorGuaranteed> { let tcx = self.tcx; - // We have already resolved the path. - let (res, opt_ty, segments) = path_resolution; + let (res, opt_ty, segments) = + self.resolve_ty_and_res_fully_qualified_call(qpath, path_id, span); match res { Res::Err => { let e = self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); - return Ty::new_error(tcx, e); + return Err(e); } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { let expected = "unit struct, unit variant or constant"; let e = report_unexpected_variant_res(tcx, res, None, qpath, span, E0533, expected); - return Ty::new_error(tcx, e); + return Err(e); } Res::SelfCtor(def_id) => { if let ty::Adt(adt_def, _) = *tcx.type_of(def_id).skip_binder().kind() @@ -1185,7 +1346,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { E0533, "unit struct", ); - return Ty::new_error(tcx, e); + return Err(e); } } Res::Def( @@ -1198,15 +1359,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => bug!("unexpected pattern resolution: {:?}", res), } - // Type-check the path. + // Find the type of the path pattern, for later checking. let (pat_ty, pat_res) = self.instantiate_value_path(segments, opt_ty, res, span, span, path_id); + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Path { res, pat_res, segments } }) + } + + fn check_pat_path( + &self, + pat_id_for_diag: HirId, + span: Span, + resolved: &ResolvedPat<'tcx>, + expected: Ty<'tcx>, + ti: &TopInfo<'tcx>, + ) -> Ty<'tcx> { if let Err(err) = - self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, pat_ty) + self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, resolved.ty) { - self.emit_bad_pat_path(err, pat_id_for_diag, span, res, pat_res, pat_ty, segments); + self.emit_bad_pat_path(err, pat_id_for_diag, span, resolved); } - pat_ty + resolved.ty } fn maybe_suggest_range_literal( @@ -1249,11 +1421,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mut e: Diag<'_>, hir_id: HirId, pat_span: Span, - res: Res, - pat_res: Res, - pat_ty: Ty<'tcx>, - segments: &'tcx [hir::PathSegment<'tcx>], + resolved_pat: &ResolvedPat<'tcx>, ) { + let ResolvedPatKind::Path { res, pat_res, segments } = resolved_pat.kind else { + span_bug!(pat_span, "unexpected resolution for path pattern: {resolved_pat:?}"); + }; + if let Some(span) = self.tcx.hir_res_span(pat_res) { e.span_label(span, format!("{} defined here", res.descr())); if let [hir::PathSegment { ident, .. }] = &*segments { @@ -1276,7 +1449,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } _ => { - let (type_def_id, item_def_id) = match pat_ty.kind() { + let (type_def_id, item_def_id) = match resolved_pat.ty.kind() { ty::Adt(def, _) => match res { Res::Def(DefKind::Const, def_id) => (Some(def.did()), Some(def_id)), _ => (None, None), @@ -1316,26 +1489,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { e.emit(); } - fn check_pat_tuple_struct( + fn resolve_pat_tuple_struct( &self, pat: &'tcx Pat<'tcx>, qpath: &'tcx hir::QPath<'tcx>, - subpats: &'tcx [Pat<'tcx>], - ddpos: hir::DotDotPos, - expected: Ty<'tcx>, - pat_info: PatInfo<'tcx>, - ) -> Ty<'tcx> { + ) -> Result<ResolvedPat<'tcx>, ErrorGuaranteed> { let tcx = self.tcx; - let on_error = |e| { - for pat in subpats { - self.check_pat(pat, Ty::new_error(tcx, e), pat_info); - } - }; let report_unexpected_res = |res: Res| { let expected = "tuple struct or tuple variant"; let e = report_unexpected_variant_res(tcx, res, None, qpath, pat.span, E0164, expected); - on_error(e); - e + Err(e) }; // Resolve the path and check the definition for errors. @@ -1344,16 +1507,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if res == Res::Err { let e = self.dcx().span_delayed_bug(pat.span, "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e); - on_error(e); - return Ty::new_error(tcx, e); + return Err(e); } // Type-check the path. let (pat_ty, res) = self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id); if !pat_ty.is_fn() { - let e = report_unexpected_res(res); - return Ty::new_error(tcx, e); + return report_unexpected_res(res); } let variant = match res { @@ -1361,8 +1522,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.dcx().span_bug(pat.span, "`Res::Err` but no error emitted"); } Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { - let e = report_unexpected_res(res); - return Ty::new_error(tcx, e); + return report_unexpected_res(res); } Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res), _ => bug!("unexpected pattern resolution: {:?}", res), @@ -1372,6 +1532,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pat_ty = pat_ty.fn_sig(tcx).output(); let pat_ty = pat_ty.no_bound_vars().expect("expected fn type"); + Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::TupleStruct { res, variant } }) + } + + fn check_pat_tuple_struct( + &self, + pat: &'tcx Pat<'tcx>, + qpath: &'tcx hir::QPath<'tcx>, + subpats: &'tcx [Pat<'tcx>], + ddpos: hir::DotDotPos, + res: Res, + pat_ty: Ty<'tcx>, + variant: &'tcx VariantDef, + expected: Ty<'tcx>, + pat_info: PatInfo<'tcx>, + ) -> Ty<'tcx> { + let tcx = self.tcx; + let on_error = |e| { + for pat in subpats { + self.check_pat(pat, Ty::new_error(tcx, e), pat_info); + } + }; + // Type-check the tuple struct pattern against the expected type. let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, &pat_info.top_info); let had_err = diag.map_err(|diag| diag.emit()); @@ -2255,36 +2437,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, pat_info: PatInfo<'tcx>, ) -> Ty<'tcx> { - let tcx = self.tcx; + let target_ty = self.deref_pat_target(span, expected); + self.check_pat(inner, target_ty, pat_info); + self.register_deref_mut_bounds_if_needed(span, inner, [expected]); + expected + } + + fn deref_pat_target(&self, span: Span, source_ty: Ty<'tcx>) -> Ty<'tcx> { // Register a `DerefPure` bound, which is required by all `deref!()` pats. + let tcx = self.tcx; self.register_bound( - expected, + source_ty, tcx.require_lang_item(hir::LangItem::DerefPure, Some(span)), self.misc(span), ); - // <expected as Deref>::Target - let ty = Ty::new_projection( + // The expected type for the deref pat's inner pattern is `<expected as Deref>::Target`. + let target_ty = Ty::new_projection( tcx, tcx.require_lang_item(hir::LangItem::DerefTarget, Some(span)), - [expected], + [source_ty], ); - let ty = self.normalize(span, ty); - let ty = self.try_structurally_resolve_type(span, ty); - self.check_pat(inner, ty, pat_info); - - // Check if the pattern has any `ref mut` bindings, which would require - // `DerefMut` to be emitted in MIR building instead of just `Deref`. - // We do this *after* checking the inner pattern, since we want to make - // sure to apply any match-ergonomics adjustments. + let target_ty = self.normalize(span, target_ty); + self.try_structurally_resolve_type(span, target_ty) + } + + /// Check if the interior of a deref pattern (either explicit or implicit) has any `ref mut` + /// bindings, which would require `DerefMut` to be emitted in MIR building instead of just + /// `Deref`. We do this *after* checking the inner pattern, since we want to make sure to + /// account for `ref mut` binding modes inherited from implicitly dereferencing `&mut` refs. + fn register_deref_mut_bounds_if_needed( + &self, + span: Span, + inner: &'tcx Pat<'tcx>, + derefed_tys: impl IntoIterator<Item = Ty<'tcx>>, + ) { if self.typeck_results.borrow().pat_has_ref_mut_binding(inner) { - self.register_bound( - expected, - tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)), - self.misc(span), - ); + for mutably_derefed_ty in derefed_tys { + self.register_bound( + mutably_derefed_ty, + self.tcx.require_lang_item(hir::LangItem::DerefMut, Some(span)), + self.misc(span), + ); + } } - - expected } // Precondition: Pat is Ref(inner) diff --git a/compiler/rustc_incremental/messages.ftl b/compiler/rustc_incremental/messages.ftl index 2a65101d360..bbc1fab05df 100644 --- a/compiler/rustc_incremental/messages.ftl +++ b/compiler/rustc_incremental/messages.ftl @@ -93,7 +93,7 @@ incremental_undefined_clean_dirty_assertions = incremental_undefined_clean_dirty_assertions_item = clean/dirty auto-assertions not yet defined for Node::Item.node={$kind} -incremental_unknown_item = unknown item `{$name}` +incremental_unknown_rustc_clean_argument = unknown `rustc_clean` argument incremental_unrecognized_depnode = unrecognized `DepNode` variant: {$name} diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs index b4a207386dc..dbc72d085be 100644 --- a/compiler/rustc_incremental/src/errors.rs +++ b/compiler/rustc_incremental/src/errors.rs @@ -107,11 +107,10 @@ pub(crate) struct NotLoaded<'a> { } #[derive(Diagnostic)] -#[diag(incremental_unknown_item)] -pub(crate) struct UnknownItem { +#[diag(incremental_unknown_rustc_clean_argument)] +pub(crate) struct UnknownRustcCleanArgument { #[primary_span] pub span: Span, - pub name: Symbol, } #[derive(Diagnostic)] diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index d40a0d514f6..64166255fa4 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -405,8 +405,7 @@ fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool { debug!("check_config: searching for cfg {:?}", value); cfg = Some(config.contains(&(value, None))); } else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) { - tcx.dcx() - .emit_err(errors::UnknownItem { span: attr.span(), name: item.name_or_empty() }); + tcx.dcx().emit_err(errors::UnknownRustcCleanArgument { span: item.span() }); } } diff --git a/compiler/rustc_index/src/slice.rs b/compiler/rustc_index/src/slice.rs index 67ac805c2bf..d2702bdb057 100644 --- a/compiler/rustc_index/src/slice.rs +++ b/compiler/rustc_index/src/slice.rs @@ -1,6 +1,6 @@ use std::fmt; use std::marker::PhantomData; -use std::ops::{Index, IndexMut}; +use std::ops::{Index, IndexMut, RangeBounds}; use std::slice::GetDisjointMutError::*; use std::slice::{self, SliceIndex}; @@ -105,6 +105,17 @@ impl<I: Idx, T> IndexSlice<I, T> { } #[inline] + pub fn copy_within( + &mut self, + src: impl IntoSliceIdx<I, [T], Output: RangeBounds<usize>>, + dest: I, + ) where + T: Copy, + { + self.raw.copy_within(src.into_slice_idx(), dest.index()); + } + + #[inline] pub fn get<R: IntoSliceIdx<I, [T]>>( &self, index: R, diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index d405d044cae..5c8c51c8bbc 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -818,8 +818,8 @@ fn test_unstable_options_tracking_hash() { tracked!(min_function_alignment, Some(Align::EIGHT)); tracked!(mir_emit_retag, true); tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]); - tracked!(mir_keep_place_mention, true); tracked!(mir_opt_level, Some(4)); + tracked!(mir_preserve_ub, true); tracked!(move_size_limit, Some(4096)); tracked!(mutable_noalias, false); tracked!(next_solver, NextSolverConfig { coherence: true, globally: true }); diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 7fdbae3a59d..b4069b317bf 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -249,7 +249,7 @@ impl Level { /// Converts an `Attribute` to a level. pub fn from_attr(attr: &impl AttributeExt) -> Option<(Self, Option<LintExpectationId>)> { - Self::from_symbol(attr.name_or_empty(), || Some(attr.id())) + attr.name().and_then(|name| Self::from_symbol(name, || Some(attr.id()))) } /// Converts a `Symbol` to a level. diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 112954eca0d..f0a898d678c 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -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), diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index cfb0de8475c..cee9cff0775 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -226,8 +226,8 @@ impl<'tcx> Collector<'tcx> { let mut wasm_import_module = None; let mut import_name_type = None; for item in items.iter() { - match item.name_or_empty() { - sym::name => { + match item.name() { + Some(sym::name) => { if name.is_some() { sess.dcx().emit_err(errors::MultipleNamesInLink { span: item.span() }); continue; @@ -242,7 +242,7 @@ impl<'tcx> Collector<'tcx> { } name = Some((link_name, span)); } - sym::kind => { + Some(sym::kind) => { if kind.is_some() { sess.dcx().emit_err(errors::MultipleKindsInLink { span: item.span() }); continue; @@ -304,7 +304,7 @@ impl<'tcx> Collector<'tcx> { }; kind = Some(link_kind); } - sym::modifiers => { + Some(sym::modifiers) => { if modifiers.is_some() { sess.dcx() .emit_err(errors::MultipleLinkModifiers { span: item.span() }); @@ -316,7 +316,7 @@ impl<'tcx> Collector<'tcx> { }; modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap())); } - sym::cfg => { + Some(sym::cfg) => { if cfg.is_some() { sess.dcx().emit_err(errors::MultipleCfgs { span: item.span() }); continue; @@ -346,7 +346,7 @@ impl<'tcx> Collector<'tcx> { } cfg = Some(link_cfg.clone()); } - sym::wasm_import_module => { + Some(sym::wasm_import_module) => { if wasm_import_module.is_some() { sess.dcx().emit_err(errors::MultipleWasmImport { span: item.span() }); continue; @@ -357,7 +357,7 @@ impl<'tcx> Collector<'tcx> { }; wasm_import_module = Some((link_wasm_import_module, item.span())); } - sym::import_name_type => { + Some(sym::import_name_type) => { if import_name_type.is_some() { sess.dcx() .emit_err(errors::MultipleImportNameType { span: item.span() }); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 177318bfe15..3ea61d1b40a 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -821,7 +821,9 @@ struct AnalyzeAttrState<'a> { #[inline] fn analyze_attr(attr: &impl AttributeExt, state: &mut AnalyzeAttrState<'_>) -> bool { let mut should_encode = false; - if !rustc_feature::encode_cross_crate(attr.name_or_empty()) { + if let Some(name) = attr.name() + && !rustc_feature::encode_cross_crate(name) + { // Attributes not marked encode-cross-crate don't need to be encoded for downstream crates. } else if attr.doc_str().is_some() { // We keep all doc comments reachable to rustdoc because they might be "imported" into diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 3425da48559..a61a6c571a2 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -214,3 +214,25 @@ pub enum CustomCoerceUnsized { /// Records the index of the field being coerced. Struct(FieldIdx), } + +/// Represents an implicit coercion applied to the scrutinee of a match before testing a pattern +/// against it. Currently, this is used only for implicit dereferences. +#[derive(Clone, Copy, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] +pub struct PatAdjustment<'tcx> { + pub kind: PatAdjust, + /// The type of the scrutinee before the adjustment is applied, or the "adjusted type" of the + /// pattern. + pub source: Ty<'tcx>, +} + +/// Represents implicit coercions of patterns' types, rather than values' types. +#[derive(Clone, Copy, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)] +#[derive(TypeFoldable, TypeVisitable)] +pub enum PatAdjust { + /// An implicit dereference before matching, such as when matching the pattern `0` against a + /// scrutinee of type `&u8` or `&mut u8`. + BuiltinDeref, + /// An implicit call to `Deref(Mut)::deref(_mut)` before matching, such as when matching the + /// pattern `[..]` against a scrutinee of type `Vec<T>`. + OverloadedDeref, +} diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 66517c97a68..d92b4f9c06b 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -55,8 +55,6 @@ bitflags::bitflags! { const IS_UNSAFE_CELL = 1 << 9; /// Indicates whether the type is `UnsafePinned`. const IS_UNSAFE_PINNED = 1 << 10; - /// Indicates whether the type is anonymous. - const IS_ANONYMOUS = 1 << 11; } } rustc_data_structures::external_bitflags_debug! { AdtFlags } diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs index 0c44fd2758d..78b2e265b48 100644 --- a/compiler/rustc_middle/src/ty/assoc.rs +++ b/compiler/rustc_middle/src/ty/assoc.rs @@ -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, diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 40eef541423..26861666c1d 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -60,6 +60,12 @@ impl<'tcx> fmt::Debug for ty::adjustment::Adjustment<'tcx> { } } +impl<'tcx> fmt::Debug for ty::adjustment::PatAdjustment<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} -> {:?}", self.source, self.kind) + } +} + impl fmt::Debug for ty::BoundRegionKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 90c6ef67fb8..4c5c669771f 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -77,8 +77,8 @@ pub struct TypeckResults<'tcx> { /// to a form valid in all Editions, either as a lint diagnostic or hard error. rust_2024_migration_desugared_pats: ItemLocalMap<Rust2024IncompatiblePatInfo>, - /// Stores the types which were implicitly dereferenced in pattern binding modes - /// for later usage in THIR lowering. For example, + /// Stores the types which were implicitly dereferenced in pattern binding modes or deref + /// patterns for later usage in THIR lowering. For example, /// /// ``` /// match &&Some(5i32) { @@ -86,11 +86,20 @@ pub struct TypeckResults<'tcx> { /// _ => {}, /// } /// ``` - /// leads to a `vec![&&Option<i32>, &Option<i32>]`. Empty vectors are not stored. + /// leads to a `vec![&&Option<i32>, &Option<i32>]` and + /// + /// ``` + /// #![feature(deref_patterns)] + /// match &Box::new(Some(5i32)) { + /// Some(n) => {}, + /// _ => {}, + /// } + /// ``` + /// leads to a `vec![&Box<Option<i32>>, Box<Option<i32>>]`. Empty vectors are not stored. /// /// See: /// <https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md#definitions> - pat_adjustments: ItemLocalMap<Vec<Ty<'tcx>>>, + pat_adjustments: ItemLocalMap<Vec<ty::adjustment::PatAdjustment<'tcx>>>, /// Set of reference patterns that match against a match-ergonomics inserted reference /// (as opposed to against a reference in the scrutinee type). @@ -403,11 +412,15 @@ impl<'tcx> TypeckResults<'tcx> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_binding_modes } } - pub fn pat_adjustments(&self) -> LocalTableInContext<'_, Vec<Ty<'tcx>>> { + pub fn pat_adjustments( + &self, + ) -> LocalTableInContext<'_, Vec<ty::adjustment::PatAdjustment<'tcx>>> { LocalTableInContext { hir_owner: self.hir_owner, data: &self.pat_adjustments } } - pub fn pat_adjustments_mut(&mut self) -> LocalTableInContextMut<'_, Vec<Ty<'tcx>>> { + pub fn pat_adjustments_mut( + &mut self, + ) -> LocalTableInContextMut<'_, Vec<ty::adjustment::PatAdjustment<'tcx>>> { LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments } } diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index bfc16816e2e..902a6e7f115 100644 --- a/compiler/rustc_mir_build/src/builder/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -103,8 +103,9 @@ fn parse_attribute(attr: &Attribute) -> MirPhase { let mut dialect: Option<String> = None; let mut phase: Option<String> = None; + // Not handling errors properly for this internal attribute; will just abort on errors. for nested in meta_items { - let name = nested.name_or_empty(); + let name = nested.name().unwrap(); let value = nested.value_str().unwrap().as_str().to_string(); match name.as_str() { "dialect" => { diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs index 8ca9ab58e45..59a52ae67cb 100644 --- a/compiler/rustc_mir_build/src/builder/mod.rs +++ b/compiler/rustc_mir_build/src/builder/mod.rs @@ -485,7 +485,7 @@ fn construct_fn<'tcx>( }; if let Some(custom_mir_attr) = - tcx.hir_attrs(fn_id).iter().find(|attr| attr.name_or_empty() == sym::custom_mir) + tcx.hir_attrs(fn_id).iter().find(|attr| attr.has_name(sym::custom_mir)) { return custom::build_custom_mir( tcx, diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index e42336a1dbb..4e4b11b8fa6 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -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; } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index b6a856a6eb4..adfce99a9b5 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -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. diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index b3daed8a7e0..2f593b9a0a7 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -113,7 +113,7 @@ impl<'tcx> ThirBuildCx<'tcx> { apply_adjustments: tcx .hir_attrs(hir_id) .iter() - .all(|attr| attr.name_or_empty() != rustc_span::sym::custom_mir), + .all(|attr| !attr.has_name(rustc_span::sym::custom_mir)), } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs index bd7787b643d..12c457f13fc 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/migration.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs @@ -4,8 +4,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_errors::MultiSpan; use rustc_hir::{BindingMode, ByRef, HirId, Mutability}; use rustc_lint as lint; -use rustc_middle::span_bug; -use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, Ty, TyCtxt}; +use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, TyCtxt}; use rustc_span::{Ident, Span}; use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg}; @@ -87,19 +86,18 @@ impl<'a> PatMigration<'a> { } /// Tracks when we're lowering a pattern that implicitly dereferences the scrutinee. - /// This should only be called when the pattern type adjustments list `adjustments` is - /// non-empty. Returns the prior default binding mode; this should be followed by a call to - /// [`PatMigration::leave_ref`] to restore it when we leave the pattern. + /// This should only be called when the pattern type adjustments list `adjustments` contains an + /// implicit deref of a reference type. Returns the prior default binding mode; this should be + /// followed by a call to [`PatMigration::leave_ref`] to restore it when we leave the pattern. pub(super) fn visit_implicit_derefs<'tcx>( &mut self, pat_span: Span, - adjustments: &[Ty<'tcx>], + adjustments: &[ty::adjustment::PatAdjustment<'tcx>], ) -> Option<(Span, Mutability)> { - let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| { - let &ty::Ref(_, _, mutbl) = ref_ty.kind() else { - span_bug!(pat_span, "pattern implicitly dereferences a non-ref type"); - }; - mutbl + // Implicitly dereferencing references changes the default binding mode, but implicit derefs + // of smart pointers do not. Thus, we only consider implicit derefs of reference types. + let implicit_deref_mutbls = adjustments.iter().filter_map(|adjust| { + if let &ty::Ref(_, _, mutbl) = adjust.source.kind() { Some(mutbl) } else { None } }); if !self.info.suggest_eliding_modes { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 73d60cf4442..8f058efdfac 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -18,6 +18,7 @@ use rustc_middle::mir::interpret::LitToConstInput; use rustc_middle::thir::{ Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary, }; +use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypingMode}; use rustc_middle::{bug, span_bug}; @@ -63,13 +64,15 @@ pub(super) fn pat_from_hir<'a, 'tcx>( impl<'a, 'tcx> PatCtxt<'a, 'tcx> { fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box<Pat<'tcx>> { - let adjustments: &[Ty<'tcx>] = + let adjustments: &[PatAdjustment<'tcx>] = self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); // Track the default binding mode for the Rust 2024 migration suggestion. + // Implicitly dereferencing references changes the default binding mode, but implicit deref + // patterns do not. Only track binding mode changes if a ref type is in the adjustments. let mut opt_old_mode_span = None; if let Some(s) = &mut self.rust_2024_migration - && !adjustments.is_empty() + && adjustments.iter().any(|adjust| adjust.kind == PatAdjust::BuiltinDeref) { opt_old_mode_span = s.visit_implicit_derefs(pat.span, adjustments); } @@ -102,17 +105,23 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { _ => self.lower_pattern_unadjusted(pat), }; - let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, ref_ty| { - debug!("{:?}: wrapping pattern with type {:?}", thir_pat, ref_ty); - Box::new(Pat { - span: thir_pat.span, - ty: *ref_ty, - kind: PatKind::Deref { subpattern: thir_pat }, - }) + let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, adjust| { + debug!("{:?}: wrapping pattern with adjustment {:?}", thir_pat, adjust); + let span = thir_pat.span; + let kind = match adjust.kind { + PatAdjust::BuiltinDeref => PatKind::Deref { subpattern: thir_pat }, + PatAdjust::OverloadedDeref => { + let mutable = self.typeck_results.pat_has_ref_mut_binding(pat); + let mutability = + if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; + PatKind::DerefPattern { subpattern: thir_pat, mutability } + } + }; + Box::new(Pat { span, ty: adjust.source, kind }) }); if let Some(s) = &mut self.rust_2024_migration - && !adjustments.is_empty() + && adjustments.iter().any(|adjust| adjust.kind == PatAdjust::BuiltinDeref) { s.leave_ref(opt_old_mode_span); } diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index c436b8c0fb0..95f488a925b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -109,27 +109,29 @@ impl RustcMirAttrs { .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter())); for attr in rustc_mir_attrs { - let attr_result = if attr.has_name(sym::borrowck_graphviz_postflow) { - Self::set_field(&mut ret.basename_and_suffix, tcx, &attr, |s| { - let path = PathBuf::from(s.to_string()); - match path.file_name() { - Some(_) => Ok(path), - None => { - tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() }); + let attr_result = match attr.name() { + Some(name @ sym::borrowck_graphviz_postflow) => { + Self::set_field(&mut ret.basename_and_suffix, tcx, name, &attr, |s| { + let path = PathBuf::from(s.to_string()); + match path.file_name() { + Some(_) => Ok(path), + None => { + tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() }); + Err(()) + } + } + }) + } + Some(name @ sym::borrowck_graphviz_format) => { + Self::set_field(&mut ret.formatter, tcx, name, &attr, |s| match s { + sym::two_phase => Ok(s), + _ => { + tcx.dcx().emit_err(UnknownFormatter { span: attr.span() }); Err(()) } - } - }) - } else if attr.has_name(sym::borrowck_graphviz_format) { - Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s { - sym::two_phase => Ok(s), - _ => { - tcx.dcx().emit_err(UnknownFormatter { span: attr.span() }); - Err(()) - } - }) - } else { - Ok(()) + }) + } + _ => Ok(()), }; result = result.and(attr_result); @@ -141,12 +143,12 @@ impl RustcMirAttrs { fn set_field<T>( field: &mut Option<T>, tcx: TyCtxt<'_>, + name: Symbol, attr: &ast::MetaItemInner, mapper: impl FnOnce(Symbol) -> Result<T, ()>, ) -> Result<(), ()> { if field.is_some() { - tcx.dcx() - .emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() }); + tcx.dcx().emit_err(DuplicateValuesFor { span: attr.span(), name }); return Err(()); } @@ -156,7 +158,7 @@ impl RustcMirAttrs { Ok(()) } else { tcx.dcx() - .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() }); + .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name().unwrap() }); Err(()) } } diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index d49f5d9f9c3..c7feb9e949b 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -223,7 +223,7 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { // Since this optimization adds new basic blocks and invalidates others, // clean up the cfg to make it nicer for other passes if should_cleanup { - simplify_cfg(body); + simplify_cfg(tcx, body); } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 69e80ed54ea..c5732194424 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -63,7 +63,7 @@ impl<'tcx> crate::MirPass<'tcx> for Inline { let _guard = span.enter(); if inline::<NormalInliner<'tcx>>(tcx, body) { debug!("running simplify cfg on {:?}", body.source); - simplify_cfg(body); + simplify_cfg(tcx, body); deref_finder(tcx, body); } } @@ -99,7 +99,7 @@ impl<'tcx> crate::MirPass<'tcx> for ForceInline { let _guard = span.enter(); if inline::<ForceInliner<'tcx>>(tcx, body) { debug!("running simplify cfg on {:?}", body.source); - simplify_cfg(body); + simplify_cfg(tcx, body); deref_finder(tcx, body); } } diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 8b4b214a3d4..9732225e48d 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -90,11 +90,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading { }; for bb in body.basic_blocks.indices() { - let old_len = finder.opportunities.len(); - // If we have any const-eval errors discard any opportunities found - if finder.start_from_switch(bb).is_none() { - finder.opportunities.truncate(old_len); - } + finder.start_from_switch(bb); } let opportunities = finder.opportunities; @@ -201,28 +197,26 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { /// Recursion entry point to find threading opportunities. #[instrument(level = "trace", skip(self))] - fn start_from_switch(&mut self, bb: BasicBlock) -> Option<()> { + fn start_from_switch(&mut self, bb: BasicBlock) { let bbdata = &self.body[bb]; if bbdata.is_cleanup || self.loop_headers.contains(bb) { - return Some(()); + return; } - let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { return Some(()) }; - let Some(discr) = discr.place() else { return Some(()) }; + let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { return }; + let Some(discr) = discr.place() else { return }; debug!(?discr, ?bb); let discr_ty = discr.ty(self.body, self.tcx).ty; - let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { - return Some(()); - }; + let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return }; - let Some(discr) = self.map.find(discr.as_ref()) else { return Some(()) }; + let Some(discr) = self.map.find(discr.as_ref()) else { return }; debug!(?discr); let cost = CostChecker::new(self.tcx, self.typing_env, None, self.body); let mut state = State::new_reachable(); let conds = if let Some((value, then, else_)) = targets.as_static_if() { - let value = ScalarInt::try_from_uint(value, discr_layout.size)?; + let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return }; self.arena.alloc_from_iter([ Condition { value, polarity: Polarity::Eq, target: then }, Condition { value, polarity: Polarity::Ne, target: else_ }, @@ -248,10 +242,10 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { mut state: State<ConditionSet<'a>>, mut cost: CostChecker<'_, 'tcx>, depth: usize, - ) -> Option<()> { + ) { // Do not thread through loop headers. if self.loop_headers.contains(bb) { - return Some(()); + return; } debug!(cost = ?cost.cost()); @@ -259,16 +253,16 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { self.body.basic_blocks[bb].statements.iter().enumerate().rev() { if self.is_empty(&state) { - return Some(()); + return; } cost.visit_statement(stmt, Location { block: bb, statement_index }); if cost.cost() > MAX_COST { - return Some(()); + return; } // Attempt to turn the `current_condition` on `lhs` into a condition on another place. - self.process_statement(bb, stmt, &mut state)?; + self.process_statement(bb, stmt, &mut state); // When a statement mutates a place, assignments to that place that happen // above the mutation cannot fulfill a condition. @@ -280,7 +274,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { } if self.is_empty(&state) || depth >= MAX_BACKTRACK { - return Some(()); + return; } let last_non_rec = self.opportunities.len(); @@ -293,9 +287,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { match term.kind { TerminatorKind::SwitchInt { ref discr, ref targets } => { self.process_switch_int(discr, targets, bb, &mut state); - self.find_opportunity(pred, state, cost, depth + 1)?; + self.find_opportunity(pred, state, cost, depth + 1); } - _ => self.recurse_through_terminator(pred, || state, &cost, depth)?, + _ => self.recurse_through_terminator(pred, || state, &cost, depth), } } else if let &[ref predecessors @ .., last_pred] = &predecessors[..] { for &pred in predecessors { @@ -320,13 +314,12 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { let first = &mut new_tos[0]; *first = ThreadingOpportunity { chain: vec![bb], target: first.target }; self.opportunities.truncate(last_non_rec + 1); - return Some(()); + return; } for op in self.opportunities[last_non_rec..].iter_mut() { op.chain.push(bb); } - Some(()) } /// Extract the mutated place from a statement. @@ -440,23 +433,23 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { lhs: PlaceIndex, rhs: &Operand<'tcx>, state: &mut State<ConditionSet<'a>>, - ) -> Option<()> { + ) { match rhs { // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. Operand::Constant(constant) => { - let constant = self - .ecx - .eval_mir_constant(&constant.const_, constant.span, None) - .discard_err()?; + let Some(constant) = + self.ecx.eval_mir_constant(&constant.const_, constant.span, None).discard_err() + else { + return; + }; self.process_constant(bb, lhs, constant, state); } // Transfer the conditions on the copied rhs. Operand::Move(rhs) | Operand::Copy(rhs) => { - let Some(rhs) = self.map.find(rhs.as_ref()) else { return Some(()) }; + let Some(rhs) = self.map.find(rhs.as_ref()) else { return }; state.insert_place_idx(rhs, lhs, &self.map); } } - Some(()) } #[instrument(level = "trace", skip(self))] @@ -466,18 +459,14 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { lhs_place: &Place<'tcx>, rhs: &Rvalue<'tcx>, state: &mut State<ConditionSet<'a>>, - ) -> Option<()> { - let Some(lhs) = self.map.find(lhs_place.as_ref()) else { - return Some(()); - }; + ) { + let Some(lhs) = self.map.find(lhs_place.as_ref()) else { return }; match rhs { - Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state)?, + Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state), // Transfer the conditions on the copy rhs. - Rvalue::CopyForDeref(rhs) => { - self.process_operand(bb, lhs, &Operand::Copy(*rhs), state)? - } + Rvalue::CopyForDeref(rhs) => self.process_operand(bb, lhs, &Operand::Copy(*rhs), state), Rvalue::Discriminant(rhs) => { - let Some(rhs) = self.map.find_discr(rhs.as_ref()) else { return Some(()) }; + let Some(rhs) = self.map.find_discr(rhs.as_ref()) else { return }; state.insert_place_idx(rhs, lhs, &self.map); } // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`. @@ -485,7 +474,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { let agg_ty = lhs_place.ty(self.body, self.tcx).ty; let lhs = match kind { // Do not support unions. - AggregateKind::Adt(.., Some(_)) => return Some(()), + AggregateKind::Adt(.., Some(_)) => return, AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => { if let Some(discr_target) = self.map.apply(lhs, TrackElem::Discriminant) && let Some(discr_value) = self @@ -498,23 +487,23 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { if let Some(idx) = self.map.apply(lhs, TrackElem::Variant(*variant_index)) { idx } else { - return Some(()); + return; } } _ => lhs, }; for (field_index, operand) in operands.iter_enumerated() { if let Some(field) = self.map.apply(lhs, TrackElem::Field(field_index)) { - self.process_operand(bb, field, operand, state)?; + self.process_operand(bb, field, operand, state); } } } // Transfer the conditions on the copy rhs, after inverting the value of the condition. Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => { let layout = self.ecx.layout_of(place.ty(self.body, self.tcx).ty).unwrap(); - let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return Some(()) }; - let Some(place) = self.map.find(place.as_ref()) else { return Some(()) }; - let conds = conditions.map(self.arena, |mut cond| { + let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return }; + let Some(place) = self.map.find(place.as_ref()) else { return }; + let Some(conds) = conditions.map(self.arena, |mut cond| { cond.value = self .ecx .unary_op(UnOp::Not, &ImmTy::from_scalar_int(cond.value, layout)) @@ -522,7 +511,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { .to_scalar_int() .discard_err()?; Some(cond) - })?; + }) else { + return; + }; state.insert_value_idx(place, conds, &self.map); } // We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`. @@ -532,34 +523,38 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { box (Operand::Move(place) | Operand::Copy(place), Operand::Constant(value)) | box (Operand::Constant(value), Operand::Move(place) | Operand::Copy(place)), ) => { - let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return Some(()) }; - let Some(place) = self.map.find(place.as_ref()) else { return Some(()) }; + let Some(conditions) = state.try_get_idx(lhs, &self.map) else { return }; + let Some(place) = self.map.find(place.as_ref()) else { return }; let equals = match op { BinOp::Eq => ScalarInt::TRUE, BinOp::Ne => ScalarInt::FALSE, - _ => return Some(()), + _ => return, }; if value.const_.ty().is_floating_point() { // Floating point equality does not follow bit-patterns. // -0.0 and NaN both have special rules for equality, // and therefore we cannot use integer comparisons for them. // Avoid handling them, though this could be extended in the future. - return Some(()); + return; } - let value = value.const_.try_eval_scalar_int(self.tcx, self.typing_env)?; - let conds = conditions.map(self.arena, |c| { + let Some(value) = value.const_.try_eval_scalar_int(self.tcx, self.typing_env) + else { + return; + }; + let Some(conds) = conditions.map(self.arena, |c| { Some(Condition { value, polarity: if c.matches(equals) { Polarity::Eq } else { Polarity::Ne }, ..c }) - })?; + }) else { + return; + }; state.insert_value_idx(place, conds, &self.map); } _ => {} } - Some(()) } #[instrument(level = "trace", skip(self))] @@ -568,7 +563,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { bb: BasicBlock, stmt: &Statement<'tcx>, state: &mut State<ConditionSet<'a>>, - ) -> Option<()> { + ) { let register_opportunity = |c: Condition| { debug!(?bb, ?c.target, "register"); self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target }) @@ -581,32 +576,30 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { // If we expect `discriminant(place) ?= A`, // we have an opportunity if `variant_index ?= A`. StatementKind::SetDiscriminant { box place, variant_index } => { - let Some(discr_target) = self.map.find_discr(place.as_ref()) else { - return Some(()); - }; + let Some(discr_target) = self.map.find_discr(place.as_ref()) else { return }; let enum_ty = place.ty(self.body, self.tcx).ty; // `SetDiscriminant` guarantees that the discriminant is now `variant_index`. // Even if the discriminant write does nothing due to niches, it is UB to set the // discriminant when the data does not encode the desired discriminant. - let discr = - self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err()?; - self.process_immediate(bb, discr_target, discr, state); + let Some(discr) = + self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err() + else { + return; + }; + self.process_immediate(bb, discr_target, discr, state) } // If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`. StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume( Operand::Copy(place) | Operand::Move(place), )) => { - let Some(conditions) = state.try_get(place.as_ref(), &self.map) else { - return Some(()); - }; - conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity); + let Some(conditions) = state.try_get(place.as_ref(), &self.map) else { return }; + conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity) } StatementKind::Assign(box (lhs_place, rhs)) => { - self.process_assign(bb, lhs_place, rhs, state)?; + self.process_assign(bb, lhs_place, rhs, state) } _ => {} } - Some(()) } #[instrument(level = "trace", skip(self, state, cost))] @@ -617,7 +610,7 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { state: impl FnOnce() -> State<ConditionSet<'a>>, cost: &CostChecker<'_, 'tcx>, depth: usize, - ) -> Option<()> { + ) { let term = self.body.basic_blocks[bb].terminator(); let place_to_flood = match term.kind { // We come from a target, so those are not possible. @@ -632,9 +625,9 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> { | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Yield { .. } => bug!("{term:?} invalid"), // Cannot reason about inline asm. - TerminatorKind::InlineAsm { .. } => return Some(()), + TerminatorKind::InlineAsm { .. } => return, // `SwitchInt` is handled specially. - TerminatorKind::SwitchInt { .. } => return Some(()), + TerminatorKind::SwitchInt { .. } => return, // We can recurse, no thing particular to do. TerminatorKind::Goto { .. } => None, // Flood the overwritten place, and progress through. diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 5059837328e..b37241185c9 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -43,7 +43,7 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { } if should_cleanup { - simplify_cfg(body); + simplify_cfg(tcx, body); } } diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs index 15fe77d5319..cb598ceb4df 100644 --- a/compiler/rustc_mir_transform/src/remove_place_mention.rs +++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs @@ -8,7 +8,7 @@ pub(super) struct RemovePlaceMention; impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention { fn is_enabled(&self, sess: &rustc_session::Session) -> bool { - !sess.opts.unstable_opts.mir_keep_place_mention + !sess.opts.unstable_opts.mir_preserve_ub } fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index 8a8cdafc690..43f80508e4a 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -35,7 +35,7 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops { // if we applied optimizations, we potentially have some cfg to cleanup to // make it easier for further passes if should_simplify { - simplify_cfg(body); + simplify_cfg(tcx, body); } } diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 5947637cded..4f2cce8ac10 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -26,6 +26,13 @@ //! Here the block (`{ return; }`) has the return type `char`, rather than `()`, but the MIR we //! naively generate still contains the `_a = ()` write in the unreachable block "after" the //! return. +//! +//! **WARNING**: This is one of the few optimizations that runs on built and analysis MIR, and +//! so its effects may affect the type-checking, borrow-checking, and other analysis of MIR. +//! We must be extremely careful to only apply optimizations that preserve UB and all +//! non-determinism, since changes here can affect which programs compile in an insta-stable way. +//! The normal logic that a program with UB can be changed to do anything does not apply to +//! pre-"runtime" MIR! use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor}; @@ -66,8 +73,8 @@ impl SimplifyCfg { } } -pub(super) fn simplify_cfg(body: &mut Body<'_>) { - CfgSimplifier::new(body).simplify(); +pub(super) fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + CfgSimplifier::new(tcx, body).simplify(); remove_dead_blocks(body); // FIXME: Should probably be moved into some kind of pass manager @@ -79,9 +86,9 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg { self.name() } - fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { debug!("SimplifyCfg({:?}) - simplifying {:?}", self.name(), body.source); - simplify_cfg(body); + simplify_cfg(tcx, body); } fn is_required(&self) -> bool { @@ -90,12 +97,13 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg { } struct CfgSimplifier<'a, 'tcx> { + preserve_switch_reads: bool, basic_blocks: &'a mut IndexSlice<BasicBlock, BasicBlockData<'tcx>>, pred_count: IndexVec<BasicBlock, u32>, } impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { - fn new(body: &'a mut Body<'tcx>) -> Self { + fn new(tcx: TyCtxt<'tcx>, body: &'a mut Body<'tcx>) -> Self { let mut pred_count = IndexVec::from_elem(0u32, &body.basic_blocks); // we can't use mir.predecessors() here because that counts @@ -110,9 +118,12 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { } } + // Preserve `SwitchInt` reads on built and analysis MIR, or if `-Zmir-preserve-ub`. + let preserve_switch_reads = matches!(body.phase, MirPhase::Built | MirPhase::Analysis(_)) + || tcx.sess.opts.unstable_opts.mir_preserve_ub; let basic_blocks = body.basic_blocks_mut(); - CfgSimplifier { basic_blocks, pred_count } + CfgSimplifier { preserve_switch_reads, basic_blocks, pred_count } } fn simplify(mut self) { @@ -253,9 +264,15 @@ impl<'a, 'tcx> CfgSimplifier<'a, 'tcx> { // turn a branch with all successors identical to a goto fn simplify_branch(&mut self, terminator: &mut Terminator<'tcx>) -> bool { - match terminator.kind { - TerminatorKind::SwitchInt { .. } => {} - _ => return false, + // Removing a `SwitchInt` terminator may remove reads that result in UB, + // so we must not apply this optimization before borrowck or when + // `-Zmir-preserve-ub` is set. + if self.preserve_switch_reads { + return false; + } + + let TerminatorKind::SwitchInt { .. } = terminator.kind else { + return false; }; let first_succ = { diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index b1b6f10e0fe..d7690a96e10 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -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()) diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 83b2465d05a..ecb57cc0ad7 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -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 diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 0b61c368d8e..7752a705cd1 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -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)) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index fdeb276a58e..9466901683e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -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, diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 409af8568d7..7bd1300f34e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -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) } } diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 4935fc03256..a8ec9a1e952 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -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, diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index b3f83a32024..0ddd9a85df8 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -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); diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs index 2bfa1ea4e05..751d13af433 100644 --- a/compiler/rustc_parse/src/lexer/unicode_chars.rs +++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs @@ -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), diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index f1bd6a22730..6061c9cb485 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -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)) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 7c8e0146c3d..23c8db7bca7 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -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; } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 20a5252624d..f3ed798eba4 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -437,7 +437,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 @@ -543,8 +543,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(); @@ -638,8 +638,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 @@ -913,8 +913,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), } } @@ -1003,7 +1003,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: @@ -1203,7 +1203,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. @@ -1233,7 +1233,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 @@ -1677,14 +1677,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, _) = @@ -1879,19 +1876,21 @@ 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()?; if let Some(expr) = &mut expr { if label.is_some() - && matches!( - expr.kind, + && match &expr.kind { ExprKind::While(_, _, None) - | ExprKind::ForLoop { label: None, .. } - | ExprKind::Loop(_, None, _) - | ExprKind::Block(_, None) - ) + | ExprKind::ForLoop { label: None, .. } + | ExprKind::Loop(_, None, _) => true, + ExprKind::Block(block, None) => { + matches!(block.rules, BlockCheckMode::Default) + } + _ => false, + } { self.psess.buffer_lint( BREAK_WITH_LABEL_AND_LOOP, @@ -2015,7 +2014,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(); } } @@ -2156,9 +2155,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"); @@ -2167,9 +2164,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"); @@ -2274,7 +2271,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) @@ -2327,8 +2324,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. @@ -2483,7 +2480,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(); @@ -2888,7 +2885,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`. @@ -2922,7 +2919,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]; @@ -2966,7 +2963,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 @@ -3116,7 +3113,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) => { @@ -3124,7 +3121,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 @@ -3185,7 +3182,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)); } @@ -3244,7 +3241,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. @@ -3292,8 +3289,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); @@ -3439,7 +3436,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, @@ -3485,7 +3482,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. @@ -3528,8 +3525,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) } @@ -3539,8 +3535,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() } @@ -3574,13 +3569,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()) )) } @@ -3704,11 +3697,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 }) @@ -3865,8 +3854,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 { diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index c3f71dd8b30..c05479feb61 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -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])) } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 39a0291cb1e..39251f1ce27 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -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); } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 2221a261b4c..d73adb39826 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -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(..)); - - // 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; - } - } - - // Consume close delimiter + 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(); - tree - } - token::CloseDelim(_) | token::Eof => unreachable!(), - _ => { - let prev_spacing = self.token_spacing; - self.bump(); - TokenTree::Token(self.prev_token, prev_spacing) + if self.token_cursor.stack.len() == target_depth { + debug_assert!(self.token.kind.close_delim().is_some()); + break; + } } + + // 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, diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index b6e89cd7fa4..7c83e96c160 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -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() } } } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index d5f469f9aa9..d6ff80b2eb4 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -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()); diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 02883655662..1a02d45f0e3 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -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()? diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 0cc8b605018..885a65d4de7 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -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 ) }) { diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 2f958f4d492..8285070839a 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -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, diff --git a/compiler/rustc_parse/src/parser/token_type.rs b/compiler/rustc_parse/src/parser/token_type.rs index add3c970201..b91548196a3 100644 --- a/compiler/rustc_parse/src/parser/token_type.rs +++ b/compiler/rustc_parse/src/parser/token_type.rs @@ -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) }; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 42ebf26784d..17481731b11 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -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)?; } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index b518fca7a65..6a1c2af48ed 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -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(), diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 6ee5e356435..413726ddd82 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -404,7 +404,7 @@ passes_invalid_attr_at_crate_level = passes_invalid_attr_at_crate_level_item = the inner attribute doesn't annotate this {$kind} -passes_invalid_macro_export_arguments = `{$name}` isn't a valid `#[macro_export]` argument +passes_invalid_macro_export_arguments = invalid `#[macro_export]` argument passes_invalid_macro_export_arguments_too_many_items = `#[macro_export]` can only take 1 or 0 arguments @@ -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 @@ -771,8 +771,8 @@ passes_unreachable_due_to_uninhabited = unreachable {$descr} .label_orig = any code following this expression is unreachable .note = this expression has type `{$ty}`, which is uninhabited -passes_unrecognized_field = - unrecognized field name `{$name}` +passes_unrecognized_argument = + unrecognized argument passes_unstable_attr_for_already_stable_feature = can't mark as unstable using an already stable feature diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs index 671b7d7ad76..b139ed6a66c 100644 --- a/compiler/rustc_passes/src/abi_test.rs +++ b/compiler/rustc_passes/src/abi_test.rs @@ -9,7 +9,7 @@ use rustc_span::sym; use rustc_target::callconv::FnAbi; use super::layout_test::ensure_wf; -use crate::errors::{AbiInvalidAttribute, AbiNe, AbiOf, UnrecognizedField}; +use crate::errors::{AbiInvalidAttribute, AbiNe, AbiOf, UnrecognizedArgument}; pub fn test_abi(tcx: TyCtxt<'_>) { if !tcx.features().rustc_attrs() { @@ -77,8 +77,8 @@ fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut // The `..` are the names of fields to dump. let meta_items = attr.meta_item_list().unwrap_or_default(); for meta_item in meta_items { - match meta_item.name_or_empty() { - sym::debug => { + match meta_item.name() { + Some(sym::debug) => { let fn_name = tcx.item_name(item_def_id.into()); tcx.dcx().emit_err(AbiOf { span: tcx.def_span(item_def_id), @@ -88,8 +88,8 @@ fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut }); } - name => { - tcx.dcx().emit_err(UnrecognizedField { span: meta_item.span(), name }); + _ => { + tcx.dcx().emit_err(UnrecognizedArgument { span: meta_item.span() }); } } } @@ -118,8 +118,8 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut } let meta_items = attr.meta_item_list().unwrap_or_default(); for meta_item in meta_items { - match meta_item.name_or_empty() { - sym::debug => { + match meta_item.name() { + Some(sym::debug) => { let ty::FnPtr(sig_tys, hdr) = ty.kind() else { span_bug!( meta_item.span(), @@ -138,7 +138,7 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut let fn_name = tcx.item_name(item_def_id.into()); tcx.dcx().emit_err(AbiOf { span, fn_name, fn_abi: format!("{:#?}", abi) }); } - sym::assert_eq => { + Some(sym::assert_eq) => { let ty::Tuple(fields) = ty.kind() else { span_bug!( meta_item.span(), @@ -188,8 +188,8 @@ fn dump_abi_of_fn_type(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribut }); } } - name => { - tcx.dcx().emit_err(UnrecognizedField { span: meta_item.span(), name }); + _ => { + tcx.dcx().emit_err(UnrecognizedArgument { span: meta_item.span() }); } } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 42279258e87..cfc71a412be 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -523,9 +523,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_no_sanitize(&self, attr: &Attribute, span: Span, target: Target) { if let Some(list) = attr.meta_item_list() { for item in list.iter() { - let sym = item.name_or_empty(); + let sym = item.name(); match sym { - sym::address | sym::hwaddress => { + Some(s @ sym::address | s @ sym::hwaddress) => { let is_valid = matches!(target, Target::Fn | Target::Method(..) | Target::Static); if !is_valid { @@ -533,7 +533,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr_span: item.span(), defn_span: span, accepted_kind: "a function or static", - attr_str: sym.as_str(), + attr_str: s.as_str(), }); } } @@ -544,7 +544,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr_span: item.span(), defn_span: span, accepted_kind: "a function", - attr_str: sym.as_str(), + attr_str: &match sym { + Some(name) => name.to_string(), + None => "...".to_string(), + }, }); } } @@ -561,12 +564,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> { allowed_target: Target, ) { if target != allowed_target { + let path = attr.path(); + let path: Vec<_> = path.iter().map(|s| s.as_str()).collect(); + let attr_name = path.join("::"); self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, attr.span(), errors::OnlyHasEffectOn { - attr_name: attr.name_or_empty(), + attr_name, target_name: allowed_target.name().replace(' ', "_"), }, ); @@ -589,7 +595,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // * `#[track_caller]` // * `#[test]`, `#[ignore]`, `#[should_panic]` // - // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains accurate + // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains + // accurate. const ALLOW_LIST: &[rustc_span::Symbol] = &[ // conditional compilation sym::cfg_trace, @@ -672,24 +679,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - if !ALLOW_LIST.iter().any(|name| other_attr.has_name(*name)) { + if !other_attr.has_any_name(ALLOW_LIST) { self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute { span: other_attr.span(), naked_span: attr.span(), - attr: other_attr.name_or_empty(), + attr: other_attr.name().unwrap(), }); return; } } } - // 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(), @@ -1150,7 +1150,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ) { match target { Target::Use | Target::ExternCrate => { - let do_inline = meta.name_or_empty() == sym::inline; + let do_inline = meta.has_name(sym::inline); if let Some((prev_inline, prev_span)) = *specified_inline { if do_inline != prev_inline { let mut spans = MultiSpan::from_spans(vec![prev_span, meta.span()]); @@ -1260,8 +1260,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { fn check_test_attr(&self, meta: &MetaItemInner, hir_id: HirId) { if let Some(metas) = meta.meta_item_list() { for i_meta in metas { - match (i_meta.name_or_empty(), i_meta.meta_item()) { - (sym::attr | sym::no_crate_inject, _) => {} + match (i_meta.name(), i_meta.meta_item()) { + (Some(sym::attr | sym::no_crate_inject), _) => {} (_, Some(m)) => { self.tcx.emit_node_span_lint( INVALID_DOC_ATTRIBUTES, @@ -1322,61 +1322,63 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if let Some(list) = attr.meta_item_list() { for meta in &list { if let Some(i_meta) = meta.meta_item() { - match i_meta.name_or_empty() { - sym::alias => { + match i_meta.name() { + Some(sym::alias) => { if self.check_attr_not_crate_level(meta, hir_id, "alias") { self.check_doc_alias(meta, hir_id, target, aliases); } } - sym::keyword => { + Some(sym::keyword) => { if self.check_attr_not_crate_level(meta, hir_id, "keyword") { self.check_doc_keyword(meta, hir_id); } } - sym::fake_variadic => { + Some(sym::fake_variadic) => { if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") { self.check_doc_fake_variadic(meta, hir_id); } } - sym::search_unbox => { + Some(sym::search_unbox) => { if self.check_attr_not_crate_level(meta, hir_id, "fake_variadic") { self.check_doc_search_unbox(meta, hir_id); } } - sym::test => { + Some(sym::test) => { if self.check_attr_crate_level(attr, meta, hir_id) { self.check_test_attr(meta, hir_id); } } - sym::html_favicon_url - | sym::html_logo_url - | sym::html_playground_url - | sym::issue_tracker_base_url - | sym::html_root_url - | sym::html_no_source => { + Some( + sym::html_favicon_url + | sym::html_logo_url + | sym::html_playground_url + | sym::issue_tracker_base_url + | sym::html_root_url + | sym::html_no_source, + ) => { self.check_attr_crate_level(attr, meta, hir_id); } - sym::cfg_hide => { + Some(sym::cfg_hide) => { if self.check_attr_crate_level(attr, meta, hir_id) { self.check_doc_cfg_hide(meta, hir_id); } } - sym::inline | sym::no_inline => { + Some(sym::inline | sym::no_inline) => { self.check_doc_inline(attr, meta, hir_id, target, specified_inline) } - sym::masked => self.check_doc_masked(attr, meta, hir_id, target), + Some(sym::masked) => self.check_doc_masked(attr, meta, hir_id, target), - sym::cfg | sym::hidden | sym::notable_trait => {} + Some(sym::cfg | sym::hidden | sym::notable_trait) => {} - sym::rust_logo => { + Some(sym::rust_logo) => { if self.check_attr_crate_level(attr, meta, hir_id) && !self.tcx.features().rustdoc_internals() { @@ -2299,7 +2301,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) { - let name = attr.name_or_empty(); + let name = attr.name().unwrap(); match target { Target::ExternCrate | Target::Mod => {} _ => { @@ -2331,12 +2333,12 @@ impl<'tcx> CheckAttrVisitor<'tcx> { attr.span(), errors::MacroExport::TooManyItems, ); - } else if meta_item_list[0].name_or_empty() != sym::local_inner_macros { + } else if !meta_item_list[0].has_name(sym::local_inner_macros) { self.tcx.emit_node_span_lint( INVALID_MACRO_EXPORT_ARGUMENTS, hir_id, meta_item_list[0].span(), - errors::MacroExport::UnknownItem { name: meta_item_list[0].name_or_empty() }, + errors::MacroExport::InvalidArgument, ); } } else { @@ -2381,33 +2383,28 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } // Warn on useless empty attributes. - let note = if (matches!( - attr.name_or_empty(), - sym::macro_use - | sym::allow - | sym::expect - | sym::warn - | sym::deny - | sym::forbid - | sym::feature - | sym::target_feature - ) && attr.meta_item_list().is_some_and(|list| list.is_empty())) + let note = if attr.has_any_name(&[ + sym::macro_use, + sym::allow, + sym::expect, + sym::warn, + sym::deny, + sym::forbid, + sym::feature, + sym::target_feature, + ]) && attr.meta_item_list().is_some_and(|list| list.is_empty()) { - errors::UnusedNote::EmptyList { name: attr.name_or_empty() } - } else if matches!( - attr.name_or_empty(), - sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect - ) && let Some(meta) = attr.meta_item_list() + errors::UnusedNote::EmptyList { name: attr.name().unwrap() } + } else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) + && let Some(meta) = attr.meta_item_list() && let [meta] = meta.as_slice() && let Some(item) = meta.meta_item() && let MetaItemKind::NameValue(_) = &item.kind && item.path == sym::reason { - errors::UnusedNote::NoLints { name: attr.name_or_empty() } - } else if matches!( - attr.name_or_empty(), - sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect - ) && let Some(meta) = attr.meta_item_list() + errors::UnusedNote::NoLints { name: attr.name().unwrap() } + } else if attr.has_any_name(&[sym::allow, sym::warn, sym::deny, sym::forbid, sym::expect]) + && let Some(meta) = attr.meta_item_list() && meta.iter().any(|meta| { meta.meta_item().map_or(false, |item| item.path == sym::linker_messages) }) @@ -2440,7 +2437,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { return; } } - } else if attr.name_or_empty() == sym::default_method_body_is_const { + } else if attr.has_name(sym::default_method_body_is_const) { errors::UnusedNote::DefaultMethodBodyConst } else { return; @@ -2897,10 +2894,11 @@ fn check_duplicates( if matches!(duplicates, WarnFollowingWordOnly) && !attr.is_word() { return; } + let attr_name = attr.name().unwrap(); match duplicates { DuplicatesOk => {} WarnFollowing | FutureWarnFollowing | WarnFollowingWordOnly | FutureWarnPreceding => { - match seen.entry(attr.name_or_empty()) { + match seen.entry(attr_name) { Entry::Occupied(mut entry) => { let (this, other) = if matches!(duplicates, FutureWarnPreceding) { let to_remove = entry.insert(attr.span()); @@ -2927,7 +2925,7 @@ fn check_duplicates( } } } - ErrorFollowing | ErrorPreceding => match seen.entry(attr.name_or_empty()) { + ErrorFollowing | ErrorPreceding => match seen.entry(attr_name) { Entry::Occupied(mut entry) => { let (this, other) = if matches!(duplicates, ErrorPreceding) { let to_remove = entry.insert(attr.span()); @@ -2935,11 +2933,7 @@ fn check_duplicates( } else { (attr.span(), *entry.get()) }; - tcx.dcx().emit_err(errors::UnusedMultiple { - this, - other, - name: attr.name_or_empty(), - }); + tcx.dcx().emit_err(errors::UnusedMultiple { this, other, name: attr_name }); } Entry::Vacant(entry) => { entry.insert(attr.span()); diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs index 062d56a79a0..7a7a8175e55 100644 --- a/compiler/rustc_passes/src/debugger_visualizer.rs +++ b/compiler/rustc_passes/src/debugger_visualizer.rs @@ -28,17 +28,17 @@ impl DebuggerVisualizerCollector<'_> { return; }; - let (visualizer_type, visualizer_path) = - match (meta_item.name_or_empty(), meta_item.value_str()) { - (sym::natvis_file, Some(value)) => (DebuggerVisualizerType::Natvis, value), - (sym::gdb_script_file, Some(value)) => { - (DebuggerVisualizerType::GdbPrettyPrinter, value) - } - (_, _) => { - self.sess.dcx().emit_err(DebugVisualizerInvalid { span: meta_item.span }); - return; - } - }; + let (visualizer_type, visualizer_path) = match (meta_item.name(), meta_item.value_str()) + { + (Some(sym::natvis_file), Some(value)) => (DebuggerVisualizerType::Natvis, value), + (Some(sym::gdb_script_file), Some(value)) => { + (DebuggerVisualizerType::GdbPrettyPrinter, value) + } + (_, _) => { + self.sess.dcx().emit_err(DebugVisualizerInvalid { span: meta_item.span }); + return; + } + }; let file = match resolve_path(&self.sess, visualizer_path.as_str(), attr.span) { Ok(file) => file, diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 995fc85676e..4052264b051 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -756,7 +756,7 @@ pub(crate) enum MacroExport { OnDeclMacro, #[diag(passes_invalid_macro_export_arguments)] - UnknownItem { name: Symbol }, + InvalidArgument, #[diag(passes_invalid_macro_export_arguments_too_many_items)] TooManyItems, @@ -1045,11 +1045,10 @@ pub(crate) struct AbiInvalidAttribute { } #[derive(Diagnostic)] -#[diag(passes_unrecognized_field)] -pub(crate) struct UnrecognizedField { +#[diag(passes_unrecognized_argument)] +pub(crate) struct UnrecognizedArgument { #[primary_span] pub span: Span, - pub name: Symbol, } #[derive(Diagnostic)] @@ -1433,7 +1432,7 @@ pub(crate) struct UselessAssignment<'a> { #[derive(LintDiagnostic)] #[diag(passes_only_has_effect_on)] pub(crate) struct OnlyHasEffectOn { - pub attr_name: Symbol, + pub attr_name: String, pub target_name: String, } diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index d4512c9417e..a19faf0fa83 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -13,7 +13,7 @@ use rustc_trait_selection::traits; use crate::errors::{ LayoutAbi, LayoutAlign, LayoutHomogeneousAggregate, LayoutInvalidAttribute, LayoutOf, - LayoutSize, UnrecognizedField, + LayoutSize, UnrecognizedArgument, }; pub fn test_layout(tcx: TyCtxt<'_>) { @@ -79,28 +79,28 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) { // The `..` are the names of fields to dump. let meta_items = attr.meta_item_list().unwrap_or_default(); for meta_item in meta_items { - match meta_item.name_or_empty() { + match meta_item.name() { // FIXME: this never was about ABI and now this dump arg is confusing - sym::abi => { + Some(sym::abi) => { tcx.dcx().emit_err(LayoutAbi { span, abi: format!("{:?}", ty_layout.backend_repr), }); } - sym::align => { + Some(sym::align) => { tcx.dcx().emit_err(LayoutAlign { span, align: format!("{:?}", ty_layout.align), }); } - sym::size => { + Some(sym::size) => { tcx.dcx() .emit_err(LayoutSize { span, size: format!("{:?}", ty_layout.size) }); } - sym::homogeneous_aggregate => { + Some(sym::homogeneous_aggregate) => { tcx.dcx().emit_err(LayoutHomogeneousAggregate { span, homogeneous_aggregate: format!( @@ -111,15 +111,15 @@ fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) { }); } - sym::debug => { + Some(sym::debug) => { let normalized_ty = tcx.normalize_erasing_regions(typing_env, ty); // FIXME: using the `Debug` impl here isn't ideal. let ty_layout = format!("{:#?}", *ty_layout); tcx.dcx().emit_err(LayoutOf { span, normalized_ty, ty_layout }); } - name => { - tcx.dcx().emit_err(UnrecognizedField { span: meta_item.span(), name }); + _ => { + tcx.dcx().emit_err(UnrecognizedArgument { span: meta_item.span() }); } } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 749b7f24c50..c58f8480572 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -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) { diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index 6b18d450e9e..80603b4a156 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -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>>, } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index ff7ea5bd718..bc92b95ce71 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -568,124 +568,205 @@ 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; -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, + fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { + *self + } } - } - 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(), - ) - } + 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, + )* + } + } - 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. #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub enum ErrorOutputType { @@ -1570,6 +1651,18 @@ static PRINT_HELP: LazyLock<String> = LazyLock::new(|| { ) }); +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. pub fn rustc_optgroups() -> Vec<RustcOptGroup> { @@ -1616,14 +1709,7 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> { make_crate_type_option(), 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, "", "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", ""), diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 1b0794f79d3..36eee5f3086 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2322,12 +2322,12 @@ options! { mir_include_spans: MirIncludeSpans = (MirIncludeSpans::default(), parse_mir_include_spans, [UNTRACKED], "include extra comments in mir pretty printing, like line numbers and statement indices, \ details about types, etc. (boolean for all passes, 'nll' to enable in NLL MIR only, default: 'nll')"), - mir_keep_place_mention: bool = (false, parse_bool, [TRACKED], - "keep place mention MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \ - (default: no)"), #[rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field")] mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED], "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"), + mir_preserve_ub: bool = (false, parse_bool, [TRACKED], + "keep place mention statements and reads in trivial SwitchInt terminators, which are interpreted \ + e.g., by miri; implies -Zmir-opt-level=0 (default: no)"), mir_strip_debuginfo: MirStripDebugInfo = (MirStripDebugInfo::None, parse_mir_strip_debuginfo, [TRACKED], "Whether to remove some of the MIR debug info from methods. Default: None"), move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED], diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index d1da68ec236..53908914965 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -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)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8f75cc5e5e6..32a5aff0cb3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -372,6 +372,7 @@ symbols! { SyncUnsafeCell, T, Target, + This, ToOwned, ToString, TokenStream, @@ -1186,6 +1187,7 @@ symbols! { instruction_set, integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below integral, + internal_features, into_async_iter_into_iter, into_future, into_iter, @@ -1822,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, @@ -2216,7 +2217,6 @@ symbols! { unsafe_extern_blocks, unsafe_fields, unsafe_no_drop_flag, - unsafe_pin_internals, unsafe_pinned, unsafe_unpin, unsize, diff --git a/compiler/rustc_target/src/spec/base/linux_musl.rs b/compiler/rustc_target/src/spec/base/linux_musl.rs index 1a854fe362d..1bef602404e 100644 --- a/compiler/rustc_target/src/spec/base/linux_musl.rs +++ b/compiler/rustc_target/src/spec/base/linux_musl.rs @@ -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() + } } diff --git a/compiler/rustc_target/src/spec/base/linux_ohos.rs b/compiler/rustc_target/src/spec/base/linux_ohos.rs index 6f4d69a996c..1b7f1e19666 100644 --- a/compiler/rustc_target/src/spec/base/linux_ohos.rs +++ b/compiler/rustc_target/src/spec/base/linux_ohos.rs @@ -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() + } } diff --git a/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs index 233a1c4fd7a..91ab3111097 100644 --- a/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs +++ b/compiler/rustc_target/src/spec/targets/i686_win7_windows_msvc.rs @@ -7,6 +7,12 @@ pub(crate) fn target() -> Target { base.cpu = "pentium4".into(); base.max_atomic_width = Some(64); base.supported_sanitizers = SanitizerSet::ADDRESS; + // On Windows 7 32-bit, the alignment characteristic of the TLS Directory + // don't appear to be respected by the PE Loader, leading to crashes. As + // a result, let's disable has_thread_local to make sure TLS goes through + // the emulation layer. + // See https://github.com/rust-lang/rust/issues/138903 + base.has_thread_local = false; base.add_pre_link_args( LinkerFlavor::Msvc(Lld::No), diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 8ff7030717a..78f9287b407 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -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; diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index f0c6e51f2a4..4c4491269b7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -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:?}")))); - } + }; - if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { - flags.push((sym::cause, Some("MainFunctionType".to_string()))); - } + let from_desugaring = obligation.cause.span.desugaring_kind(); - 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>, @@ -371,28 +393,10 @@ 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())?; - continue; - } - } else if item.has_name(sym::label) && label.is_none() { - if let Some(label_) = item.value_str() { - label = parse_value(label_, 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::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 + fn verify(&self, tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> { + if !tcx.is_trait(trait_def_id) { + return Ok(()); + }; + + let ctx = if self.is_diagnostic_namespace_variant { + Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id } } else { - tcx.trait_id_of_impl(item_def_id) - .expect("expected `on_unimplemented` to correspond to a trait") + Ctx::RustcOnUnimplemented { tcx, trait_def_id } }; - 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 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()); - } - } + } else { + for w in warnings { + match w { + FormatWarning::UnknownParam { argument_name, span } => { + let reported = struct_span_code_err!( + tcx.dcx(), + span, + E0230, + "cannot find parameter {} on this trait", + argument_name, + ) + .emit(); + result = Err(reported); } - } - // `{: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 { + FormatWarning::PositionalArgument { span, .. } => { let reported = struct_span_code_err!( tcx.dcx(), - self.span, + span, E0231, - "only named generic parameters are allowed" + "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 { + Ctx::RustcOnUnimplemented { tcx, trait_def_id } + }; + + if let Ok(s) = FormatString::parse(self.symbol, self.span, &ctx) { + s.format(args) } else { - constructed_message + // 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() } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs new file mode 100644 index 00000000000..116cfb01cb6 --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs @@ -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, + } + } +} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs new file mode 100644 index 00000000000..f835406122b --- /dev/null +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs @@ -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, + }, + ); + } + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 4ac45172a0e..7551ac5aa97 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -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) + } } } } diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs index af568171f91..bb909c54d2b 100644 --- a/compiler/rustc_transmute/src/layout/dfa.rs +++ b/compiler/rustc_transmute/src/layout/dfa.rs @@ -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; + + Self { transitions, start, accept } + } - 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); + 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(); - let mut queue = vec![(nfa_start, dfa_start)]; + transitions.entry(start).or_default().byte_transitions.insert(byte, accept); - while let Some((nfa_state, dfa_state)) = queue.pop() { - if nfa_state == nfa_accepting { - continue; - } + Self { transitions, start, accept } + } - 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 mapped_state = next_nfa_states.iter().find_map(|x| nfa_to_dfa.get(x).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)), - }; - - 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 - }); + 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 dfa_accepting = nfa_to_dfa[&nfa_accepting]; + let start = self.start; + let accept = other.accept; + + let mut transitions: Map<State, Transitions<R>> = self.transitions; - Self { transitions: dfa_transitions, start: dfa_start, accepting: dfa_accepting } + 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)) + } + } + + let ref_transitions = + a_transitions.ref_transitions.keys().chain(b_transitions.ref_transitions.keys()); + + 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(); + + 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().ref_transitions.insert(*ref_transition, dst); + + if !transitions.contains_key(&dst) { + queue.push((a_dst, b_dst)) + } + } + } + + 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, "}}") } } diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs index c4c01a8fac3..c940f7c42a8 100644 --- a/compiler/rustc_transmute/src/layout/mod.rs +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -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; } diff --git a/compiler/rustc_transmute/src/layout/nfa.rs b/compiler/rustc_transmute/src/layout/nfa.rs deleted file mode 100644 index 9c21fd94f03..00000000000 --- a/compiler/rustc_transmute/src/layout/nfa.rs +++ /dev/null @@ -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)) - } -} diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index a21be5dda4e..70ecc75403f 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -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 ), diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index 00928137d29..76fa6ceabe7 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -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; diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index 63fabc9c83d..db0e1ab8e98 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -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) diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index 69a6b1b77f4..cc6a4dce17b 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -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); + } +} |
