From c02032cd6a215f9f4da63320b96329b2477f4cbc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Nov 2024 13:44:46 -0800 Subject: [PATCH 01/19] Eliminate precedence arithmetic from rustc_ast_pretty --- .../rustc_ast_pretty/src/pprust/state/expr.rs | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index e9c49e9f682f9..c95e470218828 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -276,21 +276,22 @@ impl<'a> State<'a> { fixup: FixupContext, ) { let assoc_op = AssocOp::from_ast_binop(op.node); - let prec = assoc_op.precedence() as i8; - let fixity = assoc_op.fixity(); - - let (left_prec, right_prec) = match fixity { - Fixity::Left => (prec, prec + 1), - Fixity::Right => (prec + 1, prec), - Fixity::None => (prec + 1, prec + 1), + let binop_prec = assoc_op.precedence() as i8; + let left_prec = lhs.precedence(); + let right_prec = rhs.precedence(); + + let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() { + Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), + Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec), + Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec), }; - let left_prec = match (&lhs.kind, op.node) { + match (&lhs.kind, op.node) { // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead // of `(x as i32) < ...`. We need to convince it _not_ to do that. (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => { - parser::PREC_FORCE_PAREN + left_needs_paren = true; } // We are given `(let _ = a) OP b`. // @@ -300,26 +301,16 @@ impl<'a> State<'a> { // - Otherwise, e.g. when we have `(let a = b) < c` in AST, // parens are required since the parser would interpret `let a = b < c` as // `let a = (b < c)`. To achieve this, we force parens. - (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => { - parser::PREC_FORCE_PAREN + (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(binop_prec) => { + left_needs_paren = true; } - _ => left_prec, - }; - - self.print_expr_cond_paren( - lhs, - lhs.precedence() < left_prec, - fixup.leftmost_subexpression(), - ); + _ => {} + } + self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression()); self.space(); self.word_space(op.node.as_str()); - - self.print_expr_cond_paren( - rhs, - rhs.precedence() < right_prec, - fixup.subsequent_subexpression(), - ); + self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression()); } fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr, fixup: FixupContext) { From 34a65f203f4e6c12f45914ac1c1de406e8b56428 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Nov 2024 13:50:04 -0800 Subject: [PATCH 02/19] Eliminate precedence arithmetic from rustc_hir_pretty --- compiler/rustc_hir_pretty/src/lib.rs | 31 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 5a5b39c694f8a..c53fb71dca1a4 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1162,32 +1162,33 @@ impl<'a> State<'a> { fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { let assoc_op = AssocOp::from_ast_binop(op.node); - let prec = assoc_op.precedence() as i8; - let fixity = assoc_op.fixity(); - - let (left_prec, right_prec) = match fixity { - Fixity::Left => (prec, prec + 1), - Fixity::Right => (prec + 1, prec), - Fixity::None => (prec + 1, prec + 1), + let binop_prec = assoc_op.precedence() as i8; + let left_prec = lhs.precedence(); + let right_prec = rhs.precedence(); + + let (mut left_needs_paren, right_needs_paren) = match assoc_op.fixity() { + Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), + Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec), + Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec), }; - let left_prec = match (&lhs.kind, op.node) { + match (&lhs.kind, op.node) { // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead // of `(x as i32) < ...`. We need to convince it _not_ to do that. (&hir::ExprKind::Cast { .. }, hir::BinOpKind::Lt | hir::BinOpKind::Shl) => { - parser::PREC_FORCE_PAREN + left_needs_paren = true; } - (&hir::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => { - parser::PREC_FORCE_PAREN + (&hir::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(binop_prec) => { + left_needs_paren = true; } - _ => left_prec, - }; + _ => {} + } - self.print_expr_cond_paren(lhs, lhs.precedence() < left_prec); + self.print_expr_cond_paren(lhs, left_needs_paren); self.space(); self.word_space(op.node.as_str()); - self.print_expr_cond_paren(rhs, rhs.precedence() < right_prec) + self.print_expr_cond_paren(rhs, right_needs_paren); } fn print_expr_unary(&mut self, op: hir::UnOp, expr: &hir::Expr<'_>) { From ca8f47439efab15137f2896697c99a34e0366065 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Nov 2024 13:52:28 -0800 Subject: [PATCH 03/19] Eliminate PREC_FORCE_PAREN --- compiler/rustc_ast/src/util/parser.rs | 1 - compiler/rustc_ast_pretty/src/pprust/state/expr.rs | 8 ++++---- compiler/rustc_hir_pretty/src/lib.rs | 8 ++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index ed9265d51598a..07c72141cc968 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -235,7 +235,6 @@ pub const PREC_RANGE: i8 = -10; // The range 2..=14 is reserved for AssocOp binary operator precedences. pub const PREC_PREFIX: i8 = 50; pub const PREC_UNAMBIGUOUS: i8 = 60; -pub const PREC_FORCE_PAREN: i8 = 100; /// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`. pub fn prec_let_scrutinee_needs_par() -> usize { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index c95e470218828..679f7c4fb5935 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -212,9 +212,9 @@ impl<'a> State<'a> { } fn print_expr_call(&mut self, func: &ast::Expr, args: &[P], fixup: FixupContext) { - let prec = match func.kind { - ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, - _ => parser::PREC_UNAMBIGUOUS, + let needs_paren = match func.kind { + ast::ExprKind::Field(..) => true, + _ => func.precedence() < parser::PREC_UNAMBIGUOUS, }; // Independent of parenthesization related to precedence, we must @@ -233,7 +233,7 @@ impl<'a> State<'a> { // because the latter is valid syntax but with the incorrect meaning. // It's a match-expression followed by tuple-expression, not a function // call. - self.print_expr_cond_paren(func, func.precedence() < prec, fixup.leftmost_subexpression()); + self.print_expr_cond_paren(func, needs_paren, fixup.leftmost_subexpression()); self.print_call_post(args) } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index c53fb71dca1a4..880e527a2bbba 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1132,12 +1132,12 @@ impl<'a> State<'a> { } fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { - let prec = match func.kind { - hir::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, - _ => parser::PREC_UNAMBIGUOUS, + let needs_paren = match func.kind { + hir::ExprKind::Field(..) => true, + _ => func.precedence() < parser::PREC_UNAMBIGUOUS, }; - self.print_expr_cond_paren(func, func.precedence() < prec); + self.print_expr_cond_paren(func, needs_paren); self.print_call_post(args) } From 539c863eaf36d8f8b414cf03b422c6e959505f52 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Nov 2024 14:03:16 -0800 Subject: [PATCH 04/19] Eliminate precedence arithmetic from rustc_parse --- compiler/rustc_parse/src/parser/expr.rs | 31 ++++++++++++++----------- compiler/rustc_parse/src/parser/pat.rs | 7 ++++-- compiler/rustc_parse/src/parser/stmt.rs | 5 ++-- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 4430d2d14313b..a3b0076f5ad7d 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,7 +1,7 @@ // ignore-tidy-filelength use core::mem; -use core::ops::ControlFlow; +use core::ops::{Bound, ControlFlow}; use ast::mut_visit::{self, MutVisitor}; use ast::token::IdentIsRaw; @@ -120,7 +120,7 @@ impl<'a> Parser<'a> { r: Restrictions, attrs: AttrWrapper, ) -> PResult<'a, (P, bool)> { - self.with_res(r, |this| this.parse_expr_assoc_with(0, attrs)) + self.with_res(r, |this| this.parse_expr_assoc_with(Bound::Unbounded, attrs)) } /// Parses an associative expression with operators of at least `min_prec` precedence. @@ -128,7 +128,7 @@ impl<'a> Parser<'a> { /// followed by a subexpression (e.g. `1 + 2`). pub(super) fn parse_expr_assoc_with( &mut self, - min_prec: usize, + min_prec: Bound, attrs: AttrWrapper, ) -> PResult<'a, (P, bool)> { let lhs = if self.token.is_range_separator() { @@ -144,7 +144,7 @@ impl<'a> Parser<'a> { /// was actually parsed. pub(super) fn parse_expr_assoc_rest_with( &mut self, - min_prec: usize, + min_prec: Bound, starts_stmt: bool, mut lhs: P, ) -> PResult<'a, (P, bool)> { @@ -163,7 +163,11 @@ impl<'a> Parser<'a> { self.restrictions }; let prec = op.node.precedence(); - if prec < min_prec { + if match min_prec { + Bound::Included(min_prec) => prec < min_prec, + Bound::Excluded(min_prec) => prec <= min_prec, + Bound::Unbounded => false, + } { break; } // Check for deprecated `...` syntax @@ -276,16 +280,16 @@ impl<'a> Parser<'a> { } let fixity = op.fixity(); - let prec_adjustment = match fixity { - Fixity::Right => 0, - Fixity::Left => 1, + let min_prec = match fixity { + Fixity::Right => Bound::Included(prec), + Fixity::Left => Bound::Excluded(prec), // We currently have no non-associative operators that are not handled above by // the special cases. The code is here only for future convenience. - Fixity::None => 1, + Fixity::None => Bound::Excluded(prec), }; let (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| { let attrs = this.parse_outer_attributes()?; - this.parse_expr_assoc_with(prec + prec_adjustment, attrs) + this.parse_expr_assoc_with(min_prec, attrs) })?; let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); @@ -460,7 +464,7 @@ impl<'a> Parser<'a> { let maybe_lt = self.token.clone(); let attrs = self.parse_outer_attributes()?; Some( - self.parse_expr_assoc_with(prec + 1, attrs) + self.parse_expr_assoc_with(Bound::Excluded(prec), attrs) .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))? .0, ) @@ -518,7 +522,7 @@ impl<'a> Parser<'a> { let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() { // RHS must be parsed with more associativity than the dots. let attrs = this.parse_outer_attributes()?; - this.parse_expr_assoc_with(op.unwrap().precedence() + 1, attrs) + this.parse_expr_assoc_with(Bound::Excluded(op.unwrap().precedence()), attrs) .map(|(x, _)| (lo.to(x.span), Some(x))) .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))? } else { @@ -2643,7 +2647,8 @@ impl<'a> Parser<'a> { self.expect(&token::Eq)?; } let attrs = self.parse_outer_attributes()?; - let (expr, _) = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), attrs)?; + let (expr, _) = + self.parse_expr_assoc_with(Bound::Excluded(prec_let_scrutinee_needs_par()), attrs)?; let span = lo.to(expr.span); Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered))) } diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index bb976e092bf4c..32efe9af0c978 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1,3 +1,5 @@ +use std::ops::Bound; + use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token}; @@ -435,8 +437,9 @@ impl<'a> Parser<'a> { // Parse an associative expression such as `+ expr`, `% expr`, ... // Assignments, ranges and `|` are disabled by [`Restrictions::IS_PAT`]. - let Ok((expr, _)) = - snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel()) + let Ok((expr, _)) = snapshot + .parse_expr_assoc_rest_with(Bound::Unbounded, false, expr) + .map_err(|err| err.cancel()) else { // We got a trailing method/operator, but that wasn't an expression. return None; diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 190cd9ed0610f..5fa2e01fc863c 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::mem; +use std::ops::Bound; use ast::Label; use rustc_ast as ast; @@ -207,7 +208,7 @@ impl<'a> Parser<'a> { // Perform this outside of the `collect_tokens` closure, since our // outer attributes do not apply to this part of the expression. let (expr, _) = self.with_res(Restrictions::STMT_EXPR, |this| { - this.parse_expr_assoc_rest_with(0, true, expr) + this.parse_expr_assoc_rest_with(Bound::Unbounded, true, expr) })?; Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr))) } else { @@ -240,7 +241,7 @@ impl<'a> Parser<'a> { let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac)); let e = self.maybe_recover_from_bad_qpath(e)?; let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?; - let (e, _) = self.parse_expr_assoc_rest_with(0, false, e)?; + let (e, _) = self.parse_expr_assoc_rest_with(Bound::Unbounded, false, e)?; StmtKind::Expr(e) }; Ok(self.mk_stmt(lo.to(hi), kind)) From 7ced18f329470e5dbdbee54d273193d19e3b43f4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Nov 2024 12:47:18 -0800 Subject: [PATCH 05/19] Eliminate magic numbers from expression precedence --- compiler/rustc_ast/src/ast.rs | 22 +++--- compiler/rustc_ast/src/util/parser.rs | 74 +++++++++++++------ .../rustc_ast_pretty/src/pprust/state/expr.rs | 49 ++++++------ compiler/rustc_errors/src/diagnostic_impls.rs | 7 ++ compiler/rustc_hir/src/hir.rs | 18 ++--- compiler/rustc_hir_pretty/src/lib.rs | 37 +++++----- compiler/rustc_hir_typeck/src/callee.rs | 4 +- compiler/rustc_hir_typeck/src/cast.rs | 3 +- .../src/fn_ctxt/suggestions.rs | 10 +-- compiler/rustc_parse/src/errors.rs | 3 +- compiler/rustc_parse/src/parser/expr.rs | 8 +- compiler/rustc_parse/src/parser/pat.rs | 16 ++-- .../clippy/clippy_lints/src/dereference.rs | 28 +++---- .../src/loops/single_element_loop.rs | 4 +- .../clippy_lints/src/matches/manual_utils.rs | 4 +- .../clippy/clippy_lints/src/neg_multiply.rs | 4 +- .../clippy_lints/src/redundant_slicing.rs | 4 +- .../transmutes_expressible_as_ptr_casts.rs | 5 +- 18 files changed, 162 insertions(+), 138 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 56b20e0ad8938..14205f66491c5 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -39,9 +39,7 @@ pub use crate::format::*; use crate::ptr::P; use crate::token::{self, CommentKind, Delimiter}; use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream}; -use crate::util::parser::{ - AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_RANGE, PREC_UNAMBIGUOUS, -}; +use crate::util::parser::{AssocOp, ExprPrecedence}; /// A "Label" is an identifier of some point in sources, /// e.g. in the following code: @@ -1317,29 +1315,29 @@ impl Expr { Some(P(Ty { kind, id: self.id, span: self.span, tokens: None })) } - pub fn precedence(&self) -> i8 { + pub fn precedence(&self) -> ExprPrecedence { match self.kind { - ExprKind::Closure(..) => PREC_CLOSURE, + ExprKind::Closure(..) => ExprPrecedence::Closure, ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) | ExprKind::Yield(..) | ExprKind::Yeet(..) - | ExprKind::Become(..) => PREC_JUMP, + | ExprKind::Become(..) => ExprPrecedence::Jump, // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence // ensures that `pprust` will add parentheses in the right places to get the desired // parse. - ExprKind::Range(..) => PREC_RANGE, + ExprKind::Range(..) => ExprPrecedence::Range, // Binop-like expr kinds, handled by `AssocOp`. - ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8, - ExprKind::Cast(..) => AssocOp::As.precedence() as i8, + ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(), + ExprKind::Cast(..) => ExprPrecedence::Cast, ExprKind::Assign(..) | - ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8, + ExprKind::AssignOp(..) => ExprPrecedence::Assign, // Unary, prefix ExprKind::AddrOf(..) @@ -1348,7 +1346,7 @@ impl Expr { // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` // but we need to print `(let _ = a) < b` as-is with parens. | ExprKind::Let(..) - | ExprKind::Unary(..) => PREC_PREFIX, + | ExprKind::Unary(..) => ExprPrecedence::Prefix, // Never need parens ExprKind::Array(_) @@ -1381,7 +1379,7 @@ impl Expr { | ExprKind::Underscore | ExprKind::While(..) | ExprKind::Err(_) - | ExprKind::Dummy => PREC_UNAMBIGUOUS, + | ExprKind::Dummy => ExprPrecedence::Unambiguous, } } diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 07c72141cc968..e88bf27021aff 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -128,21 +128,21 @@ impl AssocOp { } /// Gets the precedence of this operator - pub fn precedence(&self) -> usize { + pub fn precedence(&self) -> ExprPrecedence { use AssocOp::*; match *self { - As => 14, - Multiply | Divide | Modulus => 13, - Add | Subtract => 12, - ShiftLeft | ShiftRight => 11, - BitAnd => 10, - BitXor => 9, - BitOr => 8, - Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7, - LAnd => 6, - LOr => 5, - DotDot | DotDotEq => 4, - Assign | AssignOp(_) => 2, + As => ExprPrecedence::Cast, + Multiply | Divide | Modulus => ExprPrecedence::Product, + Add | Subtract => ExprPrecedence::Sum, + ShiftLeft | ShiftRight => ExprPrecedence::Shift, + BitAnd => ExprPrecedence::BitAnd, + BitXor => ExprPrecedence::BitXor, + BitOr => ExprPrecedence::BitOr, + Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare, + LAnd => ExprPrecedence::LAnd, + LOr => ExprPrecedence::LOr, + DotDot | DotDotEq => ExprPrecedence::Range, + Assign | AssignOp(_) => ExprPrecedence::Assign, } } @@ -229,16 +229,44 @@ impl AssocOp { } } -pub const PREC_CLOSURE: i8 = -40; -pub const PREC_JUMP: i8 = -30; -pub const PREC_RANGE: i8 = -10; -// The range 2..=14 is reserved for AssocOp binary operator precedences. -pub const PREC_PREFIX: i8 = 50; -pub const PREC_UNAMBIGUOUS: i8 = 60; +#[derive(Clone, Copy, PartialEq, PartialOrd)] +pub enum ExprPrecedence { + Closure, + // return, break, yield + Jump, + // = += -= *= /= %= &= |= ^= <<= >>= + Assign, + // .. ..= + Range, + // || + LOr, + // && + LAnd, + // == != < > <= >= + Compare, + // | + BitOr, + // ^ + BitXor, + // & + BitAnd, + // << >> + Shift, + // + - + Sum, + // * / % + Product, + // as + Cast, + // unary - * ! & &mut + Prefix, + // paths, loops, function calls, array indexing, field expressions, method calls + Unambiguous, +} /// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`. -pub fn prec_let_scrutinee_needs_par() -> usize { - AssocOp::LAnd.precedence() +pub fn prec_let_scrutinee_needs_par() -> ExprPrecedence { + ExprPrecedence::LAnd } /// Suppose we have `let _ = e` and the `order` of `e`. @@ -246,8 +274,8 @@ pub fn prec_let_scrutinee_needs_par() -> usize { /// /// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`. /// Can we print this as `let _ = a OP b`? -pub fn needs_par_as_let_scrutinee(order: i8) -> bool { - order <= prec_let_scrutinee_needs_par() as i8 +pub fn needs_par_as_let_scrutinee(order: ExprPrecedence) -> bool { + order <= prec_let_scrutinee_needs_par() } /// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 679f7c4fb5935..c239cb249c3c2 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -5,7 +5,7 @@ use itertools::{Itertools, Position}; use rustc_ast::ptr::P; use rustc_ast::util::classify; use rustc_ast::util::literal::escape_byte_str_symbol; -use rustc_ast::util::parser::{self, AssocOp, Fixity}; +use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity}; use rustc_ast::{ self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount, FormatDebugHex, FormatSign, FormatTrait, token, @@ -214,7 +214,7 @@ impl<'a> State<'a> { fn print_expr_call(&mut self, func: &ast::Expr, args: &[P], fixup: FixupContext) { let needs_paren = match func.kind { ast::ExprKind::Field(..) => true, - _ => func.precedence() < parser::PREC_UNAMBIGUOUS, + _ => func.precedence() < ExprPrecedence::Unambiguous, }; // Independent of parenthesization related to precedence, we must @@ -256,7 +256,7 @@ impl<'a> State<'a> { // a statement containing an expression. self.print_expr_cond_paren( receiver, - receiver.precedence() < parser::PREC_UNAMBIGUOUS, + receiver.precedence() < ExprPrecedence::Unambiguous, fixup, ); @@ -276,7 +276,7 @@ impl<'a> State<'a> { fixup: FixupContext, ) { let assoc_op = AssocOp::from_ast_binop(op.node); - let binop_prec = assoc_op.precedence() as i8; + let binop_prec = assoc_op.precedence(); let left_prec = lhs.precedence(); let right_prec = rhs.precedence(); @@ -317,7 +317,7 @@ impl<'a> State<'a> { self.word(op.as_str()); self.print_expr_cond_paren( expr, - expr.precedence() < parser::PREC_PREFIX, + expr.precedence() < ExprPrecedence::Prefix, fixup.subsequent_subexpression(), ); } @@ -339,7 +339,7 @@ impl<'a> State<'a> { } self.print_expr_cond_paren( expr, - expr.precedence() < parser::PREC_PREFIX, + expr.precedence() < ExprPrecedence::Prefix, fixup.subsequent_subexpression(), ); } @@ -423,10 +423,9 @@ impl<'a> State<'a> { self.print_token_literal(lit, expr.span) } ast::ExprKind::Cast(expr, ty) => { - let prec = AssocOp::As.precedence() as i8; self.print_expr_cond_paren( expr, - expr.precedence() < prec, + expr.precedence() < ExprPrecedence::Cast, fixup.leftmost_subexpression(), ); self.space(); @@ -503,7 +502,7 @@ impl<'a> State<'a> { MatchKind::Postfix => { self.print_expr_cond_paren( expr, - expr.precedence() < parser::PREC_UNAMBIGUOUS, + expr.precedence() < ExprPrecedence::Unambiguous, fixup, ); self.word_nbsp(".match"); @@ -567,31 +566,31 @@ impl<'a> State<'a> { ast::ExprKind::Await(expr, _) => { self.print_expr_cond_paren( expr, - expr.precedence() < parser::PREC_UNAMBIGUOUS, + expr.precedence() < ExprPrecedence::Unambiguous, fixup, ); self.word(".await"); } ast::ExprKind::Assign(lhs, rhs, _) => { - let prec = AssocOp::Assign.precedence() as i8; self.print_expr_cond_paren( lhs, - lhs.precedence() <= prec, + // Ranges are allowed on the right-hand side of assignment, + // but not the left. `(a..b) = c` needs parentheses. + lhs.precedence() <= ExprPrecedence::Range, fixup.leftmost_subexpression(), ); self.space(); self.word_space("="); self.print_expr_cond_paren( rhs, - rhs.precedence() < prec, + rhs.precedence() < ExprPrecedence::Assign, fixup.subsequent_subexpression(), ); } ast::ExprKind::AssignOp(op, lhs, rhs) => { - let prec = AssocOp::Assign.precedence() as i8; self.print_expr_cond_paren( lhs, - lhs.precedence() <= prec, + lhs.precedence() <= ExprPrecedence::Range, fixup.leftmost_subexpression(), ); self.space(); @@ -599,14 +598,14 @@ impl<'a> State<'a> { self.word_space("="); self.print_expr_cond_paren( rhs, - rhs.precedence() < prec, + rhs.precedence() < ExprPrecedence::Assign, fixup.subsequent_subexpression(), ); } ast::ExprKind::Field(expr, ident) => { self.print_expr_cond_paren( expr, - expr.precedence() < parser::PREC_UNAMBIGUOUS, + expr.precedence() < ExprPrecedence::Unambiguous, fixup, ); self.word("."); @@ -615,7 +614,7 @@ impl<'a> State<'a> { ast::ExprKind::Index(expr, index, _) => { self.print_expr_cond_paren( expr, - expr.precedence() < parser::PREC_UNAMBIGUOUS, + expr.precedence() < ExprPrecedence::Unambiguous, fixup.leftmost_subexpression(), ); self.word("["); @@ -627,7 +626,7 @@ impl<'a> State<'a> { // than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`. // Here we use a fake precedence value so that any child with lower precedence than // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.) - let fake_prec = AssocOp::LOr.precedence() as i8; + let fake_prec = ExprPrecedence::LOr; if let Some(e) = start { self.print_expr_cond_paren( e, @@ -662,7 +661,7 @@ impl<'a> State<'a> { expr, // Parenthesize if required by precedence, or in the // case of `break 'inner: loop { break 'inner 1 } + 1` - expr.precedence() < parser::PREC_JUMP + expr.precedence() < ExprPrecedence::Jump || (opt_label.is_none() && classify::leading_labeled_expr(expr)), fixup.subsequent_subexpression(), ); @@ -681,7 +680,7 @@ impl<'a> State<'a> { self.word(" "); self.print_expr_cond_paren( expr, - expr.precedence() < parser::PREC_JUMP, + expr.precedence() < ExprPrecedence::Jump, fixup.subsequent_subexpression(), ); } @@ -694,7 +693,7 @@ impl<'a> State<'a> { self.word(" "); self.print_expr_cond_paren( expr, - expr.precedence() < parser::PREC_JUMP, + expr.precedence() < ExprPrecedence::Jump, fixup.subsequent_subexpression(), ); } @@ -704,7 +703,7 @@ impl<'a> State<'a> { self.word(" "); self.print_expr_cond_paren( result, - result.precedence() < parser::PREC_JUMP, + result.precedence() < ExprPrecedence::Jump, fixup.subsequent_subexpression(), ); } @@ -758,13 +757,13 @@ impl<'a> State<'a> { self.space(); self.print_expr_cond_paren( expr, - expr.precedence() < parser::PREC_JUMP, + expr.precedence() < ExprPrecedence::Jump, fixup.subsequent_subexpression(), ); } } ast::ExprKind::Try(e) => { - self.print_expr_cond_paren(e, e.precedence() < parser::PREC_UNAMBIGUOUS, fixup); + self.print_expr_cond_paren(e, e.precedence() < ExprPrecedence::Unambiguous, fixup); self.word("?") } ast::ExprKind::TryBlock(blk) => { diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 798668b8bc1f0..b451037132397 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -6,6 +6,7 @@ use std::path::{Path, PathBuf}; use std::process::ExitStatus; use rustc_abi::TargetDataLayoutErrors; +use rustc_ast::util::parser::ExprPrecedence; use rustc_ast_pretty::pprust; use rustc_macros::Subdiagnostic; use rustc_span::Span; @@ -298,6 +299,12 @@ impl IntoDiagArg for hir::def::Namespace { } } +impl IntoDiagArg for ExprPrecedence { + fn into_diag_arg(self) -> DiagArgValue { + DiagArgValue::Number(self as i32) + } +} + #[derive(Clone)] pub struct DiagSymbolList(Vec); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 12dec75e65cf0..f13289ac48ff0 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,7 +1,7 @@ use std::fmt; use rustc_abi::ExternAbi; -use rustc_ast::util::parser::{AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_UNAMBIGUOUS}; +use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::{ self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitKind, TraitObjectSyntax, UintTy, @@ -1708,22 +1708,22 @@ pub struct Expr<'hir> { } impl Expr<'_> { - pub fn precedence(&self) -> i8 { + pub fn precedence(&self) -> ExprPrecedence { match self.kind { - ExprKind::Closure { .. } => PREC_CLOSURE, + ExprKind::Closure { .. } => ExprPrecedence::Closure, ExprKind::Break(..) | ExprKind::Continue(..) | ExprKind::Ret(..) | ExprKind::Yield(..) - | ExprKind::Become(..) => PREC_JUMP, + | ExprKind::Become(..) => ExprPrecedence::Jump, // Binop-like expr kinds, handled by `AssocOp`. - ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8, - ExprKind::Cast(..) => AssocOp::As.precedence() as i8, + ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(), + ExprKind::Cast(..) => ExprPrecedence::Cast, ExprKind::Assign(..) | - ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8, + ExprKind::AssignOp(..) => ExprPrecedence::Assign, // Unary, prefix ExprKind::AddrOf(..) @@ -1732,7 +1732,7 @@ impl Expr<'_> { // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` // but we need to print `(let _ = a) < b` as-is with parens. | ExprKind::Let(..) - | ExprKind::Unary(..) => PREC_PREFIX, + | ExprKind::Unary(..) => ExprPrecedence::Prefix, // Never need parens ExprKind::Array(_) @@ -1753,7 +1753,7 @@ impl Expr<'_> { | ExprKind::Struct(..) | ExprKind::Tup(_) | ExprKind::Type(..) - | ExprKind::Err(_) => PREC_UNAMBIGUOUS, + | ExprKind::Err(_) => ExprPrecedence::Unambiguous, ExprKind::DropTemps(ref expr, ..) => expr.precedence(), } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 880e527a2bbba..9099e14b33045 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -10,7 +10,7 @@ use std::cell::Cell; use std::vec; use rustc_abi::ExternAbi; -use rustc_ast::util::parser::{self, AssocOp, Fixity}; +use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity}; use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::{self, Breaks}; use rustc_ast_pretty::pprust::{Comments, PrintState}; @@ -1134,7 +1134,7 @@ impl<'a> State<'a> { fn print_expr_call(&mut self, func: &hir::Expr<'_>, args: &[hir::Expr<'_>]) { let needs_paren = match func.kind { hir::ExprKind::Field(..) => true, - _ => func.precedence() < parser::PREC_UNAMBIGUOUS, + _ => func.precedence() < ExprPrecedence::Unambiguous, }; self.print_expr_cond_paren(func, needs_paren); @@ -1148,7 +1148,7 @@ impl<'a> State<'a> { args: &[hir::Expr<'_>], ) { let base_args = args; - self.print_expr_cond_paren(receiver, receiver.precedence() < parser::PREC_UNAMBIGUOUS); + self.print_expr_cond_paren(receiver, receiver.precedence() < ExprPrecedence::Unambiguous); self.word("."); self.print_ident(segment.ident); @@ -1162,7 +1162,7 @@ impl<'a> State<'a> { fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { let assoc_op = AssocOp::from_ast_binop(op.node); - let binop_prec = assoc_op.precedence() as i8; + let binop_prec = assoc_op.precedence(); let left_prec = lhs.precedence(); let right_prec = rhs.precedence(); @@ -1193,7 +1193,7 @@ impl<'a> State<'a> { fn print_expr_unary(&mut self, op: hir::UnOp, expr: &hir::Expr<'_>) { self.word(op.as_str()); - self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_PREFIX) + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix); } fn print_expr_addr_of( @@ -1210,7 +1210,7 @@ impl<'a> State<'a> { self.print_mutability(mutability, true); } } - self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_PREFIX) + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Prefix); } fn print_literal(&mut self, lit: &hir::Lit) { @@ -1348,8 +1348,7 @@ impl<'a> State<'a> { self.print_literal(lit); } hir::ExprKind::Cast(expr, ty) => { - let prec = AssocOp::As.precedence() as i8; - self.print_expr_cond_paren(expr, expr.precedence() < prec); + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Cast); self.space(); self.word_space("as"); self.print_type(ty); @@ -1450,27 +1449,25 @@ impl<'a> State<'a> { self.print_block(blk); } hir::ExprKind::Assign(lhs, rhs, _) => { - let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_cond_paren(lhs, lhs.precedence() <= prec); + self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign); self.space(); self.word_space("="); - self.print_expr_cond_paren(rhs, rhs.precedence() < prec); + self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign); } hir::ExprKind::AssignOp(op, lhs, rhs) => { - let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_cond_paren(lhs, lhs.precedence() <= prec); + self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign); self.space(); self.word(op.node.as_str()); self.word_space("="); - self.print_expr_cond_paren(rhs, rhs.precedence() < prec); + self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign); } hir::ExprKind::Field(expr, ident) => { - self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_UNAMBIGUOUS); + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Unambiguous); self.word("."); self.print_ident(ident); } hir::ExprKind::Index(expr, index, _) => { - self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_UNAMBIGUOUS); + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Unambiguous); self.word("["); self.print_expr(index); self.word("]"); @@ -1484,7 +1481,7 @@ impl<'a> State<'a> { } if let Some(expr) = opt_expr { self.space(); - self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_JUMP); + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); } } hir::ExprKind::Continue(destination) => { @@ -1498,13 +1495,13 @@ impl<'a> State<'a> { self.word("return"); if let Some(expr) = result { self.word(" "); - self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_JUMP); + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); } } hir::ExprKind::Become(result) => { self.word("become"); self.word(" "); - self.print_expr_cond_paren(result, result.precedence() < parser::PREC_JUMP); + self.print_expr_cond_paren(result, result.precedence() < ExprPrecedence::Jump); } hir::ExprKind::InlineAsm(asm) => { self.word("asm!"); @@ -1529,7 +1526,7 @@ impl<'a> State<'a> { } hir::ExprKind::Yield(expr, _) => { self.word_space("yield"); - self.print_expr_cond_paren(expr, expr.precedence() < parser::PREC_JUMP); + self.print_expr_cond_paren(expr, expr.precedence() < ExprPrecedence::Jump); } hir::ExprKind::Err(_) => { self.popen(); diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index a92482e6a0e39..ee44c799757c8 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -1,6 +1,6 @@ use std::iter; -use rustc_ast::util::parser::PREC_UNAMBIGUOUS; +use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey}; use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; @@ -606,7 +606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Ok(rest_snippet) = rest_snippet { - let sugg = if callee_expr.precedence() >= PREC_UNAMBIGUOUS { + let sugg = if callee_expr.precedence() >= ExprPrecedence::Unambiguous { vec![ (up_to_rcvr_span, "".to_string()), (rest_span, format!(".{}({rest_snippet}", segment.ident)), diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 0c3f21d540dcd..80b91c215980a 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -28,6 +28,7 @@ //! expression, `e as U2` is not necessarily so (in fact it will only be valid if //! `U1` coerces to `U2`). +use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; @@ -1108,7 +1109,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) { let expr_prec = self.expr.precedence(); - let needs_parens = expr_prec < rustc_ast::util::parser::PREC_UNAMBIGUOUS; + let needs_parens = expr_prec < ExprPrecedence::Unambiguous; let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize)); let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index b493a61b9f44d..61260dbd16c51 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2,7 +2,7 @@ use core::cmp::min; use core::iter; use hir::def_id::LocalDefId; -use rustc_ast::util::parser::PREC_UNAMBIGUOUS; +use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::packed::Pu128; use rustc_errors::{Applicability, Diag, MultiSpan}; use rustc_hir as hir; @@ -398,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so we remove the user's `clone` call. { vec![(receiver_method.ident.span, conversion_method.name.to_string())] - } else if expr.precedence() < PREC_UNAMBIGUOUS { + } else if expr.precedence() < ExprPrecedence::Unambiguous { vec![ (expr.span.shrink_to_lo(), "(".to_string()), (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), @@ -1376,7 +1376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let span = expr.span.find_oldest_ancestor_in_same_ctxt(); - let mut sugg = if expr.precedence() >= PREC_UNAMBIGUOUS { + let mut sugg = if expr.precedence() >= ExprPrecedence::Unambiguous { vec![(span.shrink_to_hi(), ".into()".to_owned())] } else { vec![ @@ -3000,7 +3000,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`", ); - let close_paren = if expr.precedence() < PREC_UNAMBIGUOUS { + let close_paren = if expr.precedence() < ExprPrecedence::Unambiguous { sugg.push((expr.span.shrink_to_lo(), "(".to_string())); ")" } else { @@ -3025,7 +3025,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let len = src.trim_end_matches(&checked_ty.to_string()).len(); expr.span.with_lo(expr.span.lo() + BytePos(len as u32)) }, - if expr.precedence() < PREC_UNAMBIGUOUS { + if expr.precedence() < ExprPrecedence::Unambiguous { // Readd `)` format!("{expected_ty})") } else { diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 91e8ba2e1f996..2579e4c1f2596 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3,6 +3,7 @@ use std::borrow::Cow; use rustc_ast::token::Token; +use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{Path, Visibility}; use rustc_errors::codes::*; use rustc_errors::{ @@ -2686,7 +2687,7 @@ pub(crate) struct UnexpectedExpressionInPattern { /// Was a `RangePatternBound` expected? pub is_bound: bool, /// The unexpected expr's precedence (used in match arm guard suggestions). - pub expr_precedence: i8, + pub expr_precedence: ExprPrecedence, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index a3b0076f5ad7d..8d16d44b0a26a 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -10,7 +10,7 @@ use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::util::case::Case; use rustc_ast::util::classify; -use rustc_ast::util::parser::{AssocOp, Fixity, prec_let_scrutinee_needs_par}; +use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par}; use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{ self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy, @@ -128,7 +128,7 @@ impl<'a> Parser<'a> { /// followed by a subexpression (e.g. `1 + 2`). pub(super) fn parse_expr_assoc_with( &mut self, - min_prec: Bound, + min_prec: Bound, attrs: AttrWrapper, ) -> PResult<'a, (P, bool)> { let lhs = if self.token.is_range_separator() { @@ -144,7 +144,7 @@ impl<'a> Parser<'a> { /// was actually parsed. pub(super) fn parse_expr_assoc_rest_with( &mut self, - min_prec: Bound, + min_prec: Bound, starts_stmt: bool, mut lhs: P, ) -> PResult<'a, (P, bool)> { @@ -455,7 +455,7 @@ impl<'a> Parser<'a> { /// The other two variants are handled in `parse_prefix_range_expr` below. fn parse_expr_range( &mut self, - prec: usize, + prec: ExprPrecedence, lhs: P, op: AssocOp, cur_op_span: Span, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 32efe9af0c978..e08b925f00893 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -3,12 +3,11 @@ use std::ops::Bound; use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token}; -use rustc_ast::util::parser::AssocOp; +use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{ - self as ast, Arm, AttrVec, BinOpKind, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, - Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, - StmtKind, + self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability, + Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind, }; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey}; @@ -548,10 +547,7 @@ impl<'a> Parser<'a> { // HACK: a neater way would be preferable. let expr = match &err.args["expr_precedence"] { DiagArgValue::Number(expr_precedence) => { - if *expr_precedence - <= AssocOp::from_ast_binop(BinOpKind::Eq).precedence() - as i32 - { + if *expr_precedence <= ExprPrecedence::Compare as i32 { format!("({expr})") } else { format!("{expr}") @@ -573,9 +569,7 @@ impl<'a> Parser<'a> { } Some(guard) => { // Are parentheses required around the old guard? - let wrap_guard = guard.precedence() - <= AssocOp::from_ast_binop(BinOpKind::And).precedence() - as i8; + let wrap_guard = guard.precedence() <= ExprPrecedence::LAnd; err.subdiagnostic( UnexpectedExpressionInPatternSugg::UpdateGuard { diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index b1d192adff9a7..c449a1a875b9c 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -7,7 +7,7 @@ use clippy_utils::{ peel_middle_ty_refs, }; use core::mem; -use rustc_ast::util::parser::{PREC_PREFIX, PREC_UNAMBIGUOUS}; +use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -963,7 +963,7 @@ fn report<'tcx>( // expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's // `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary. /* - expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < PREC_PREFIX { + expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < ExprPrecedence::Prefix { Cow::Owned(format!("({expr_str})")) } else { expr_str @@ -999,13 +999,13 @@ fn report<'tcx>( data.first_expr.span, state.msg, |diag| { - let (precedence, calls_field) = match cx.tcx.parent_hir_node(data.first_expr.hir_id) { + let needs_paren = match cx.tcx.parent_hir_node(data.first_expr.hir_id) { Node::Expr(e) => match e.kind { - ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false), - ExprKind::Call(..) => (PREC_UNAMBIGUOUS, matches!(expr.kind, ExprKind::Field(..))), - _ => (e.precedence(), false), + ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => false, + ExprKind::Call(..) => expr.precedence() < ExprPrecedence::Unambiguous || matches!(expr.kind, ExprKind::Field(..)), + _ => expr.precedence() < e.precedence(), }, - _ => (0, false), + _ => false, }; let is_in_tuple = matches!( get_parent_expr(cx, data.first_expr), @@ -1016,7 +1016,7 @@ fn report<'tcx>( ); let sugg = if !snip_is_macro - && (calls_field || expr.precedence() < precedence) + && needs_paren && !has_enclosing_paren(&snip) && !is_in_tuple { @@ -1049,16 +1049,16 @@ fn report<'tcx>( } } - let (prefix, precedence) = match mutability { + let (prefix, needs_paren) = match mutability { Some(mutability) if !ty.is_ref() => { let prefix = match mutability { Mutability::Not => "&", Mutability::Mut => "&mut ", }; - (prefix, PREC_PREFIX) + (prefix, expr.precedence() < ExprPrecedence::Prefix) }, - None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", 0), - _ => ("", 0), + None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", false), + _ => ("", false), }; span_lint_hir_and_then( cx, @@ -1070,7 +1070,7 @@ fn report<'tcx>( let mut app = Applicability::MachineApplicable; let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); - let sugg = if !snip_is_macro && expr.precedence() < precedence && !has_enclosing_paren(&snip) { + let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) { format!("{prefix}({snip})") } else { format!("{prefix}{snip}") @@ -1157,7 +1157,7 @@ impl<'tcx> Dereferencing<'tcx> { }, Some(parent) if !parent.span.from_expansion() => { // Double reference might be needed at this point. - if parent.precedence() == PREC_UNAMBIGUOUS { + if parent.precedence() == ExprPrecedence::Unambiguous { // Parentheses would be needed here, don't lint. *outer_pat = None; } else { diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs index 35dc8e9aa4e24..12719c4f94bfd 100644 --- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet, snippet_with_applicability}; use clippy_utils::visitors::contains_break_or_continue; use rustc_ast::Mutability; -use rustc_ast::util::parser::PREC_PREFIX; +use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind, is_range_literal}; use rustc_lint::LateContext; @@ -84,7 +84,7 @@ pub(super) fn check<'tcx>( if !prefix.is_empty() && ( // Precedence of internal expression is less than or equal to precedence of `&expr`. - arg_expression.precedence() <= PREC_PREFIX || is_range_literal(arg_expression) + arg_expression.precedence() <= ExprPrecedence::Prefix || is_range_literal(arg_expression) ) { arg_snip = format!("({arg_snip})").into(); diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index 9c6df4d8ac0d9..bac5cf88cfbf8 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -7,7 +7,7 @@ use clippy_utils::{ CaptureKind, can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, }; -use rustc_ast::util::parser::PREC_UNAMBIGUOUS; +use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::def::Res; @@ -117,7 +117,7 @@ where // it's being passed by value. let scrutinee = peel_hir_expr_refs(scrutinee).0; let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < PREC_UNAMBIGUOUS { + let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < ExprPrecedence::Unambiguous { format!("({scrutinee_str})") } else { scrutinee_str.into() diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs index a0ba2aaf55236..429afff9b6642 100644 --- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs +++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs @@ -2,7 +2,7 @@ use clippy_utils::consts::{self, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::has_enclosing_paren; -use rustc_ast::util::parser::PREC_PREFIX; +use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -58,7 +58,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { { let mut applicability = Applicability::MachineApplicable; let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); - let suggestion = if !from_macro && exp.precedence() < PREC_PREFIX && !has_enclosing_paren(&snip) { + let suggestion = if !from_macro && exp.precedence() < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) { format!("-({snip})") } else { format!("-{snip}") diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index 79baa914b0319..8e3472b1b5a1a 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{get_parent_expr, peel_middle_ty_refs}; -use rustc_ast::util::parser::PREC_PREFIX; +use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability}; use rustc_lint::{LateContext, LateLintPass, Lint}; @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr)); let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed)); let parent_expr = get_parent_expr(cx, expr); - let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence() > PREC_PREFIX); + let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence() > ExprPrecedence::Prefix); if expr_ty == indexed_ty { if expr_ref_count > indexed_ref_count { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index 8d71036084d3d..0d5cf45a5e653 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -1,7 +1,7 @@ use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use rustc_ast::util::parser::AssocOp; +use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{Expr, Node}; use rustc_hir_typeck::cast::check_cast; @@ -44,8 +44,7 @@ pub(super) fn check<'tcx>( }; if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id) - && parent.precedence() - > i8::try_from(AssocOp::As.precedence()).expect("AssocOp always returns a precedence < 128") + && parent.precedence() > ExprPrecedence::Cast { sugg = format!("({sugg})"); } From 51ea7c12e70244dc89893888f3bfd6cead59d4c5 Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Sun, 1 Dec 2024 21:37:18 +0000 Subject: [PATCH 06/19] rustdoc-json: Add tests for `static`s --- tests/rustdoc-json/statics/statics.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/rustdoc-json/statics/statics.rs diff --git a/tests/rustdoc-json/statics/statics.rs b/tests/rustdoc-json/statics/statics.rs new file mode 100644 index 0000000000000..e3ed936992563 --- /dev/null +++ b/tests/rustdoc-json/statics/statics.rs @@ -0,0 +1,10 @@ +//@ is '$.index[*][?(@.name=="A")].inner.static.type.primitive' '"i32"' +//@ is '$.index[*][?(@.name=="A")].inner.static.is_mutable' false +//@ is '$.index[*][?(@.name=="A")].inner.static.expr' '"5"' +pub static A: i32 = 5; + +//@ is '$.index[*][?(@.name=="B")].inner.static.type.primitive' '"u32"' +//@ is '$.index[*][?(@.name=="B")].inner.static.is_mutable' true +// Expr value isn't gaurenteed, it'd be fine to change it. +//@ is '$.index[*][?(@.name=="B")].inner.static.expr' '"_"' +pub static mut B: u32 = 2 + 3; From f33dba028704d108497b8c06943b9bbc3d14c42b Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Sun, 1 Dec 2024 21:39:58 +0000 Subject: [PATCH 07/19] rustdoc-json: Include safety of `static`s --- src/librustdoc/json/conversions.rs | 29 +++++++++++--------- src/rustdoc-json-types/lib.rs | 18 ++++++++++++- tests/rustdoc-json/statics/extern.rs | 39 +++++++++++++++++++++++++++ tests/rustdoc-json/statics/statics.rs | 2 ++ 4 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 tests/rustdoc-json/statics/extern.rs diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 1c8303d4c2087..bb967b7f163ed 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -321,8 +321,8 @@ fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum { MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), renderer)), TyMethodItem(m) => ItemEnum::Function(from_function(m, false, header.unwrap(), renderer)), ImplItem(i) => ItemEnum::Impl((*i).into_json(renderer)), - StaticItem(s) => ItemEnum::Static(s.into_json(renderer)), - ForeignStaticItem(s, _) => ItemEnum::Static(s.into_json(renderer)), + StaticItem(s) => ItemEnum::Static(convert_static(s, rustc_hir::Safety::Safe, renderer)), + ForeignStaticItem(s, safety) => ItemEnum::Static(convert_static(s, safety, renderer)), ForeignTypeItem => ItemEnum::ExternType, TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_json(renderer)), // FIXME(generic_const_items): Add support for generic free consts @@ -831,17 +831,20 @@ impl FromClean> for TypeAlias { } } -impl FromClean for Static { - fn from_clean(stat: clean::Static, renderer: &JsonRenderer<'_>) -> Self { - let tcx = renderer.tcx; - Static { - type_: (*stat.type_).into_json(renderer), - is_mutable: stat.mutability == ast::Mutability::Mut, - expr: stat - .expr - .map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e))) - .unwrap_or_default(), - } +fn convert_static( + stat: clean::Static, + safety: rustc_hir::Safety, + renderer: &JsonRenderer<'_>, +) -> Static { + let tcx = renderer.tcx; + Static { + type_: (*stat.type_).into_json(renderer), + is_mutable: stat.mutability == ast::Mutability::Mut, + is_unsafe: safety == rustc_hir::Safety::Unsafe, + expr: stat + .expr + .map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e))) + .unwrap_or_default(), } } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index f553a78d766e5..84b33e3d86068 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -30,7 +30,7 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc /// This integer is incremented with every breaking change to the API, /// and is returned along with the JSON blob as [`Crate::format_version`]. /// Consuming code should assert that this value matches the format version(s) that it supports. -pub const FORMAT_VERSION: u32 = 36; +pub const FORMAT_VERSION: u32 = 37; /// The root of the emitted JSON blob. /// @@ -1238,6 +1238,22 @@ pub struct Static { /// /// It's not guaranteed that it'll match the actual source code for the initial value. pub expr: String, + + /// Is the static `unsafe`? + /// + /// This is only true if it's in an `extern` block, and not explicity marked + /// as `safe`. + /// + /// ```rust + /// unsafe extern { + /// static A: i32; // unsafe + /// safe static B: i32; // safe + /// } + /// + /// static C: i32 = 0; // safe + /// static mut D: i32 = 0; // safe + /// ``` + pub is_unsafe: bool, } /// A primitive type declaration. Declarations of this kind can only come from the core library. diff --git a/tests/rustdoc-json/statics/extern.rs b/tests/rustdoc-json/statics/extern.rs new file mode 100644 index 0000000000000..d38fdf1cd1cdd --- /dev/null +++ b/tests/rustdoc-json/statics/extern.rs @@ -0,0 +1,39 @@ +// ignore-tidy-linelength +//@ edition: 2021 + +extern "C" { + //@ is '$.index[*][?(@.name=="A")].inner.static.is_unsafe' true + //@ is '$.index[*][?(@.name=="A")].inner.static.is_mutable' false + pub static A: i32; + //@ is '$.index[*][?(@.name=="B")].inner.static.is_unsafe' true + //@ is '$.index[*][?(@.name=="B")].inner.static.is_mutable' true + pub static mut B: i32; + + // items in unadorned `extern` blocks cannot have safety qualifiers +} + +unsafe extern "C" { + //@ is '$.index[*][?(@.name=="C")].inner.static.is_unsafe' true + //@ is '$.index[*][?(@.name=="C")].inner.static.is_mutable' false + pub static C: i32; + //@ is '$.index[*][?(@.name=="D")].inner.static.is_unsafe' true + //@ is '$.index[*][?(@.name=="D")].inner.static.is_mutable' true + pub static mut D: i32; + + //@ is '$.index[*][?(@.name=="E")].inner.static.is_unsafe' false + //@ is '$.index[*][?(@.name=="E")].inner.static.is_mutable' false + pub safe static E: i32; + //@ is '$.index[*][?(@.name=="F")].inner.static.is_unsafe' false + //@ is '$.index[*][?(@.name=="F")].inner.static.is_mutable' true + pub safe static mut F: i32; + + //@ is '$.index[*][?(@.name=="G")].inner.static.is_unsafe' true + //@ is '$.index[*][?(@.name=="G")].inner.static.is_mutable' false + pub unsafe static G: i32; + //@ is '$.index[*][?(@.name=="H")].inner.static.is_unsafe' true + //@ is '$.index[*][?(@.name=="H")].inner.static.is_mutable' true + pub unsafe static mut H: i32; +} + +//@ ismany '$.index[*][?(@.inner.static)].inner.static.expr' '""' '""' '""' '""' '""' '""' '""' '""' +//@ ismany '$.index[*][?(@.inner.static)].inner.static.type.primitive' '"i32"' '"i32"' '"i32"' '"i32"' '"i32"' '"i32"' '"i32"' '"i32"' diff --git a/tests/rustdoc-json/statics/statics.rs b/tests/rustdoc-json/statics/statics.rs index e3ed936992563..a8af23cc87dcc 100644 --- a/tests/rustdoc-json/statics/statics.rs +++ b/tests/rustdoc-json/statics/statics.rs @@ -1,10 +1,12 @@ //@ is '$.index[*][?(@.name=="A")].inner.static.type.primitive' '"i32"' //@ is '$.index[*][?(@.name=="A")].inner.static.is_mutable' false //@ is '$.index[*][?(@.name=="A")].inner.static.expr' '"5"' +//@ is '$.index[*][?(@.name=="A")].inner.static.is_unsafe' false pub static A: i32 = 5; //@ is '$.index[*][?(@.name=="B")].inner.static.type.primitive' '"u32"' //@ is '$.index[*][?(@.name=="B")].inner.static.is_mutable' true // Expr value isn't gaurenteed, it'd be fine to change it. //@ is '$.index[*][?(@.name=="B")].inner.static.expr' '"_"' +//@ is '$.index[*][?(@.name=="B")].inner.static.is_unsafe' false pub static mut B: u32 = 2 + 3; From b5d73fc16739abd2982af2e49de1e143bf7248e3 Mon Sep 17 00:00:00 2001 From: Alona Enraght-Moony Date: Sun, 1 Dec 2024 23:13:57 +0000 Subject: [PATCH 08/19] rustdoc-json: Add test for `impl Trait for dyn Trait` --- tests/rustdoc-json/impls/trait-for-dyn-trait.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/rustdoc-json/impls/trait-for-dyn-trait.rs diff --git a/tests/rustdoc-json/impls/trait-for-dyn-trait.rs b/tests/rustdoc-json/impls/trait-for-dyn-trait.rs new file mode 100644 index 0000000000000..0fbb4df002847 --- /dev/null +++ b/tests/rustdoc-json/impls/trait-for-dyn-trait.rs @@ -0,0 +1,15 @@ +//@ set t1 = '$.index[*][?(@.name=="T1")].id' +pub trait T1 {} + +//@ set t2 = '$.index[*][?(@.name=="T2")].id' +pub trait T2 {} + +/// Fun impl +impl T1 for dyn T2 {} + +//@ set impl = '$.index[*][?(@.docs=="Fun impl")].id' +//@ is '$.index[*][?(@.name=="T1")].inner.trait.implementations[*]' $impl +//@ is '$.index[*][?(@.name=="T2")].inner.trait.implementations' [] + +//@ is '$.index[*][?(@.docs=="Fun impl")].inner.impl.trait.id' $t1 +//@ is '$.index[*][?(@.docs=="Fun impl")].inner.impl.for.dyn_trait.traits[*].trait.id' $t2 From 2f17ea0ff53ddedfa659b1173e5bbbd57fb0231d Mon Sep 17 00:00:00 2001 From: jyn Date: Sun, 1 Dec 2024 21:47:20 -0500 Subject: [PATCH 09/19] Remove `//@ compare-output-lines-by-subset` There was only ever one test which used this flag, and it was removed in https://github.com/rust-lang/rust/pull/132244. I think this is a bad flag that should never have been added; comparing by subset makes the test failures extremely hard to debug. Any test that needs complicated output filtering like this should just use run-make instead. Note that this does not remove the underlying comparison code, because it's still used if `runner` is set. I don't quite understand what's going on there, but since we still test on other platforms and in CI that the full output is accurate, I think it will be easier to debug than a test that uses compare-by-subset unconditionally. --- src/tools/compiletest/src/directive-list.rs | 1 - src/tools/compiletest/src/header.rs | 10 ---------- src/tools/compiletest/src/runtest.rs | 11 ++++------- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs index 952533e904c5a..f3f769714641a 100644 --- a/src/tools/compiletest/src/directive-list.rs +++ b/src/tools/compiletest/src/directive-list.rs @@ -17,7 +17,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "check-run-results", "check-stdout", "check-test-line-numbers-match", - "compare-output-lines-by-subset", "compile-flags", "doc-flags", "dont-check-compiler-stderr", diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index fe4c5fdd8b51c..73e56cfac8f68 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -115,9 +115,6 @@ pub struct TestProps { pub dont_check_compiler_stdout: bool, // For UI tests, allows compiler to generate arbitrary output to stderr pub dont_check_compiler_stderr: bool, - // When checking the output of stdout or stderr check - // that the lines of expected output are a subset of the actual output. - pub compare_output_lines_by_subset: bool, // Don't force a --crate-type=dylib flag on the command line // // Set this for example if you have an auxiliary test file that contains @@ -240,7 +237,6 @@ mod directives { pub const KNOWN_BUG: &'static str = "known-bug"; pub const TEST_MIR_PASS: &'static str = "test-mir-pass"; pub const REMAP_SRC_BASE: &'static str = "remap-src-base"; - pub const COMPARE_OUTPUT_LINES_BY_SUBSET: &'static str = "compare-output-lines-by-subset"; pub const LLVM_COV_FLAGS: &'static str = "llvm-cov-flags"; pub const FILECHECK_FLAGS: &'static str = "filecheck-flags"; pub const NO_AUTO_CHECK_CFG: &'static str = "no-auto-check-cfg"; @@ -274,7 +270,6 @@ impl TestProps { check_run_results: false, dont_check_compiler_stdout: false, dont_check_compiler_stderr: false, - compare_output_lines_by_subset: false, no_prefer_dynamic: false, pretty_mode: "normal".to_string(), pretty_compare_only: false, @@ -550,11 +545,6 @@ impl TestProps { |s| s.trim().to_string(), ); config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base); - config.set_name_directive( - ln, - COMPARE_OUTPUT_LINES_BY_SUBSET, - &mut self.compare_output_lines_by_subset, - ); if let Some(flags) = config.parse_name_value_directive(ln, LLVM_COV_FLAGS) { self.llvm_cov_flags.extend(split_flags(&flags)); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 93e5980f1f5fb..7be9e2f2d5772 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2541,13 +2541,10 @@ impl<'test> TestCx<'test> { return 0; } - // If `compare-output-lines-by-subset` is not explicitly enabled then - // auto-enable it when a `runner` is in use since wrapper tools might - // provide extra output on failure, for example a WebAssembly runtime - // might print the stack trace of an `unreachable` instruction by - // default. - let compare_output_by_lines = - self.props.compare_output_lines_by_subset || self.config.runner.is_some(); + // Wrapper tools set by `runner` might provide extra output on failure, + // for example a WebAssembly runtime might print the stack trace of an + // `unreachable` instruction by default. + let compare_output_by_lines = self.config.runner.is_some(); let tmp; let (expected, actual): (&str, &str) = if compare_output_by_lines { From 267dcb28c289209462aa9695fa06e77ba532a4b1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 1 Dec 2024 15:37:39 -0800 Subject: [PATCH 10/19] Add pretty-printer parenthesis insertion test --- .../pprust-parenthesis-insertion.rs | 242 ++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 tests/ui-fulldeps/pprust-parenthesis-insertion.rs diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs new file mode 100644 index 0000000000000..fd6644d73c161 --- /dev/null +++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs @@ -0,0 +1,242 @@ +//@ run-pass +//@ ignore-cross-compile + +// This test covers the AST pretty-printer's automatic insertion of parentheses +// into unparenthesized syntax trees according to precedence and various grammar +// restrictions and edge cases. +// +// For example if the following syntax tree represents the expression a*(b+c), +// in which the parenthesis is necessary for precedence: +// +// Binary('*', Path("a"), Paren(Binary('+', Path("b"), Path("c")))) +// +// then the pretty-printer needs to be able to print the following +// unparenthesized syntax tree with an automatically inserted parenthesization. +// +// Binary('*', Path("a"), Binary('+', Path("b"), Path("c"))) +// +// Handling this correctly is relevant in real-world code when pretty-printing +// macro-generated syntax trees, in which expressions can get interpolated into +// one another without any parenthesization being visible in the syntax tree. +// +// macro_rules! repro { +// ($rhs:expr) => { +// a * $rhs +// }; +// } +// +// let _ = repro!(b + c); + +#![feature(rustc_private)] + +extern crate rustc_ast; +extern crate rustc_ast_pretty; +extern crate rustc_driver; +extern crate rustc_errors; +extern crate rustc_parse; +extern crate rustc_session; +extern crate rustc_span; +extern crate smallvec; + +use std::mem; +use std::process::ExitCode; + +use rustc_ast::ast::{DUMMY_NODE_ID, Expr, ExprKind, Stmt}; +use rustc_ast::mut_visit::{self, DummyAstNode as _, MutVisitor}; +use rustc_ast::node_id::NodeId; +use rustc_ast::ptr::P; +use rustc_ast_pretty::pprust; +use rustc_errors::Diag; +use rustc_parse::parser::Recovery; +use rustc_session::parse::ParseSess; +use rustc_span::{DUMMY_SP, FileName, Span}; +use smallvec::SmallVec; + +// Every parenthesis in the following expressions is re-inserted by the +// pretty-printer. +// +// FIXME: Some of them shouldn't be. +static EXPRS: &[&str] = &[ + // Straightforward binary operator precedence. + "2 * 2 + 2", + "2 + 2 * 2", + "(2 + 2) * 2", + "2 * (2 + 2)", + "2 + 2 + 2", + // Return has lower precedence than a binary operator. + "(return 2) + 2", + "2 + (return 2)", // FIXME: no parenthesis needed. + "(return) + 2", // FIXME: no parenthesis needed. + // These mean different things. + "return - 2", + "(return) - 2", + // These mean different things. + "if let _ = true && false {}", + "if let _ = (true && false) {}", + // Conditions end at the first curly brace, so struct expressions need to be + // parenthesized. Except in a match guard, where conditions end at arrow. + "if let _ = (Struct {}) {}", + "match 2 { _ if let _ = Struct {} => {} }", + // Match arms terminate eagerly, so parenthesization is needed around some + // expressions. + "match 2 { _ => 1 - 1 }", + "match 2 { _ => ({ 1 }) - 1 }", + // Grammar restriction: break value starting with a labeled loop is not + // allowed, except if the break is also labeled. + "break 'outer 'inner: loop {} + 2", + "break ('inner: loop {} + 2)", + // Grammar restriction: the value in let-else is not allowed to end in a + // curly brace. + "{ let _ = 1 + 1 else {}; }", + "{ let _ = (loop {}) else {}; }", + "{ let _ = mac!() else {}; }", + "{ let _ = (mac! {}) else {}; }", + // Parentheses are necessary to prevent an eager statement boundary. + "{ 2 - 1 }", + "{ (match 2 {}) - 1 }", + "{ (match 2 {})() - 1 }", + "{ (match 2 {})[0] - 1 }", + "{ (loop {}) - 1 }", + // Angle bracket is eagerly parsed as a path's generic argument list. + "(2 as T) < U", + "(2 as T) < V", // FIXME: no parentheses needed. + /* + // FIXME: pretty-printer produces invalid syntax. `2 + 2 as T < U` + "(2 + 2 as T) < U", + */ + /* + // FIXME: pretty-printer produces invalid syntax. `if (let _ = () && Struct {}.x) {}` + "if let _ = () && (Struct {}).x {}", + */ + /* + // FIXME: pretty-printer produces invalid syntax. `(1 < 2 == false) as usize` + "((1 < 2) == false) as usize", + */ + /* + // FIXME: pretty-printer produces invalid syntax. `for _ in 1..{ 2 } {}` + "for _ in (1..{ 2 }) {}", + */ + /* + // FIXME: pretty-printer loses the attribute. `{ let Struct { field } = s; }` + "{ let Struct { #[attr] field } = s; }", + */ + /* + // FIXME: pretty-printer turns this into a range. `0..to_string()` + "(0.).to_string()", + "0. .. 1.", + */ + /* + // FIXME: pretty-printer loses the dyn*. `i as Trait` + "i as dyn* Trait", + */ +]; + +// Flatten the content of parenthesis nodes into their parent node. For example +// this syntax tree representing the expression a*(b+c): +// +// Binary('*', Path("a"), Paren(Binary('+', Path("b"), Path("c")))) +// +// would unparenthesize to: +// +// Binary('*', Path("a"), Binary('+', Path("b"), Path("c"))) +struct Unparenthesize; + +impl MutVisitor for Unparenthesize { + fn visit_expr(&mut self, e: &mut P) { + while let ExprKind::Paren(paren) = &mut e.kind { + **e = mem::replace(&mut *paren, Expr::dummy()); + } + mut_visit::walk_expr(self, e); + } +} + +// Erase Span information that could distinguish between identical expressions +// parsed from different source strings. +struct Normalize; + +impl MutVisitor for Normalize { + const VISIT_TOKENS: bool = true; + + fn visit_id(&mut self, id: &mut NodeId) { + *id = DUMMY_NODE_ID; + } + + fn visit_span(&mut self, span: &mut Span) { + *span = DUMMY_SP; + } + + fn visit_expr(&mut self, expr: &mut P) { + if let ExprKind::Binary(binop, _left, _right) = &mut expr.kind { + self.visit_span(&mut binop.span); + } + mut_visit::walk_expr(self, expr); + } + + fn flat_map_stmt(&mut self, mut stmt: Stmt) -> SmallVec<[Stmt; 1]> { + self.visit_span(&mut stmt.span); + mut_visit::walk_flat_map_stmt(self, stmt) + } +} + +fn parse_expr(psess: &ParseSess, source_code: &str) -> Option> { + let parser = rustc_parse::unwrap_or_emit_fatal(rustc_parse::new_parser_from_source_str( + psess, + FileName::anon_source_code(source_code), + source_code.to_owned(), + )); + + let mut expr = parser.recovery(Recovery::Forbidden).parse_expr().map_err(Diag::cancel).ok()?; + Normalize.visit_expr(&mut expr); + Some(expr) +} + +fn main() -> ExitCode { + let mut status = ExitCode::SUCCESS; + let mut fail = |description: &str, before: &str, after: &str| { + status = ExitCode::FAILURE; + eprint!( + "{description}\n BEFORE: {before}\n AFTER: {after}\n\n", + before = before.replace('\n', "\n "), + after = after.replace('\n', "\n "), + ); + }; + + rustc_span::create_default_session_globals_then(|| { + let psess = &ParseSess::new(vec![rustc_parse::DEFAULT_LOCALE_RESOURCE]); + + for &source_code in EXPRS { + let expr = parse_expr(psess, source_code).unwrap(); + + // Check for FALSE POSITIVE: pretty-printer inserting parentheses where not needed. + // Pseudocode: + // assert(expr == parse(print(expr))) + let printed = &pprust::expr_to_string(&expr); + let Some(expr2) = parse_expr(psess, printed) else { + fail("Pretty-printer produced invalid syntax", source_code, printed); + continue; + }; + if format!("{expr:#?}") != format!("{expr2:#?}") { + fail("Pretty-printer inserted unnecessary parenthesis", source_code, printed); + continue; + } + + // Check for FALSE NEGATIVE: pretty-printer failing to place necessary parentheses. + // Pseudocode: + // assert(unparenthesize(expr) == unparenthesize(parse(print(unparenthesize(expr))))) + let mut expr = expr; + Unparenthesize.visit_expr(&mut expr); + let printed = &pprust::expr_to_string(&expr); + let Some(mut expr2) = parse_expr(psess, printed) else { + fail("Pretty-printer with no parens produced invalid syntax", source_code, printed); + continue; + }; + Unparenthesize.visit_expr(&mut expr2); + if format!("{expr:#?}") != format!("{expr2:#?}") { + fail("Pretty-printer lost necessary parentheses", source_code, printed); + continue; + } + } + }); + + status +} From 99c232223f8330debc199f616308bc559b38c361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:48:01 +0800 Subject: [PATCH 11/19] Add `needs-target-has-atomic` directive Before this commit, the test writer has to specify platforms and architectures by hand for targets that have differing atomic width support. `#[cfg(target_has_atomic)]` is not quite the same because (1) you may have to specify additional matchers manually which has to be maintained individually, and (2) the `#[cfg]` blocks does not communicate to compiletest that a test would be ignored for a given target. This commit implements a `//@ needs-target-has-atomic` directive which admits a comma-separated list of required atomic widths that the target must satisfy in order for the test to run. ``` //@ needs-target-has-atomic: 8, 16, ptr ``` See . Co-authored-by: kei519 --- src/tools/compiletest/src/common.rs | 22 ++++++++- src/tools/compiletest/src/directive-list.rs | 1 + src/tools/compiletest/src/header/needs.rs | 51 +++++++++++++++++++-- src/tools/compiletest/src/header/tests.rs | 37 +++++++++++++++ 4 files changed, 106 insertions(+), 5 deletions(-) diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index e6fbba943a48c..36f876bcdc604 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::ffi::OsString; use std::path::{Path, PathBuf}; use std::process::Command; @@ -486,6 +486,9 @@ impl Config { } } +/// Known widths of `target_has_atomic`. +pub const KNOWN_TARGET_HAS_ATOMIC_WIDTHS: &[&str] = &["8", "16", "32", "64", "128", "ptr"]; + #[derive(Debug, Clone)] pub struct TargetCfgs { pub current: TargetCfg, @@ -611,6 +614,17 @@ impl TargetCfgs { ("panic", Some("abort")) => cfg.panic = PanicStrategy::Abort, ("panic", Some("unwind")) => cfg.panic = PanicStrategy::Unwind, ("panic", other) => panic!("unexpected value for panic cfg: {other:?}"), + + ("target_has_atomic", Some(width)) + if KNOWN_TARGET_HAS_ATOMIC_WIDTHS.contains(&width) => + { + cfg.target_has_atomic.insert(width.to_string()); + } + ("target_has_atomic", Some(other)) => { + panic!("unexpected value for `target_has_atomic` cfg: {other:?}") + } + // Nightly-only std-internal impl detail. + ("target_has_atomic", None) => {} _ => {} } } @@ -645,6 +659,12 @@ pub struct TargetCfg { pub(crate) xray: bool, #[serde(default = "default_reloc_model")] pub(crate) relocation_model: String, + + // Not present in target cfg json output, additional derived information. + #[serde(skip)] + /// Supported target atomic widths: e.g. `8` to `128` or `ptr`. This is derived from the builtin + /// `target_has_atomic` `cfg`s e.g. `target_has_atomic="8"`. + pub(crate) target_has_atomic: BTreeSet, } impl TargetCfg { diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs index 952533e904c5a..c7e8d4b58a562 100644 --- a/src/tools/compiletest/src/directive-list.rs +++ b/src/tools/compiletest/src/directive-list.rs @@ -154,6 +154,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "needs-sanitizer-thread", "needs-std-debug-assertions", "needs-symlink", + "needs-target-has-atomic", "needs-threads", "needs-unwind", "needs-wasmtime", diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs index 77570c58db5cf..e19dcd992fc2e 100644 --- a/src/tools/compiletest/src/header/needs.rs +++ b/src/tools/compiletest/src/header/needs.rs @@ -1,4 +1,4 @@ -use crate::common::{Config, Sanitizer}; +use crate::common::{Config, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer}; use crate::header::{IgnoreDecision, llvm_has_libzstd}; pub(super) fn handle_needs( @@ -171,11 +171,54 @@ pub(super) fn handle_needs( }, ]; - let (name, comment) = match ln.split_once([':', ' ']) { - Some((name, comment)) => (name, Some(comment)), + let (name, rest) = match ln.split_once([':', ' ']) { + Some((name, rest)) => (name, Some(rest)), None => (ln, None), }; + // FIXME(jieyouxu): tighten up this parsing to reject using both `:` and ` ` as means to + // delineate value. + if name == "needs-target-has-atomic" { + let Some(rest) = rest else { + return IgnoreDecision::Error { + message: "expected `needs-target-has-atomic` to have a comma-separated list of atomic widths".to_string(), + }; + }; + + // Expect directive value to be a list of comma-separated atomic widths. + let specified_widths = rest + .split(',') + .map(|width| width.trim()) + .map(ToString::to_string) + .collect::>(); + + for width in &specified_widths { + if !KNOWN_TARGET_HAS_ATOMIC_WIDTHS.contains(&width.as_str()) { + return IgnoreDecision::Error { + message: format!( + "unknown width specified in `needs-target-has-atomic`: `{width}` is not a \ + known `target_has_atomic_width`, known values are `{:?}`", + KNOWN_TARGET_HAS_ATOMIC_WIDTHS + ), + }; + } + } + + let satisfies_all_specified_widths = specified_widths + .iter() + .all(|specified| config.target_cfg().target_has_atomic.contains(specified)); + if satisfies_all_specified_widths { + return IgnoreDecision::Continue; + } else { + return IgnoreDecision::Ignore { + reason: format!( + "skipping test as target does not support all of the required `target_has_atomic` widths `{:?}`", + specified_widths + ), + }; + } + } + if !name.starts_with("needs-") { return IgnoreDecision::Continue; } @@ -193,7 +236,7 @@ pub(super) fn handle_needs( break; } else { return IgnoreDecision::Ignore { - reason: if let Some(comment) = comment { + reason: if let Some(comment) = rest { format!("{} ({})", need.ignore_reason, comment.trim()) } else { need.ignore_reason.into() diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs index 4d75c38dd3206..cd7c6f8361ed3 100644 --- a/src/tools/compiletest/src/header/tests.rs +++ b/src/tools/compiletest/src/header/tests.rs @@ -787,3 +787,40 @@ fn test_not_trailing_directive() { run_path(&mut poisoned, Path::new("a.rs"), b"//@ revisions: incremental"); assert!(!poisoned); } + +#[test] +fn test_needs_target_has_atomic() { + use std::collections::BTreeSet; + + // `x86_64-unknown-linux-gnu` supports `["8", "16", "32", "64", "ptr"]` but not `128`. + + let config = cfg().target("x86_64-unknown-linux-gnu").build(); + // Expectation sanity check. + assert_eq!( + config.target_cfg().target_has_atomic, + BTreeSet::from([ + "8".to_string(), + "16".to_string(), + "32".to_string(), + "64".to_string(), + "ptr".to_string() + ]), + "expected `x86_64-unknown-linux-gnu` to not have 128-bit atomic support" + ); + + assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 8")); + assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 16")); + assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 32")); + assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 64")); + assert!(!check_ignore(&config, "//@ needs-target-has-atomic: ptr")); + + assert!(check_ignore(&config, "//@ needs-target-has-atomic: 128")); + + assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 8,16,32,64,ptr")); + + assert!(check_ignore(&config, "//@ needs-target-has-atomic: 8,16,32,64,ptr,128")); + + // Check whitespace between widths is permitted. + assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 8, ptr")); + assert!(check_ignore(&config, "//@ needs-target-has-atomic: 8, ptr, 128")); +} From 1a976e01801d3cdec76b86c4adf626e1e2c8d446 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 2 Dec 2024 08:54:12 +0000 Subject: [PATCH 12/19] Re-add myself to rotation --- triagebot.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 47b968240984d..4d6806711fbc8 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -996,7 +996,6 @@ warn_non_default_branch = true contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" users_on_vacation = [ "jyn514", - "oli-obk", ] [assign.adhoc_groups] From b1ff3c8b924b911187d8fe456af03a6a197bb98c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Bj=C3=B8rnager=20Jensen?= Date: Mon, 2 Dec 2024 10:38:40 +0100 Subject: [PATCH 13/19] Fix docs for '<[T]>::as_array'; --- library/core/src/slice/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index bc49b7d97972b..5ff888628af45 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -858,7 +858,7 @@ impl [T] { /// Gets a reference to the underlying array. /// - /// If `N` is not exactly equal to slice's the length of `self`, then this method returns `None`. + /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. #[unstable(feature = "slice_as_array", issue = "133508")] #[rustc_const_unstable(feature = "slice_as_array", issue = "133508")] #[inline] From c8e0724937df40f8e8323a2677b3130707aec47f Mon Sep 17 00:00:00 2001 From: Xelph Date: Mon, 2 Dec 2024 03:06:57 -0700 Subject: [PATCH 14/19] Fix typos on tests/ui/README.md --- tests/ui/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/README.md b/tests/ui/README.md index c14d0ee78c8df..aa36481ae06eb 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -6,9 +6,9 @@ This folder contains `rustc`'s ## Test Directives (Headers) Typically, a UI test will have some test directives / headers which are -special comments that tell compiletest how to build and intepret a test. +special comments that tell compiletest how to build and interpret a test. -As part of an on-going effort to rewrite compiletest +As part of an ongoing effort to rewrite compiletest (see ), a major change proposal to change legacy compiletest-style headers `// ` to [`ui_test`](https://github.com/oli-obk/ui_test)-style headers @@ -30,6 +30,6 @@ but in `ui_test` style, the header would be written as compiletest is changed to accept only `//@` directives for UI tests (currently), and will reject and report an error if it encounters any -comments `// ` that may be parsed as an legacy compiletest-style +comments `// ` that may be parsed as a legacy compiletest-style test header. To fix this, you should migrate to the `ui_test`-style header `//@ `. From b863c0d28b8f59f0c986fac696acf82d3d87e788 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 2 Dec 2024 12:03:32 +0100 Subject: [PATCH 15/19] Remove static HashSet for default IDs list --- src/librustdoc/html/markdown.rs | 116 +++++++++++++++----------------- 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index d18673c8140da..5e3d06a4ae79a 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -32,12 +32,11 @@ use std::iter::Peekable; use std::ops::{ControlFlow, Range}; use std::path::PathBuf; use std::str::{self, CharIndices}; -use std::sync::OnceLock; use pulldown_cmark::{ BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html, }; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Diag, DiagMessage}; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::TyCtxt; @@ -1882,66 +1881,63 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec, usize>, + map: FxHashMap, existing_footnotes: usize, } -// The map is pre-initialized and then can be used as is to prevent cloning it for each item -// (in the sidebar rendering). -static DEFAULT_ID_MAP: OnceLock> = OnceLock::new(); - -fn init_id_map() -> FxHashSet<&'static str> { - let mut map = FxHashSet::default(); - // This is the list of IDs used in JavaScript. - map.insert("help"); - map.insert("settings"); - map.insert("not-displayed"); - map.insert("alternative-display"); - map.insert("search"); - map.insert("crate-search"); - map.insert("crate-search-div"); - // This is the list of IDs used in HTML generated in Rust (including the ones - // used in tera template files). - map.insert("themeStyle"); - map.insert("settings-menu"); - map.insert("help-button"); - map.insert("sidebar-button"); - map.insert("main-content"); - map.insert("toggle-all-docs"); - map.insert("all-types"); - map.insert("default-settings"); - map.insert("sidebar-vars"); - map.insert("copy-path"); - map.insert("rustdoc-toc"); - map.insert("rustdoc-modnav"); - // This is the list of IDs used by rustdoc sections (but still generated by - // rustdoc). - map.insert("fields"); - map.insert("variants"); - map.insert("implementors-list"); - map.insert("synthetic-implementors-list"); - map.insert("foreign-impls"); - map.insert("implementations"); - map.insert("trait-implementations"); - map.insert("synthetic-implementations"); - map.insert("blanket-implementations"); - map.insert("required-associated-types"); - map.insert("provided-associated-types"); - map.insert("provided-associated-consts"); - map.insert("required-associated-consts"); - map.insert("required-methods"); - map.insert("provided-methods"); - map.insert("dyn-compatibility"); - map.insert("implementors"); - map.insert("synthetic-implementors"); - map.insert("implementations-list"); - map.insert("trait-implementations-list"); - map.insert("synthetic-implementations-list"); - map.insert("blanket-implementations-list"); - map.insert("deref-methods"); - map.insert("layout"); - map.insert("aliased-type"); - map +fn is_default_id(id: &str) -> bool { + matches!( + id, + // This is the list of IDs used in JavaScript. + "help" + | "settings" + | "not-displayed" + | "alternative-display" + | "search" + | "crate-search" + | "crate-search-div" + // This is the list of IDs used in HTML generated in Rust (including the ones + // used in tera template files). + | "themeStyle" + | "settings-menu" + | "help-button" + | "sidebar-button" + | "main-content" + | "toggle-all-docs" + | "all-types" + | "default-settings" + | "sidebar-vars" + | "copy-path" + | "rustdoc-toc" + | "rustdoc-modnav" + // This is the list of IDs used by rustdoc sections (but still generated by + // rustdoc). + | "fields" + | "variants" + | "implementors-list" + | "synthetic-implementors-list" + | "foreign-impls" + | "implementations" + | "trait-implementations" + | "synthetic-implementations" + | "blanket-implementations" + | "required-associated-types" + | "provided-associated-types" + | "provided-associated-consts" + | "required-associated-consts" + | "required-methods" + | "provided-methods" + | "dyn-compatibility" + | "implementors" + | "synthetic-implementors" + | "implementations-list" + | "trait-implementations-list" + | "synthetic-implementations-list" + | "blanket-implementations-list" + | "deref-methods" + | "layout" + | "aliased-type" + ) } impl IdMap { @@ -1953,7 +1949,7 @@ impl IdMap { let id = match self.map.get_mut(candidate.as_ref()) { None => { let candidate = candidate.to_string(); - if DEFAULT_ID_MAP.get_or_init(init_id_map).contains(candidate.as_str()) { + if is_default_id(&candidate) { let id = format!("{}-{}", candidate, 1); self.map.insert(candidate.into(), 2); id From 8b90e70e0634a3392b77f514c9755058b1fb85f7 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 2 Dec 2024 13:23:16 +0100 Subject: [PATCH 16/19] mir validator: don't store mir phase --- compiler/rustc_mir_transform/src/inline.rs | 10 +-- .../rustc_mir_transform/src/pass_manager.rs | 2 +- compiler/rustc_mir_transform/src/validate.rs | 79 ++++++++----------- 3 files changed, 34 insertions(+), 57 deletions(-) diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 0878fa26a92c6..79c38b50459c4 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -220,15 +220,7 @@ impl<'tcx> Inliner<'tcx> { // Normally, this shouldn't be required, but trait normalization failure can create a // validation ICE. - if !validate_types( - self.tcx, - MirPhase::Runtime(RuntimePhase::Optimized), - self.typing_env, - &callee_body, - &caller_body, - ) - .is_empty() - { + if !validate_types(self.tcx, self.typing_env, &callee_body, &caller_body).is_empty() { return Err("failed to validate callee body"); } diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 779e7f22101c2..8a45ce0762d59 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -292,7 +292,7 @@ fn run_passes_inner<'tcx>( } pub(super) fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) { - validate::Validator { when, mir_phase: body.phase }.run_pass(tcx, body); + validate::Validator { when }.run_pass(tcx, body); } fn dump_mir_for_pass<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, pass_name: &str, is_after: bool) { diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 51e5c49cea104..936d88e9ac034 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -29,12 +29,6 @@ enum EdgeKind { pub(super) struct Validator { /// Describes at which point in the pipeline this validation is happening. pub when: String, - /// The phase for which we are upholding the dialect. If the given phase forbids a specific - /// element, this validator will now emit errors if that specific element is encountered. - /// Note that phases that change the dialect cause all *following* phases to check the - /// invariants of the new dialect. A phase that changes dialects never checks the new invariants - /// itself. - pub mir_phase: MirPhase, } impl<'tcx> crate::MirPass<'tcx> for Validator { @@ -46,11 +40,9 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { if matches!(body.source.instance, InstanceKind::Intrinsic(..) | InstanceKind::Virtual(..)) { return; } - debug_assert_eq!(self.mir_phase, body.phase); let def_id = body.source.def_id(); - let mir_phase = body.phase; let typing_env = body.typing_env(tcx); - let can_unwind = if mir_phase <= MirPhase::Runtime(RuntimePhase::Initial) { + let can_unwind = if body.phase <= MirPhase::Runtime(RuntimePhase::Initial) { // In this case `AbortUnwindingCalls` haven't yet been executed. true } else if !tcx.def_kind(def_id).is_fn_like() { @@ -64,9 +56,7 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { ty::Coroutine(..) => ExternAbi::Rust, // No need to do MIR validation on error bodies ty::Error(_) => return, - _ => { - span_bug!(body.span, "unexpected body ty: {:?} phase {:?}", body_ty, mir_phase) - } + _ => span_bug!(body.span, "unexpected body ty: {body_ty:?}"), }; ty::layout::fn_can_unwind(tcx, Some(def_id), body_abi) @@ -76,7 +66,6 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { when: &self.when, body, tcx, - mir_phase, unwind_edge_count: 0, reachable_blocks: traversal::reachable_as_bitset(body), value_cache: FxHashSet::default(), @@ -86,7 +75,7 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { cfg_checker.check_cleanup_control_flow(); // Also run the TypeChecker. - for (location, msg) in validate_types(tcx, self.mir_phase, typing_env, body, body) { + for (location, msg) in validate_types(tcx, typing_env, body, body) { cfg_checker.fail(location, msg); } @@ -107,7 +96,6 @@ struct CfgChecker<'a, 'tcx> { when: &'a str, body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, - mir_phase: MirPhase, unwind_edge_count: usize, reachable_blocks: BitSet, value_cache: FxHashSet, @@ -294,7 +282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { match &statement.kind { StatementKind::AscribeUserType(..) => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`AscribeUserType` should have been removed after drop lowering phase", @@ -302,7 +290,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } } StatementKind::FakeRead(..) => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`FakeRead` should have been removed after drop lowering phase", @@ -310,17 +298,17 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } } StatementKind::SetDiscriminant { .. } => { - if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`SetDiscriminant`is not allowed until deaggregation"); } } StatementKind::Deinit(..) => { - if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`Deinit`is not allowed until deaggregation"); } } StatementKind::Retag(kind, _) => { - // FIXME(JakobDegen) The validator should check that `self.mir_phase < + // FIXME(JakobDegen) The validator should check that `self.body.phase < // DropsLowered`. However, this causes ICEs with generation of drop shims, which // seem to fail to set their `MirPhase` correctly. if matches!(kind, RetagKind::TwoPhase) { @@ -328,7 +316,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } } StatementKind::Coverage(kind) => { - if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) + if self.body.phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) && let CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. } = kind { self.fail( @@ -391,7 +379,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { // the return edge from the call. FIXME(tmiasko): Since this is a strictly code // generation concern, the code generation should be responsible for handling // it. - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized) + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Optimized) && self.is_critical_call_edge(target, unwind) { self.fail( @@ -440,7 +428,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { if self.body.coroutine.is_none() { self.fail(location, "`Yield` cannot appear outside coroutine bodies"); } - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`Yield` should have been replaced by coroutine lowering"); } self.check_edge(location, *resume, EdgeKind::Normal); @@ -449,7 +437,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { } } TerminatorKind::FalseEdge { real_target, imaginary_target } => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`FalseEdge` should have been removed after drop elaboration", @@ -459,7 +447,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { self.check_edge(location, *imaginary_target, EdgeKind::Normal); } TerminatorKind::FalseUnwind { real_target, unwind } => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`FalseUnwind` should have been removed after drop elaboration", @@ -478,7 +466,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { if self.body.coroutine.is_none() { self.fail(location, "`CoroutineDrop` cannot appear outside coroutine bodies"); } - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`CoroutineDrop` should have been replaced by coroutine lowering", @@ -532,13 +520,11 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { /// `optimized_mir` is available. pub(super) fn validate_types<'tcx>( tcx: TyCtxt<'tcx>, - mir_phase: MirPhase, typing_env: ty::TypingEnv<'tcx>, body: &Body<'tcx>, caller_body: &Body<'tcx>, ) -> Vec<(Location, String)> { - let mut type_checker = - TypeChecker { body, caller_body, tcx, typing_env, mir_phase, failures: Vec::new() }; + let mut type_checker = TypeChecker { body, caller_body, tcx, typing_env, failures: Vec::new() }; type_checker.visit_body(body); type_checker.failures } @@ -548,7 +534,6 @@ struct TypeChecker<'a, 'tcx> { caller_body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, - mir_phase: MirPhase, failures: Vec<(Location, String)>, } @@ -577,7 +562,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // After borrowck subtyping should be fully explicit via // `Subtype` projections. - let variance = if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + let variance = if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { Variance::Invariant } else { Variance::Covariant @@ -618,7 +603,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) { // This check is somewhat expensive, so only run it when -Zvalidate-mir is passed. if self.tcx.sess.opts.unstable_opts.validate_mir - && self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) + && self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { // `Operand::Copy` is only supposed to be used with `Copy` types. if let Operand::Copy(place) = operand { @@ -642,7 +627,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ) { match elem { ProjectionElem::OpaqueCast(ty) - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) => + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) => { self.fail( location, @@ -656,7 +641,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } ProjectionElem::Deref - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) => + if self.body.phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) => { let base_ty = place_ref.ty(&self.body.local_decls, self.tcx).ty; @@ -856,7 +841,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // Set off any `bug!`s in the type computation code let _ = place.ty(&self.body.local_decls, self.tcx); - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) && place.projection.len() > 1 && cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo) && place.projection[1..].contains(&ProjectionElem::Deref) @@ -974,7 +959,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } AggregateKind::RawPtr(pointee_ty, mutability) => { - if !matches!(self.mir_phase, MirPhase::Runtime(_)) { + if !matches!(self.body.phase, MirPhase::Runtime(_)) { // It would probably be fine to support this in earlier phases, but at the // time of writing it's only ever introduced from intrinsic lowering, so // earlier things just `bug!` on it. @@ -1016,7 +1001,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } }, Rvalue::Ref(_, BorrowKind::Fake(_), _) => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`Assign` statement with a `Fake` borrow should have been removed in runtime MIR", @@ -1130,7 +1115,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } UnOp::PtrMetadata => { - if !matches!(self.mir_phase, MirPhase::Runtime(_)) { + if !matches!(self.body.phase, MirPhase::Runtime(_)) { // It would probably be fine to support this in earlier phases, but at // the time of writing it's only ever introduced from intrinsic // lowering or other runtime-phase optimization passes, so earlier @@ -1206,7 +1191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { "CastKind::{kind:?} output must be a raw const pointer, not {:?}", ty::RawPtr(_, Mutability::Not) ); - if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) { + if self.body.phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) { self.fail(location, format!("After borrowck, MIR disallows {kind:?}")); } } @@ -1222,7 +1207,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { "CastKind::{kind:?} output must be a raw pointer, not {:?}", ty::RawPtr(..) ); - if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) { + if self.body.phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) { self.fail(location, format!("After borrowck, MIR disallows {kind:?}")); } } @@ -1288,7 +1273,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } CastKind::Transmute => { - if let MirPhase::Runtime(..) = self.mir_phase { + if let MirPhase::Runtime(..) = self.body.phase { // Unlike `mem::transmute`, a MIR `Transmute` is well-formed // for any two `Sized` types, just potentially UB to run. @@ -1317,7 +1302,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { location, format!( "Transmute is not supported in non-runtime phase {:?}.", - self.mir_phase + self.body.phase ), ); } @@ -1404,7 +1389,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::AscribeUserType(..) => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`AscribeUserType` should have been removed after drop lowering phase", @@ -1412,7 +1397,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::FakeRead(..) => { - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) { self.fail( location, "`FakeRead` should have been removed after drop lowering phase", @@ -1463,7 +1448,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::SetDiscriminant { place, .. } => { - if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`SetDiscriminant`is not allowed until deaggregation"); } let pty = place.ty(&self.body.local_decls, self.tcx).ty.kind(); @@ -1477,12 +1462,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::Deinit(..) => { - if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) { + if self.body.phase < MirPhase::Runtime(RuntimePhase::Initial) { self.fail(location, "`Deinit`is not allowed until deaggregation"); } } StatementKind::Retag(kind, _) => { - // FIXME(JakobDegen) The validator should check that `self.mir_phase < + // FIXME(JakobDegen) The validator should check that `self.body.phase < // DropsLowered`. However, this causes ICEs with generation of drop shims, which // seem to fail to set their `MirPhase` correctly. if matches!(kind, RetagKind::TwoPhase) { From cfee10ce899b694e2896cd2f578bdda79746d0a6 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 2 Dec 2024 13:43:16 +0100 Subject: [PATCH 17/19] remove outdated comment --- compiler/rustc_lint/src/internal.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 38c38b59bc584..7c8a65059d117 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -242,17 +242,10 @@ fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &Path<'_>) -> Option { } // Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait. Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => { - if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind() { - if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(adt.did()) - { - // NOTE: This path is currently unreachable as `Ty<'tcx>` is - // defined as a type alias meaning that `impl<'tcx> Ty<'tcx>` - // is not actually allowed. - // - // I(@lcnr) still kept this branch in so we don't miss this - // if we ever change it in the future. - return Some(format!("{}<{}>", name, args[0])); - } + if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind() + && let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(adt.did()) + { + return Some(format!("{}<{}>", name, args[0])); } } _ => (), From e089bead327795727e1a72fd276776f12f186cea Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 2 Dec 2024 13:57:56 +0100 Subject: [PATCH 18/19] remove `Ty::is_copy_modulo_regions` --- .../rustc_hir_analysis/src/check/check.rs | 2 +- .../src/check/intrinsicck.rs | 2 +- .../rustc_hir_typeck/src/expr_use_visitor.rs | 2 +- compiler/rustc_lint/src/builtin.rs | 2 +- compiler/rustc_lint/src/context.rs | 4 +++ .../rustc_lint/src/drop_forget_useless.rs | 2 +- compiler/rustc_middle/src/ty/util.rs | 33 ++++++++++--------- .../src/build/expr/as_operand.rs | 2 +- .../src/thir/pattern/check_match.rs | 2 +- compiler/rustc_mir_transform/src/validate.rs | 2 +- compiler/rustc_trait_selection/src/infer.rs | 2 +- compiler/rustc_ty_utils/src/needs_drop.rs | 2 +- .../src/methods/manual_inspect.rs | 4 +-- .../clippy/clippy_lints/src/question_mark.rs | 2 +- src/tools/clippy/clippy_utils/src/ty.rs | 2 +- 15 files changed, 36 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index e1f4ccca97e94..022a6d457ec56 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -103,7 +103,7 @@ fn allowed_union_or_unsafe_field<'tcx>( // Fallback case: allow `ManuallyDrop` and things that are `Copy`, // also no need to report an error if the type is unresolved. ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) - || ty.is_copy_modulo_regions(tcx, typing_env) + || tcx.type_is_copy_modulo_regions(typing_env, ty) || ty.references_error() } }; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index b96469f503c8b..df4da03f0f59d 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -178,7 +178,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { // Check that the type implements Copy. The only case where this can // possibly fail is for SIMD types which don't #[derive(Copy)]. - if !ty.is_copy_modulo_regions(self.tcx, self.typing_env) { + if !self.tcx.type_is_copy_modulo_regions(self.typing_env, ty) { let msg = "arguments for inline assembly must be copyable"; self.tcx .dcx() diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 2a00530c43436..774d00edea035 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -228,7 +228,7 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) { } fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool { - ty.is_copy_modulo_regions(self.0.tcx, self.0.typing_env()) + self.0.type_is_copy_modulo_regions(ty) } fn body_owner_def_id(&self) -> LocalDefId { diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index e130cfc1d736d..ec08519892203 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -586,7 +586,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { return; } } - if ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()) { + if cx.type_is_copy_modulo_regions(ty) { return; } if type_implements_negative_copy_modulo_regions(cx.tcx, ty, cx.typing_env()) { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 7248b19276381..44c7888a530ca 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -710,6 +710,10 @@ impl<'tcx> LateContext<'tcx> { TypingEnv { typing_mode: self.typing_mode(), param_env: self.param_env } } + pub fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool { + self.tcx.type_is_copy_modulo_regions(self.typing_env(), ty) + } + /// Gets the type-checking results for the current body, /// or `None` if outside a body. pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> { diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs index 8fe8673865887..ce23892508b5d 100644 --- a/compiler/rustc_lint/src/drop_forget_useless.rs +++ b/compiler/rustc_lint/src/drop_forget_useless.rs @@ -144,7 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless { && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id) { let arg_ty = cx.typeck_results().expr_ty(arg); - let is_copy = arg_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()); + let is_copy = cx.type_is_copy_modulo_regions(arg_ty); let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr); let let_underscore_ignore_sugg = || { if let Some((_, node)) = cx.tcx.hir().parent_iter(expr.hir_id).nth(0) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 797ac9685b1e0..57054bd1a0b24 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -172,6 +172,24 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Checks whether `ty: Copy` holds while ignoring region constraints. + /// + /// This impacts whether values of `ty` are *moved* or *copied* + /// when referenced. This means that we may generate MIR which + /// does copies even when the type actually doesn't satisfy the + /// full requirements for the `Copy` trait (cc #29149) -- this + /// winds up being reported as an error during NLL borrow check. + /// + /// This function should not be used if there is an `InferCtxt` available. + /// Use `InferCtxt::type_is_copy_modulo_regions` instead. + pub fn type_is_copy_modulo_regions( + self, + typing_env: ty::TypingEnv<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + ty.is_trivially_pure_clone_copy() || self.is_copy_raw(typing_env.as_query_input(ty)) + } + /// Returns the deeply last field of nested structures, or the same type if /// not a structure at all. Corresponds to the only possible unsized field, /// and its type can be used to determine unsizing strategy. @@ -1174,21 +1192,6 @@ impl<'tcx> Ty<'tcx> { .map(|(min, _)| ty::Const::from_bits(tcx, min, typing_env, self)) } - /// Checks whether values of this type `T` are *moved* or *copied* - /// when referenced -- this amounts to a check for whether `T: - /// Copy`, but note that we **don't** consider lifetimes when - /// doing this check. This means that we may generate MIR which - /// does copies even when the type actually doesn't satisfy the - /// full requirements for the `Copy` trait (cc #29149) -- this - /// winds up being reported as an error during NLL borrow check. - pub fn is_copy_modulo_regions( - self, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> bool { - self.is_trivially_pure_clone_copy() || tcx.is_copy_raw(typing_env.as_query_input(self)) - } - /// Checks whether values of this type `T` have a size known at /// compile time (i.e., whether `T: Sized`). Lifetimes are ignored /// for the purposes of this check, so it can be an diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs index 0cab853196b8e..777ff9e68f029 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs @@ -176,7 +176,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let ty = expr.ty; if !ty.is_sized(tcx, this.typing_env()) { // !sized means !copy, so this is an unsized move - assert!(!ty.is_copy_modulo_regions(tcx, this.typing_env())); + assert!(!tcx.type_is_copy_modulo_regions(this.typing_env(), ty)); // As described above, detect the case where we are passing a value of unsized // type, and that value is coming from the deref of a box. 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 e55cd35e2436d..a13b00e192167 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -780,7 +780,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: return; }; - let is_binding_by_move = |ty: Ty<'tcx>| !ty.is_copy_modulo_regions(cx.tcx, cx.typing_env); + let is_binding_by_move = |ty: Ty<'tcx>| !cx.tcx.type_is_copy_modulo_regions(cx.typing_env, ty); let sess = cx.tcx.sess; diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 51e5c49cea104..f67d9a7782532 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -624,7 +624,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { if let Operand::Copy(place) = operand { let ty = place.ty(&self.body.local_decls, self.tcx).ty; - if !ty.is_copy_modulo_regions(self.tcx, self.typing_env) { + if !self.tcx.type_is_copy_modulo_regions(self.typing_env, ty) { self.fail(location, format!("`Operand::Copy` with non-`Copy` type {ty}")); } } diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 43244eb5dcb12..ee708564a8046 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -35,7 +35,7 @@ impl<'tcx> InferCtxt<'tcx> { // FIXME(#132279): This should be removed as it causes us to incorrectly // handle opaques in their defining scope. if !(param_env, ty).has_infer() { - return ty.is_copy_modulo_regions(self.tcx, self.typing_env(param_env)); + return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty); } let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None); diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index d85da7d355e6f..1c85eb2a86130 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -202,7 +202,7 @@ where } } - _ if component.is_copy_modulo_regions(tcx, self.typing_env) => {} + _ if tcx.type_is_copy_modulo_regions(self.typing_env, component) => {} ty::Closure(_, args) => { for upvar in args.as_closure().upvar_tys() { diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs index 865a42b65c624..20e4d233525a1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs @@ -148,7 +148,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: _ => {}, } } - requires_copy |= !ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()); + requires_copy |= !cx.type_is_copy_modulo_regions(ty); break; } }, @@ -158,7 +158,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: } if can_lint - && (!requires_copy || arg_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env())) + && (!requires_copy || cx.type_is_copy_modulo_regions(arg_ty)) // This case could be handled, but a fair bit of care would need to be taken. && (!requires_deref || arg_ty.is_freeze(cx.tcx, cx.typing_env())) { diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 4b96858d8f66f..77abe7151f071 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -251,7 +251,7 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex { let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability); - let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()) + let by_ref = !cx.type_is_copy_modulo_regions(caller_ty) && !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)); let sugg = if let Some(else_inner) = r#else { if eq_expr_value(cx, caller, peel_blocks(else_inner)) { diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 39c9d234a1a16..260d1b801e3d9 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -38,7 +38,7 @@ pub use type_certainty::expr_type_is_certain; /// Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()) + cx.type_is_copy_modulo_regions(ty) } /// This checks whether a given type is known to implement Debug. From 42174f03964759f849a2f22653b264038948443a Mon Sep 17 00:00:00 2001 From: jyn Date: Mon, 2 Dec 2024 09:55:04 -0500 Subject: [PATCH 19/19] `impl Default for EarlyDiagCtxt` for small rustc_driver programs, most of their imports will currently be related to diagnostics. this change simplifiers their code so it's more clear what in the driver is modified from the default. this is especially important for external drivers which are out of tree and not updated in response to breaking changes. for these drivers, each import is a liability for future code, since it can be broken when refactors happen. here is an example driver which is simplified by these changes: ``` diff --git a/src/main.rs b/src/main.rs index f81aa3e..11e5f18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,8 @@ #![feature(rustc_private)] extern crate rustc_driver; extern crate rustc_interface; -extern crate rustc_errors; -extern crate rustc_session; use rustc_driver::Callbacks; -use rustc_errors::{emitter::HumanReadableErrorType, ColorConfig}; use rustc_interface::interface; -use rustc_session::config::ErrorOutputType; -use rustc_session::EarlyDiagCtxt; struct DisableSafetyChecks; @@ -26,11 +18,7 @@ fn main() { "https://github.com/jyn514/jyn514.github.io/issues/new", |_| (), ); - let handler = EarlyDiagCtxt::new(ErrorOutputType::HumanReadable( - HumanReadableErrorType::Default, - ColorConfig::Auto, - )); - rustc_driver::init_rustc_env_logger(&handler); + rustc_driver::init_rustc_env_logger(&Default::default()); std::process::exit(rustc_driver::catch_with_exit_code(move || { let args: Vec = std::env::args().collect(); rustc_driver::RunCompiler::new(&args, &mut DisableSafetyChecks).run() ``` --- compiler/rustc_session/src/session.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index f585410adb97d..120ae9946eac3 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1358,6 +1358,12 @@ pub struct EarlyDiagCtxt { dcx: DiagCtxt, } +impl Default for EarlyDiagCtxt { + fn default() -> Self { + Self::new(ErrorOutputType::default()) + } +} + impl EarlyDiagCtxt { pub fn new(output: ErrorOutputType) -> Self { let emitter = mk_emitter(output);