From 739f4ac0d1997025e2284e0a7e70e28dd9597aff Mon Sep 17 00:00:00 2001 From: ranger-ross Date: Sat, 19 Oct 2024 19:39:35 +0900 Subject: [PATCH 01/16] Derive Copy+Hash for IntErrorKind --- library/core/src/num/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/num/error.rs b/library/core/src/num/error.rs index 6ef2fdd14c149..a5242d60bf14d 100644 --- a/library/core/src/num/error.rs +++ b/library/core/src/num/error.rs @@ -79,7 +79,7 @@ pub struct ParseIntError { /// # } /// ``` #[stable(feature = "int_error_matching", since = "1.55.0")] -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)] #[non_exhaustive] pub enum IntErrorKind { /// Value being parsed is empty. From 6c04e0a7aeeee15fc11759dd563aecfe6df194a0 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 26 Jun 2025 14:19:15 -0700 Subject: [PATCH 02/16] Rewrite `macro_rules!` parser to not use the MBE engine itself The `macro_rules!` parser was written to match the series of rules using the macros-by-example (MBE) engine and a hand-written equivalent of the left-hand side of a MBE macro. This was complex to read, difficult to extend, and produced confusing error messages. Because it was using the MBE engine, any parse failure would be reported as if some macro was being applied to the `macro_rules!` invocation itself; for instance, errors would talk about "macro invocation", "macro arguments", and "macro call", when they were actually about the macro *definition*. And in practice, the `macro_rules!` parser only used the MBE engine to extract the left-hand side and right-hand side of each rule as a token tree, and then parsed the rest using a separate parser. Rewrite it to parse the series of rules using a simple loop, instead. This makes it more extensible in the future, and improves error messages. For instance, omitting a semicolon between rules will result in "expected `;`" and "unexpected token", rather than the confusing "no rules expected this token in macro call". This work was greatly aided by pair programming with Vincenzo Palazzo and Eric Holk. --- compiler/rustc_expand/src/mbe/diagnostics.rs | 36 +--- compiler/rustc_expand/src/mbe/macro_parser.rs | 2 - compiler/rustc_expand/src/mbe/macro_rules.rs | 203 +++++------------- compiler/rustc_span/src/symbol.rs | 1 - tests/ui/attributes/crate-type-macro-empty.rs | 2 +- .../attributes/crate-type-macro-empty.stderr | 4 +- tests/ui/macros/missing-semi.stderr | 4 +- tests/ui/parser/issues/issue-7970b.rs | 2 +- tests/ui/parser/issues/issue-7970b.stderr | 4 +- tests/ui/parser/macros-no-semicolon-items.rs | 2 +- .../parser/macros-no-semicolon-items.stderr | 4 +- 11 files changed, 65 insertions(+), 199 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 99aa376626d45..c607a3a3652c9 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -195,38 +195,6 @@ impl<'dcx> CollectTrackerAndEmitter<'dcx, '_> { } } -/// Currently used by macro_rules! compilation to extract a little information from the `Failure` -/// case. -pub(crate) struct FailureForwarder<'matcher> { - expected_token: Option<&'matcher Token>, -} - -impl<'matcher> FailureForwarder<'matcher> { - pub(crate) fn new() -> Self { - Self { expected_token: None } - } -} - -impl<'matcher> Tracker<'matcher> for FailureForwarder<'matcher> { - type Failure = (Token, u32, &'static str); - - fn build_failure(tok: Token, position: u32, msg: &'static str) -> Self::Failure { - (tok, position, msg) - } - - fn description() -> &'static str { - "failure-forwarder" - } - - fn set_expected_token(&mut self, tok: &'matcher Token) { - self.expected_token = Some(tok); - } - - fn get_expected_token(&self) -> Option<&'matcher Token> { - self.expected_token - } -} - pub(super) fn emit_frag_parse_err( mut e: Diag<'_>, parser: &Parser<'_>, @@ -321,7 +289,7 @@ enum ExplainDocComment { }, } -pub(super) fn annotate_doc_comment(err: &mut Diag<'_>, sm: &SourceMap, span: Span) { +fn annotate_doc_comment(err: &mut Diag<'_>, sm: &SourceMap, span: Span) { if let Ok(src) = sm.span_to_snippet(span) { if src.starts_with("///") || src.starts_with("/**") { err.subdiagnostic(ExplainDocComment::Outer { span }); @@ -333,7 +301,7 @@ pub(super) fn annotate_doc_comment(err: &mut Diag<'_>, sm: &SourceMap, span: Spa /// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For /// other tokens, this is "unexpected token...". -pub(super) fn parse_failure_msg(tok: &Token, expected_token: Option<&Token>) -> Cow<'static, str> { +fn parse_failure_msg(tok: &Token, expected_token: Option<&Token>) -> Cow<'static, str> { if let Some(expected_token) = expected_token { Cow::from(format!("expected {}, found {}", token_descr(expected_token), token_descr(tok))) } else { diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index 802e43209a51e..3f1fc841ea343 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -536,8 +536,6 @@ impl TtParser { // The separator matches the current token. Advance past it. mp.idx += 1; self.next_mps.push(mp); - } else { - track.set_expected_token(separator); } } &MatcherLoc::SequenceKleeneOpAfterSep { idx_first } => { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 432ab32474059..234e0257530c5 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -19,12 +19,13 @@ use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::{ RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, }; -use rustc_parse::parser::{ParseNtResult, Parser, Recovery}; +use rustc_parse::exp; +use rustc_parse::parser::{Parser, Recovery}; use rustc_session::Session; use rustc_session::parse::ParseSess; use rustc_span::edition::Edition; use rustc_span::hygiene::Transparency; -use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, kw, sym}; +use rustc_span::{Ident, Span, kw, sym}; use tracing::{debug, instrument, trace, trace_span}; use super::macro_parser::{NamedMatches, NamedParseResult}; @@ -34,8 +35,6 @@ use crate::base::{ SyntaxExtensionKind, TTMacroExpander, }; use crate::expand::{AstFragment, AstFragmentKind, ensure_complete_parse, parse_ast_fragment}; -use crate::mbe::diagnostics::{annotate_doc_comment, parse_failure_msg}; -use crate::mbe::macro_parser::NamedMatch::*; use crate::mbe::macro_parser::{Error, ErrorReported, Failure, MatcherLoc, Success, TtParser}; use crate::mbe::transcribe::transcribe; use crate::mbe::{self, KleeneOp, macro_check}; @@ -168,11 +167,6 @@ pub(super) trait Tracker<'matcher> { fn recovery() -> Recovery { Recovery::Forbidden } - - fn set_expected_token(&mut self, _tok: &'matcher Token) {} - fn get_expected_token(&self) -> Option<&'matcher Token> { - None - } } /// A noop tracker that is used in the hot path of the expansion, has zero overhead thanks to @@ -360,11 +354,6 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>( Err(CanRetry::Yes) } -// Note that macro-by-example's input is also matched against a token tree: -// $( $lhs:tt => $rhs:tt );+ -// -// Holy self-referential! - /// Converts a macro item into a syntax extension. pub fn compile_declarative_macro( sess: &Session, @@ -390,153 +379,63 @@ pub fn compile_declarative_macro( }; let dummy_syn_ext = |guar| (mk_syn_ext(Arc::new(DummyExpander(guar))), Vec::new()); - let lhs_nm = Ident::new(sym::lhs, span); - let rhs_nm = Ident::new(sym::rhs, span); - let tt_spec = NonterminalKind::TT; let macro_rules = macro_def.macro_rules; + let exp_sep = if macro_rules { exp!(Semi) } else { exp!(Comma) }; - // Parse the macro_rules! invocation - - // The pattern that macro_rules matches. - // The grammar for macro_rules! is: - // $( $lhs:tt => $rhs:tt );+ - // ...quasiquoting this would be nice. - // These spans won't matter, anyways - let argument_gram = vec![ - mbe::TokenTree::Sequence( - DelimSpan::dummy(), - mbe::SequenceRepetition { - tts: vec![ - mbe::TokenTree::MetaVarDecl { span, name: lhs_nm, kind: tt_spec }, - mbe::TokenTree::token(token::FatArrow, span), - mbe::TokenTree::MetaVarDecl { span, name: rhs_nm, kind: tt_spec }, - ], - separator: Some(Token::new( - if macro_rules { token::Semi } else { token::Comma }, - span, - )), - kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, span), - num_captures: 2, - }, - ), - // to phase into semicolon-termination instead of semicolon-separation - mbe::TokenTree::Sequence( - DelimSpan::dummy(), - mbe::SequenceRepetition { - tts: vec![mbe::TokenTree::token( - if macro_rules { token::Semi } else { token::Comma }, - span, - )], - separator: None, - kleene: mbe::KleeneToken::new(mbe::KleeneOp::ZeroOrMore, span), - num_captures: 0, - }, - ), - ]; - // Convert it into `MatcherLoc` form. - let argument_gram = mbe::macro_parser::compute_locs(&argument_gram); - - let create_parser = || { - let body = macro_def.body.tokens.clone(); - Parser::new(&sess.psess, body, rustc_parse::MACRO_ARGUMENTS) - }; - - let parser = create_parser(); - let mut tt_parser = - TtParser::new(Ident::with_dummy_span(if macro_rules { kw::MacroRules } else { kw::Macro })); - let argument_map = - match tt_parser.parse_tt(&mut Cow::Owned(parser), &argument_gram, &mut NoopTracker) { - Success(m) => m, - Failure(()) => { - debug!("failed to parse macro tt"); - // The fast `NoopTracker` doesn't have any info on failure, so we need to retry it - // with another one that gives us the information we need. - // For this we need to reclone the macro body as the previous parser consumed it. - let retry_parser = create_parser(); - - let mut track = diagnostics::FailureForwarder::new(); - let parse_result = - tt_parser.parse_tt(&mut Cow::Owned(retry_parser), &argument_gram, &mut track); - let Failure((token, _, msg)) = parse_result else { - unreachable!("matcher returned something other than Failure after retry"); - }; - - let s = parse_failure_msg(&token, track.get_expected_token()); - let sp = token.span.substitute_dummy(span); - let mut err = sess.dcx().struct_span_err(sp, s); - err.span_label(sp, msg); - annotate_doc_comment(&mut err, sess.source_map(), sp); - let guar = err.emit(); - return dummy_syn_ext(guar); - } - Error(sp, msg) => { - let guar = sess.dcx().span_err(sp.substitute_dummy(span), msg); - return dummy_syn_ext(guar); - } - ErrorReported(guar) => { - return dummy_syn_ext(guar); - } - }; + let body = macro_def.body.tokens.clone(); + let mut p = Parser::new(&sess.psess, body, rustc_parse::MACRO_ARGUMENTS); + // Don't abort iteration early, so that multiple errors can be reported. let mut guar = None; let mut check_emission = |ret: Result<(), ErrorGuaranteed>| guar = guar.or(ret.err()); - // Extract the arguments: - let lhses = match &argument_map[&MacroRulesNormalizedIdent::new(lhs_nm)] { - MatchedSeq(s) => s - .iter() - .map(|m| { - if let MatchedSingle(ParseNtResult::Tt(tt)) = m { - let tt = mbe::quoted::parse( - &TokenStream::new(vec![tt.clone()]), - true, - sess, - node_id, - features, - edition, - ) - .pop() - .unwrap(); - // We don't handle errors here, the driver will abort - // after parsing/expansion. We can report every error in every macro this way. - check_emission(check_lhs_nt_follows(sess, node_id, &tt)); - return tt; - } - sess.dcx().span_bug(span, "wrong-structured lhs") - }) - .collect::>(), - _ => sess.dcx().span_bug(span, "wrong-structured lhs"), - }; - - let rhses = match &argument_map[&MacroRulesNormalizedIdent::new(rhs_nm)] { - MatchedSeq(s) => s - .iter() - .map(|m| { - if let MatchedSingle(ParseNtResult::Tt(tt)) = m { - return mbe::quoted::parse( - &TokenStream::new(vec![tt.clone()]), - false, - sess, - node_id, - features, - edition, - ) - .pop() - .unwrap(); - } - sess.dcx().span_bug(span, "wrong-structured rhs") - }) - .collect::>(), - _ => sess.dcx().span_bug(span, "wrong-structured rhs"), - }; + let mut lhses = Vec::new(); + let mut rhses = Vec::new(); - for rhs in &rhses { - check_emission(check_rhs(sess, rhs)); + while p.token != token::Eof { + let lhs_tt = p.parse_token_tree(); + let lhs_tt = mbe::quoted::parse( + &TokenStream::new(vec![lhs_tt]), + true, // LHS + sess, + node_id, + features, + edition, + ) + .pop() + .unwrap(); + // We don't handle errors here, the driver will abort after parsing/expansion. We can + // report every error in every macro this way. + check_emission(check_lhs_nt_follows(sess, node_id, &lhs_tt)); + check_emission(check_lhs_no_empty_seq(sess, slice::from_ref(&lhs_tt))); + if let Err(e) = p.expect(exp!(FatArrow)) { + return dummy_syn_ext(e.emit()); + } + let rhs_tt = p.parse_token_tree(); + let rhs_tt = mbe::quoted::parse( + &TokenStream::new(vec![rhs_tt]), + false, // RHS + sess, + node_id, + features, + edition, + ) + .pop() + .unwrap(); + check_emission(check_rhs(sess, &rhs_tt)); + lhses.push(lhs_tt); + rhses.push(rhs_tt); + if p.token == token::Eof { + break; + } + if let Err(e) = p.expect(exp_sep) { + return dummy_syn_ext(e.emit()); + } } - // Don't abort iteration early, so that errors for multiple lhses can be reported. - for lhs in &lhses { - check_emission(check_lhs_no_empty_seq(sess, slice::from_ref(lhs))); + if lhses.is_empty() { + let guar = sess.dcx().span_err(span, "macros must contain at least one rule"); + return dummy_syn_ext(guar); } check_emission(macro_check::check_meta_variables(&sess.psess, node_id, span, &lhses, &rhses)); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 11463ad354a96..c17411d55f7c2 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1777,7 +1777,6 @@ symbols! { resume, return_position_impl_trait_in_trait, return_type_notation, - rhs, riscv_target_feature, rlib, ropi, diff --git a/tests/ui/attributes/crate-type-macro-empty.rs b/tests/ui/attributes/crate-type-macro-empty.rs index 5ff7fc002fde3..217ff598f7a4c 100644 --- a/tests/ui/attributes/crate-type-macro-empty.rs +++ b/tests/ui/attributes/crate-type-macro-empty.rs @@ -2,6 +2,6 @@ #[crate_type = foo!()] //~^ ERROR cannot find macro `foo` in this scope -macro_rules! foo {} //~ ERROR unexpected end of macro invocation +macro_rules! foo {} //~ ERROR macros must contain at least one rule fn main() {} diff --git a/tests/ui/attributes/crate-type-macro-empty.stderr b/tests/ui/attributes/crate-type-macro-empty.stderr index e48d3d95470d4..130fa454ca19e 100644 --- a/tests/ui/attributes/crate-type-macro-empty.stderr +++ b/tests/ui/attributes/crate-type-macro-empty.stderr @@ -1,8 +1,8 @@ -error: unexpected end of macro invocation +error: macros must contain at least one rule --> $DIR/crate-type-macro-empty.rs:5:1 | LL | macro_rules! foo {} - | ^^^^^^^^^^^^^^^^^^^ missing tokens in macro arguments + | ^^^^^^^^^^^^^^^^^^^ error: cannot find macro `foo` in this scope --> $DIR/crate-type-macro-empty.rs:2:16 diff --git a/tests/ui/macros/missing-semi.stderr b/tests/ui/macros/missing-semi.stderr index 0a7afe50059d3..c2e12adbb4b03 100644 --- a/tests/ui/macros/missing-semi.stderr +++ b/tests/ui/macros/missing-semi.stderr @@ -1,8 +1,10 @@ error: expected `;`, found `(` --> $DIR/missing-semi.rs:6:5 | +LL | } + | - expected `;` LL | () => { - | ^ no rules expected this token in macro call + | ^ unexpected token error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-7970b.rs b/tests/ui/parser/issues/issue-7970b.rs index 1c4abce39598c..ae06aff7cef7e 100644 --- a/tests/ui/parser/issues/issue-7970b.rs +++ b/tests/ui/parser/issues/issue-7970b.rs @@ -1,4 +1,4 @@ fn main() {} macro_rules! test {} -//~^ ERROR unexpected end of macro invocation +//~^ ERROR macros must contain at least one rule diff --git a/tests/ui/parser/issues/issue-7970b.stderr b/tests/ui/parser/issues/issue-7970b.stderr index b23b09e752ce0..4715eb07c6d99 100644 --- a/tests/ui/parser/issues/issue-7970b.stderr +++ b/tests/ui/parser/issues/issue-7970b.stderr @@ -1,8 +1,8 @@ -error: unexpected end of macro invocation +error: macros must contain at least one rule --> $DIR/issue-7970b.rs:3:1 | LL | macro_rules! test {} - | ^^^^^^^^^^^^^^^^^^^^ missing tokens in macro arguments + | ^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/parser/macros-no-semicolon-items.rs b/tests/ui/parser/macros-no-semicolon-items.rs index 3afc275d61a2b..86889279cea6a 100644 --- a/tests/ui/parser/macros-no-semicolon-items.rs +++ b/tests/ui/parser/macros-no-semicolon-items.rs @@ -1,5 +1,5 @@ macro_rules! foo() //~ ERROR semicolon - //~| ERROR unexpected end of macro + //~| ERROR macros must contain at least one rule macro_rules! bar { ($($tokens:tt)*) => {} diff --git a/tests/ui/parser/macros-no-semicolon-items.stderr b/tests/ui/parser/macros-no-semicolon-items.stderr index 07fa2439df504..f8f3ed83688ce 100644 --- a/tests/ui/parser/macros-no-semicolon-items.stderr +++ b/tests/ui/parser/macros-no-semicolon-items.stderr @@ -38,11 +38,11 @@ help: add a semicolon LL | ); | + -error: unexpected end of macro invocation +error: macros must contain at least one rule --> $DIR/macros-no-semicolon-items.rs:1:1 | LL | macro_rules! foo() - | ^^^^^^^^^^^^^^^^^^ missing tokens in macro arguments + | ^^^^^^^^^^^^^^^^^^ error: aborting due to 3 previous errors From 07760822db8dab9dc2bec7d2af23d804ab19da22 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Thu, 26 Jun 2025 15:06:31 -0700 Subject: [PATCH 03/16] mbe: Fold calls to `check_meta_variables` into the parser loop --- compiler/rustc_expand/src/mbe/macro_check.rs | 23 ++++++-------------- compiler/rustc_expand/src/mbe/macro_rules.rs | 3 +-- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index dc2d46c4a1419..bbdff866feba4 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -105,8 +105,6 @@ //! stored when entering a macro definition starting from the state in which the meta-variable is //! bound. -use std::iter; - use rustc_ast::token::{Delimiter, IdentIsRaw, Token, TokenKind}; use rustc_ast::{DUMMY_NODE_ID, NodeId}; use rustc_data_structures::fx::FxHashMap; @@ -190,29 +188,22 @@ struct MacroState<'a> { ops: SmallVec<[KleeneToken; 1]>, } -/// Checks that meta-variables are used correctly in a macro definition. +/// Checks that meta-variables are used correctly in one rule of a macro definition. /// /// Arguments: /// - `psess` is used to emit diagnostics and lints /// - `node_id` is used to emit lints -/// - `span` is used when no spans are available -/// - `lhses` and `rhses` should have the same length and represent the macro definition +/// - `lhs` and `rhs` represent the rule pub(super) fn check_meta_variables( psess: &ParseSess, node_id: NodeId, - span: Span, - lhses: &[TokenTree], - rhses: &[TokenTree], + lhs: &TokenTree, + rhs: &TokenTree, ) -> Result<(), ErrorGuaranteed> { - if lhses.len() != rhses.len() { - psess.dcx().span_bug(span, "length mismatch between LHSes and RHSes") - } let mut guar = None; - for (lhs, rhs) in iter::zip(lhses, rhses) { - let mut binders = Binders::default(); - check_binders(psess, node_id, lhs, &Stack::Empty, &mut binders, &Stack::Empty, &mut guar); - check_occurrences(psess, node_id, rhs, &Stack::Empty, &binders, &Stack::Empty, &mut guar); - } + let mut binders = Binders::default(); + check_binders(psess, node_id, lhs, &Stack::Empty, &mut binders, &Stack::Empty, &mut guar); + check_occurrences(psess, node_id, rhs, &Stack::Empty, &binders, &Stack::Empty, &mut guar); guar.map_or(Ok(()), Err) } diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 234e0257530c5..dad2fd99ef205 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -423,6 +423,7 @@ pub fn compile_declarative_macro( .pop() .unwrap(); check_emission(check_rhs(sess, &rhs_tt)); + check_emission(macro_check::check_meta_variables(&sess.psess, node_id, &lhs_tt, &rhs_tt)); lhses.push(lhs_tt); rhses.push(rhs_tt); if p.token == token::Eof { @@ -438,8 +439,6 @@ pub fn compile_declarative_macro( return dummy_syn_ext(guar); } - check_emission(macro_check::check_meta_variables(&sess.psess, node_id, span, &lhses, &rhses)); - let transparency = find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x) .unwrap_or(Transparency::fallback(macro_rules)); From 4698c92101349358a8d70f3e32a571a90109842d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 30 Jun 2025 13:33:46 +0000 Subject: [PATCH 04/16] Assemble const bounds via normal item bounds in old solver too --- .../src/traits/effects.rs | 67 ++++++++++++++++++- .../const-traits/const-via-item-bound.rs | 19 ++++++ 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 tests/ui/traits/const-traits/const-via-item-bound.rs diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index fc95e42d67fc4..a294981b92dc3 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -44,6 +44,12 @@ pub fn evaluate_host_effect_obligation<'tcx>( Err(EvaluationFailure::NoSolution) => {} } + match evaluate_host_effect_from_conditionally_const_item_bounds(selcx, obligation) { + Ok(result) => return Ok(result), + Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), + Err(EvaluationFailure::NoSolution) => {} + } + match evaluate_host_effect_from_item_bounds(selcx, obligation) { Ok(result) => return Ok(result), Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous), @@ -153,7 +159,9 @@ fn evaluate_host_effect_from_bounds<'tcx>( } } -fn evaluate_host_effect_from_item_bounds<'tcx>( +/// Assembles constness bounds from `~const` item bounds on alias types, which only +/// hold if the `~const` where bounds also hold and the parent trait is `~const`. +fn evaluate_host_effect_from_conditionally_const_item_bounds<'tcx>( selcx: &mut SelectionContext<'_, 'tcx>, obligation: &HostEffectObligation<'tcx>, ) -> Result>, EvaluationFailure> { @@ -232,6 +240,63 @@ fn evaluate_host_effect_from_item_bounds<'tcx>( } } +/// Assembles constness bounds "normal" item bounds on aliases, which may include +/// unconditionally `const` bounds that are *not* conditional and thus always hold. +fn evaluate_host_effect_from_item_bounds<'tcx>( + selcx: &mut SelectionContext<'_, 'tcx>, + obligation: &HostEffectObligation<'tcx>, +) -> Result>, EvaluationFailure> { + let infcx = selcx.infcx; + let tcx = infcx.tcx; + let drcx = DeepRejectCtxt::relate_rigid_rigid(selcx.tcx()); + let mut candidate = None; + + let mut consider_ty = obligation.predicate.self_ty(); + while let ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) = *consider_ty.kind() { + for clause in tcx.item_bounds(alias_ty.def_id).iter_instantiated(tcx, alias_ty.args) { + let bound_clause = clause.kind(); + let ty::ClauseKind::HostEffect(data) = bound_clause.skip_binder() else { + continue; + }; + let data = bound_clause.rebind(data); + if data.skip_binder().trait_ref.def_id != obligation.predicate.trait_ref.def_id { + continue; + } + + if !drcx.args_may_unify( + obligation.predicate.trait_ref.args, + data.skip_binder().trait_ref.args, + ) { + continue; + } + + let is_match = + infcx.probe(|_| match_candidate(selcx, obligation, data, true, |_, _| {}).is_ok()); + + if is_match { + if candidate.is_some() { + return Err(EvaluationFailure::Ambiguous); + } else { + candidate = Some(data); + } + } + } + + if kind != ty::Projection { + break; + } + + consider_ty = alias_ty.self_ty(); + } + + if let Some(data) = candidate { + Ok(match_candidate(selcx, obligation, data, true, |_, _| {}) + .expect("candidate matched before, so it should match again")) + } else { + Err(EvaluationFailure::NoSolution) + } +} + fn evaluate_host_effect_from_builtin_impls<'tcx>( selcx: &mut SelectionContext<'_, 'tcx>, obligation: &HostEffectObligation<'tcx>, diff --git a/tests/ui/traits/const-traits/const-via-item-bound.rs b/tests/ui/traits/const-traits/const-via-item-bound.rs new file mode 100644 index 0000000000000..23f122b741308 --- /dev/null +++ b/tests/ui/traits/const-traits/const-via-item-bound.rs @@ -0,0 +1,19 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +#![feature(const_trait_impl)] + +#[const_trait] +trait Bar {} + +trait Baz: const Bar {} + +trait Foo { + // Well-formedenss of `Baz` requires `::Bar: const Bar`. + // Make sure we assemble a candidate for that via the item bounds. + type Bar: Baz; +} + +fn main() {} From 187babc35f07153e2853ee9b6984f56aa158abc5 Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Thu, 19 Jun 2025 02:05:47 +0300 Subject: [PATCH 05/16] NoArgsAttributeParser --- .../src/attributes/codegen_attrs.rs | 48 ++++++------------- .../src/attributes/lint_helpers.rs | 31 ++++-------- .../src/attributes/loop_match.rs | 24 ++++------ .../rustc_attr_parsing/src/attributes/mod.rs | 37 +++++++++++++- .../src/attributes/semantics.rs | 19 +++----- .../src/attributes/stability.rs | 17 +++---- compiler/rustc_attr_parsing/src/context.rs | 22 +++++---- ...issue-43106-gating-of-builtin-attrs.stderr | 16 +++---- 8 files changed, 100 insertions(+), 114 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 7c412d4fa892b..360c28dafab9f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -3,7 +3,10 @@ use rustc_feature::{AttributeTemplate, template}; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; -use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser}; +use super::{ + AcceptMapping, AttributeOrder, AttributeParser, NoArgsAttributeParser, OnDuplicate, + SingleAttributeParser, +}; use crate::context::{AcceptContext, FinalizeContext, Stage}; use crate::parser::ArgParser; use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport}; @@ -43,19 +46,12 @@ impl SingleAttributeParser for OptimizeParser { pub(crate) struct ColdParser; -impl SingleAttributeParser for ColdParser { +impl NoArgsAttributeParser for ColdParser { const PATH: &[Symbol] = &[sym::cold]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const TEMPLATE: AttributeTemplate = template!(Word); - - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - if let Err(span) = args.no_args() { - cx.expected_no_args(span); - return None; - } - Some(AttributeKind::Cold(cx.attr_span)) + fn create(span: Span) -> AttributeKind { + AttributeKind::Cold(span) } } @@ -194,38 +190,22 @@ impl AttributeParser for NakedParser { } pub(crate) struct TrackCallerParser; - -impl SingleAttributeParser for TrackCallerParser { +impl NoArgsAttributeParser for TrackCallerParser { const PATH: &[Symbol] = &[sym::track_caller]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - if let Err(span) = args.no_args() { - cx.expected_no_args(span); - return None; - } - - Some(AttributeKind::TrackCaller(cx.attr_span)) + fn create(span: Span) -> AttributeKind { + AttributeKind::TrackCaller(span) } } pub(crate) struct NoMangleParser; - -impl SingleAttributeParser for NoMangleParser { - const PATH: &[rustc_span::Symbol] = &[sym::no_mangle]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; +impl NoArgsAttributeParser for NoMangleParser { + const PATH: &[Symbol] = &[sym::no_mangle]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const TEMPLATE: AttributeTemplate = template!(Word); - - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - if let Err(span) = args.no_args() { - cx.expected_no_args(span); - return None; - } - Some(AttributeKind::NoMangle(cx.attr_span)) + fn create(span: Span) -> AttributeKind { + AttributeKind::NoMangle(span) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 1c8fc5079dad3..0dfcf3cb89907 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -1,38 +1,25 @@ use rustc_attr_data_structures::AttributeKind; -use rustc_feature::{AttributeTemplate, template}; -use rustc_span::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; -use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; -use crate::context::{AcceptContext, Stage}; -use crate::parser::ArgParser; +use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; +use crate::context::Stage; pub(crate) struct AsPtrParser; - -impl SingleAttributeParser for AsPtrParser { +impl NoArgsAttributeParser for AsPtrParser { const PATH: &[Symbol] = &[sym::rustc_as_ptr]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - if let Err(span) = args.no_args() { - cx.expected_no_args(span); - } - Some(AttributeKind::AsPtr(cx.attr_span)) + fn create(span: Span) -> AttributeKind { + AttributeKind::AsPtr(span) } } pub(crate) struct PubTransparentParser; -impl SingleAttributeParser for PubTransparentParser { +impl NoArgsAttributeParser for PubTransparentParser { const PATH: &[Symbol] = &[sym::rustc_pub_transparent]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - if let Err(span) = args.no_args() { - cx.expected_no_args(span); - } - Some(AttributeKind::PubTransparent(cx.attr_span)) + fn create(span: Span) -> AttributeKind { + AttributeKind::PubTransparent(span) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs index f6c7ac5e3a39c..1a5368c092f11 100644 --- a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs +++ b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs @@ -1,31 +1,25 @@ use rustc_attr_data_structures::AttributeKind; -use rustc_feature::{AttributeTemplate, template}; -use rustc_span::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; -use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; -use crate::context::{AcceptContext, Stage}; -use crate::parser::ArgParser; +use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; +use crate::context::Stage; pub(crate) struct LoopMatchParser; -impl SingleAttributeParser for LoopMatchParser { +impl NoArgsAttributeParser for LoopMatchParser { const PATH: &[Symbol] = &[sym::loop_match]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option { - Some(AttributeKind::LoopMatch(cx.attr_span)) + fn create(span: Span) -> AttributeKind { + AttributeKind::LoopMatch(span) } } pub(crate) struct ConstContinueParser; -impl SingleAttributeParser for ConstContinueParser { +impl NoArgsAttributeParser for ConstContinueParser { const PATH: &[Symbol] = &[sym::const_continue]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option { - Some(AttributeKind::ConstContinue(cx.attr_span)) + fn create(span: Span) -> AttributeKind { + AttributeKind::ConstContinue(span) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 584dadadcf7b3..abddc75ab8bf0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -17,7 +17,7 @@ use std::marker::PhantomData; use rustc_attr_data_structures::AttributeKind; -use rustc_feature::AttributeTemplate; +use rustc_feature::{AttributeTemplate, template}; use rustc_span::{Span, Symbol}; use thin_vec::ThinVec; @@ -228,6 +228,41 @@ pub(crate) enum AttributeOrder { KeepLast, } +/// An even simpler version of [`SingleAttributeParser`]: +/// now automatically check that there are no arguments provided to the attribute. +/// +/// [`WithoutArgs where T: NoArgsAttributeParser`](WithoutArgs) implements [`SingleAttributeParser`]. +// +pub(crate) trait NoArgsAttributeParser: 'static { + const PATH: &[Symbol]; + const ON_DUPLICATE: OnDuplicate; + + /// Create the [`AttributeKind`] given attribute's [`Span`]. + fn create(span: Span) -> AttributeKind; +} + +pub(crate) struct WithoutArgs, S: Stage>(PhantomData<(S, T)>); + +impl, S: Stage> Default for WithoutArgs { + fn default() -> Self { + Self(Default::default()) + } +} + +impl, S: Stage> SingleAttributeParser for WithoutArgs { + const PATH: &[Symbol] = T::PATH; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ON_DUPLICATE: OnDuplicate = T::ON_DUPLICATE; + const TEMPLATE: AttributeTemplate = template!(Word); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + if let Err(span) = args.no_args() { + cx.expected_no_args(span); + } + Some(T::create(cx.attr_span)) + } +} + type ConvertFn = fn(ThinVec) -> AttributeKind; /// Alternative to [`AttributeParser`] that automatically handles state management. diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs index 54f50445fbdff..c5e2bf6862fbd 100644 --- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs +++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs @@ -1,22 +1,15 @@ use rustc_attr_data_structures::AttributeKind; -use rustc_feature::{AttributeTemplate, template}; -use rustc_span::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; -use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; -use crate::context::{AcceptContext, Stage}; -use crate::parser::ArgParser; +use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; +use crate::context::Stage; pub(crate) struct MayDangleParser; -impl SingleAttributeParser for MayDangleParser { +impl NoArgsAttributeParser for MayDangleParser { const PATH: &[Symbol] = &[sym::may_dangle]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - if let Err(span) = args.no_args() { - cx.expected_no_args(span); - } - Some(AttributeKind::MayDangle(cx.attr_span)) + fn create(span: Span) -> AttributeKind { + AttributeKind::MayDangle(span) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 37104855623f4..ffe60f59cc475 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -5,11 +5,12 @@ use rustc_attr_data_structures::{ StableSince, UnstableReason, VERSION_PLACEHOLDER, }; use rustc_errors::ErrorGuaranteed; -use rustc_feature::{AttributeTemplate, template}; +use rustc_feature::template; use rustc_span::{Ident, Span, Symbol, sym}; use super::util::parse_version; -use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser}; +use super::{AcceptMapping, AttributeParser, OnDuplicate}; +use crate::attributes::NoArgsAttributeParser; use crate::context::{AcceptContext, FinalizeContext, Stage}; use crate::parser::{ArgParser, MetaItemParser}; use crate::session_diagnostics::{self, UnsupportedLiteralReason}; @@ -132,18 +133,12 @@ impl AttributeParser for BodyStabilityParser { } pub(crate) struct ConstStabilityIndirectParser; -// FIXME(jdonszelmann): single word attribute group when we have these -impl SingleAttributeParser for ConstStabilityIndirectParser { +impl NoArgsAttributeParser for ConstStabilityIndirectParser { const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; - const TEMPLATE: AttributeTemplate = template!(Word); - fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - if let Err(span) = args.no_args() { - cx.expected_no_args(span); - } - Some(AttributeKind::ConstStabilityIndirect) + fn create(_: Span) -> AttributeKind { + AttributeKind::ConstStabilityIndirect } } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index eee6d860550eb..4b960a07dd79e 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -33,7 +33,7 @@ use crate::attributes::stability::{ }; use crate::attributes::traits::SkipDuringMethodDispatchParser; use crate::attributes::transparency::TransparencyParser; -use crate::attributes::{AttributeParser as _, Combine, Single}; +use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; use crate::parser::{ArgParser, MetaItemParser, PathParser}; use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem}; @@ -54,6 +54,7 @@ macro_rules! attribute_parsers { use super::*; type Combine = super::Combine; type Single = super::Single; + type WithoutArgs = super::WithoutArgs; attribute_parsers!(@[Early] pub(crate) static $name = [$($names),*];); } @@ -61,6 +62,7 @@ macro_rules! attribute_parsers { use super::*; type Combine = super::Combine; type Single = super::Single; + type WithoutArgs = super::WithoutArgs; attribute_parsers!(@[Late] pub(crate) static $name = [$($names),*];); } @@ -115,25 +117,25 @@ attribute_parsers!( // tidy-alphabetical-end // tidy-alphabetical-start - Single, - Single, - Single, - Single, Single, Single, Single, Single, Single, - Single, - Single, Single, - Single, Single, - Single, Single, Single, - Single, Single, + Single>, + Single>, + Single>, + Single>, + Single>, + Single>, + Single>, + Single>, + Single>, // tidy-alphabetical-end ]; ); diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 02b9e2f06eed5..9280dfdf92e5b 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -387,14 +387,6 @@ LL | #![link()] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -warning: attribute should be applied to a function definition - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1 - | -LL | #![cold] - | ^^^^^^^^ cannot be applied to crates - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - warning: attribute should be applied to a foreign function or static --> $DIR/issue-43106-gating-of-builtin-attrs.rs:66:1 | @@ -417,6 +409,14 @@ warning: `#[must_use]` has no effect when applied to a module LL | #![must_use] | ^^^^^^^^^^^^ +warning: attribute should be applied to a function definition + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1 + | +LL | #![cold] + | ^^^^^^^^ cannot be applied to crates + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + warning: `#[macro_use]` only has an effect on `extern crate` and modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:176:5 | From 96fea30d92d57b514442e40e64f03617c76ceddd Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 30 Jun 2025 22:16:53 +0000 Subject: [PATCH 06/16] Feed explicit_predicates_of instead of predicates_of --- compiler/rustc_middle/src/query/mod.rs | 1 - compiler/rustc_mir_transform/src/coroutine/by_move_body.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 8a3d26e1b0314..42b24497f082f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -439,7 +439,6 @@ rustc_queries! { query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> { desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } - feedable } query opaque_types_defined_by( diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 0a839d91404ec..81d7b7ba02c2c 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -239,7 +239,7 @@ pub(crate) fn coroutine_by_move_body_def_id<'tcx>( body_def.explicit_predicates_of(tcx.explicit_predicates_of(coroutine_def_id)); body_def.generics_of(tcx.generics_of(coroutine_def_id).clone()); body_def.param_env(tcx.param_env(coroutine_def_id)); - body_def.predicates_of(tcx.predicates_of(coroutine_def_id)); + body_def.explicit_predicates_of(tcx.explicit_predicates_of(coroutine_def_id)); // The type of the coroutine is the `by_move_coroutine_ty`. body_def.type_of(ty::EarlyBinder::bind(by_move_coroutine_ty)); From ef4f71957dbfb542746dc64ba65af51ee6852fe7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 1 Jul 2025 00:14:06 +0000 Subject: [PATCH 07/16] Remove doc comments from TyCtxtFeed --- compiler/rustc_macros/src/query.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 2196f71299a53..bd765ff8d1cf0 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -413,7 +413,6 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { "Query {name} cannot be both `feedable` and `eval_always`." ); feedable_queries.extend(quote! { - #(#doc_comments)* [#attribute_stream] fn #name(#arg) #result, }); } From 7d6764a45bd05913e7f34a8fbba12bf384b0269a Mon Sep 17 00:00:00 2001 From: Benjamin Schulz Date: Wed, 4 Jun 2025 19:03:18 +0200 Subject: [PATCH 08/16] Detect more cases of unused_parens around types --- compiler/rustc_ast/src/ast.rs | 14 ++ compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/lib.rs | 1 + compiler/rustc_expand/src/build.rs | 1 + compiler/rustc_lint/src/unused.rs | 128 ++++++++++- compiler/rustc_parse/src/parser/ty.rs | 53 +++-- .../rustc_resolve/src/late/diagnostics.rs | 1 + library/core/src/error.rs | 2 +- tests/ui/lint/lint-unnecessary-parens.fixed | 87 +++++++- tests/ui/lint/lint-unnecessary-parens.rs | 87 +++++++- tests/ui/lint/lint-unnecessary-parens.stderr | 210 ++++++++++++++---- .../lint/unused/issue-105061-should-lint.rs | 2 +- .../unused/issue-105061-should-lint.stderr | 10 +- .../unused-parens-trait-obj.edition2018.fixed | 27 +++ ...unused-parens-trait-obj.edition2018.stderr | 19 ++ .../ui/lint/unused/unused-parens-trait-obj.rs | 27 +++ tests/ui/sanitizer/cfi/closures.rs | 4 +- tests/ui/traits/dyn-trait.rs | 2 +- tests/ui/traits/impl-2.rs | 2 +- 19 files changed, 596 insertions(+), 83 deletions(-) create mode 100644 tests/ui/lint/unused/unused-parens-trait-obj.edition2018.fixed create mode 100644 tests/ui/lint/unused/unused-parens-trait-obj.edition2018.stderr create mode 100644 tests/ui/lint/unused/unused-parens-trait-obj.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index d9272986a7e09..329fa723b1cb0 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1391,6 +1391,7 @@ impl Expr { path.clone(), TraitBoundModifiers::NONE, self.span, + Parens::No, ))), _ => None, } @@ -3360,6 +3361,13 @@ pub struct TraitRef { pub ref_id: NodeId, } +/// Whether enclosing parentheses are present or not. +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum Parens { + Yes, + No, +} + #[derive(Clone, Encodable, Decodable, Debug)] pub struct PolyTraitRef { /// The `'a` in `for<'a> Foo<&'a T>`. @@ -3372,6 +3380,10 @@ pub struct PolyTraitRef { pub trait_ref: TraitRef, pub span: Span, + + /// When `Yes`, the first and last character of `span` are an opening + /// and a closing paren respectively. + pub parens: Parens, } impl PolyTraitRef { @@ -3380,12 +3392,14 @@ impl PolyTraitRef { path: Path, modifiers: TraitBoundModifiers, span: Span, + parens: Parens, ) -> Self { PolyTraitRef { bound_generic_params: generic_params, modifiers, trait_ref: TraitRef { path, ref_id: DUMMY_NODE_ID }, span, + parens, } } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index d0c2b2bf68b0d..867ab7d947898 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -1142,7 +1142,7 @@ macro_rules! common_visitor_and_walkers { vis: &mut V, p: &$($lt)? $($mut)? PolyTraitRef, ) -> V::Result { - let PolyTraitRef { bound_generic_params, modifiers, trait_ref, span } = p; + let PolyTraitRef { bound_generic_params, modifiers, trait_ref, span, parens: _ } = p; try_visit!(visit_modifiers(vis, modifiers)); try_visit!(visit_generic_params(vis, bound_generic_params)); try_visit!(vis.visit_trait_ref(trait_ref)); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 26d7c0cd6d38f..d14e27982ef50 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1209,6 +1209,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { modifiers: TraitBoundModifiers::NONE, trait_ref: TraitRef { path: path.clone(), ref_id: t.id }, span: t.span, + parens: ast::Parens::No, }, itctx, ); diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 14b8cc90d97d6..a333f2c7cb787 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -195,6 +195,7 @@ impl<'a> ExtCtxt<'a> { }, trait_ref: self.trait_ref(path), span, + parens: ast::Parens::No, } } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index a868c887493c9..e958908440ed8 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -3,6 +3,7 @@ use std::iter; use rustc_ast::util::{classify, parser}; use rustc_ast::{self as ast, ExprKind, HasAttrs as _, StmtKind}; use rustc_attr_data_structures::{AttributeKind, find_attr}; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{MultiSpan, pluralize}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -10,6 +11,7 @@ use rustc_hir::{self as hir, LangItem}; use rustc_infer::traits::util::elaborate; use rustc_middle::ty::{self, Ty, adjustment}; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; +use rustc_span::edition::Edition::Edition2015; use rustc_span::{BytePos, Span, Symbol, kw, sym}; use tracing::instrument; @@ -1032,6 +1034,31 @@ pub(crate) struct UnusedParens { /// `1 as (i32) < 2` parses to ExprKind::Lt /// `1 as i32 < 2` parses to i32::<2[missing angle bracket] parens_in_cast_in_lt: Vec, + /// Ty nodes in this map are in TypeNoBounds position. Any bounds they + /// contain may be ambiguous w/r/t trailing `+` operators. + in_no_bounds_pos: FxHashMap, +} + +/// Whether parentheses may be omitted from a type without resulting in ambiguity. +/// +/// ``` +/// type Example = Box &'static (dyn Send) + Sync>; +/// ``` +/// +/// Here, `&'static (dyn Send) + Sync` is a `TypeNoBounds`. As such, it may not directly +/// contain `ImplTraitType` or `TraitObjectType` which is why `(dyn Send)` is parenthesized. +/// However, an exception is made for `ImplTraitTypeOneBound` and `TraitObjectTypeOneBound`. +/// The following is accepted because there is no `+`. +/// +/// ``` +/// type Example = Box &'static dyn Send>; +/// ``` +enum NoBoundsException { + /// The type must be parenthesized. + None, + /// The type is the last bound of the containing type expression. If it has exactly one bound, + /// parentheses around the type are unnecessary. + OneBound, } impl_lint_pass!(UnusedParens => [UNUSED_PARENS]); @@ -1275,23 +1302,100 @@ impl EarlyLintPass for UnusedParens { ); } ast::TyKind::Paren(r) => { - match &r.kind { - ast::TyKind::TraitObject(..) => {} - ast::TyKind::BareFn(b) - if self.with_self_ty_parens && b.generic_params.len() > 0 => {} - ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {} - _ => { - let spans = if !ty.span.from_expansion() { + let unused_parens = match &r.kind { + ast::TyKind::ImplTrait(_, bounds) | ast::TyKind::TraitObject(bounds, _) => { + match self.in_no_bounds_pos.get(&ty.id) { + Some(NoBoundsException::None) => false, + Some(NoBoundsException::OneBound) => bounds.len() <= 1, + None => true, + } + } + ast::TyKind::BareFn(b) => { + !self.with_self_ty_parens || b.generic_params.is_empty() + } + _ => true, + }; + + if unused_parens { + let spans = (!ty.span.from_expansion()) + .then(|| { r.span .find_ancestor_inside(ty.span) .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi()))) + }) + .flatten(); + + self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false); + } + + self.with_self_ty_parens = false; + } + ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => { + self.in_no_bounds_pos.insert(mut_ty.ty.id, NoBoundsException::OneBound); + } + ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => { + for i in 0..bounds.len() { + let is_last = i == bounds.len() - 1; + + if let ast::GenericBound::Trait(poly_trait_ref) = &bounds[i] { + let fn_with_explicit_ret_ty = if let [.., segment] = + &*poly_trait_ref.trait_ref.path.segments + && let Some(args) = segment.args.as_ref() + && let ast::GenericArgs::Parenthesized(paren_args) = &**args + && let ast::FnRetTy::Ty(ret_ty) = &paren_args.output + { + self.in_no_bounds_pos.insert( + ret_ty.id, + if is_last { + NoBoundsException::OneBound + } else { + NoBoundsException::None + }, + ); + + true } else { - None + false }; - self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false); + + // In edition 2015, dyn is a contextual keyword and `dyn::foo::Bar` is + // parsed as a path, so parens are necessary to disambiguate. See + // - tests/ui/lint/unused/unused-parens-trait-obj-e2015.rs and + // - https://doc.rust-lang.org/reference/types/trait-object.html#r-type.trait-object.syntax-edition2018 + let dyn2015_exception = cx.sess().psess.edition == Edition2015 + && matches!(ty.kind, ast::TyKind::TraitObject(..)) + && i == 0 + && poly_trait_ref + .trait_ref + .path + .segments + .first() + .map(|s| s.ident.name == kw::PathRoot) + .unwrap_or(false); + + if let ast::Parens::Yes = poly_trait_ref.parens + && (is_last || !fn_with_explicit_ret_ty) + && !dyn2015_exception + { + let s = poly_trait_ref.span; + let spans = (!s.from_expansion()).then(|| { + ( + s.with_hi(s.lo() + rustc_span::BytePos(1)), + s.with_lo(s.hi() - rustc_span::BytePos(1)), + ) + }); + + self.emit_unused_delims( + cx, + poly_trait_ref.span, + spans, + "type", + (false, false), + false, + ); + } } } - self.with_self_ty_parens = false; } _ => {} } @@ -1301,6 +1405,10 @@ impl EarlyLintPass for UnusedParens { ::check_item(self, cx, item) } + fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Item) { + self.in_no_bounds_pos.clear(); + } + fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) { use rustc_ast::{WhereBoundPredicate, WherePredicateKind}; if let WherePredicateKind::BoundPredicate(WhereBoundPredicate { diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 620a34044d12e..e926be2909203 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -305,8 +305,13 @@ impl<'a> Parser<'a> { let removal_span = kw.span.with_hi(self.token.span.lo()); let path = self.parse_path(PathStyle::Type)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); - let kind = - self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)?; + let kind = self.parse_remaining_bounds_path( + lifetime_defs, + path, + lo, + parse_plus, + ast::Parens::No, + )?; let err = self.dcx().create_err(errors::TransposeDynOrImpl { span: kw.span, kw: kw.name.as_str(), @@ -333,7 +338,13 @@ impl<'a> Parser<'a> { } else { let path = self.parse_path(PathStyle::Type)?; let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus(); - self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)? + self.parse_remaining_bounds_path( + lifetime_defs, + path, + lo, + parse_plus, + ast::Parens::No, + )? } } } else if self.eat_keyword(exp!(Impl)) { @@ -413,9 +424,13 @@ impl<'a> Parser<'a> { let maybe_bounds = allow_plus == AllowPlus::Yes && self.token.is_like_plus(); match ty.kind { // `(TY_BOUND_NOPAREN) + BOUND + ...`. - TyKind::Path(None, path) if maybe_bounds => { - self.parse_remaining_bounds_path(ThinVec::new(), path, lo, true) - } + TyKind::Path(None, path) if maybe_bounds => self.parse_remaining_bounds_path( + ThinVec::new(), + path, + lo, + true, + ast::Parens::Yes, + ), // For `('a) + …`, we know that `'a` in type position already lead to an error being // emitted. To reduce output, let's indirectly suppress E0178 (bad `+` in type) and // other irrelevant consequential errors. @@ -495,12 +510,14 @@ impl<'a> Parser<'a> { path: ast::Path, lo: Span, parse_plus: bool, + parens: ast::Parens, ) -> PResult<'a, TyKind> { let poly_trait_ref = PolyTraitRef::new( generic_params, path, TraitBoundModifiers::NONE, lo.to(self.prev_token.span), + parens, ); let bounds = vec![GenericBound::Trait(poly_trait_ref)]; self.parse_remaining_bounds(bounds, parse_plus) @@ -832,7 +849,7 @@ impl<'a> Parser<'a> { Ok(TyKind::MacCall(P(MacCall { path, args: self.parse_delim_args()? }))) } else if allow_plus == AllowPlus::Yes && self.check_plus() { // `Trait1 + Trait2 + 'a` - self.parse_remaining_bounds_path(ThinVec::new(), path, lo, true) + self.parse_remaining_bounds_path(ThinVec::new(), path, lo, true, ast::Parens::No) } else { // Just a type path. Ok(TyKind::Path(None, path)) @@ -897,10 +914,10 @@ impl<'a> Parser<'a> { fn parse_generic_bound(&mut self) -> PResult<'a, GenericBound> { let lo = self.token.span; let leading_token = self.prev_token; - let has_parens = self.eat(exp!(OpenParen)); + let parens = if self.eat(exp!(OpenParen)) { ast::Parens::Yes } else { ast::Parens::No }; let bound = if self.token.is_lifetime() { - self.parse_generic_lt_bound(lo, has_parens)? + self.parse_generic_lt_bound(lo, parens)? } else if self.eat_keyword(exp!(Use)) { // parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of // lifetimes and ident params (including SelfUpper). These are validated later @@ -909,7 +926,7 @@ impl<'a> Parser<'a> { let (args, args_span) = self.parse_precise_capturing_args()?; GenericBound::Use(args, use_span.to(args_span)) } else { - self.parse_generic_ty_bound(lo, has_parens, &leading_token)? + self.parse_generic_ty_bound(lo, parens, &leading_token)? }; Ok(bound) @@ -919,10 +936,14 @@ impl<'a> Parser<'a> { /// ```ebnf /// LT_BOUND = LIFETIME /// ``` - fn parse_generic_lt_bound(&mut self, lo: Span, has_parens: bool) -> PResult<'a, GenericBound> { + fn parse_generic_lt_bound( + &mut self, + lo: Span, + parens: ast::Parens, + ) -> PResult<'a, GenericBound> { let lt = self.expect_lifetime(); let bound = GenericBound::Outlives(lt); - if has_parens { + if let ast::Parens::Yes = parens { // FIXME(Centril): Consider not erroring here and accepting `('lt)` instead, // possibly introducing `GenericBound::Paren(P)`? self.recover_paren_lifetime(lo)?; @@ -1078,7 +1099,7 @@ impl<'a> Parser<'a> { fn parse_generic_ty_bound( &mut self, lo: Span, - has_parens: bool, + parens: ast::Parens, leading_token: &Token, ) -> PResult<'a, GenericBound> { let (mut lifetime_defs, binder_span) = self.parse_late_bound_lifetime_defs()?; @@ -1104,7 +1125,7 @@ impl<'a> Parser<'a> { // e.g. `T: for<'a> 'a` or `T: ~const 'a`. if self.token.is_lifetime() { let _: ErrorGuaranteed = self.error_lt_bound_with_modifiers(modifiers, binder_span); - return self.parse_generic_lt_bound(lo, has_parens); + return self.parse_generic_lt_bound(lo, parens); } if let (more_lifetime_defs, Some(binder_span)) = self.parse_late_bound_lifetime_defs()? { @@ -1171,7 +1192,7 @@ impl<'a> Parser<'a> { self.recover_fn_trait_with_lifetime_params(&mut path, &mut lifetime_defs)?; } - if has_parens { + if let ast::Parens::Yes = parens { // Someone has written something like `&dyn (Trait + Other)`. The correct code // would be `&(dyn Trait + Other)` if self.token.is_like_plus() && leading_token.is_keyword(kw::Dyn) { @@ -1191,7 +1212,7 @@ impl<'a> Parser<'a> { } let poly_trait = - PolyTraitRef::new(lifetime_defs, path, modifiers, lo.to(self.prev_token.span)); + PolyTraitRef::new(lifetime_defs, path, modifiers, lo.to(self.prev_token.span), parens); Ok(GenericBound::Trait(poly_trait)) } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index e7b8c988cd4af..6230b8cfbec7d 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3832,6 +3832,7 @@ fn mk_where_bound_predicate( ref_id: DUMMY_NODE_ID, }, span: DUMMY_SP, + parens: ast::Parens::No, })], }; diff --git a/library/core/src/error.rs b/library/core/src/error.rs index bfa392003b91b..7f5c6ac42bc1c 100644 --- a/library/core/src/error.rs +++ b/library/core/src/error.rs @@ -347,7 +347,7 @@ impl dyn Error { /// let b = B(Some(Box::new(A))); /// /// // let err : Box = b.into(); // or - /// let err = &b as &(dyn Error); + /// let err = &b as &dyn Error; /// /// let mut iter = err.sources(); /// diff --git a/tests/ui/lint/lint-unnecessary-parens.fixed b/tests/ui/lint/lint-unnecessary-parens.fixed index a8c8dd1d512fb..be322a31363de 100644 --- a/tests/ui/lint/lint-unnecessary-parens.fixed +++ b/tests/ui/lint/lint-unnecessary-parens.fixed @@ -1,5 +1,6 @@ //@ run-rustfix +#![feature(impl_trait_in_fn_trait_return)] #![deny(unused_parens)] #![allow(while_true)] // for rustfix @@ -16,11 +17,11 @@ fn bar(y: bool) -> X { return X { y }; //~ ERROR unnecessary parentheses around `return` value } -pub fn unused_parens_around_return_type() -> u32 { //~ ERROR unnecessary parentheses around type +pub fn around_return_type() -> u32 { //~ ERROR unnecessary parentheses around type panic!() } -pub fn unused_parens_around_block_return() -> u32 { +pub fn around_block_return() -> u32 { let _foo = { 5 //~ ERROR unnecessary parentheses around block return value }; @@ -31,10 +32,90 @@ pub trait Trait { fn test(&self); } -pub fn passes_unused_parens_lint() -> &'static (dyn Trait) { +pub fn around_multi_bound_ref() -> &'static (dyn Trait + Send) { panic!() } +//~v ERROR unnecessary parentheses around type +pub fn around_single_bound_ref() -> &'static dyn Trait { + panic!() +} + +pub fn around_multi_bound_ptr() -> *const (dyn Trait + Send) { + panic!() +} + +//~v ERROR unnecessary parentheses around type +pub fn around_single_bound_ptr() -> *const dyn Trait { + panic!() +} + +pub fn around_multi_bound_dyn_fn_output() -> &'static dyn FnOnce() -> (impl Send + Sync) { + &|| () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_single_bound_dyn_fn_output() -> &'static dyn FnOnce() -> impl Send { + &|| () +} + +pub fn around_dyn_fn_output_given_more_bounds() -> &'static (dyn FnOnce() -> (impl Send) + Sync) { + &|| () +} + +pub fn around_multi_bound_impl_fn_output() -> impl FnOnce() -> (impl Send + Sync) { + || () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_single_bound_impl_fn_output() -> impl FnOnce() -> impl Send { + || () +} + +pub fn around_impl_fn_output_given_more_bounds() -> impl FnOnce() -> (impl Send) + Sync { + || () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_dyn_bound() -> &'static dyn FnOnce() { + &|| () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_impl_trait_bound() -> impl FnOnce() { + || () +} + +// these parens aren't strictly required but they help disambiguate => no lint +pub fn around_fn_bound_with_explicit_ret_ty() -> impl (Fn() -> ()) + Send { + || () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_fn_bound_with_implicit_ret_ty() -> impl Fn() + Send { + || () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_last_fn_bound_with_explicit_ret_ty() -> impl Send + Fn() -> () { + || () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_regular_bound1() -> &'static (dyn Send + Sync) { + &|| () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_regular_bound2() -> &'static (dyn Send + Sync) { + &|| () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_regular_bound3() -> &'static (dyn Send + ::std::marker::Sync) { + &|| () +} + pub fn parens_with_keyword(e: &[()]) -> i32 { if true {} //~ ERROR unnecessary parentheses around `if` while true {} //~ ERROR unnecessary parentheses around `while` diff --git a/tests/ui/lint/lint-unnecessary-parens.rs b/tests/ui/lint/lint-unnecessary-parens.rs index 02aa78283c78f..dccad07311bca 100644 --- a/tests/ui/lint/lint-unnecessary-parens.rs +++ b/tests/ui/lint/lint-unnecessary-parens.rs @@ -1,5 +1,6 @@ //@ run-rustfix +#![feature(impl_trait_in_fn_trait_return)] #![deny(unused_parens)] #![allow(while_true)] // for rustfix @@ -16,11 +17,11 @@ fn bar(y: bool) -> X { return (X { y }); //~ ERROR unnecessary parentheses around `return` value } -pub fn unused_parens_around_return_type() -> (u32) { //~ ERROR unnecessary parentheses around type +pub fn around_return_type() -> (u32) { //~ ERROR unnecessary parentheses around type panic!() } -pub fn unused_parens_around_block_return() -> u32 { +pub fn around_block_return() -> u32 { let _foo = { (5) //~ ERROR unnecessary parentheses around block return value }; @@ -31,10 +32,90 @@ pub trait Trait { fn test(&self); } -pub fn passes_unused_parens_lint() -> &'static (dyn Trait) { +pub fn around_multi_bound_ref() -> &'static (dyn Trait + Send) { panic!() } +//~v ERROR unnecessary parentheses around type +pub fn around_single_bound_ref() -> &'static (dyn Trait) { + panic!() +} + +pub fn around_multi_bound_ptr() -> *const (dyn Trait + Send) { + panic!() +} + +//~v ERROR unnecessary parentheses around type +pub fn around_single_bound_ptr() -> *const (dyn Trait) { + panic!() +} + +pub fn around_multi_bound_dyn_fn_output() -> &'static dyn FnOnce() -> (impl Send + Sync) { + &|| () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_single_bound_dyn_fn_output() -> &'static dyn FnOnce() -> (impl Send) { + &|| () +} + +pub fn around_dyn_fn_output_given_more_bounds() -> &'static (dyn FnOnce() -> (impl Send) + Sync) { + &|| () +} + +pub fn around_multi_bound_impl_fn_output() -> impl FnOnce() -> (impl Send + Sync) { + || () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_single_bound_impl_fn_output() -> impl FnOnce() -> (impl Send) { + || () +} + +pub fn around_impl_fn_output_given_more_bounds() -> impl FnOnce() -> (impl Send) + Sync { + || () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_dyn_bound() -> &'static dyn (FnOnce()) { + &|| () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_impl_trait_bound() -> impl (FnOnce()) { + || () +} + +// these parens aren't strictly required but they help disambiguate => no lint +pub fn around_fn_bound_with_explicit_ret_ty() -> impl (Fn() -> ()) + Send { + || () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_fn_bound_with_implicit_ret_ty() -> impl (Fn()) + Send { + || () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_last_fn_bound_with_explicit_ret_ty() -> impl Send + (Fn() -> ()) { + || () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_regular_bound1() -> &'static (dyn (Send) + Sync) { + &|| () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_regular_bound2() -> &'static (dyn Send + (Sync)) { + &|| () +} + +//~v ERROR unnecessary parentheses around type +pub fn around_regular_bound3() -> &'static (dyn Send + (::std::marker::Sync)) { + &|| () +} + pub fn parens_with_keyword(e: &[()]) -> i32 { if(true) {} //~ ERROR unnecessary parentheses around `if` while(true) {} //~ ERROR unnecessary parentheses around `while` diff --git a/tests/ui/lint/lint-unnecessary-parens.stderr b/tests/ui/lint/lint-unnecessary-parens.stderr index f2e5debd6e08f..a7fc1e89c6ccb 100644 --- a/tests/ui/lint/lint-unnecessary-parens.stderr +++ b/tests/ui/lint/lint-unnecessary-parens.stderr @@ -1,11 +1,11 @@ error: unnecessary parentheses around `return` value - --> $DIR/lint-unnecessary-parens.rs:13:12 + --> $DIR/lint-unnecessary-parens.rs:14:12 | LL | return (1); | ^ ^ | note: the lint level is defined here - --> $DIR/lint-unnecessary-parens.rs:3:9 + --> $DIR/lint-unnecessary-parens.rs:4:9 | LL | #![deny(unused_parens)] | ^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL + return 1; | error: unnecessary parentheses around `return` value - --> $DIR/lint-unnecessary-parens.rs:16:12 + --> $DIR/lint-unnecessary-parens.rs:17:12 | LL | return (X { y }); | ^ ^ @@ -28,19 +28,19 @@ LL + return X { y }; | error: unnecessary parentheses around type - --> $DIR/lint-unnecessary-parens.rs:19:46 + --> $DIR/lint-unnecessary-parens.rs:20:32 | -LL | pub fn unused_parens_around_return_type() -> (u32) { - | ^ ^ +LL | pub fn around_return_type() -> (u32) { + | ^ ^ | help: remove these parentheses | -LL - pub fn unused_parens_around_return_type() -> (u32) { -LL + pub fn unused_parens_around_return_type() -> u32 { +LL - pub fn around_return_type() -> (u32) { +LL + pub fn around_return_type() -> u32 { | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:25:9 + --> $DIR/lint-unnecessary-parens.rs:26:9 | LL | (5) | ^ ^ @@ -52,7 +52,7 @@ LL + 5 | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:27:5 + --> $DIR/lint-unnecessary-parens.rs:28:5 | LL | (5) | ^ ^ @@ -63,8 +63,140 @@ LL - (5) LL + 5 | +error: unnecessary parentheses around type + --> $DIR/lint-unnecessary-parens.rs:40:46 + | +LL | pub fn around_single_bound_ref() -> &'static (dyn Trait) { + | ^ ^ + | +help: remove these parentheses + | +LL - pub fn around_single_bound_ref() -> &'static (dyn Trait) { +LL + pub fn around_single_bound_ref() -> &'static dyn Trait { + | + +error: unnecessary parentheses around type + --> $DIR/lint-unnecessary-parens.rs:49:44 + | +LL | pub fn around_single_bound_ptr() -> *const (dyn Trait) { + | ^ ^ + | +help: remove these parentheses + | +LL - pub fn around_single_bound_ptr() -> *const (dyn Trait) { +LL + pub fn around_single_bound_ptr() -> *const dyn Trait { + | + +error: unnecessary parentheses around type + --> $DIR/lint-unnecessary-parens.rs:58:72 + | +LL | pub fn around_single_bound_dyn_fn_output() -> &'static dyn FnOnce() -> (impl Send) { + | ^ ^ + | +help: remove these parentheses + | +LL - pub fn around_single_bound_dyn_fn_output() -> &'static dyn FnOnce() -> (impl Send) { +LL + pub fn around_single_bound_dyn_fn_output() -> &'static dyn FnOnce() -> impl Send { + | + +error: unnecessary parentheses around type + --> $DIR/lint-unnecessary-parens.rs:71:65 + | +LL | pub fn around_single_bound_impl_fn_output() -> impl FnOnce() -> (impl Send) { + | ^ ^ + | +help: remove these parentheses + | +LL - pub fn around_single_bound_impl_fn_output() -> impl FnOnce() -> (impl Send) { +LL + pub fn around_single_bound_impl_fn_output() -> impl FnOnce() -> impl Send { + | + +error: unnecessary parentheses around type + --> $DIR/lint-unnecessary-parens.rs:80:43 + | +LL | pub fn around_dyn_bound() -> &'static dyn (FnOnce()) { + | ^ ^ + | +help: remove these parentheses + | +LL - pub fn around_dyn_bound() -> &'static dyn (FnOnce()) { +LL + pub fn around_dyn_bound() -> &'static dyn FnOnce() { + | + +error: unnecessary parentheses around type + --> $DIR/lint-unnecessary-parens.rs:85:42 + | +LL | pub fn around_impl_trait_bound() -> impl (FnOnce()) { + | ^ ^ + | +help: remove these parentheses + | +LL - pub fn around_impl_trait_bound() -> impl (FnOnce()) { +LL + pub fn around_impl_trait_bound() -> impl FnOnce() { + | + +error: unnecessary parentheses around type + --> $DIR/lint-unnecessary-parens.rs:95:55 + | +LL | pub fn around_fn_bound_with_implicit_ret_ty() -> impl (Fn()) + Send { + | ^ ^ + | +help: remove these parentheses + | +LL - pub fn around_fn_bound_with_implicit_ret_ty() -> impl (Fn()) + Send { +LL + pub fn around_fn_bound_with_implicit_ret_ty() -> impl Fn() + Send { + | + +error: unnecessary parentheses around type + --> $DIR/lint-unnecessary-parens.rs:100:67 + | +LL | pub fn around_last_fn_bound_with_explicit_ret_ty() -> impl Send + (Fn() -> ()) { + | ^ ^ + | +help: remove these parentheses + | +LL - pub fn around_last_fn_bound_with_explicit_ret_ty() -> impl Send + (Fn() -> ()) { +LL + pub fn around_last_fn_bound_with_explicit_ret_ty() -> impl Send + Fn() -> () { + | + +error: unnecessary parentheses around type + --> $DIR/lint-unnecessary-parens.rs:105:49 + | +LL | pub fn around_regular_bound1() -> &'static (dyn (Send) + Sync) { + | ^ ^ + | +help: remove these parentheses + | +LL - pub fn around_regular_bound1() -> &'static (dyn (Send) + Sync) { +LL + pub fn around_regular_bound1() -> &'static (dyn Send + Sync) { + | + +error: unnecessary parentheses around type + --> $DIR/lint-unnecessary-parens.rs:110:56 + | +LL | pub fn around_regular_bound2() -> &'static (dyn Send + (Sync)) { + | ^ ^ + | +help: remove these parentheses + | +LL - pub fn around_regular_bound2() -> &'static (dyn Send + (Sync)) { +LL + pub fn around_regular_bound2() -> &'static (dyn Send + Sync) { + | + +error: unnecessary parentheses around type + --> $DIR/lint-unnecessary-parens.rs:115:56 + | +LL | pub fn around_regular_bound3() -> &'static (dyn Send + (::std::marker::Sync)) { + | ^ ^ + | +help: remove these parentheses + | +LL - pub fn around_regular_bound3() -> &'static (dyn Send + (::std::marker::Sync)) { +LL + pub fn around_regular_bound3() -> &'static (dyn Send + ::std::marker::Sync) { + | + error: unnecessary parentheses around `if` condition - --> $DIR/lint-unnecessary-parens.rs:39:7 + --> $DIR/lint-unnecessary-parens.rs:120:7 | LL | if(true) {} | ^ ^ @@ -76,7 +208,7 @@ LL + if true {} | error: unnecessary parentheses around `while` condition - --> $DIR/lint-unnecessary-parens.rs:40:10 + --> $DIR/lint-unnecessary-parens.rs:121:10 | LL | while(true) {} | ^ ^ @@ -88,7 +220,7 @@ LL + while true {} | error: unnecessary parentheses around `for` iterator expression - --> $DIR/lint-unnecessary-parens.rs:41:13 + --> $DIR/lint-unnecessary-parens.rs:122:13 | LL | for _ in(e) {} | ^ ^ @@ -100,7 +232,7 @@ LL + for _ in e {} | error: unnecessary parentheses around `match` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:42:10 + --> $DIR/lint-unnecessary-parens.rs:123:10 | LL | match(1) { _ => ()} | ^ ^ @@ -112,7 +244,7 @@ LL + match 1 { _ => ()} | error: unnecessary parentheses around `return` value - --> $DIR/lint-unnecessary-parens.rs:43:11 + --> $DIR/lint-unnecessary-parens.rs:124:11 | LL | return(1); | ^ ^ @@ -124,7 +256,7 @@ LL + return 1; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:74:31 + --> $DIR/lint-unnecessary-parens.rs:155:31 | LL | pub const CONST_ITEM: usize = (10); | ^ ^ @@ -136,7 +268,7 @@ LL + pub const CONST_ITEM: usize = 10; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:75:33 + --> $DIR/lint-unnecessary-parens.rs:156:33 | LL | pub static STATIC_ITEM: usize = (10); | ^ ^ @@ -148,7 +280,7 @@ LL + pub static STATIC_ITEM: usize = 10; | error: unnecessary parentheses around function argument - --> $DIR/lint-unnecessary-parens.rs:79:9 + --> $DIR/lint-unnecessary-parens.rs:160:9 | LL | bar((true)); | ^ ^ @@ -160,7 +292,7 @@ LL + bar(true); | error: unnecessary parentheses around `if` condition - --> $DIR/lint-unnecessary-parens.rs:81:8 + --> $DIR/lint-unnecessary-parens.rs:162:8 | LL | if (true) {} | ^ ^ @@ -172,7 +304,7 @@ LL + if true {} | error: unnecessary parentheses around `while` condition - --> $DIR/lint-unnecessary-parens.rs:82:11 + --> $DIR/lint-unnecessary-parens.rs:163:11 | LL | while (true) {} | ^ ^ @@ -184,7 +316,7 @@ LL + while true {} | error: unnecessary parentheses around `match` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:83:11 + --> $DIR/lint-unnecessary-parens.rs:164:11 | LL | match (true) { | ^ ^ @@ -196,7 +328,7 @@ LL + match true { | error: unnecessary parentheses around `let` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:86:16 + --> $DIR/lint-unnecessary-parens.rs:167:16 | LL | if let 1 = (1) {} | ^ ^ @@ -208,7 +340,7 @@ LL + if let 1 = 1 {} | error: unnecessary parentheses around `let` scrutinee expression - --> $DIR/lint-unnecessary-parens.rs:87:19 + --> $DIR/lint-unnecessary-parens.rs:168:19 | LL | while let 1 = (2) {} | ^ ^ @@ -220,7 +352,7 @@ LL + while let 1 = 2 {} | error: unnecessary parentheses around method argument - --> $DIR/lint-unnecessary-parens.rs:103:24 + --> $DIR/lint-unnecessary-parens.rs:184:24 | LL | X { y: false }.foo((true)); | ^ ^ @@ -232,7 +364,7 @@ LL + X { y: false }.foo(true); | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:105:18 + --> $DIR/lint-unnecessary-parens.rs:186:18 | LL | let mut _a = (0); | ^ ^ @@ -244,7 +376,7 @@ LL + let mut _a = 0; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:106:10 + --> $DIR/lint-unnecessary-parens.rs:187:10 | LL | _a = (0); | ^ ^ @@ -256,7 +388,7 @@ LL + _a = 0; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:107:11 + --> $DIR/lint-unnecessary-parens.rs:188:11 | LL | _a += (1); | ^ ^ @@ -268,7 +400,7 @@ LL + _a += 1; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:109:8 + --> $DIR/lint-unnecessary-parens.rs:190:8 | LL | let(mut _a) = 3; | ^ ^ @@ -280,7 +412,7 @@ LL + let mut _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:110:9 + --> $DIR/lint-unnecessary-parens.rs:191:9 | LL | let (mut _a) = 3; | ^ ^ @@ -292,7 +424,7 @@ LL + let mut _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:111:8 + --> $DIR/lint-unnecessary-parens.rs:192:8 | LL | let( mut _a) = 3; | ^^ ^ @@ -304,7 +436,7 @@ LL + let mut _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:113:8 + --> $DIR/lint-unnecessary-parens.rs:194:8 | LL | let(_a) = 3; | ^ ^ @@ -316,7 +448,7 @@ LL + let _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:114:9 + --> $DIR/lint-unnecessary-parens.rs:195:9 | LL | let (_a) = 3; | ^ ^ @@ -328,7 +460,7 @@ LL + let _a = 3; | error: unnecessary parentheses around pattern - --> $DIR/lint-unnecessary-parens.rs:115:8 + --> $DIR/lint-unnecessary-parens.rs:196:8 | LL | let( _a) = 3; | ^^ ^ @@ -340,7 +472,7 @@ LL + let _a = 3; | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:121:9 + --> $DIR/lint-unnecessary-parens.rs:202:9 | LL | (unit!() - One) | ^ ^ @@ -352,7 +484,7 @@ LL + unit!() - One | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:123:9 + --> $DIR/lint-unnecessary-parens.rs:204:9 | LL | (unit![] - One) | ^ ^ @@ -364,7 +496,7 @@ LL + unit![] - One | error: unnecessary parentheses around block return value - --> $DIR/lint-unnecessary-parens.rs:126:9 + --> $DIR/lint-unnecessary-parens.rs:207:9 | LL | (unit! {} - One) | ^ ^ @@ -376,7 +508,7 @@ LL + unit! {} - One | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:131:14 + --> $DIR/lint-unnecessary-parens.rs:212:14 | LL | let _r = (&x); | ^ ^ @@ -388,7 +520,7 @@ LL + let _r = &x; | error: unnecessary parentheses around assigned value - --> $DIR/lint-unnecessary-parens.rs:132:14 + --> $DIR/lint-unnecessary-parens.rs:213:14 | LL | let _r = (&mut x); | ^ ^ @@ -399,5 +531,5 @@ LL - let _r = (&mut x); LL + let _r = &mut x; | -error: aborting due to 33 previous errors +error: aborting due to 44 previous errors diff --git a/tests/ui/lint/unused/issue-105061-should-lint.rs b/tests/ui/lint/unused/issue-105061-should-lint.rs index 433c288208910..74a0ff83739a8 100644 --- a/tests/ui/lint/unused/issue-105061-should-lint.rs +++ b/tests/ui/lint/unused/issue-105061-should-lint.rs @@ -14,7 +14,7 @@ where trait Hello {} fn with_dyn_bound() where - (dyn Hello<(for<'b> fn(&'b ()))>): Hello //~ ERROR unnecessary parentheses around type + dyn Hello<(for<'b> fn(&'b ()))>: Hello //~ ERROR unnecessary parentheses around type {} fn main() { diff --git a/tests/ui/lint/unused/issue-105061-should-lint.stderr b/tests/ui/lint/unused/issue-105061-should-lint.stderr index e591f1ffb6b89..ae69f018eae97 100644 --- a/tests/ui/lint/unused/issue-105061-should-lint.stderr +++ b/tests/ui/lint/unused/issue-105061-should-lint.stderr @@ -17,15 +17,15 @@ LL + for<'b> for<'a> fn(Inv<'a>): Trait<'b>, | error: unnecessary parentheses around type - --> $DIR/issue-105061-should-lint.rs:17:16 + --> $DIR/issue-105061-should-lint.rs:17:15 | -LL | (dyn Hello<(for<'b> fn(&'b ()))>): Hello - | ^ ^ +LL | dyn Hello<(for<'b> fn(&'b ()))>: Hello + | ^ ^ | help: remove these parentheses | -LL - (dyn Hello<(for<'b> fn(&'b ()))>): Hello -LL + (dyn Hello fn(&'b ())>): Hello +LL - dyn Hello<(for<'b> fn(&'b ()))>: Hello +LL + dyn Hello fn(&'b ())>: Hello | error: aborting due to 2 previous errors diff --git a/tests/ui/lint/unused/unused-parens-trait-obj.edition2018.fixed b/tests/ui/lint/unused/unused-parens-trait-obj.edition2018.fixed new file mode 100644 index 0000000000000..f95418868e15f --- /dev/null +++ b/tests/ui/lint/unused/unused-parens-trait-obj.edition2018.fixed @@ -0,0 +1,27 @@ +//@ revisions: edition2015 edition2018 +//@[edition2015] check-pass +//@[edition2015] edition: 2015 +//@[edition2018] run-rustfix +//@[edition2018] edition: 2018 + +#![deny(unused_parens)] + +#[allow(unused)] +macro_rules! edition2015_only { + () => { + mod dyn { + pub type IsAContextualKeywordIn2015 = (); + } + + pub type DynIsAContextualKeywordIn2015A = dyn::IsAContextualKeywordIn2015; + } +} + +#[cfg(edition2015)] +edition2015_only!(); + +// there's a lint for 2018 and later only because of how dyn is parsed in edition 2015 +//[edition2018]~v ERROR unnecessary parentheses around type +pub type DynIsAContextualKeywordIn2015B = Box; + +fn main() {} diff --git a/tests/ui/lint/unused/unused-parens-trait-obj.edition2018.stderr b/tests/ui/lint/unused/unused-parens-trait-obj.edition2018.stderr new file mode 100644 index 0000000000000..aed8cec68e8da --- /dev/null +++ b/tests/ui/lint/unused/unused-parens-trait-obj.edition2018.stderr @@ -0,0 +1,19 @@ +error: unnecessary parentheses around type + --> $DIR/unused-parens-trait-obj.rs:25:51 + | +LL | pub type DynIsAContextualKeywordIn2015B = Box; + | ^ ^ + | +note: the lint level is defined here + --> $DIR/unused-parens-trait-obj.rs:7:9 + | +LL | #![deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - pub type DynIsAContextualKeywordIn2015B = Box; +LL + pub type DynIsAContextualKeywordIn2015B = Box; + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/unused/unused-parens-trait-obj.rs b/tests/ui/lint/unused/unused-parens-trait-obj.rs new file mode 100644 index 0000000000000..2192baa2e0205 --- /dev/null +++ b/tests/ui/lint/unused/unused-parens-trait-obj.rs @@ -0,0 +1,27 @@ +//@ revisions: edition2015 edition2018 +//@[edition2015] check-pass +//@[edition2015] edition: 2015 +//@[edition2018] run-rustfix +//@[edition2018] edition: 2018 + +#![deny(unused_parens)] + +#[allow(unused)] +macro_rules! edition2015_only { + () => { + mod dyn { + pub type IsAContextualKeywordIn2015 = (); + } + + pub type DynIsAContextualKeywordIn2015A = dyn::IsAContextualKeywordIn2015; + } +} + +#[cfg(edition2015)] +edition2015_only!(); + +// there's a lint for 2018 and later only because of how dyn is parsed in edition 2015 +//[edition2018]~v ERROR unnecessary parentheses around type +pub type DynIsAContextualKeywordIn2015B = Box; + +fn main() {} diff --git a/tests/ui/sanitizer/cfi/closures.rs b/tests/ui/sanitizer/cfi/closures.rs index 9f9002da674f5..424e70560dbb4 100644 --- a/tests/ui/sanitizer/cfi/closures.rs +++ b/tests/ui/sanitizer/cfi/closures.rs @@ -31,7 +31,7 @@ fn dyn_fn_with_params() { #[test] fn call_fn_trait() { - let f: &(dyn Fn()) = &(|| {}) as _; + let f: &dyn Fn() = &(|| {}) as _; f.call(()); } @@ -47,7 +47,7 @@ fn use_fnmut(mut f: F) { #[test] fn fn_to_fnmut() { - let f: &(dyn Fn()) = &(|| {}) as _; + let f: &dyn Fn() = &(|| {}) as _; use_fnmut(f); } diff --git a/tests/ui/traits/dyn-trait.rs b/tests/ui/traits/dyn-trait.rs index 4fb7aea5cbabe..a378ce5a696fd 100644 --- a/tests/ui/traits/dyn-trait.rs +++ b/tests/ui/traits/dyn-trait.rs @@ -7,7 +7,7 @@ static BYTE: u8 = 33; fn main() { let x: &(dyn 'static + Display) = &BYTE; let y: Box = Box::new(BYTE); - let _: &dyn (Display) = &BYTE; + let _: &dyn Display = &BYTE; let _: &dyn (::std::fmt::Display) = &BYTE; let xstr = format!("{}", x); let ystr = format!("{}", y); diff --git a/tests/ui/traits/impl-2.rs b/tests/ui/traits/impl-2.rs index 41fa1cd334f40..eafbaeaa167b9 100644 --- a/tests/ui/traits/impl-2.rs +++ b/tests/ui/traits/impl-2.rs @@ -10,7 +10,7 @@ pub mod Foo { } mod Bar { - impl<'a> dyn (crate::Foo::Trait) + 'a { + impl<'a> dyn crate::Foo::Trait + 'a { fn bar(&self) { self.foo() } } } From aa7cc5d2f453853a4025cf029f3e42625c7e1e18 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 1 Jul 2025 15:29:09 +0200 Subject: [PATCH 09/16] loop match: run exhaustiveness check --- compiler/rustc_middle/src/thir.rs | 14 +- compiler/rustc_middle/src/thir/visit.rs | 4 +- .../rustc_mir_build/src/builder/expr/into.rs | 12 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 7 +- .../src/thir/pattern/check_match.rs | 8 +- compiler/rustc_mir_build/src/thir/print.rs | 19 +- tests/ui/loop-match/invalid.rs | 13 + tests/ui/loop-match/invalid.stderr | 28 +- .../ui/thir-print/thir-tree-loop-match.stdout | 290 ++++++++++-------- 9 files changed, 242 insertions(+), 153 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index bda8dcadbced8..730c1147684b4 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -380,11 +380,11 @@ pub enum ExprKind<'tcx> { }, /// A `#[loop_match] loop { state = 'blk: { match state { ... } } }` expression. LoopMatch { - /// The state variable that is updated, and also the scrutinee of the match. + /// The state variable that is updated. + /// The `match_data.scrutinee` is the same variable, but with a different span. state: ExprId, region_scope: region::Scope, - arms: Box<[ArmId]>, - match_span: Span, + match_data: Box, }, /// Special expression representing the `let` part of an `if let` or similar construct /// (including `if let` guards in match arms, and let-chains formed by `&&`). @@ -599,6 +599,14 @@ pub struct Arm<'tcx> { pub span: Span, } +/// The `match` part of a `#[loop_match]` +#[derive(Clone, Debug, HashStable)] +pub struct LoopMatchMatchData { + pub scrutinee: ExprId, + pub arms: Box<[ArmId]>, + pub span: Span, +} + #[derive(Copy, Clone, Debug, HashStable)] pub enum LogicalOp { /// The `&&` operator. diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index c9ef723aea43a..dcfa6c4db3274 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -2,6 +2,7 @@ use super::{ AdtExpr, AdtExprBase, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat, PatKind, Stmt, StmtKind, Thir, }; +use crate::thir::LoopMatchMatchData; /// Every `walk_*` method uses deconstruction to access fields of structs and /// enums. This will result in a compile error if a field is added, which makes @@ -83,7 +84,8 @@ pub fn walk_expr<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( visitor.visit_pat(pat); } Loop { body } => visitor.visit_expr(&visitor.thir()[body]), - LoopMatch { state: scrutinee, ref arms, .. } | Match { scrutinee, ref arms, .. } => { + LoopMatch { match_data: box LoopMatchMatchData { scrutinee, ref arms, .. }, .. } + | Match { scrutinee, ref arms, .. } => { visitor.visit_expr(&visitor.thir()[scrutinee]); for &arm in &**arms { visitor.visit_arm(&visitor.thir()[arm]); diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index fe3d072fa88e2..82b883a99a11c 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -245,7 +245,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None }) } - ExprKind::LoopMatch { state, region_scope, match_span, ref arms } => { + ExprKind::LoopMatch { + state, + region_scope, + match_data: box LoopMatchMatchData { box ref arms, span: match_span, scrutinee }, + } => { // Intuitively, this is a combination of a loop containing a labeled block // containing a match. // @@ -292,8 +296,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Logic for `match`. let scrutinee_place_builder = - unpack!(body_block = this.as_place_builder(body_block, state)); - let scrutinee_span = this.thir.exprs[state].span; + unpack!(body_block = this.as_place_builder(body_block, scrutinee)); + let scrutinee_span = this.thir.exprs[scrutinee].span; let match_start_span = match_span.shrink_to_lo().to(scrutinee_span); let mut patterns = Vec::with_capacity(arms.len()); @@ -335,7 +339,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { move |this| { this.in_breakable_scope(None, state_place, expr_span, |this| { Some(this.in_const_continuable_scope( - arms.clone(), + Box::from(arms), built_tree.clone(), state_place, expr_span, diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 5197e93fda73a..b694409f327bd 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -983,8 +983,11 @@ impl<'tcx> ThirBuildCx<'tcx> { data: region::ScopeData::Node, }, - arms: arms.iter().map(|a| self.convert_arm(a)).collect(), - match_span: block_body_expr.span, + match_data: Box::new(LoopMatchMatchData { + scrutinee: self.mirror_expr(scrutinee), + arms: arms.iter().map(|a| self.convert_arm(a)).collect(), + span: block_body_expr.span, + }), } } else { let block_ty = self.typeck_results.node_type(body.hir_id); diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 41fbabc253930..b7b160c738d22 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -6,7 +6,7 @@ use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, struct_span_code_err}; use rustc_hir::def::*; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, MatchSource}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::Level; use rustc_middle::bug; @@ -154,6 +154,12 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> { ExprKind::Match { scrutinee, box ref arms, match_source } => { self.check_match(scrutinee, arms, match_source, ex.span); } + ExprKind::LoopMatch { + match_data: box LoopMatchMatchData { scrutinee, box ref arms, span }, + .. + } => { + self.check_match(scrutinee, arms, MatchSource::Normal, span); + } ExprKind::Let { box ref pat, expr } => { self.check_let(pat, Some(expr), ex.span); } diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 1507b6b8c068a..5efc4be2de2df 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -318,18 +318,23 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_expr(*body, depth_lvl + 2); print_indented!(self, ")", depth_lvl); } - LoopMatch { state, region_scope, match_span, arms } => { + LoopMatch { state, region_scope, match_data } => { print_indented!(self, "LoopMatch {", depth_lvl); print_indented!(self, "state:", depth_lvl + 1); self.print_expr(*state, depth_lvl + 2); print_indented!(self, format!("region_scope: {:?}", region_scope), depth_lvl + 1); - print_indented!(self, format!("match_span: {:?}", match_span), depth_lvl + 1); - - print_indented!(self, "arms: [", depth_lvl + 1); - for arm_id in arms.iter() { - self.print_arm(*arm_id, depth_lvl + 2); + print_indented!(self, "match_data:", depth_lvl + 1); + print_indented!(self, "LoopMatchMatchData {", depth_lvl + 2); + print_indented!(self, format!("span: {:?}", match_data.span), depth_lvl + 3); + print_indented!(self, "scrutinee:", depth_lvl + 3); + self.print_expr(match_data.scrutinee, depth_lvl + 4); + + print_indented!(self, "arms: [", depth_lvl + 3); + for arm_id in match_data.arms.iter() { + self.print_arm(*arm_id, depth_lvl + 4); } - print_indented!(self, "]", depth_lvl + 1); + print_indented!(self, "]", depth_lvl + 3); + print_indented!(self, "}", depth_lvl + 2); print_indented!(self, "}", depth_lvl); } Let { expr, pat } => { diff --git a/tests/ui/loop-match/invalid.rs b/tests/ui/loop-match/invalid.rs index 2ddc19f4fc62a..2f3b0a00f1f9f 100644 --- a/tests/ui/loop-match/invalid.rs +++ b/tests/ui/loop-match/invalid.rs @@ -159,3 +159,16 @@ fn arm_has_guard(cond: bool) { } } } + +fn non_exhaustive() { + let mut state = State::A; + #[loop_match] + loop { + state = 'blk: { + match state { + //~^ ERROR non-exhaustive patterns: `State::B` and `State::C` not covered + State::A => State::B, + } + } + } +} diff --git a/tests/ui/loop-match/invalid.stderr b/tests/ui/loop-match/invalid.stderr index 51fdd024c6fa9..0444c713d920f 100644 --- a/tests/ui/loop-match/invalid.stderr +++ b/tests/ui/loop-match/invalid.stderr @@ -86,6 +86,30 @@ error: match arms that are part of a `#[loop_match]` cannot have guards LL | State::B if cond => break 'a, | ^^^^ -error: aborting due to 12 previous errors +error[E0004]: non-exhaustive patterns: `State::B` and `State::C` not covered + --> $DIR/invalid.rs:168:19 + | +LL | match state { + | ^^^^^ patterns `State::B` and `State::C` not covered + | +note: `State` defined here + --> $DIR/invalid.rs:7:6 + | +LL | enum State { + | ^^^^^ +LL | A, +LL | B, + | - not covered +LL | C, + | - not covered + = note: the matched value is of type `State` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms + | +LL ~ State::A => State::B, +LL ~ State::B | State::C => todo!(), + | + +error: aborting due to 13 previous errors -For more information about this error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0004, E0308. +For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/thir-print/thir-tree-loop-match.stdout b/tests/ui/thir-print/thir-tree-loop-match.stdout index 828b93da6beb5..5c4c50cb15623 100644 --- a/tests/ui/thir-print/thir-tree-loop-match.stdout +++ b/tests/ui/thir-print/thir-tree-loop-match.stdout @@ -89,158 +89,182 @@ body: } } region_scope: Node(10) - match_span: $DIR/thir-tree-loop-match.rs:11:13: 17:14 (#0) - arms: [ - Arm { - pattern: - Pat: { - ty: bool - span: $DIR/thir-tree-loop-match.rs:12:17: 12:21 (#0) - kind: PatKind { - Constant { - value: Ty(bool, true) - } - } - } - guard: None - body: + match_data: + LoopMatchMatchData { + span: $DIR/thir-tree-loop-match.rs:11:13: 17:14 (#0) + scrutinee: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } - span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(5)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:11:19: 11:24 (#0) kind: Scope { - region_scope: Node(17) - lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).17)) + region_scope: Node(12) + lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).12)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } - span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(5)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:11:19: 11:24 (#0) kind: - NeverToAny { - source: - Expr { - ty: ! - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } - span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) - kind: - Block { - targeted_by_break: false + VarRef { + id: LocalVarId(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).2)) + } + } + } + } + arms: [ + Arm { + pattern: + Pat: { + ty: bool + span: $DIR/thir-tree-loop-match.rs:12:17: 12:21 (#0) + kind: PatKind { + Constant { + value: Ty(bool, true) + } + } + } + guard: None + body: + Expr { + ty: bool + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) + kind: + Scope { + region_scope: Node(17) + lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).17)) + value: + Expr { + ty: bool + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) + kind: + NeverToAny { + source: + Expr { + ty: ! + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(16)), backwards_incompatible: None } span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) - region_scope: Node(18) - safety_mode: Safe - stmts: [ - Stmt { - kind: Expr { - scope: Node(21) - expr: - Expr { - ty: ! - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None } - span: $DIR/thir-tree-loop-match.rs:14:21: 14:37 (#0) - kind: - Scope { - region_scope: Node(19) - lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).19)) - value: - Expr { - ty: ! - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None } - span: $DIR/thir-tree-loop-match.rs:14:21: 14:37 (#0) - kind: - ConstContinue ( - label: Node(10) - value: - Expr { - ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None } - span: $DIR/thir-tree-loop-match.rs:14:32: 14:37 (#0) - kind: - Scope { - region_scope: Node(20) - lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).20)) - value: - Expr { - ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None } - span: $DIR/thir-tree-loop-match.rs:14:32: 14:37 (#0) - kind: - Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-loop-match.rs:14:32: 14:37 (#0) }, neg: false) + kind: + Block { + targeted_by_break: false + span: $DIR/thir-tree-loop-match.rs:12:25: 15:18 (#0) + region_scope: Node(18) + safety_mode: Safe + stmts: [ + Stmt { + kind: Expr { + scope: Node(21) + expr: + Expr { + ty: ! + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:14:21: 14:37 (#0) + kind: + Scope { + region_scope: Node(19) + lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).19)) + value: + Expr { + ty: ! + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:14:21: 14:37 (#0) + kind: + ConstContinue ( + label: Node(10) + value: + Expr { + ty: bool + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:14:32: 14:37 (#0) + kind: + Scope { + region_scope: Node(20) + lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).20)) + value: + Expr { + ty: bool + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(21)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:14:32: 14:37 (#0) + kind: + Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-loop-match.rs:14:32: 14:37 (#0) }, neg: false) + } } } - } - ) + ) + } } } } - } + } + ] + expr: [] } - ] - expr: [] } } } } } + lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).16)) + scope: Node(16) + span: $DIR/thir-tree-loop-match.rs:12:17: 15:18 (#0) } - lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).16)) - scope: Node(16) - span: $DIR/thir-tree-loop-match.rs:12:17: 15:18 (#0) - } - Arm { - pattern: - Pat: { - ty: bool - span: $DIR/thir-tree-loop-match.rs:16:17: 16:22 (#0) - kind: PatKind { - Constant { - value: Ty(bool, false) + Arm { + pattern: + Pat: { + ty: bool + span: $DIR/thir-tree-loop-match.rs:16:17: 16:22 (#0) + kind: PatKind { + Constant { + value: Ty(bool, false) + } + } } - } - } - guard: None - body: - Expr { - ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } - span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) - kind: - Scope { - region_scope: Node(25) - lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).25)) - value: - Expr { - ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } - span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) - kind: - NeverToAny { - source: - Expr { - ty: ! - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } - span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) - kind: - Return { - value: - Expr { - ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } - span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0) - kind: - Scope { - region_scope: Node(26) - lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).26)) - value: - Expr { - ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } - span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0) - kind: - VarRef { - id: LocalVarId(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).2)) + guard: None + body: + Expr { + ty: bool + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) + kind: + Scope { + region_scope: Node(25) + lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).25)) + value: + Expr { + ty: bool + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) + kind: + NeverToAny { + source: + Expr { + ty: ! + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:16:26: 16:38 (#0) + kind: + Return { + value: + Expr { + ty: bool + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0) + kind: + Scope { + region_scope: Node(26) + lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).26)) + value: + Expr { + ty: bool + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + span: $DIR/thir-tree-loop-match.rs:16:33: 16:38 (#0) + kind: + VarRef { + id: LocalVarId(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).2)) + } } } } @@ -250,12 +274,12 @@ body: } } } + lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).24)) + scope: Node(24) + span: $DIR/thir-tree-loop-match.rs:16:17: 16:38 (#0) } - lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree_loop_match[3c53]::boolean).24)) - scope: Node(24) - span: $DIR/thir-tree-loop-match.rs:16:17: 16:38 (#0) + ] } - ] } } } From 8fdf0ef0ae32369a6fe82b2ef69b1f5e5dc68be1 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Tue, 1 Jul 2025 11:48:42 +0200 Subject: [PATCH 10/16] loop match: handle opaque patterns fixes issue 143203 --- .../rustc_mir_build/src/builder/matches/mod.rs | 6 ++++-- compiler/rustc_mir_build/src/errors.rs | 1 - tests/ui/loop-match/invalid.rs | 18 ++++++++++++++++++ tests/ui/loop-match/invalid.stderr | 10 ++++++++-- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 9600067a85fe3..2c29b8628417f 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2970,6 +2970,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } Constructor::Wildcard => true, + // Opaque patterns must not be matched on structurally. + Constructor::Opaque(_) => false, + // These we may eventually support: Constructor::Struct | Constructor::Ref @@ -2980,8 +2983,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | Constructor::Str(_) => bug!("unsupported pattern constructor {:?}", pat.ctor()), // These should never occur here: - Constructor::Opaque(_) - | Constructor::Never + Constructor::Never | Constructor::NonExhaustive | Constructor::Hidden | Constructor::Missing diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 20e836f6bf208..16b49bf384c52 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1230,7 +1230,6 @@ pub(crate) struct ConstContinueMissingValue { #[derive(Diagnostic)] #[diag(mir_build_const_continue_unknown_jump_target)] -#[note] pub(crate) struct ConstContinueUnknownJumpTarget { #[primary_span] pub span: Span, diff --git a/tests/ui/loop-match/invalid.rs b/tests/ui/loop-match/invalid.rs index 2f3b0a00f1f9f..0c47b1e0057ae 100644 --- a/tests/ui/loop-match/invalid.rs +++ b/tests/ui/loop-match/invalid.rs @@ -172,3 +172,21 @@ fn non_exhaustive() { } } } + +fn invalid_range_pattern(state: f32) { + #[loop_match] + loop { + state = 'blk: { + match state { + 1.0 => { + #[const_continue] + break 'blk 2.5; + } + 4.0..3.0 => { + //~^ ERROR lower range bound must be less than upper + todo!() + } + } + } + } +} diff --git a/tests/ui/loop-match/invalid.stderr b/tests/ui/loop-match/invalid.stderr index 0444c713d920f..70f246caa9c2d 100644 --- a/tests/ui/loop-match/invalid.stderr +++ b/tests/ui/loop-match/invalid.stderr @@ -109,7 +109,13 @@ LL ~ State::A => State::B, LL ~ State::B | State::C => todo!(), | -error: aborting due to 13 previous errors +error[E0579]: lower range bound must be less than upper + --> $DIR/invalid.rs:185:17 + | +LL | 4.0..3.0 => { + | ^^^^^^^^ + +error: aborting due to 14 previous errors -Some errors have detailed explanations: E0004, E0308. +Some errors have detailed explanations: E0004, E0308, E0579. For more information about an error, try `rustc --explain E0004`. From 15bd619d5f12b4d01bb645340e7eea97d7b0e7c7 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Sun, 18 May 2025 13:01:35 +0200 Subject: [PATCH 11/16] Change `{Box,Arc,Rc,Weak}::into_raw` to only work with `A = Global` Also applies to `Vec::into_raw_parts`. The expectation is that you can round-trip these methods with `from_raw`, but this is only true when using the global allocator. With custom allocators you should instead be using `into_raw_with_allocator` and `from_raw_in`. The implementation of `Box::leak` is changed to use `Box::into_raw_with_allocator` and explicitly leak the allocator (which was already the existing behavior). This is because, for `leak` to be safe, the allocator must not free its underlying backing store. The `Allocator` trait only guarantees that allocated memory remains valid until the allocator is dropped. --- library/alloc/src/boxed.rs | 222 ++++++++++++++++++----------------- library/alloc/src/rc.rs | 122 +++++++++---------- library/alloc/src/sync.rs | 122 +++++++++---------- library/alloc/src/vec/mod.rs | 164 +++++++++++++------------- 4 files changed, 316 insertions(+), 314 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 4536f55544354..c84799bc83c2f 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1098,115 +1098,6 @@ impl Box { pub unsafe fn from_non_null(ptr: NonNull) -> Self { unsafe { Self::from_raw(ptr.as_ptr()) } } -} - -impl Box { - /// Constructs a box from a raw pointer in the given allocator. - /// - /// After calling this function, the raw pointer is owned by the - /// resulting `Box`. Specifically, the `Box` destructor will call - /// the destructor of `T` and free the allocated memory. For this - /// to be safe, the memory must have been allocated in accordance - /// with the [memory layout] used by `Box` . - /// - /// # Safety - /// - /// This function is unsafe because improper use may lead to - /// memory problems. For example, a double-free may occur if the - /// function is called twice on the same raw pointer. - /// - /// The raw pointer must point to a block of memory allocated by `alloc`. - /// - /// # Examples - /// - /// Recreate a `Box` which was previously converted to a raw pointer - /// using [`Box::into_raw_with_allocator`]: - /// ``` - /// #![feature(allocator_api)] - /// - /// use std::alloc::System; - /// - /// let x = Box::new_in(5, System); - /// let (ptr, alloc) = Box::into_raw_with_allocator(x); - /// let x = unsafe { Box::from_raw_in(ptr, alloc) }; - /// ``` - /// Manually create a `Box` from scratch by using the system allocator: - /// ``` - /// #![feature(allocator_api, slice_ptr_get)] - /// - /// use std::alloc::{Allocator, Layout, System}; - /// - /// unsafe { - /// let ptr = System.allocate(Layout::new::())?.as_mut_ptr() as *mut i32; - /// // In general .write is required to avoid attempting to destruct - /// // the (uninitialized) previous contents of `ptr`, though for this - /// // simple example `*ptr = 5` would have worked as well. - /// ptr.write(5); - /// let x = Box::from_raw_in(ptr, System); - /// } - /// # Ok::<(), std::alloc::AllocError>(()) - /// ``` - /// - /// [memory layout]: self#memory-layout - #[unstable(feature = "allocator_api", issue = "32838")] - #[inline] - pub unsafe fn from_raw_in(raw: *mut T, alloc: A) -> Self { - Box(unsafe { Unique::new_unchecked(raw) }, alloc) - } - - /// Constructs a box from a `NonNull` pointer in the given allocator. - /// - /// After calling this function, the `NonNull` pointer is owned by - /// the resulting `Box`. Specifically, the `Box` destructor will call - /// the destructor of `T` and free the allocated memory. For this - /// to be safe, the memory must have been allocated in accordance - /// with the [memory layout] used by `Box` . - /// - /// # Safety - /// - /// This function is unsafe because improper use may lead to - /// memory problems. For example, a double-free may occur if the - /// function is called twice on the same raw pointer. - /// - /// The non-null pointer must point to a block of memory allocated by `alloc`. - /// - /// # Examples - /// - /// Recreate a `Box` which was previously converted to a `NonNull` pointer - /// using [`Box::into_non_null_with_allocator`]: - /// ``` - /// #![feature(allocator_api, box_vec_non_null)] - /// - /// use std::alloc::System; - /// - /// let x = Box::new_in(5, System); - /// let (non_null, alloc) = Box::into_non_null_with_allocator(x); - /// let x = unsafe { Box::from_non_null_in(non_null, alloc) }; - /// ``` - /// Manually create a `Box` from scratch by using the system allocator: - /// ``` - /// #![feature(allocator_api, box_vec_non_null, slice_ptr_get)] - /// - /// use std::alloc::{Allocator, Layout, System}; - /// - /// unsafe { - /// let non_null = System.allocate(Layout::new::())?.cast::(); - /// // In general .write is required to avoid attempting to destruct - /// // the (uninitialized) previous contents of `non_null`. - /// non_null.write(5); - /// let x = Box::from_non_null_in(non_null, System); - /// } - /// # Ok::<(), std::alloc::AllocError>(()) - /// ``` - /// - /// [memory layout]: self#memory-layout - #[unstable(feature = "allocator_api", issue = "32838")] - // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] - #[inline] - pub unsafe fn from_non_null_in(raw: NonNull, alloc: A) -> Self { - // SAFETY: guaranteed by the caller. - unsafe { Box::from_raw_in(raw.as_ptr(), alloc) } - } /// Consumes the `Box`, returning a wrapped raw pointer. /// @@ -1322,6 +1213,115 @@ impl Box { // SAFETY: `Box` is guaranteed to be non-null. unsafe { NonNull::new_unchecked(Self::into_raw(b)) } } +} + +impl Box { + /// Constructs a box from a raw pointer in the given allocator. + /// + /// After calling this function, the raw pointer is owned by the + /// resulting `Box`. Specifically, the `Box` destructor will call + /// the destructor of `T` and free the allocated memory. For this + /// to be safe, the memory must have been allocated in accordance + /// with the [memory layout] used by `Box` . + /// + /// # Safety + /// + /// This function is unsafe because improper use may lead to + /// memory problems. For example, a double-free may occur if the + /// function is called twice on the same raw pointer. + /// + /// The raw pointer must point to a block of memory allocated by `alloc`. + /// + /// # Examples + /// + /// Recreate a `Box` which was previously converted to a raw pointer + /// using [`Box::into_raw_with_allocator`]: + /// ``` + /// #![feature(allocator_api)] + /// + /// use std::alloc::System; + /// + /// let x = Box::new_in(5, System); + /// let (ptr, alloc) = Box::into_raw_with_allocator(x); + /// let x = unsafe { Box::from_raw_in(ptr, alloc) }; + /// ``` + /// Manually create a `Box` from scratch by using the system allocator: + /// ``` + /// #![feature(allocator_api, slice_ptr_get)] + /// + /// use std::alloc::{Allocator, Layout, System}; + /// + /// unsafe { + /// let ptr = System.allocate(Layout::new::())?.as_mut_ptr() as *mut i32; + /// // In general .write is required to avoid attempting to destruct + /// // the (uninitialized) previous contents of `ptr`, though for this + /// // simple example `*ptr = 5` would have worked as well. + /// ptr.write(5); + /// let x = Box::from_raw_in(ptr, System); + /// } + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [memory layout]: self#memory-layout + #[unstable(feature = "allocator_api", issue = "32838")] + #[inline] + pub unsafe fn from_raw_in(raw: *mut T, alloc: A) -> Self { + Box(unsafe { Unique::new_unchecked(raw) }, alloc) + } + + /// Constructs a box from a `NonNull` pointer in the given allocator. + /// + /// After calling this function, the `NonNull` pointer is owned by + /// the resulting `Box`. Specifically, the `Box` destructor will call + /// the destructor of `T` and free the allocated memory. For this + /// to be safe, the memory must have been allocated in accordance + /// with the [memory layout] used by `Box` . + /// + /// # Safety + /// + /// This function is unsafe because improper use may lead to + /// memory problems. For example, a double-free may occur if the + /// function is called twice on the same raw pointer. + /// + /// The non-null pointer must point to a block of memory allocated by `alloc`. + /// + /// # Examples + /// + /// Recreate a `Box` which was previously converted to a `NonNull` pointer + /// using [`Box::into_non_null_with_allocator`]: + /// ``` + /// #![feature(allocator_api, box_vec_non_null)] + /// + /// use std::alloc::System; + /// + /// let x = Box::new_in(5, System); + /// let (non_null, alloc) = Box::into_non_null_with_allocator(x); + /// let x = unsafe { Box::from_non_null_in(non_null, alloc) }; + /// ``` + /// Manually create a `Box` from scratch by using the system allocator: + /// ``` + /// #![feature(allocator_api, box_vec_non_null, slice_ptr_get)] + /// + /// use std::alloc::{Allocator, Layout, System}; + /// + /// unsafe { + /// let non_null = System.allocate(Layout::new::())?.cast::(); + /// // In general .write is required to avoid attempting to destruct + /// // the (uninitialized) previous contents of `non_null`. + /// non_null.write(5); + /// let x = Box::from_non_null_in(non_null, System); + /// } + /// # Ok::<(), std::alloc::AllocError>(()) + /// ``` + /// + /// [memory layout]: self#memory-layout + #[unstable(feature = "allocator_api", issue = "32838")] + // #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + #[inline] + pub unsafe fn from_non_null_in(raw: NonNull, alloc: A) -> Self { + // SAFETY: guaranteed by the caller. + unsafe { Box::from_raw_in(raw.as_ptr(), alloc) } + } /// Consumes the `Box`, returning a wrapped raw pointer and the allocator. /// @@ -1602,7 +1602,9 @@ impl Box { where A: 'a, { - unsafe { &mut *Box::into_raw(b) } + let (ptr, alloc) = Box::into_raw_with_allocator(b); + mem::forget(alloc); + unsafe { &mut *ptr } } /// Converts a `Box` into a `Pin>`. If `T` does not implement [`Unpin`], then diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 010d17f74762c..5018ff4ad71f3 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1322,6 +1322,30 @@ impl Rc { unsafe { Self::from_raw_in(ptr, Global) } } + /// Consumes the `Rc`, returning the wrapped pointer. + /// + /// To avoid a memory leak the pointer must be converted back to an `Rc` using + /// [`Rc::from_raw`]. + /// + /// # Examples + /// + /// ``` + /// use std::rc::Rc; + /// + /// let x = Rc::new("hello".to_owned()); + /// let x_ptr = Rc::into_raw(x); + /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// # // Prevent leaks for Miri. + /// # drop(unsafe { Rc::from_raw(x_ptr) }); + /// ``` + #[must_use = "losing the pointer will leak memory"] + #[stable(feature = "rc_raw", since = "1.17.0")] + #[rustc_never_returns_null_ptr] + pub fn into_raw(this: Self) -> *const T { + let this = ManuallyDrop::new(this); + Self::as_ptr(&*this) + } + /// Increments the strong reference count on the `Rc` associated with the /// provided pointer by one. /// @@ -1408,30 +1432,6 @@ impl Rc { &this.alloc } - /// Consumes the `Rc`, returning the wrapped pointer. - /// - /// To avoid a memory leak the pointer must be converted back to an `Rc` using - /// [`Rc::from_raw`]. - /// - /// # Examples - /// - /// ``` - /// use std::rc::Rc; - /// - /// let x = Rc::new("hello".to_owned()); - /// let x_ptr = Rc::into_raw(x); - /// assert_eq!(unsafe { &*x_ptr }, "hello"); - /// # // Prevent leaks for Miri. - /// # drop(unsafe { Rc::from_raw(x_ptr) }); - /// ``` - #[must_use = "losing the pointer will leak memory"] - #[stable(feature = "rc_raw", since = "1.17.0")] - #[rustc_never_returns_null_ptr] - pub fn into_raw(this: Self) -> *const T { - let this = ManuallyDrop::new(this); - Self::as_ptr(&*this) - } - /// Consumes the `Rc`, returning the wrapped pointer and allocator. /// /// To avoid a memory leak the pointer must be converted back to an `Rc` using @@ -1525,7 +1525,7 @@ impl Rc { /// use std::alloc::System; /// /// let x = Rc::new_in("hello".to_owned(), System); - /// let x_ptr = Rc::into_raw(x); + /// let (x_ptr, _alloc) = Rc::into_raw_with_allocator(x); /// /// unsafe { /// // Convert back to an `Rc` to prevent leak. @@ -1547,7 +1547,7 @@ impl Rc { /// use std::alloc::System; /// /// let x: Rc<[u32], _> = Rc::new_in([1, 2, 3], System); - /// let x_ptr: *const [u32] = Rc::into_raw(x); + /// let x_ptr: *const [u32] = Rc::into_raw_with_allocator(x).0; /// /// unsafe { /// let x: Rc<[u32; 3], _> = Rc::from_raw_in(x_ptr.cast::<[u32; 3]>(), System); @@ -1648,7 +1648,7 @@ impl Rc { /// let five = Rc::new_in(5, System); /// /// unsafe { - /// let ptr = Rc::into_raw(five); + /// let (ptr, _alloc) = Rc::into_raw_with_allocator(five); /// Rc::increment_strong_count_in(ptr, System); /// /// let five = Rc::from_raw_in(ptr, System); @@ -1694,7 +1694,7 @@ impl Rc { /// let five = Rc::new_in(5, System); /// /// unsafe { - /// let ptr = Rc::into_raw(five); + /// let (ptr, _alloc) = Rc::into_raw_with_allocator(five); /// Rc::increment_strong_count_in(ptr, System); /// /// let five = Rc::from_raw_in(ptr, System); @@ -3123,6 +3123,39 @@ impl Weak { pub unsafe fn from_raw(ptr: *const T) -> Self { unsafe { Self::from_raw_in(ptr, Global) } } + + /// Consumes the `Weak` and turns it into a raw pointer. + /// + /// This converts the weak pointer into a raw pointer, while still preserving the ownership of + /// one weak reference (the weak count is not modified by this operation). It can be turned + /// back into the `Weak` with [`from_raw`]. + /// + /// The same restrictions of accessing the target of the pointer as with + /// [`as_ptr`] apply. + /// + /// # Examples + /// + /// ``` + /// use std::rc::{Rc, Weak}; + /// + /// let strong = Rc::new("hello".to_owned()); + /// let weak = Rc::downgrade(&strong); + /// let raw = weak.into_raw(); + /// + /// assert_eq!(1, Rc::weak_count(&strong)); + /// assert_eq!("hello", unsafe { &*raw }); + /// + /// drop(unsafe { Weak::from_raw(raw) }); + /// assert_eq!(0, Rc::weak_count(&strong)); + /// ``` + /// + /// [`from_raw`]: Weak::from_raw + /// [`as_ptr`]: Weak::as_ptr + #[must_use = "losing the pointer will leak memory"] + #[stable(feature = "weak_into_raw", since = "1.45.0")] + pub fn into_raw(self) -> *const T { + mem::ManuallyDrop::new(self).as_ptr() + } } impl Weak { @@ -3175,39 +3208,6 @@ impl Weak { } } - /// Consumes the `Weak` and turns it into a raw pointer. - /// - /// This converts the weak pointer into a raw pointer, while still preserving the ownership of - /// one weak reference (the weak count is not modified by this operation). It can be turned - /// back into the `Weak` with [`from_raw`]. - /// - /// The same restrictions of accessing the target of the pointer as with - /// [`as_ptr`] apply. - /// - /// # Examples - /// - /// ``` - /// use std::rc::{Rc, Weak}; - /// - /// let strong = Rc::new("hello".to_owned()); - /// let weak = Rc::downgrade(&strong); - /// let raw = weak.into_raw(); - /// - /// assert_eq!(1, Rc::weak_count(&strong)); - /// assert_eq!("hello", unsafe { &*raw }); - /// - /// drop(unsafe { Weak::from_raw(raw) }); - /// assert_eq!(0, Rc::weak_count(&strong)); - /// ``` - /// - /// [`from_raw`]: Weak::from_raw - /// [`as_ptr`]: Weak::as_ptr - #[must_use = "losing the pointer will leak memory"] - #[stable(feature = "weak_into_raw", since = "1.45.0")] - pub fn into_raw(self) -> *const T { - mem::ManuallyDrop::new(self).as_ptr() - } - /// Consumes the `Weak`, returning the wrapped pointer and allocator. /// /// This converts the weak pointer into a raw pointer, while still preserving the ownership of diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 1e3c03977bd75..b8925f4544f44 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1467,6 +1467,30 @@ impl Arc { unsafe { Arc::from_raw_in(ptr, Global) } } + /// Consumes the `Arc`, returning the wrapped pointer. + /// + /// To avoid a memory leak the pointer must be converted back to an `Arc` using + /// [`Arc::from_raw`]. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new("hello".to_owned()); + /// let x_ptr = Arc::into_raw(x); + /// assert_eq!(unsafe { &*x_ptr }, "hello"); + /// # // Prevent leaks for Miri. + /// # drop(unsafe { Arc::from_raw(x_ptr) }); + /// ``` + #[must_use = "losing the pointer will leak memory"] + #[stable(feature = "rc_raw", since = "1.17.0")] + #[rustc_never_returns_null_ptr] + pub fn into_raw(this: Self) -> *const T { + let this = ManuallyDrop::new(this); + Self::as_ptr(&*this) + } + /// Increments the strong reference count on the `Arc` associated with the /// provided pointer by one. /// @@ -1558,30 +1582,6 @@ impl Arc { &this.alloc } - /// Consumes the `Arc`, returning the wrapped pointer. - /// - /// To avoid a memory leak the pointer must be converted back to an `Arc` using - /// [`Arc::from_raw`]. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new("hello".to_owned()); - /// let x_ptr = Arc::into_raw(x); - /// assert_eq!(unsafe { &*x_ptr }, "hello"); - /// # // Prevent leaks for Miri. - /// # drop(unsafe { Arc::from_raw(x_ptr) }); - /// ``` - #[must_use = "losing the pointer will leak memory"] - #[stable(feature = "rc_raw", since = "1.17.0")] - #[rustc_never_returns_null_ptr] - pub fn into_raw(this: Self) -> *const T { - let this = ManuallyDrop::new(this); - Self::as_ptr(&*this) - } - /// Consumes the `Arc`, returning the wrapped pointer and allocator. /// /// To avoid a memory leak the pointer must be converted back to an `Arc` using @@ -1676,7 +1676,7 @@ impl Arc { /// use std::alloc::System; /// /// let x = Arc::new_in("hello".to_owned(), System); - /// let x_ptr = Arc::into_raw(x); + /// let (x_ptr, alloc) = Arc::into_raw_with_allocator(x); /// /// unsafe { /// // Convert back to an `Arc` to prevent leak. @@ -1698,7 +1698,7 @@ impl Arc { /// use std::alloc::System; /// /// let x: Arc<[u32], _> = Arc::new_in([1, 2, 3], System); - /// let x_ptr: *const [u32] = Arc::into_raw(x); + /// let x_ptr: *const [u32] = Arc::into_raw_with_allocator(x).0; /// /// unsafe { /// let x: Arc<[u32; 3], _> = Arc::from_raw_in(x_ptr.cast::<[u32; 3]>(), System); @@ -1850,7 +1850,7 @@ impl Arc { /// let five = Arc::new_in(5, System); /// /// unsafe { - /// let ptr = Arc::into_raw(five); + /// let (ptr, _alloc) = Arc::into_raw_with_allocator(five); /// Arc::increment_strong_count_in(ptr, System); /// /// // This assertion is deterministic because we haven't shared @@ -1899,7 +1899,7 @@ impl Arc { /// let five = Arc::new_in(5, System); /// /// unsafe { - /// let ptr = Arc::into_raw(five); + /// let (ptr, _alloc) = Arc::into_raw_with_allocator(five); /// Arc::increment_strong_count_in(ptr, System); /// /// // Those assertions are deterministic because we haven't shared @@ -2863,6 +2863,39 @@ impl Weak { pub unsafe fn from_raw(ptr: *const T) -> Self { unsafe { Weak::from_raw_in(ptr, Global) } } + + /// Consumes the `Weak` and turns it into a raw pointer. + /// + /// This converts the weak pointer into a raw pointer, while still preserving the ownership of + /// one weak reference (the weak count is not modified by this operation). It can be turned + /// back into the `Weak` with [`from_raw`]. + /// + /// The same restrictions of accessing the target of the pointer as with + /// [`as_ptr`] apply. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// let strong = Arc::new("hello".to_owned()); + /// let weak = Arc::downgrade(&strong); + /// let raw = weak.into_raw(); + /// + /// assert_eq!(1, Arc::weak_count(&strong)); + /// assert_eq!("hello", unsafe { &*raw }); + /// + /// drop(unsafe { Weak::from_raw(raw) }); + /// assert_eq!(0, Arc::weak_count(&strong)); + /// ``` + /// + /// [`from_raw`]: Weak::from_raw + /// [`as_ptr`]: Weak::as_ptr + #[must_use = "losing the pointer will leak memory"] + #[stable(feature = "weak_into_raw", since = "1.45.0")] + pub fn into_raw(self) -> *const T { + ManuallyDrop::new(self).as_ptr() + } } impl Weak { @@ -2915,39 +2948,6 @@ impl Weak { } } - /// Consumes the `Weak` and turns it into a raw pointer. - /// - /// This converts the weak pointer into a raw pointer, while still preserving the ownership of - /// one weak reference (the weak count is not modified by this operation). It can be turned - /// back into the `Weak` with [`from_raw`]. - /// - /// The same restrictions of accessing the target of the pointer as with - /// [`as_ptr`] apply. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// let strong = Arc::new("hello".to_owned()); - /// let weak = Arc::downgrade(&strong); - /// let raw = weak.into_raw(); - /// - /// assert_eq!(1, Arc::weak_count(&strong)); - /// assert_eq!("hello", unsafe { &*raw }); - /// - /// drop(unsafe { Weak::from_raw(raw) }); - /// assert_eq!(0, Arc::weak_count(&strong)); - /// ``` - /// - /// [`from_raw`]: Weak::from_raw - /// [`as_ptr`]: Weak::as_ptr - #[must_use = "losing the pointer will leak memory"] - #[stable(feature = "weak_into_raw", since = "1.45.0")] - pub fn into_raw(self) -> *const T { - ManuallyDrop::new(self).as_ptr() - } - /// Consumes the `Weak`, returning the wrapped pointer and allocator. /// /// This converts the weak pointer into a raw pointer, while still preserving the ownership of diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 5bd82560da7ed..0e4417a0a6bbf 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -761,6 +761,88 @@ impl Vec { pub fn peek_mut(&mut self) -> Option> { PeekMut::new(self) } + + /// Decomposes a `Vec` into its raw components: `(pointer, length, capacity)`. + /// + /// Returns the raw pointer to the underlying data, the length of + /// the vector (in elements), and the allocated capacity of the + /// data (in elements). These are the same arguments in the same + /// order as the arguments to [`from_raw_parts`]. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `Vec`. The only way to do + /// this is to convert the raw pointer, length, and capacity back + /// into a `Vec` with the [`from_raw_parts`] function, allowing + /// the destructor to perform the cleanup. + /// + /// [`from_raw_parts`]: Vec::from_raw_parts + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_into_raw_parts)] + /// let v: Vec = vec![-1, 0, 1]; + /// + /// let (ptr, len, cap) = v.into_raw_parts(); + /// + /// let rebuilt = unsafe { + /// // We can now make changes to the components, such as + /// // transmuting the raw pointer to a compatible type. + /// let ptr = ptr as *mut u32; + /// + /// Vec::from_raw_parts(ptr, len, cap) + /// }; + /// assert_eq!(rebuilt, [4294967295, 0, 1]); + /// ``` + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] + pub fn into_raw_parts(self) -> (*mut T, usize, usize) { + let mut me = ManuallyDrop::new(self); + (me.as_mut_ptr(), me.len(), me.capacity()) + } + + #[doc(alias = "into_non_null_parts")] + /// Decomposes a `Vec` into its raw components: `(NonNull pointer, length, capacity)`. + /// + /// Returns the `NonNull` pointer to the underlying data, the length of + /// the vector (in elements), and the allocated capacity of the + /// data (in elements). These are the same arguments in the same + /// order as the arguments to [`from_parts`]. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `Vec`. The only way to do + /// this is to convert the `NonNull` pointer, length, and capacity back + /// into a `Vec` with the [`from_parts`] function, allowing + /// the destructor to perform the cleanup. + /// + /// [`from_parts`]: Vec::from_parts + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_into_raw_parts, box_vec_non_null)] + /// + /// let v: Vec = vec![-1, 0, 1]; + /// + /// let (ptr, len, cap) = v.into_parts(); + /// + /// let rebuilt = unsafe { + /// // We can now make changes to the components, such as + /// // transmuting the raw pointer to a compatible type. + /// let ptr = ptr.cast::(); + /// + /// Vec::from_parts(ptr, len, cap) + /// }; + /// assert_eq!(rebuilt, [4294967295, 0, 1]); + /// ``` + #[must_use = "losing the pointer will leak memory"] + #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] + // #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] + pub fn into_parts(self) -> (NonNull, usize, usize) { + let (ptr, len, capacity) = self.into_raw_parts(); + // SAFETY: A `Vec` always has a non-null pointer. + (unsafe { NonNull::new_unchecked(ptr) }, len, capacity) + } } impl Vec { @@ -1095,88 +1177,6 @@ impl Vec { unsafe { Vec { buf: RawVec::from_nonnull_in(ptr, capacity, alloc), len: length } } } - /// Decomposes a `Vec` into its raw components: `(pointer, length, capacity)`. - /// - /// Returns the raw pointer to the underlying data, the length of - /// the vector (in elements), and the allocated capacity of the - /// data (in elements). These are the same arguments in the same - /// order as the arguments to [`from_raw_parts`]. - /// - /// After calling this function, the caller is responsible for the - /// memory previously managed by the `Vec`. The only way to do - /// this is to convert the raw pointer, length, and capacity back - /// into a `Vec` with the [`from_raw_parts`] function, allowing - /// the destructor to perform the cleanup. - /// - /// [`from_raw_parts`]: Vec::from_raw_parts - /// - /// # Examples - /// - /// ``` - /// #![feature(vec_into_raw_parts)] - /// let v: Vec = vec![-1, 0, 1]; - /// - /// let (ptr, len, cap) = v.into_raw_parts(); - /// - /// let rebuilt = unsafe { - /// // We can now make changes to the components, such as - /// // transmuting the raw pointer to a compatible type. - /// let ptr = ptr as *mut u32; - /// - /// Vec::from_raw_parts(ptr, len, cap) - /// }; - /// assert_eq!(rebuilt, [4294967295, 0, 1]); - /// ``` - #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] - pub fn into_raw_parts(self) -> (*mut T, usize, usize) { - let mut me = ManuallyDrop::new(self); - (me.as_mut_ptr(), me.len(), me.capacity()) - } - - #[doc(alias = "into_non_null_parts")] - /// Decomposes a `Vec` into its raw components: `(NonNull pointer, length, capacity)`. - /// - /// Returns the `NonNull` pointer to the underlying data, the length of - /// the vector (in elements), and the allocated capacity of the - /// data (in elements). These are the same arguments in the same - /// order as the arguments to [`from_parts`]. - /// - /// After calling this function, the caller is responsible for the - /// memory previously managed by the `Vec`. The only way to do - /// this is to convert the `NonNull` pointer, length, and capacity back - /// into a `Vec` with the [`from_parts`] function, allowing - /// the destructor to perform the cleanup. - /// - /// [`from_parts`]: Vec::from_parts - /// - /// # Examples - /// - /// ``` - /// #![feature(vec_into_raw_parts, box_vec_non_null)] - /// - /// let v: Vec = vec![-1, 0, 1]; - /// - /// let (ptr, len, cap) = v.into_parts(); - /// - /// let rebuilt = unsafe { - /// // We can now make changes to the components, such as - /// // transmuting the raw pointer to a compatible type. - /// let ptr = ptr.cast::(); - /// - /// Vec::from_parts(ptr, len, cap) - /// }; - /// assert_eq!(rebuilt, [4294967295, 0, 1]); - /// ``` - #[must_use = "losing the pointer will leak memory"] - #[unstable(feature = "box_vec_non_null", reason = "new API", issue = "130364")] - // #[unstable(feature = "vec_into_raw_parts", reason = "new API", issue = "65816")] - pub fn into_parts(self) -> (NonNull, usize, usize) { - let (ptr, len, capacity) = self.into_raw_parts(); - // SAFETY: A `Vec` always has a non-null pointer. - (unsafe { NonNull::new_unchecked(ptr) }, len, capacity) - } - /// Decomposes a `Vec` into its raw components: `(pointer, length, capacity, allocator)`. /// /// Returns the raw pointer to the underlying data, the length of the vector (in elements), From 8797d54812393baeff99ffe257fa056c3dfdb656 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 19 May 2025 13:54:23 +0200 Subject: [PATCH 12/16] make Box::into_raw compatible with Stacked Borrows again --- library/alloc/src/boxed.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index c84799bc83c2f..4e3f76de49e99 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1150,8 +1150,11 @@ impl Box { #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub fn into_raw(b: Self) -> *mut T { - // Make sure Miri realizes that we transition from a noalias pointer to a raw pointer here. - unsafe { &raw mut *&mut *Self::into_raw_with_allocator(b).0 } + // Avoid `into_raw_with_allocator` as that interacts poorly with Miri's Stacked Borrows. + let mut b = mem::ManuallyDrop::new(b); + // We go through the built-in deref for `Box`, which is crucial for Miri to recognize this + // operation for it's alias tracking. + &raw mut **b } /// Consumes the `Box`, returning a wrapped `NonNull` pointer. From 8bb7fdb236b50eb14a0b39abbb8cfaa4e2dc853f Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Wed, 2 Jul 2025 00:20:47 +0300 Subject: [PATCH 13/16] NoArgsAttributeParser: use an assoc const instead --- .../src/attributes/codegen_attrs.rs | 15 +++------------ .../src/attributes/lint_helpers.rs | 10 ++-------- .../src/attributes/loop_match.rs | 10 ++-------- compiler/rustc_attr_parsing/src/attributes/mod.rs | 4 ++-- .../src/attributes/semantics.rs | 5 +---- .../src/attributes/stability.rs | 5 +---- 6 files changed, 11 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 360c28dafab9f..1132402ea0083 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -49,10 +49,7 @@ pub(crate) struct ColdParser; impl NoArgsAttributeParser for ColdParser { const PATH: &[Symbol] = &[sym::cold]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - - fn create(span: Span) -> AttributeKind { - AttributeKind::Cold(span) - } + const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold; } pub(crate) struct ExportNameParser; @@ -193,20 +190,14 @@ pub(crate) struct TrackCallerParser; impl NoArgsAttributeParser for TrackCallerParser { const PATH: &[Symbol] = &[sym::track_caller]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - - fn create(span: Span) -> AttributeKind { - AttributeKind::TrackCaller(span) - } + const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller; } pub(crate) struct NoMangleParser; impl NoArgsAttributeParser for NoMangleParser { const PATH: &[Symbol] = &[sym::no_mangle]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - - fn create(span: Span) -> AttributeKind { - AttributeKind::NoMangle(span) - } + const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle; } #[derive(Default)] diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 0dfcf3cb89907..5437803d781d8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -8,18 +8,12 @@ pub(crate) struct AsPtrParser; impl NoArgsAttributeParser for AsPtrParser { const PATH: &[Symbol] = &[sym::rustc_as_ptr]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - - fn create(span: Span) -> AttributeKind { - AttributeKind::AsPtr(span) - } + const CREATE: fn(Span) -> AttributeKind = AttributeKind::AsPtr; } pub(crate) struct PubTransparentParser; impl NoArgsAttributeParser for PubTransparentParser { const PATH: &[Symbol] = &[sym::rustc_pub_transparent]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; - - fn create(span: Span) -> AttributeKind { - AttributeKind::PubTransparent(span) - } + const CREATE: fn(Span) -> AttributeKind = AttributeKind::PubTransparent; } diff --git a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs index 1a5368c092f11..80808b90dc66c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs +++ b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs @@ -8,18 +8,12 @@ pub(crate) struct LoopMatchParser; impl NoArgsAttributeParser for LoopMatchParser { const PATH: &[Symbol] = &[sym::loop_match]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - - fn create(span: Span) -> AttributeKind { - AttributeKind::LoopMatch(span) - } + const CREATE: fn(Span) -> AttributeKind = AttributeKind::LoopMatch; } pub(crate) struct ConstContinueParser; impl NoArgsAttributeParser for ConstContinueParser { const PATH: &[Symbol] = &[sym::const_continue]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - - fn create(span: Span) -> AttributeKind { - AttributeKind::ConstContinue(span) - } + const CREATE: fn(Span) -> AttributeKind = AttributeKind::ConstContinue; } diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index abddc75ab8bf0..1755d2d74afeb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -238,7 +238,7 @@ pub(crate) trait NoArgsAttributeParser: 'static { const ON_DUPLICATE: OnDuplicate; /// Create the [`AttributeKind`] given attribute's [`Span`]. - fn create(span: Span) -> AttributeKind; + const CREATE: fn(Span) -> AttributeKind; } pub(crate) struct WithoutArgs, S: Stage>(PhantomData<(S, T)>); @@ -259,7 +259,7 @@ impl, S: Stage> SingleAttributeParser for Without if let Err(span) = args.no_args() { cx.expected_no_args(span); } - Some(T::create(cx.attr_span)) + Some(T::CREATE(cx.attr_span)) } } diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs index c5e2bf6862fbd..74fdff5d2e183 100644 --- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs +++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs @@ -8,8 +8,5 @@ pub(crate) struct MayDangleParser; impl NoArgsAttributeParser for MayDangleParser { const PATH: &[Symbol] = &[sym::may_dangle]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn; - - fn create(span: Span) -> AttributeKind { - AttributeKind::MayDangle(span) - } + const CREATE: fn(span: Span) -> AttributeKind = AttributeKind::MayDangle; } diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index ffe60f59cc475..6bccd0042a80d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -136,10 +136,7 @@ pub(crate) struct ConstStabilityIndirectParser; impl NoArgsAttributeParser for ConstStabilityIndirectParser { const PATH: &[Symbol] = &[sym::rustc_const_stable_indirect]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Ignore; - - fn create(_: Span) -> AttributeKind { - AttributeKind::ConstStabilityIndirect - } + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ConstStabilityIndirect; } #[derive(Default)] From b4d35fde7e2b7a08eafc537344ed861132e8bf50 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Wed, 2 Jul 2025 00:57:59 +0200 Subject: [PATCH 14/16] Add `track_caller` attributes to trace origin of Clippy lints This allows the use of `-Z track-diagnostics` to see the origin of Clippy lints emission, as is already the case for lints coming from rustc. --- compiler/rustc_lint/src/context.rs | 2 ++ .../clippy/clippy_utils/src/diagnostics.rs | 7 +++++ .../tests/ui/track-diagnostics-clippy.rs | 22 ++++++++++++++ .../tests/ui/track-diagnostics-clippy.stderr | 29 +++++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 src/tools/clippy/tests/ui/track-diagnostics-clippy.rs create mode 100644 src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 95663204ec368..b694d3dd49b79 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -504,6 +504,7 @@ pub trait LintContext { /// /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature #[rustc_lint_diagnostics] + #[track_caller] fn opt_span_lint>( &self, lint: &'static Lint, @@ -542,6 +543,7 @@ pub trait LintContext { /// /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature #[rustc_lint_diagnostics] + #[track_caller] fn span_lint>( &self, lint: &'static Lint, diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index dc240dd067b12..8453165818b31 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -98,6 +98,7 @@ fn validate_diag(diag: &Diag<'_, impl EmissionGuarantee>) { /// 17 | std::mem::forget(seven); /// | ^^^^^^^^^^^^^^^^^^^^^^^ /// ``` +#[track_caller] pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into, msg: impl Into) { #[expect(clippy::disallowed_methods)] cx.span_lint(lint, sp, |diag| { @@ -143,6 +144,7 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( cx: &T, lint: &'static Lint, @@ -203,6 +205,7 @@ pub fn span_lint_and_help( /// 10 | forget(&SomeStruct); /// | ^^^^^^^^^^^ /// ``` +#[track_caller] pub fn span_lint_and_note( cx: &T, lint: &'static Lint, @@ -244,6 +247,7 @@ pub fn span_lint_and_note( /// If you're unsure which function you should use, you can test if the `#[expect]` attribute works /// where you would expect it to. /// If it doesn't, you likely need to use [`span_lint_hir_and_then`] instead. +#[track_caller] pub fn span_lint_and_then(cx: &C, lint: &'static Lint, sp: S, msg: M, f: F) where C: LintContext, @@ -286,6 +290,7 @@ where /// Instead, use this function and also pass the `HirId` of ``, which will let /// the compiler check lint level attributes at the place of the expression and /// the `#[allow]` will work. +#[track_caller] pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: impl Into) { #[expect(clippy::disallowed_methods)] cx.tcx.node_span_lint(lint, hir_id, sp, |diag| { @@ -321,6 +326,7 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s /// Instead, use this function and also pass the `HirId` of ``, which will let /// the compiler check lint level attributes at the place of the expression and /// the `#[allow]` will work. +#[track_caller] pub fn span_lint_hir_and_then( cx: &LateContext<'_>, lint: &'static Lint, @@ -374,6 +380,7 @@ pub fn span_lint_hir_and_then( /// = note: `-D fold-any` implied by `-D warnings` /// ``` #[cfg_attr(not(debug_assertions), expect(clippy::collapsible_span_lint_calls))] +#[track_caller] pub fn span_lint_and_sugg( cx: &T, lint: &'static Lint, diff --git a/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs b/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs new file mode 100644 index 0000000000000..2e67fb65efcd9 --- /dev/null +++ b/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs @@ -0,0 +1,22 @@ +//@compile-flags: -Z track-diagnostics +//@no-rustfix + +// Normalize the emitted location so this doesn't need +// updating everytime someone adds or removes a line. +//@normalize-stderr-test: ".rs:\d+:\d+" -> ".rs:LL:CC" + +#![warn(clippy::let_and_return, clippy::unnecessary_cast)] + +fn main() { + // Check the provenance of a lint sent through `LintContext::span_lint()` + let a = 3u32; + let b = a as u32; + //~^ unnecessary_cast + + // Check the provenance of a lint sent through `TyCtxt::node_span_lint()` + let c = { + let d = 42; + d + //~^ let_and_return + }; +} diff --git a/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr b/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr new file mode 100644 index 0000000000000..f3aca6854174e --- /dev/null +++ b/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr @@ -0,0 +1,29 @@ +error: casting to the same type is unnecessary (`u32` -> `u32`) + --> tests/ui/track-diagnostics-clippy.rs:LL:CC + | +LL | let b = a as u32; + | ^^^^^^^^ help: try: `a` +-Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs:LL:CC + | + = note: `-D clippy::unnecessary-cast` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` + +error: returning the result of a `let` binding from a block + --> tests/ui/track-diagnostics-clippy.rs:LL:CC + | +LL | let d = 42; + | ----------- unnecessary `let` binding +LL | d + | ^ +-Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/returns.rs:LL:CC + | + = note: `-D clippy::let-and-return` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]` +help: return the expression directly + | +LL ~ +LL ~ 42 + | + +error: aborting due to 2 previous errors + From 2ab641d75e344b1243f56ba98431f2eea1798c8f Mon Sep 17 00:00:00 2001 From: Pavel Grigorenko Date: Fri, 27 Jun 2025 01:44:28 +0300 Subject: [PATCH 15/16] bootstrap: `validate rust.codegen-backends` & `targer..codegen-backends` --- src/bootstrap/src/core/config/toml/rust.rs | 43 ++++++++++++-------- src/bootstrap/src/core/config/toml/target.rs | 22 ++-------- 2 files changed, 29 insertions(+), 36 deletions(-) diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index 642f2f2271d8f..ac5eaea3bcb6d 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -393,6 +393,27 @@ pub fn check_incompatible_options_for_ci_rustc( Ok(()) } +pub(crate) const VALID_CODEGEN_BACKENDS: &[&str] = &["llvm", "cranelift", "gcc"]; + +pub(crate) fn validate_codegen_backends(backends: Vec, section: &str) -> Vec { + for backend in &backends { + if let Some(stripped) = backend.strip_prefix(CODEGEN_BACKEND_PREFIX) { + panic!( + "Invalid value '{backend}' for '{section}.codegen-backends'. \ + Codegen backends are defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \ + Please, use '{stripped}' instead." + ) + } + if !VALID_CODEGEN_BACKENDS.contains(&backend.as_str()) { + println!( + "HELP: '{backend}' for '{section}.codegen-backends' might fail. \ + List of known good values: {VALID_CODEGEN_BACKENDS:?}" + ); + } + } + backends +} + impl Config { pub fn apply_rust_config( &mut self, @@ -571,24 +592,10 @@ impl Config { set(&mut self.ehcont_guard, ehcont_guard); self.llvm_libunwind_default = llvm_libunwind.map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")); - - if let Some(ref backends) = codegen_backends { - let available_backends = ["llvm", "cranelift", "gcc"]; - - self.rust_codegen_backends = backends.iter().map(|s| { - if let Some(backend) = s.strip_prefix(CODEGEN_BACKEND_PREFIX) { - if available_backends.contains(&backend) { - panic!("Invalid value '{s}' for 'rust.codegen-backends'. Instead, please use '{backend}'."); - } else { - println!("HELP: '{s}' for 'rust.codegen-backends' might fail. \ - Codegen backends are mostly defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \ - In this case, it would be referred to as '{backend}'."); - } - } - - s.clone() - }).collect(); - } + set( + &mut self.rust_codegen_backends, + codegen_backends.map(|backends| validate_codegen_backends(backends, "rust")), + ); self.rust_codegen_units = codegen_units.map(threads_from_config); self.rust_codegen_units_std = codegen_units_std.map(threads_from_config); diff --git a/src/bootstrap/src/core/config/toml/target.rs b/src/bootstrap/src/core/config/toml/target.rs index b9f6780ca3fe1..337276948b324 100644 --- a/src/bootstrap/src/core/config/toml/target.rs +++ b/src/bootstrap/src/core/config/toml/target.rs @@ -16,7 +16,7 @@ use std::collections::HashMap; use serde::{Deserialize, Deserializer}; -use crate::core::build_steps::compile::CODEGEN_BACKEND_PREFIX; +use crate::core::config::toml::rust::validate_codegen_backends; use crate::core::config::{LlvmLibunwind, Merge, ReplaceOpt, SplitDebuginfo, StringOrBool}; use crate::{Config, HashSet, PathBuf, TargetSelection, define_config, exit}; @@ -142,23 +142,9 @@ impl Config { target.rpath = cfg.rpath; target.optimized_compiler_builtins = cfg.optimized_compiler_builtins; target.jemalloc = cfg.jemalloc; - - if let Some(ref backends) = cfg.codegen_backends { - let available_backends = ["llvm", "cranelift", "gcc"]; - - target.codegen_backends = Some(backends.iter().map(|s| { - if let Some(backend) = s.strip_prefix(CODEGEN_BACKEND_PREFIX) { - if available_backends.contains(&backend) { - panic!("Invalid value '{s}' for 'target.{triple}.codegen-backends'. Instead, please use '{backend}'."); - } else { - println!("HELP: '{s}' for 'target.{triple}.codegen-backends' might fail. \ - Codegen backends are mostly defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \ - In this case, it would be referred to as '{backend}'."); - } - } - - s.clone() - }).collect()); + if let Some(backends) = cfg.codegen_backends { + target.codegen_backends = + Some(validate_codegen_backends(backends, &format!("target.{triple}"))) } target.split_debuginfo = cfg.split_debuginfo.as_ref().map(|v| { From 845d9ff96327bc3c9a8ca07d4f9f275ce09384df Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 6 Mar 2025 10:51:59 +0000 Subject: [PATCH 16/16] Remove some unsized tuple impls now that we don't support unsizing tuples anymore --- library/core/src/fmt/mod.rs | 7 +------ library/core/src/hash/mod.rs | 7 +------ library/core/src/ops/range.rs | 2 +- library/core/src/tuple.rs | 18 ++---------------- 4 files changed, 5 insertions(+), 29 deletions(-) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 2be8d37bbee67..c593737af8ac6 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -2867,7 +2867,7 @@ macro_rules! tuple { maybe_tuple_doc! { $($name)+ @ #[stable(feature = "rust1", since = "1.0.0")] - impl<$($name:Debug),+> Debug for ($($name,)+) where last_type!($($name,)+): ?Sized { + impl<$($name:Debug),+> Debug for ($($name,)+) { #[allow(non_snake_case, unused_assignments)] fn fmt(&self, f: &mut Formatter<'_>) -> Result { let mut builder = f.debug_tuple(""); @@ -2898,11 +2898,6 @@ macro_rules! maybe_tuple_doc { }; } -macro_rules! last_type { - ($a:ident,) => { $a }; - ($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) }; -} - tuple! { E, D, C, B, A, Z, Y, X, W, V, U, T, } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/hash/mod.rs b/library/core/src/hash/mod.rs index efda64791d403..a10c85640bbb6 100644 --- a/library/core/src/hash/mod.rs +++ b/library/core/src/hash/mod.rs @@ -886,7 +886,7 @@ mod impls { maybe_tuple_doc! { $($name)+ @ #[stable(feature = "rust1", since = "1.0.0")] - impl<$($name: Hash),+> Hash for ($($name,)+) where last_type!($($name,)+): ?Sized { + impl<$($name: Hash),+> Hash for ($($name,)+) { #[allow(non_snake_case)] #[inline] fn hash(&self, state: &mut S) { @@ -912,11 +912,6 @@ mod impls { }; } - macro_rules! last_type { - ($a:ident,) => { $a }; - ($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) }; - } - impl_hash_tuple! {} impl_hash_tuple! { T } impl_hash_tuple! { T B } diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 1935268cda891..ad3b6439a6105 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -1211,7 +1211,7 @@ pub enum OneSidedRangeBound { /// Types that implement `OneSidedRange` must return `Bound::Unbounded` /// from one of `RangeBounds::start_bound` or `RangeBounds::end_bound`. #[unstable(feature = "one_sided_range", issue = "69780")] -pub trait OneSidedRange: RangeBounds { +pub trait OneSidedRange: RangeBounds { /// An internal-only helper function for `split_off` and /// `split_off_mut` that returns the bound of the one-sided range. fn bound(self) -> (OneSidedRangeBound, T); diff --git a/library/core/src/tuple.rs b/library/core/src/tuple.rs index 6327c41f052c9..9cf08e74ff692 100644 --- a/library/core/src/tuple.rs +++ b/library/core/src/tuple.rs @@ -1,7 +1,7 @@ // See core/src/primitive_docs.rs for documentation. use crate::cmp::Ordering::{self, *}; -use crate::marker::{ConstParamTy_, PointeeSized, StructuralPartialEq, UnsizedConstParamTy}; +use crate::marker::{ConstParamTy_, StructuralPartialEq, UnsizedConstParamTy}; use crate::ops::ControlFlow::{self, Break, Continue}; use crate::random::{Random, RandomSource}; @@ -24,10 +24,7 @@ macro_rules! tuple_impls { maybe_tuple_doc! { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T: PartialEq),+> PartialEq for ($($T,)+) - where - last_type!($($T,)+): PointeeSized - { + impl<$($T: PartialEq),+> PartialEq for ($($T,)+) { #[inline] fn eq(&self, other: &($($T,)+)) -> bool { $( ${ignore($T)} self.${index()} == other.${index()} )&&+ @@ -43,8 +40,6 @@ macro_rules! tuple_impls { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] impl<$($T: Eq),+> Eq for ($($T,)+) - where - last_type!($($T,)+): PointeeSized {} } @@ -73,8 +68,6 @@ macro_rules! tuple_impls { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] impl<$($T: PartialOrd),+> PartialOrd for ($($T,)+) - where - last_type!($($T,)+): PointeeSized { #[inline] fn partial_cmp(&self, other: &($($T,)+)) -> Option { @@ -119,8 +112,6 @@ macro_rules! tuple_impls { $($T)+ @ #[stable(feature = "rust1", since = "1.0.0")] impl<$($T: Ord),+> Ord for ($($T,)+) - where - last_type!($($T,)+): PointeeSized { #[inline] fn cmp(&self, other: &($($T,)+)) -> Ordering { @@ -245,9 +236,4 @@ macro_rules! lexical_cmp { ($a:expr, $b:expr) => { ($a).cmp(&$b) }; } -macro_rules! last_type { - ($a:ident,) => { $a }; - ($a:ident, $($rest_a:ident,)+) => { last_type!($($rest_a,)+) }; -} - tuple_impls!(E D C B A Z Y X W V U T);