diff options
Diffstat (limited to 'compiler')
379 files changed, 10720 insertions, 5814 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index c1b26ca0925..7de594719dd 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1307,6 +1307,7 @@ impl Expr { pub struct Closure { pub binder: ClosureBinder, pub capture_clause: CaptureBy, + pub constness: Const, pub asyncness: Async, pub movability: Movability, pub fn_decl: P<FnDecl>, @@ -2170,10 +2171,10 @@ impl fmt::Display for InlineAsmTemplatePiece { Ok(()) } Self::Placeholder { operand_idx, modifier: Some(modifier), .. } => { - write!(f, "{{{}:{}}}", operand_idx, modifier) + write!(f, "{{{operand_idx}:{modifier}}}") } Self::Placeholder { operand_idx, modifier: None, .. } => { - write!(f, "{{{}}}", operand_idx) + write!(f, "{{{operand_idx}}}") } } } @@ -2185,7 +2186,7 @@ impl InlineAsmTemplatePiece { use fmt::Write; let mut out = String::new(); for p in s.iter() { - let _ = write!(out, "{}", p); + let _ = write!(out, "{p}"); } out } diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 1b31be07f7a..4dc9c30a2c8 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -214,7 +214,7 @@ impl HasTokens for Attribute { match &self.kind { AttrKind::Normal(normal) => normal.tokens.as_ref(), kind @ AttrKind::DocComment(..) => { - panic!("Called tokens on doc comment attr {:?}", kind) + panic!("Called tokens on doc comment attr {kind:?}") } } } @@ -222,7 +222,7 @@ impl HasTokens for Attribute { Some(match &mut self.kind { AttrKind::Normal(normal) => &mut normal.tokens, kind @ AttrKind::DocComment(..) => { - panic!("Called tokens_mut on doc comment attr {:?}", kind) + panic!("Called tokens_mut on doc comment attr {kind:?}") } }) } diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index d99f6ed2c1c..c6b6207b318 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -310,7 +310,7 @@ impl Attribute { AttrKind::Normal(normal) => normal .tokens .as_ref() - .unwrap_or_else(|| panic!("attribute is missing tokens: {:?}", self)) + .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}")) .to_attr_token_stream() .to_tokenstream(), &AttrKind::DocComment(comment_kind, data) => TokenStream::new(vec![TokenTree::Token( diff --git a/compiler/rustc_ast/src/expand/allocator.rs b/compiler/rustc_ast/src/expand/allocator.rs index 1976e4ad3c9..35939496348 100644 --- a/compiler/rustc_ast/src/expand/allocator.rs +++ b/compiler/rustc_ast/src/expand/allocator.rs @@ -9,8 +9,8 @@ pub enum AllocatorKind { impl AllocatorKind { pub fn fn_name(&self, base: Symbol) -> String { match *self { - AllocatorKind::Global => format!("__rg_{}", base), - AllocatorKind::Default => format!("__rdl_{}", base), + AllocatorKind::Global => format!("__rg_{base}"), + AllocatorKind::Default => format!("__rdl_{base}"), } } } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index c572171e8f4..77f342d1eb3 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1362,6 +1362,7 @@ pub fn noop_visit_expr<T: MutVisitor>( ExprKind::Closure(box Closure { binder, capture_clause: _, + constness, asyncness, movability: _, fn_decl, @@ -1370,6 +1371,7 @@ pub fn noop_visit_expr<T: MutVisitor>( fn_arg_span: _, }) => { vis.visit_closure_binder(binder); + visit_constness(constness, vis); vis.visit_asyncness(asyncness); vis.visit_fn_decl(fn_decl); vis.visit_expr(body); diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 5b6cf30fa96..f947ae4d057 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -125,27 +125,27 @@ impl fmt::Display for Lit { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Lit { kind, symbol, suffix } = *self; match kind { - Byte => write!(f, "b'{}'", symbol)?, - Char => write!(f, "'{}'", symbol)?, - Str => write!(f, "\"{}\"", symbol)?, + Byte => write!(f, "b'{symbol}'")?, + Char => write!(f, "'{symbol}'")?, + Str => write!(f, "\"{symbol}\"")?, StrRaw(n) => write!( f, "r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol )?, - ByteStr => write!(f, "b\"{}\"", symbol)?, + ByteStr => write!(f, "b\"{symbol}\"")?, ByteStrRaw(n) => write!( f, "br{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol )?, - Integer | Float | Bool | Err => write!(f, "{}", symbol)?, + Integer | Float | Bool | Err => write!(f, "{symbol}")?, } if let Some(suffix) = suffix { - write!(f, "{}", suffix)?; + write!(f, "{suffix}")?; } Ok(()) @@ -756,7 +756,7 @@ impl Token { _ => return None, }, SingleQuote => match joint.kind { - Ident(name, false) => Lifetime(Symbol::intern(&format!("'{}", name))), + Ident(name, false) => Lifetime(Symbol::intern(&format!("'{name}"))), _ => return None, }, diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 29a5eb4b7c5..fabd43a1618 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -258,8 +258,7 @@ impl AttrTokenStream { assert!( found, - "Failed to find trailing delimited group in: {:?}", - target_tokens + "Failed to find trailing delimited group in: {target_tokens:?}" ); } let mut flat: SmallVec<[_; 1]> = SmallVec::new(); diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 0daeecb53a8..74b842ac96e 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -34,7 +34,7 @@ pub enum LitError { InvalidIntSuffix, InvalidFloatSuffix, NonDecimalFloat(u32), - IntTooLarge, + IntTooLarge(u32), } impl LitKind { @@ -168,7 +168,7 @@ impl fmt::Display for LitKind { match *self { LitKind::Byte(b) => { let b: String = ascii::escape_default(b).map(Into::<char>::into).collect(); - write!(f, "b'{}'", b)?; + write!(f, "b'{b}'")?; } LitKind::Char(ch) => write!(f, "'{}'", escape_char_symbol(ch))?, LitKind::Str(sym, StrStyle::Cooked) => write!(f, "\"{}\"", escape_string_symbol(sym))?, @@ -192,7 +192,7 @@ impl fmt::Display for LitKind { )?; } LitKind::Int(n, ty) => { - write!(f, "{}", n)?; + write!(f, "{n}")?; match ty { ast::LitIntType::Unsigned(ty) => write!(f, "{}", ty.name())?, ast::LitIntType::Signed(ty) => write!(f, "{}", ty.name())?, @@ -200,7 +200,7 @@ impl fmt::Display for LitKind { } } LitKind::Float(symbol, ty) => { - write!(f, "{}", symbol)?; + write!(f, "{symbol}")?; match ty { ast::LitFloatType::Suffixed(ty) => write!(f, "{}", ty.name())?, ast::LitFloatType::Unsuffixed => {} @@ -333,6 +333,6 @@ fn integer_lit(symbol: Symbol, suffix: Option<Symbol>) -> Result<LitKind, LitErr // but these kinds of errors are already reported by the lexer. let from_lexer = base < 10 && s.chars().any(|c| c.to_digit(10).map_or(false, |d| d >= base)); - if from_lexer { LitError::LexerError } else { LitError::IntTooLarge } + if from_lexer { LitError::LexerError } else { LitError::IntTooLarge(base) } }) } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index df7145a722a..e8823eff83a 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -836,6 +836,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { binder, capture_clause: _, asyncness: _, + constness: _, movability: _, fn_decl, body, diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index dfef6ec70fc..941d3179587 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -104,7 +104,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { Err(supported_abis) => { let mut abis = format!("`{}`", supported_abis[0]); for m in &supported_abis[1..] { - let _ = write!(abis, ", `{}`", m); + let _ = write!(abis, ", `{m}`"); } self.tcx.sess.emit_err(InvalidAbiClobberAbi { abi_span: *abi_span, @@ -262,7 +262,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let sub = if !valid_modifiers.is_empty() { let mut mods = format!("`{}`", valid_modifiers[0]); for m in &valid_modifiers[1..] { - let _ = write!(mods, ", `{}`", m); + let _ = write!(mods, ", `{m}`"); } InvalidAsmTemplateModifierRegClassSub::SupportModifier { class_name: class.name(), diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 3634e6e47ce..c3611b2f522 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -209,6 +209,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Closure(box Closure { binder, capture_clause, + constness, asyncness, movability, fn_decl, @@ -233,6 +234,7 @@ impl<'hir> LoweringContext<'_, 'hir> { binder, *capture_clause, e.id, + *constness, *movability, fn_decl, body, @@ -651,6 +653,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn_decl_span: self.lower_span(span), fn_arg_span: None, movability: Some(hir::Movability::Static), + constness: hir::Constness::NotConst, }); hir::ExprKind::Closure(c) @@ -689,8 +692,8 @@ impl<'hir> LoweringContext<'_, 'hir> { // call (like the identity function), as otherwise type and lifetime // inference have a hard time figuring things out. // Without this, we would get: - // E0720 in src/test/ui/impl-trait/in-trait/default-body-with-rpit.rs - // E0700 in src/test/ui/self/self_lifetime-async.rs + // E0720 in tests/ui/impl-trait/in-trait/default-body-with-rpit.rs + // E0700 in tests/ui/self/self_lifetime-async.rs // `future::identity_future`: let identity_future = @@ -890,6 +893,7 @@ impl<'hir> LoweringContext<'_, 'hir> { binder: &ClosureBinder, capture_clause: CaptureBy, closure_id: NodeId, + constness: Const, movability: Movability, decl: &FnDecl, body: &Expr, @@ -927,6 +931,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn_decl_span: self.lower_span(fn_decl_span), fn_arg_span: Some(self.lower_span(fn_arg_span)), movability: generator_option, + constness: self.lower_constness(constness), }); hir::ExprKind::Closure(c) @@ -1041,6 +1046,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn_decl_span: self.lower_span(fn_decl_span), fn_arg_span: Some(self.lower_span(fn_arg_span)), movability: None, + constness: hir::Constness::NotConst, }); hir::ExprKind::Closure(c) } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index b6b242bfc27..065779d0670 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1051,7 +1051,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } _ => { // Replace the ident for bindings that aren't simple. - let name = format!("__arg{}", index); + let name = format!("__arg{index}"); let ident = Ident::from_str(&name); (ident, false) @@ -1239,7 +1239,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn lower_constness(&mut self, c: Const) -> hir::Constness { + pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness { match c { Const::Yes(_) => hir::Constness::Const, Const::No => hir::Constness::NotConst, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 83174afdb12..41d4a5679f1 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -296,7 +296,7 @@ impl std::fmt::Display for ImplTraitPosition { ImplTraitPosition::ImplReturn => "`impl` method return", }; - write!(f, "{}", name) + write!(f, "{name}") } } @@ -416,8 +416,7 @@ fn compute_hir_hash( pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> { let sess = tcx.sess; - let krate = tcx.untracked_crate.steal(); - let mut resolver = tcx.resolver_for_lowering(()).steal(); + let (mut resolver, krate) = tcx.resolver_for_lowering(()).steal(); let ast_index = index_crate(&resolver.node_id_to_def_id, &krate); let mut owners = IndexVec::from_fn_n( @@ -503,7 +502,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn orig_local_def_id(&self, node: NodeId) -> LocalDefId { self.orig_opt_local_def_id(node) - .unwrap_or_else(|| panic!("no entry for node id: `{:?}`", node)) + .unwrap_or_else(|| panic!("no entry for node id: `{node:?}`")) } /// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name @@ -524,7 +523,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } fn local_def_id(&self, node: NodeId) -> LocalDefId { - self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{:?}`", node)) + self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{node:?}`")) } /// Get the previously recorded `to` local def id given the `from` local def id, obtained using @@ -2197,7 +2196,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_trait_ref(&mut self, p: &TraitRef, itctx: &ImplTraitContext) -> hir::TraitRef<'hir> { let path = match self.lower_qpath(p.ref_id, &None, &p.path, ParamMode::Explicit, itctx) { hir::QPath::Resolved(None, path) => path, - qpath => panic!("lower_trait_ref: unexpected QPath `{:?}`", qpath), + qpath => panic!("lower_trait_ref: unexpected QPath `{qpath:?}`"), }; hir::TraitRef { path, hir_ref_id: self.lower_node_id(p.ref_id) } } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 039338f543c..89ba6f936d1 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -385,6 +385,14 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { ast::ExprKind::TryBlock(_) => { gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental"); } + ast::ExprKind::Closure(box ast::Closure { constness: ast::Const::Yes(_), .. }) => { + gate_feature_post!( + &self, + const_closures, + e.span, + "const closures are experimental" + ); + } _ => {} } visit::walk_expr(self, e) diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 104cdd3a8e1..6a8064b0e87 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -191,23 +191,23 @@ fn doc_comment_to_string( data: Symbol, ) -> String { match (comment_kind, attr_style) { - (CommentKind::Line, ast::AttrStyle::Outer) => format!("///{}", data), - (CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{}", data), - (CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{}*/", data), - (CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{}*/", data), + (CommentKind::Line, ast::AttrStyle::Outer) => format!("///{data}"), + (CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{data}"), + (CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{data}*/"), + (CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{data}*/"), } } pub fn literal_to_string(lit: token::Lit) -> String { let token::Lit { kind, symbol, suffix } = lit; let mut out = match kind { - token::Byte => format!("b'{}'", symbol), - token::Char => format!("'{}'", symbol), - token::Str => format!("\"{}\"", symbol), + token::Byte => format!("b'{symbol}'"), + token::Char => format!("'{symbol}'"), + token::Str => format!("\"{symbol}\""), token::StrRaw(n) => { format!("r{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol) } - token::ByteStr => format!("b\"{}\"", symbol), + token::ByteStr => format!("b\"{symbol}\""), token::ByteStrRaw(n) => { format!("br{delim}\"{string}\"{delim}", delim = "#".repeat(n as usize), string = symbol) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 3b17f6dd627..b125c6407d0 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -399,6 +399,7 @@ impl<'a> State<'a> { ast::ExprKind::Closure(box ast::Closure { binder, capture_clause, + constness, asyncness, movability, fn_decl, @@ -407,6 +408,7 @@ impl<'a> State<'a> { fn_arg_span: _, }) => { self.print_closure_binder(binder); + self.print_constness(*constness); self.print_movability(*movability); self.print_asyncness(*asyncness); self.print_capture_clause(*capture_clause); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 5b6a07721e2..bf2c73a66a2 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -411,9 +411,9 @@ impl<'a> State<'a> { ast::VisibilityKind::Restricted { path, shorthand, .. } => { let path = Self::to_string(|s| s.print_path(path, false, 0)); if *shorthand && (path == "crate" || path == "self" || path == "super") { - self.word_nbsp(format!("pub({})", path)) + self.word_nbsp(format!("pub({path})")) } else { - self.word_nbsp(format!("pub(in {})", path)) + self.word_nbsp(format!("pub(in {path})")) } } ast::VisibilityKind::Inherited => {} diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index ab5e19050ea..40531c1c164 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -619,7 +619,7 @@ fn try_gate_cfg(name: Symbol, span: Span, sess: &ParseSess, features: Option<&Fe fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &ParseSess, features: &Features) { let (cfg, feature, has_feature) = gated_cfg; if !has_feature(features) && !cfg_span.allows_unstable(*feature) { - let explain = format!("`cfg({})` is experimental and subject to change", cfg); + let explain = format!("`cfg({cfg})` is experimental and subject to change"); feature_err(sess, *feature, cfg_span, &explain).emit(); } } @@ -975,7 +975,7 @@ pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { } pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> { - assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {:?}", attr); + assert!(attr.has_name(sym::repr), "expected `#[repr(..)]`, found: {attr:?}"); use ReprAttr::*; let mut acc = Vec::new(); let diagnostic = &sess.parse_sess.span_diagnostic; diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs index 91c6bcb08a0..3ba7a3c5336 100644 --- a/compiler/rustc_attr/src/session_diagnostics.rs +++ b/compiler/rustc_attr/src/session_diagnostics.rs @@ -51,7 +51,7 @@ pub(crate) struct UnknownMetaItem<'a> { // Manual implementation to be able to format `expected` items correctly. impl<'a> IntoDiagnostic<'a> for UnknownMetaItem<'_> { fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - let expected = self.expected.iter().map(|name| format!("`{}`", name)).collect::<Vec<_>>(); + let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>(); let mut diag = handler.struct_span_err_with_code( self.span, fluent::attr_unknown_meta_item, diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index e4942f9b666..a4943d11204 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -440,15 +440,14 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { closure_kind: &str, borrowed_path: &str, capture_span: Span, + scope: &str, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let mut err = struct_span_err!( self, closure_span, E0373, - "{} may outlive the current function, but it borrows {}, which is owned by the current \ - function", - closure_kind, - borrowed_path, + "{closure_kind} may outlive the current {scope}, but it borrows {borrowed_path}, \ + which is owned by the current {scope}", ); err.span_label(capture_span, format!("{} is borrowed here", borrowed_path)) .span_label(closure_span, format!("may outlive borrowed value {}", borrowed_path)); diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index becc04bbdab..055b281bc2e 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -25,7 +25,7 @@ pub use super::{ /// can, for example, happen when requesting a body of a `const` function /// because they are evaluated during typechecking. The panic can be avoided /// by overriding the `mir_borrowck` query. You can find a complete example -/// that shows how to do this at `src/test/run-make/obtain-borrowck/`. +/// that shows how to do this at `tests/run-make/obtain-borrowck/`. /// /// * Polonius is highly unstable, so expect regular changes in its signature or other details. pub fn get_body_with_borrowck_facts( diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 50cd13a2ccc..968c1f49b95 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -6,6 +6,7 @@ use rustc_errors::{ struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; +use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem}; use rustc_infer::infer::TyCtxtInferExt; @@ -20,7 +21,7 @@ use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; -use rustc_span::symbol::sym; +use rustc_span::symbol::{kw, sym}; use rustc_span::{BytePos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; @@ -29,6 +30,7 @@ use crate::borrowck_errors; use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead; use crate::diagnostics::find_all_local_uses; +use crate::diagnostics::mutability_errors::mut_borrow_of_mutable_ref; use crate::{ borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf, InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind, @@ -156,7 +158,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_note( MultiSpan::from_spans(reinit_spans), &if reinits <= 3 { - format!("these {} reinitializations might get skipped", reinits) + format!("these {reinits} reinitializations might get skipped") } else { format!( "these 3 reinitializations and {} other{} might get skipped", @@ -225,9 +227,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( span, format!( - "value {} here after {}move", + "value {} here after {partial_str}move", desired_action.as_verb_in_past_tense(), - partial_str ), ); } @@ -257,7 +258,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &format!( "consider creating a fresh reborrow of {} here", self.describe_place(moved_place) - .map(|n| format!("`{}`", n)) + .map(|n| format!("`{n}`")) .unwrap_or_else(|| "the mutable reference".to_string()), ), "&mut *", @@ -271,7 +272,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, ); let note_msg = match opt_name { - Some(name) => format!("`{}`", name), + Some(name) => format!("`{name}`"), None => "value".to_owned(), }; if self.suggest_borrow_fn_like(&mut err, ty, &move_site_vec, ¬e_msg) { @@ -297,9 +298,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } = use_spans { err.note(&format!( - "{} occurs due to deref coercion to `{}`", + "{} occurs due to deref coercion to `{deref_target_ty}`", desired_action.as_noun(), - deref_target_ty )); // Check first whether the source is accessible (issue #87060) @@ -358,7 +358,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. - })) = hir.find(hir.local_def_id_to_hir_id(self.mir_def_id())) + })) = hir.find(self.mir_hir_id()) && let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) { let place = &self.move_data.move_paths[mpi].place; @@ -394,7 +394,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } let typeck = self.infcx.tcx.typeck(self.mir_def_id()); - let hir_id = hir.get_parent_node(expr.hir_id); + let hir_id = hir.parent_id(expr.hir_id); if let Some(parent) = hir.find(hir_id) { let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind @@ -950,7 +950,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } (BorrowKind::Mut { .. }, BorrowKind::Shared) => { first_borrow_desc = "immutable "; - self.cannot_reborrow_already_borrowed( + let mut err = self.cannot_reborrow_already_borrowed( span, &desc_place, &msg_place, @@ -960,7 +960,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { "immutable", &msg_borrow, None, - ) + ); + self.suggest_binding_for_closure_capture_self( + &mut err, + issued_borrow.borrowed_place, + &issued_spans, + ); + err } (BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => { @@ -1242,6 +1248,138 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } + fn suggest_binding_for_closure_capture_self( + &self, + err: &mut Diagnostic, + borrowed_place: Place<'tcx>, + issued_spans: &UseSpans<'tcx>, + ) { + let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return }; + let hir = self.infcx.tcx.hir(); + + // check whether the borrowed place is capturing `self` by mut reference + let local = borrowed_place.local; + let Some(_) = self + .body + .local_decls + .get(local) + .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) else { return }; + + struct ExpressionFinder<'hir> { + capture_span: Span, + closure_change_spans: Vec<Span>, + closure_arg_span: Option<Span>, + in_closure: bool, + suggest_arg: String, + hir: rustc_middle::hir::map::Map<'hir>, + closure_local_id: Option<hir::HirId>, + closure_call_changes: Vec<(Span, String)>, + } + impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> { + fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) { + if e.span.contains(self.capture_span) { + if let hir::ExprKind::Closure(&hir::Closure { + movability: None, + body, + fn_arg_span, + fn_decl: hir::FnDecl{ inputs, .. }, + .. + }) = e.kind && + let Some(hir::Node::Expr(body )) = self.hir.find(body.hir_id) { + self.suggest_arg = "this: &Self".to_string(); + if inputs.len() > 0 { + self.suggest_arg.push_str(", "); + } + self.in_closure = true; + self.closure_arg_span = fn_arg_span; + self.visit_expr(body); + self.in_closure = false; + } + } + if let hir::Expr { kind: hir::ExprKind::Path(path), .. } = e { + if let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path && + seg.ident.name == kw::SelfLower && self.in_closure { + self.closure_change_spans.push(e.span); + } + } + hir::intravisit::walk_expr(self, e); + } + + fn visit_local(&mut self, local: &'hir hir::Local<'hir>) { + if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat && + let Some(init) = local.init + { + if let hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { + movability: None, + .. + }), .. } = init && + init.span.contains(self.capture_span) { + self.closure_local_id = Some(*hir_id); + } + } + hir::intravisit::walk_local(self, local); + } + + fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) { + if let hir::StmtKind::Semi(e) = s.kind && + let hir::ExprKind::Call(hir::Expr { kind: hir::ExprKind::Path(path), ..}, args) = e.kind && + let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path && + let Res::Local(hir_id) = seg.res && + Some(hir_id) == self.closure_local_id { + let (span, arg_str) = if args.len() > 0 { + (args[0].span.shrink_to_lo(), "self, ".to_string()) + } else { + let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span); + (span, "(self)".to_string()) + }; + self.closure_call_changes.push((span, arg_str)); + } + hir::intravisit::walk_stmt(self, s); + } + } + + if let Some(hir::Node::ImplItem( + hir::ImplItem { kind: hir::ImplItemKind::Fn(_fn_sig, body_id), .. } + )) = hir.find(self.mir_hir_id()) && + let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) { + let mut finder = ExpressionFinder { + capture_span: *capture_kind_span, + closure_change_spans: vec![], + closure_arg_span: None, + in_closure: false, + suggest_arg: String::new(), + closure_local_id: None, + closure_call_changes: vec![], + hir, + }; + finder.visit_expr(expr); + + if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() { + return; + } + + let mut sugg = vec![]; + let sm = self.infcx.tcx.sess.source_map(); + + if let Some(span) = finder.closure_arg_span { + sugg.push((sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg)); + } + for span in finder.closure_change_spans { + sugg.push((span, "this".to_string())); + } + + for (span, suggest) in finder.closure_call_changes { + sugg.push((span, suggest)); + } + + err.multipart_suggestion_verbose( + "try explicitly pass `&Self` into the Closure as an argument", + sugg, + Applicability::MachineApplicable, + ); + } + } + /// Returns the description of the root place for a conflicting borrow and the full /// descriptions of the places that caused the conflict. /// @@ -1425,6 +1563,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // and `move` will not help here. ( Some(name), + BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _), + ) => self.report_escaping_closure_capture( + borrow_spans, + borrow_span, + &RegionName { + name: self.synthesize_region_name(), + source: RegionNameSource::Static, + }, + ConstraintCategory::CallArgument(None), + var_or_use_span, + &format!("`{}`", name), + "block", + ), + ( + Some(name), BorrowExplanation::MustBeValidFor { category: category @ (ConstraintCategory::Return(_) @@ -1443,6 +1596,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { category, span, &format!("`{}`", name), + "function", ), ( name, @@ -1723,7 +1877,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// We check that there's a single level of block nesting to ensure always correct /// suggestions. If we don't, then we only provide a free-form message to avoid - /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`. + /// misleading users in cases like `tests/ui/nll/borrowed-temporary-error.rs`. /// We could expand the analysis to suggest hoising all of the relevant parts of /// the users' code to make the code compile, but that could be too much. struct NestedStatementVisitor { @@ -1895,6 +2049,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(err) } + #[instrument(level = "debug", skip(self))] fn report_escaping_closure_capture( &mut self, use_span: UseSpans<'tcx>, @@ -1903,6 +2058,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { category: ConstraintCategory<'tcx>, constraint_span: Span, captured_var: &str, + scope: &str, ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { let tcx = self.infcx.tcx; let args_span = use_span.args_or_use(); @@ -1933,8 +2089,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { None => "closure", }; - let mut err = - self.cannot_capture_in_long_lived_closure(args_span, kind, captured_var, var_span); + let mut err = self.cannot_capture_in_long_lived_closure( + args_span, + kind, + captured_var, + var_span, + scope, + ); err.span_suggestion_verbose( sugg_span, &format!( @@ -1956,10 +2117,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if matches!(use_span.generator_kind(), Some(GeneratorKind::Async(_))) { err.note( "async blocks are not executed immediately and must either take a \ - reference or ownership of outside variables they use", + reference or ownership of outside variables they use", ); } else { - let msg = format!("function requires argument type to outlive `{}`", fr_name); + let msg = format!("{scope} requires argument type to outlive `{fr_name}`"); err.span_note(constraint_span, &msg); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 00f5e8a8397..2095747097b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -77,7 +77,7 @@ impl<'tcx> BorrowExplanation<'tcx> { if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) { err.span_label( var_or_use_span, - format!("{}borrow later {}", borrow_desc, message), + format!("{borrow_desc}borrow later {message}"), ); } } else { @@ -90,7 +90,7 @@ impl<'tcx> BorrowExplanation<'tcx> { let capture_kind_label = message; err.span_label( var_or_use_span, - format!("{}borrow later {}", borrow_desc, capture_kind_label), + format!("{borrow_desc}borrow later {capture_kind_label}"), ); err.span_label(path_span, path_label); } @@ -110,7 +110,7 @@ impl<'tcx> BorrowExplanation<'tcx> { }; // We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) { - err.span_label(var_or_use_span, format!("{}{}", borrow_desc, message)); + err.span_label(var_or_use_span, format!("{borrow_desc}{message}")); } else { // path_span must be `Some` as otherwise the if condition is true let path_span = path_span.unwrap(); @@ -121,7 +121,7 @@ impl<'tcx> BorrowExplanation<'tcx> { let capture_kind_label = message; err.span_label( var_or_use_span, - format!("{}borrow later {}", borrow_desc, capture_kind_label), + format!("{borrow_desc}borrow later {capture_kind_label}"), ); err.span_label(path_span, path_label); } @@ -160,12 +160,8 @@ impl<'tcx> BorrowExplanation<'tcx> { match local_names[dropped_local] { Some(local_name) if !local_decl.from_compiler_desugaring() => { let message = format!( - "{B}borrow might be used here, when `{LOC}` is dropped \ - and runs the {DTOR} for {TYPE}", - B = borrow_desc, - LOC = local_name, - TYPE = type_desc, - DTOR = dtor_desc + "{borrow_desc}borrow might be used here, when `{local_name}` is dropped \ + and runs the {dtor_desc} for {type_desc}", ); err.span_label(body.source_info(drop_loc).span, message); @@ -180,18 +176,14 @@ impl<'tcx> BorrowExplanation<'tcx> { err.span_label( local_decl.source_info.span, format!( - "a temporary with access to the {B}borrow \ + "a temporary with access to the {borrow_desc}borrow \ is created here ...", - B = borrow_desc ), ); let message = format!( - "... and the {B}borrow might be used here, \ + "... and the {borrow_desc}borrow might be used here, \ when that temporary is dropped \ - and runs the {DTOR} for {TYPE}", - B = borrow_desc, - TYPE = type_desc, - DTOR = dtor_desc + and runs the {dtor_desc} for {type_desc}", ); err.span_label(body.source_info(drop_loc).span, message); @@ -249,20 +241,16 @@ impl<'tcx> BorrowExplanation<'tcx> { err.span_label( span, format!( - "{}requires that `{}` is borrowed for `{}`", + "{}requires that `{desc}` is borrowed for `{region_name}`", category.description(), - desc, - region_name, ), ); } else { err.span_label( span, format!( - "{}requires that {}borrow lasts for `{}`", + "{}requires that {borrow_desc}borrow lasts for `{region_name}`", category.description(), - borrow_desc, - region_name, ), ); }; @@ -296,15 +284,14 @@ impl<'tcx> BorrowExplanation<'tcx> { if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime }; let msg = format!( - "you can add a bound to the {}to make it last less than `'static` and match `{}`", + "you can add a bound to the {}to make it last less than `'static` and match `{region_name}`", category.description(), - region_name, ); err.span_suggestion_verbose( span.shrink_to_hi(), &msg, - format!(" + {}", suggestable_name), + format!(" + {suggestable_name}"), Applicability::Unspecified, ); } @@ -444,6 +431,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// First span returned points to the location of the conflicting use /// Second span if `Some` is returned in the case of closures and points /// to the use of the path + #[instrument(level = "debug", skip(self))] fn later_use_kind( &self, borrow: &BorrowData<'tcx>, @@ -461,11 +449,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let block = &self.body.basic_blocks[location.block]; let kind = if let Some(&Statement { - kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)), + kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), place)), .. }) = block.statements.get(location.statement_index) { - LaterUseKind::FakeLetRead + if let Some(l) = place.as_local() + && let local_decl = &self.body.local_decls[l] + && local_decl.ty.is_closure() + { + LaterUseKind::ClosureCapture + } else { + LaterUseKind::FakeLetRead + } } else if self.was_captured_by_trait_object(borrow) { LaterUseKind::TraitCapture } else if location.statement_index == block.statements.len() { diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 63b16aa95a6..1b40b7143cb 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -403,8 +403,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { move_prefix: &str, ) { let message = format!( - "{}move occurs because {} has type `{}`, which does not implement the `Copy` trait", - move_prefix, place_desc, ty, + "{move_prefix}move occurs because {place_desc} has type `{ty}`, which does not implement the `Copy` trait", ); if let Some(span) = span { err.span_label(span, message); @@ -739,11 +738,11 @@ impl<'tcx> BorrowedContentSource<'tcx> { BorrowedContentSource::OverloadedDeref(ty) => ty .ty_adt_def() .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? { - name @ (sym::Rc | sym::Arc) => Some(format!("an `{}`", name)), + name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")), _ => None, }) - .unwrap_or_else(|| format!("dereference of `{}`", ty)), - BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{}`", ty), + .unwrap_or_else(|| format!("dereference of `{ty}`")), + BorrowedContentSource::OverloadedIndex(ty) => format!("index of `{ty}`"), } } @@ -769,11 +768,11 @@ impl<'tcx> BorrowedContentSource<'tcx> { BorrowedContentSource::OverloadedDeref(ty) => ty .ty_adt_def() .and_then(|adt| match tcx.get_diagnostic_name(adt.did())? { - name @ (sym::Rc | sym::Arc) => Some(format!("an `{}`", name)), + name @ (sym::Rc | sym::Arc) => Some(format!("an `{name}`")), _ => None, }) - .unwrap_or_else(|| format!("dereference of `{}`", ty)), - BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{}`", ty), + .unwrap_or_else(|| format!("dereference of `{ty}`")), + BorrowedContentSource::OverloadedIndex(ty) => format!("an index of `{ty}`"), } } @@ -1033,7 +1032,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if let UseSpans::FnSelfUse { var_span, fn_call_span, fn_span, kind } = move_spans { let place_name = self .describe_place(moved_place.as_ref()) - .map(|n| format!("`{}`", n)) + .map(|n| format!("`{n}`")) .unwrap_or_else(|| "value".to_owned()); match kind { CallKind::FnCall { fn_trait_id, .. } @@ -1042,8 +1041,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( fn_call_span, &format!( - "{} {}moved due to this call{}", - place_name, partially_str, loop_message + "{place_name} {partially_str}moved due to this call{loop_message}", ), ); err.span_note( @@ -1056,8 +1054,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( fn_call_span, &format!( - "{} {}moved due to usage in operator{}", - place_name, partially_str, loop_message + "{place_name} {partially_str}moved due to usage in operator{loop_message}", ), ); if self.fn_self_span_reported.insert(fn_span) { @@ -1089,9 +1086,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_suggestion_verbose( move_span.shrink_to_lo(), &format!( - "consider iterating over a slice of the `{}`'s content to \ + "consider iterating over a slice of the `{ty}`'s content to \ avoid moving into the `for` loop", - ty, ), "&", Applicability::MaybeIncorrect, @@ -1101,8 +1097,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( fn_call_span, &format!( - "{} {}moved due to this implicit call to `.into_iter()`{}", - place_name, partially_str, loop_message + "{place_name} {partially_str}moved due to this implicit call to `.into_iter()`{loop_message}", ), ); // If the moved place was a `&mut` ref, then we can @@ -1118,7 +1113,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &format!( "consider creating a fresh reborrow of {} here", self.describe_place(moved_place.as_ref()) - .map(|n| format!("`{}`", n)) + .map(|n| format!("`{n}`")) .unwrap_or_else(|| "the mutable reference".to_string()), ), "&mut *", @@ -1130,8 +1125,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err.span_label( fn_call_span, &format!( - "{} {}moved due to this method call{}", - place_name, partially_str, loop_message + "{place_name} {partially_str}moved due to this method call{loop_message}", ), ); let infcx = tcx.infer_ctxt().build(); @@ -1206,7 +1200,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if move_span != span || !loop_message.is_empty() { err.span_label( move_span, - format!("value {}moved{} here{}", partially_str, move_msg, loop_message), + format!("value {partially_str}moved{move_msg} here{loop_message}"), ); } // If the move error occurs due to a loop, don't show @@ -1214,7 +1208,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { if loop_message.is_empty() { move_spans.var_span_label( err, - format!("variable {}moved due to use{}", partially_str, move_spans.describe()), + format!("variable {partially_str}moved due to use{}", move_spans.describe()), "moved", ); } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 6f6d1b01bd4..45b15c2c5bd 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -264,7 +264,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ProjectionElem::Deref, ], } => { - err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.span_label(span, format!("cannot {act}")); if let Some(span) = get_mut_span_in_struct_field( self.infcx.tcx, @@ -290,7 +290,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { .unwrap_or(false) => { let decl = &self.body.local_decls[local]; - err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.span_label(span, format!("cannot {act}")); if let Some(mir::Statement { source_info, kind: @@ -344,20 +344,25 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } else { err.span_help(source_info.span, "try removing `&mut` here"); } - } else if decl.mutability == Mutability::Not - && !matches!( + } else if decl.mutability == Mutability::Not { + if matches!( decl.local_info, Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf( hir::ImplicitSelfKind::MutRef - )))) - ) - { - err.span_suggestion_verbose( - decl.source_info.span.shrink_to_lo(), - "consider making the binding mutable", - "mut ", - Applicability::MachineApplicable, - ); + ),))) + ) { + err.note( + "as `Self` may be unsized, this call attempts to take `&mut &mut self`", + ); + err.note("however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably"); + } else { + err.span_suggestion_verbose( + decl.source_info.span.shrink_to_lo(), + "consider making the binding mutable", + "mut ", + Applicability::MachineApplicable, + ); + }; } } @@ -634,7 +639,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => { - err.span_label(span, format!("cannot {ACT}", ACT = act)); + err.span_label(span, format!("cannot {act}")); match opt_source { Some(BorrowedContentSource::OverloadedDeref(ty)) => { @@ -1004,7 +1009,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let hir = self.infcx.tcx.hir(); let closure_id = self.mir_hir_id(); let closure_span = self.infcx.tcx.def_span(self.mir_def_id()); - let fn_call_id = hir.get_parent_node(closure_id); + let fn_call_id = hir.parent_id(closure_id); let node = hir.get(fn_call_id); let def_id = hir.enclosing_body_owner(fn_call_id); let mut look_at_return = true; @@ -1089,7 +1094,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } } -fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool { +pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool { debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind()); match local_decl.local_info.as_deref() { @@ -1207,7 +1212,7 @@ fn suggest_ampmut<'tcx>( { let lt_name = &src[1..ws_pos]; let ty = &src[ws_pos..]; - return (true, highlight_span, format!("&{} mut{}", lt_name, ty)); + return (true, highlight_span, format!("&{lt_name} mut{ty}")); } let ty_mut = local_decl.ty.builtin_deref(true).unwrap(); diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index 35c3df76899..1eaf0a2f15c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -209,14 +209,14 @@ impl OutlivesSuggestionBuilder { let mut diag = if suggested.len() == 1 { mbcx.infcx.tcx.sess.diagnostic().struct_help(&match suggested.last().unwrap() { SuggestedConstraint::Outlives(a, bs) => { - let bs: SmallVec<[String; 2]> = bs.iter().map(|r| format!("{}", r)).collect(); - format!("add bound `{}: {}`", a, bs.join(" + ")) + let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect(); + format!("add bound `{a}: {}`", bs.join(" + ")) } SuggestedConstraint::Equal(a, b) => { - format!("`{}` and `{}` must be the same: replace one with the other", a, b) + format!("`{a}` and `{b}` must be the same: replace one with the other") } - SuggestedConstraint::Static(a) => format!("replace `{}` with `'static`", a), + SuggestedConstraint::Static(a) => format!("replace `{a}` with `'static`"), }) } else { // Create a new diagnostic. @@ -231,18 +231,16 @@ impl OutlivesSuggestionBuilder { for constraint in suggested { match constraint { SuggestedConstraint::Outlives(a, bs) => { - let bs: SmallVec<[String; 2]> = - bs.iter().map(|r| format!("{}", r)).collect(); - diag.help(&format!("add bound `{}: {}`", a, bs.join(" + "))); + let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect(); + diag.help(&format!("add bound `{a}: {}`", bs.join(" + "))); } SuggestedConstraint::Equal(a, b) => { diag.help(&format!( - "`{}` and `{}` must be the same: replace one with the other", - a, b + "`{a}` and `{b}` must be the same: replace one with the other", )); } SuggestedConstraint::Static(a) => { - diag.help(&format!("replace `{}` with `'static`", a)); + diag.help(&format!("replace `{a}` with `'static`")); } } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 567a9814fcc..f3050a6ef3f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -79,7 +79,7 @@ impl<'tcx> RegionErrors<'tcx> { #[track_caller] pub fn push(&mut self, val: impl Into<RegionErrorKind<'tcx>>) { let val = val.into(); - self.1.sess.delay_span_bug(DUMMY_SP, "{val:?}"); + self.1.sess.delay_span_bug(DUMMY_SP, format!("{val:?}")); self.0.push(val); } pub fn is_empty(&self) -> bool { @@ -192,6 +192,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { // buffered in the `MirBorrowckCtxt`. let mut outlives_suggestion = OutlivesSuggestionBuilder::default(); + let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> = + None; for nll_error in nll_errors.into_iter() { match nll_error { @@ -234,13 +236,19 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty); let named_key = self.regioncx.name_regions(self.infcx.tcx, key); let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region); - self.buffer_error(unexpected_hidden_region_diagnostic( + let mut diag = unexpected_hidden_region_diagnostic( self.infcx.tcx, span, named_ty, named_region, named_key, - )); + ); + if last_unexpected_hidden_region != Some((span, named_ty, named_key)) { + self.buffer_error(diag); + last_unexpected_hidden_region = Some((span, named_ty, named_key)); + } else { + diag.delay_as_bug(); + } } RegionErrorKind::BoundUniversalRegionError { @@ -414,7 +422,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ); (desc, note) } - _ => panic!("Unexpected type {:?}", ty), + _ => panic!("Unexpected type {ty:?}"), }; diag.note(&format!("requirement occurs because of {desc}",)); diag.note(¬e); @@ -717,10 +725,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let lifetime = if f.has_name() { fr_name.name } else { kw::UnderscoreLifetime }; let arg = match param.param.pat.simple_ident() { - Some(simple_ident) => format!("argument `{}`", simple_ident), + Some(simple_ident) => format!("argument `{simple_ident}`"), None => "the argument".to_string(), }; - let captures = format!("captures data from {}", arg); + let captures = format!("captures data from {arg}"); return nice_region_error::suggest_new_region_bound( self.infcx.tcx, @@ -730,6 +738,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { Some(arg), captures, Some((param.param_ty_span, param.param_ty.to_string())), + self.infcx.tcx.is_suitable_region(f).map(|r| r.def_id), ); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index dbd4cac7b14..9233287cf3a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -200,9 +200,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { /// increment the counter. /// /// This is _not_ idempotent. Call `give_region_a_name` when possible. - fn synthesize_region_name(&self) -> Symbol { + pub(crate) fn synthesize_region_name(&self) -> Symbol { let c = self.next_region_name.replace_with(|counter| *counter + 1); - Symbol::intern(&format!("'{:?}", c)) + Symbol::intern(&format!("'{c:?}")) } /// Maps from an internal MIR region vid to something that we can @@ -619,7 +619,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { // programs, so we need to use delay_span_bug here. See #82126. self.infcx.tcx.sess.delay_span_bug( hir_arg.span(), - &format!("unmatched subst and hir arg: found {:?} vs {:?}", kind, hir_arg), + &format!("unmatched subst and hir arg: found {kind:?} vs {hir_arg:?}"), ); } } @@ -783,8 +783,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { } else { span_bug!( hir_ty.span, - "bounds from lowered return type of async fn did not match expected format: {:?}", - opaque_ty + "bounds from lowered return type of async fn did not match expected format: {opaque_ty:?}", ); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index b385f95b67c..ada3310d807 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -18,7 +18,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { upvars: &[Upvar<'tcx>], fr: RegionVid, ) -> Option<(Option<Symbol>, Span)> { - debug!("get_var_name_and_span_for_region(fr={:?})", fr); + debug!("get_var_name_and_span_for_region(fr={fr:?})"); assert!(self.universal_regions().is_universal_region(fr)); debug!("get_var_name_and_span_for_region: attempting upvar"); @@ -44,10 +44,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) -> Option<usize> { let upvar_index = self.universal_regions().defining_ty.upvar_tys().position(|upvar_ty| { - debug!("get_upvar_index_for_region: upvar_ty={:?}", upvar_ty); + debug!("get_upvar_index_for_region: upvar_ty={upvar_ty:?}"); tcx.any_free_region_meets(&upvar_ty, |r| { let r = r.to_region_vid(); - debug!("get_upvar_index_for_region: r={:?} fr={:?}", r, fr); + debug!("get_upvar_index_for_region: r={r:?} fr={fr:?}"); r == fr }) })?; @@ -55,8 +55,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let upvar_ty = self.universal_regions().defining_ty.upvar_tys().nth(upvar_index); debug!( - "get_upvar_index_for_region: found {:?} in upvar {} which has type {:?}", - fr, upvar_index, upvar_ty, + "get_upvar_index_for_region: found {fr:?} in upvar {upvar_index} which has type {upvar_ty:?}", ); Some(upvar_index) @@ -71,13 +70,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { upvar_index: usize, ) -> (Symbol, Span) { let upvar_hir_id = upvars[upvar_index].place.get_root_variable(); - debug!("get_upvar_name_and_span_for_region: upvar_hir_id={:?}", upvar_hir_id); + debug!("get_upvar_name_and_span_for_region: upvar_hir_id={upvar_hir_id:?}"); let upvar_name = tcx.hir().name(upvar_hir_id); let upvar_span = tcx.hir().span(upvar_hir_id); debug!( - "get_upvar_name_and_span_for_region: upvar_name={:?} upvar_span={:?}", - upvar_name, upvar_span + "get_upvar_name_and_span_for_region: upvar_name={upvar_name:?} upvar_span={upvar_span:?}", ); (upvar_name, upvar_span) @@ -97,15 +95,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { let argument_index = self.universal_regions().unnormalized_input_tys.iter().skip(implicit_inputs).position( |arg_ty| { - debug!("get_argument_index_for_region: arg_ty = {:?}", arg_ty); + debug!("get_argument_index_for_region: arg_ty = {arg_ty:?}"); tcx.any_free_region_meets(arg_ty, |r| r.to_region_vid() == fr) }, )?; debug!( - "get_argument_index_for_region: found {:?} in argument {} which has type {:?}", - fr, - argument_index, + "get_argument_index_for_region: found {fr:?} in argument {argument_index} which has type {:?}", self.universal_regions().unnormalized_input_tys[argument_index], ); @@ -122,13 +118,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { ) -> (Option<Symbol>, Span) { let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs(); let argument_local = Local::new(implicit_inputs + argument_index + 1); - debug!("get_argument_name_and_span_for_region: argument_local={:?}", argument_local); + debug!("get_argument_name_and_span_for_region: argument_local={argument_local:?}"); let argument_name = local_names[argument_local]; let argument_span = body.local_decls[argument_local].source_info.span; debug!( - "get_argument_name_and_span_for_region: argument_name={:?} argument_span={:?}", - argument_name, argument_span + "get_argument_name_and_span_for_region: argument_name={argument_name:?} argument_span={argument_span:?}", ); (argument_name, argument_span) diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs index 51ed27c167d..02ffb51fbb7 100644 --- a/compiler/rustc_borrowck/src/facts.rs +++ b/compiler/rustc_borrowck/src/facts.rs @@ -192,7 +192,7 @@ fn write_row( ) -> Result<(), Box<dyn Error>> { for (index, c) in columns.iter().enumerate() { let tail = if index == columns.len() - 1 { "\n" } else { "\t" }; - write!(out, "{:?}{}", c.to_string(location_table), tail)?; + write!(out, "{:?}{tail}", c.to_string(location_table))?; } Ok(()) } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index ae1bea008b6..278ffed0747 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -863,7 +863,6 @@ enum WriteKind { /// local place can be mutated. // // FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications: -// - Merge `check_access_permissions()` and `check_if_reassignment_to_immutable_state()`. // - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and // `is_declared_mutable()`. // - Take flow state into consideration in `is_assignable()` for local variables. @@ -1132,20 +1131,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Write of P[i] or *P requires P init'd. self.check_if_assigned_path_is_moved(location, place_span, flow_state); - // Special case: you can assign an immutable local variable - // (e.g., `x = ...`) so long as it has never been initialized - // before (at this point in the flow). - if let Some(local) = place_span.0.as_local() { - if let Mutability::Not = self.body.local_decls[local].mutability { - // check for reassignments to immutable local variables - self.check_if_reassignment_to_immutable_state( - location, local, place_span, flow_state, - ); - return; - } - } - - // Otherwise, use the normal access permission rules. self.access_place( location, place_span, @@ -1554,24 +1539,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - fn check_if_reassignment_to_immutable_state( - &mut self, - location: Location, - local: Local, - place_span: (Place<'tcx>, Span), - flow_state: &Flows<'cx, 'tcx>, - ) { - debug!("check_if_reassignment_to_immutable_state({:?})", local); - - // Check if any of the initializations of `local` have happened yet: - if let Some(init_index) = self.is_local_ever_initialized(local, flow_state) { - // And, if so, report an error. - let init = &self.move_data.inits[init_index]; - let span = init.span(&self.body); - self.report_illegal_reassignment(location, place_span, span, place_span.0); - } - } - fn check_if_full_path_is_moved( &mut self, location: Location, @@ -2037,12 +2004,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // partial initialization, do not complain about mutability // errors except for actual mutation (as opposed to an attempt // to do a partial initialization). - let previously_initialized = - self.is_local_ever_initialized(place.local, flow_state).is_some(); + let previously_initialized = self.is_local_ever_initialized(place.local, flow_state); // at this point, we have set up the error reporting state. - if previously_initialized { - self.report_mutability_error(place, span, the_place_err, error_access, location); + if let Some(init_index) = previously_initialized { + if let (AccessKind::Mutate, Some(_)) = (error_access, place.as_local()) { + // If this is a mutate access to an immutable local variable with no projections + // report the error as an illegal reassignment + let init = &self.move_data.inits[init_index]; + let assigned_span = init.span(&self.body); + self.report_illegal_reassignment(location, (place, span), assigned_span, place); + } else { + self.report_mutability_error(place, span, the_place_err, error_access, location) + } true } else { false diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index d82d4cc39fb..767f9fe39c6 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -262,7 +262,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { return self.tcx.ty_error(); } - // Only check this for TAIT. RPIT already supports `src/test/ui/impl-trait/nested-return-type2.rs` + // Only check this for TAIT. RPIT already supports `tests/ui/impl-trait/nested-return-type2.rs` // on stable and we'd break that. let OpaqueTyOrigin::TyAlias = origin else { return definition_ty; @@ -318,7 +318,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { // This is still required for many(half of the tests in ui/type-alias-impl-trait) // tests to pass - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + let _ = infcx.take_opaque_types(); if errors.is_empty() { definition_ty diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 3617bf58be9..02222c0a03c 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -1,13 +1,13 @@ use std::fmt; -use rustc_infer::infer::canonical::Canonical; -use rustc_infer::traits::query::NoSolution; +use rustc_infer::infer::{canonical::Canonical, InferOk}; use rustc_middle::mir::ConstraintCategory; -use rustc_middle::ty::{self, ToPredicate, TypeFoldable}; +use rustc_middle::ty::{self, ToPredicate, Ty, TypeFoldable}; use rustc_span::def_id::DefId; use rustc_span::Span; use rustc_trait_selection::traits::query::type_op::{self, TypeOpOutput}; -use rustc_trait_selection::traits::query::Fallible; +use rustc_trait_selection::traits::query::{Fallible, NoSolution}; +use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt}; use crate::diagnostics::{ToUniverseInfo, UniverseInfo}; @@ -177,4 +177,74 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { value }) } + + #[instrument(skip(self), level = "debug")] + pub(super) fn ascribe_user_type( + &mut self, + mir_ty: Ty<'tcx>, + user_ty: ty::UserType<'tcx>, + span: Span, + ) { + // FIXME: Ideally MIR types are normalized, but this is not always true. + let mir_ty = self.normalize(mir_ty, Locations::All(span)); + + self.fully_perform_op( + Locations::All(span), + ConstraintCategory::Boring, + self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new(mir_ty, user_ty)), + ) + .unwrap_or_else(|err| { + span_mirbug!( + self, + span, + "ascribe_user_type `{mir_ty:?}=={user_ty:?}` failed with `{err:?}`", + ); + }); + } + + /// *Incorrectly* skips the WF checks we normally do in `ascribe_user_type`. + /// + /// FIXME(#104478, #104477): This is a hack for backward-compatibility. + #[instrument(skip(self), level = "debug")] + pub(super) fn ascribe_user_type_skip_wf( + &mut self, + mir_ty: Ty<'tcx>, + user_ty: ty::UserType<'tcx>, + span: Span, + ) { + let ty::UserType::Ty(user_ty) = user_ty else { bug!() }; + + // A fast path for a common case with closure input/output types. + if let ty::Infer(_) = user_ty.kind() { + self.eq_types(user_ty, mir_ty, Locations::All(span), ConstraintCategory::Boring) + .unwrap(); + return; + } + + let mir_ty = self.normalize(mir_ty, Locations::All(span)); + let cause = ObligationCause::dummy_with_span(span); + let param_env = self.param_env; + let op = |infcx: &'_ _| { + let ocx = ObligationCtxt::new_in_snapshot(infcx); + let user_ty = ocx.normalize(&cause, param_env, user_ty); + ocx.eq(&cause, param_env, user_ty, mir_ty)?; + if !ocx.select_all_or_error().is_empty() { + return Err(NoSolution); + } + Ok(InferOk { value: (), obligations: vec![] }) + }; + + self.fully_perform_op( + Locations::All(span), + ConstraintCategory::Boring, + type_op::custom::CustomTypeOp::new(op, || "ascribe_user_type_skip_wf".to_string()), + ) + .unwrap_or_else(|err| { + span_mirbug!( + self, + span, + "ascribe_user_type_skip_wf `{mir_ty:?}=={user_ty:?}` failed with `{err:?}`", + ); + }); + } } diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs index 8fa43f8528c..fa9ea769a14 100644 --- a/compiler/rustc_borrowck/src/type_check/input_output.rs +++ b/compiler/rustc_borrowck/src/type_check/input_output.rs @@ -10,7 +10,7 @@ use rustc_index::vec::Idx; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_middle::mir::*; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{self, Ty}; use rustc_span::Span; use crate::universal_regions::UniversalRegions; @@ -18,6 +18,52 @@ use crate::universal_regions::UniversalRegions; use super::{Locations, TypeChecker}; impl<'a, 'tcx> TypeChecker<'a, 'tcx> { + /// Check explicit closure signature annotation, + /// e.g., `|x: FxHashMap<_, &'static u32>| ...`. + #[instrument(skip(self, body), level = "debug")] + pub(super) fn check_signature_annotation(&mut self, body: &Body<'tcx>) { + let mir_def_id = body.source.def_id().expect_local(); + if !self.tcx().is_closure(mir_def_id.to_def_id()) { + return; + } + let Some(user_provided_poly_sig) = + self.tcx().typeck(mir_def_id).user_provided_sigs.get(&mir_def_id) + else { + return; + }; + + // Instantiate the canonicalized variables from user-provided signature + // (e.g., the `_` in the code above) with fresh variables. + // Then replace the bound items in the fn sig with fresh variables, + // so that they represent the view from "inside" the closure. + let user_provided_sig = self + .instantiate_canonical_with_fresh_inference_vars(body.span, &user_provided_poly_sig); + let user_provided_sig = self.infcx.replace_bound_vars_with_fresh_vars( + body.span, + LateBoundRegionConversionTime::FnCall, + user_provided_sig, + ); + + for (&user_ty, arg_decl) in user_provided_sig.inputs().iter().zip( + // In MIR, closure args begin with an implicit `self`. Skip it! + body.args_iter().skip(1).map(|local| &body.local_decls[local]), + ) { + self.ascribe_user_type_skip_wf( + arg_decl.ty, + ty::UserType::Ty(user_ty), + arg_decl.source_info.span, + ); + } + + // If the user explicitly annotated the output type, enforce it. + let output_decl = &body.local_decls[RETURN_PLACE]; + self.ascribe_user_type_skip_wf( + output_decl.ty, + ty::UserType::Ty(user_provided_sig.output()), + output_decl.source_info.span, + ); + } + #[instrument(skip(self, body, universal_regions), level = "debug")] pub(super) fn equate_inputs_and_outputs( &mut self, @@ -31,39 +77,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { debug!(?normalized_output_ty); debug!(?normalized_input_tys); - let mir_def_id = body.source.def_id().expect_local(); - - // If the user explicitly annotated the input types, extract - // those. - // - // e.g., `|x: FxHashMap<_, &'static u32>| ...` - let user_provided_sig = if !self.tcx().is_closure(mir_def_id.to_def_id()) { - None - } else { - let typeck_results = self.tcx().typeck(mir_def_id); - - typeck_results.user_provided_sigs.get(&mir_def_id).map(|user_provided_poly_sig| { - // Instantiate the canonicalized variables from - // user-provided signature (e.g., the `_` in the code - // above) with fresh variables. - let poly_sig = self.instantiate_canonical_with_fresh_inference_vars( - body.span, - &user_provided_poly_sig, - ); - - // Replace the bound items in the fn sig with fresh - // variables, so that they represent the view from - // "inside" the closure. - self.infcx.replace_bound_vars_with_fresh_vars( - body.span, - LateBoundRegionConversionTime::FnCall, - poly_sig, - ) - }) - }; - - debug!(?normalized_input_tys, ?body.local_decls); - // Equate expected input tys with those in the MIR. for (argument_index, &normalized_input_ty) in normalized_input_tys.iter().enumerate() { if argument_index + 1 >= body.local_decls.len() { @@ -86,28 +99,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - if let Some(user_provided_sig) = user_provided_sig { - for (argument_index, &user_provided_input_ty) in - user_provided_sig.inputs().iter().enumerate() - { - // In MIR, closures begin an implicit `self`, so - // argument N is stored in local N+2. - let local = Local::new(argument_index + 2); - let mir_input_ty = body.local_decls[local].ty; - let mir_input_span = body.local_decls[local].source_info.span; - - // If the user explicitly annotated the input types, enforce those. - let user_provided_input_ty = - self.normalize(user_provided_input_ty, Locations::All(mir_input_span)); - - self.equate_normalized_input_or_output( - user_provided_input_ty, - mir_input_ty, - mir_input_span, - ); - } - } - debug!( "equate_inputs_and_outputs: body.yield_ty {:?}, universal_regions.yield_ty {:?}", body.yield_ty(), @@ -153,29 +144,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { terr ); }; - - // If the user explicitly annotated the output types, enforce those. - // Note that this only happens for closures. - if let Some(user_provided_sig) = user_provided_sig { - let user_provided_output_ty = user_provided_sig.output(); - let user_provided_output_ty = - self.normalize(user_provided_output_ty, Locations::All(output_span)); - if let Err(err) = self.eq_types( - user_provided_output_ty, - mir_output_ty, - Locations::All(output_span), - ConstraintCategory::BoringNoLocation, - ) { - span_mirbug!( - self, - Location::START, - "equate_inputs_and_outputs: `{:?}=={:?}` failed with `{:?}`", - mir_output_ty, - user_provided_output_ty, - err - ); - } - } } #[instrument(skip(self), level = "debug")] diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 247607ff29e..7a3db191f0c 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -38,7 +38,6 @@ use rustc_middle::ty::{ use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::VariantIdx; -use rustc_trait_selection::traits::query::type_op; use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; @@ -197,6 +196,8 @@ pub(crate) fn type_check<'mir, 'tcx>( } checker.equate_inputs_and_outputs(&body, universal_regions, &normalized_inputs_and_output); + checker.check_signature_annotation(&body); + liveness::generate( &mut checker, body, @@ -208,7 +209,7 @@ pub(crate) fn type_check<'mir, 'tcx>( ); translate_outlives_facts(&mut checker); - let opaque_type_values = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + let opaque_type_values = infcx.take_opaque_types(); let opaque_type_values = opaque_type_values .into_iter() @@ -391,23 +392,14 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { check_err(self, promoted_body, ty, promoted_ty); } } else { - if let Err(terr) = self.cx.fully_perform_op( - locations, - ConstraintCategory::Boring, - self.cx.param_env.and(type_op::ascribe_user_type::AscribeUserType::new( - constant.literal.ty(), + self.cx.ascribe_user_type( + constant.literal.ty(), + UserType::TypeOf( uv.def.did, UserSubsts { substs: uv.substs, user_self_ty: None }, - )), - ) { - span_mirbug!( - self, - constant, - "bad constant type {:?} ({:?})", - constant, - terr - ); - } + ), + locations.span(&self.cx.body), + ); } } else if let Some(static_def_id) = constant.check_static_ptr(tcx) { let unnormalized_ty = tcx.type_of(static_def_id); @@ -1041,58 +1033,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { debug!(?self.user_type_annotations); for user_annotation in self.user_type_annotations { let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation; - let inferred_ty = self.normalize(inferred_ty, Locations::All(span)); let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty); - debug!(?annotation); - match annotation { - UserType::Ty(mut ty) => { - ty = self.normalize(ty, Locations::All(span)); - - if let Err(terr) = self.eq_types( - ty, - inferred_ty, - Locations::All(span), - ConstraintCategory::BoringNoLocation, - ) { - span_mirbug!( - self, - user_annotation, - "bad user type ({:?} = {:?}): {:?}", - ty, - inferred_ty, - terr - ); - } - - self.prove_predicate( - ty::Binder::dummy(ty::PredicateKind::WellFormed(inferred_ty.into())), - Locations::All(span), - ConstraintCategory::TypeAnnotation, - ); - } - UserType::TypeOf(def_id, user_substs) => { - if let Err(terr) = self.fully_perform_op( - Locations::All(span), - ConstraintCategory::BoringNoLocation, - self.param_env.and(type_op::ascribe_user_type::AscribeUserType::new( - inferred_ty, - def_id, - user_substs, - )), - ) { - span_mirbug!( - self, - user_annotation, - "bad user type AscribeUserType({:?}, {:?} {:?}, type_of={:?}): {:?}", - inferred_ty, - def_id, - user_substs, - self.tcx().type_of(def_id), - terr, - ); - } - } - } + self.ascribe_user_type(inferred_ty, annotation, span); } } diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 04ad77ec97e..12ecb8cf4e1 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -10,118 +10,118 @@ pushd rust command -v rg >/dev/null 2>&1 || cargo install ripgrep -rm -r src/test/ui/{extern/,unsized-locals/,lto/,linkage*} || true -for test in $(rg --files-with-matches "lto|// needs-asm-support|// needs-unwind" src/test/{ui,incremental}); do +rm -r tests/ui/{extern/,unsized-locals/,lto/,linkage*} || true +for test in $(rg --files-with-matches "lto|// needs-asm-support|// needs-unwind" tests/{ui,incremental}); do rm $test done -for test in $(rg -i --files-with-matches "//(\[\w+\])?~[^\|]*\s*ERR|// error-pattern:|// build-fail|// run-fail|-Cllvm-args" src/test/ui); do +for test in $(rg -i --files-with-matches "//(\[\w+\])?~[^\|]*\s*ERR|// error-pattern:|// build-fail|// run-fail|-Cllvm-args" tests/ui); do rm $test done -git checkout -- src/test/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed -git checkout -- src/test/ui/proc-macro/pretty-print-hack/ +git checkout -- tests/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed +git checkout -- tests/ui/proc-macro/pretty-print-hack/ # missing features # ================ # requires stack unwinding -rm src/test/incremental/change_crate_dep_kind.rs -rm src/test/incremental/issue-80691-bad-eval-cache.rs # -Cpanic=abort causes abort instead of exit(101) +rm tests/incremental/change_crate_dep_kind.rs +rm tests/incremental/issue-80691-bad-eval-cache.rs # -Cpanic=abort causes abort instead of exit(101) # requires compiling with -Cpanic=unwind -rm -r src/test/ui/macros/rfc-2011-nicer-assert-messages/ -rm -r src/test/run-make/test-benches +rm -r tests/ui/macros/rfc-2011-nicer-assert-messages/ +rm -r tests/run-make/test-benches # vendor intrinsics -rm src/test/ui/sse2.rs # cpuid not supported, so sse2 not detected -rm src/test/ui/intrinsics/const-eval-select-x86_64.rs # requires x86_64 vendor intrinsics -rm src/test/ui/simd/array-type.rs # "Index argument for `simd_insert` is not a constant" -rm src/test/ui/simd/intrinsic/generic-bitmask-pass.rs # simd_bitmask unimplemented -rm src/test/ui/simd/intrinsic/generic-as.rs # simd_as unimplemented -rm src/test/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs # simd_saturating_add unimplemented -rm src/test/ui/simd/intrinsic/float-math-pass.rs # simd_fcos unimplemented -rm src/test/ui/simd/intrinsic/generic-gather-pass.rs # simd_gather unimplemented -rm src/test/ui/simd/intrinsic/generic-select-pass.rs # simd_select_bitmask unimplemented -rm src/test/ui/simd/issue-85915-simd-ptrs.rs # simd_gather unimplemented -rm src/test/ui/simd/issue-89193.rs # simd_gather unimplemented -rm src/test/ui/simd/simd-bitmask.rs # simd_bitmask unimplemented +rm tests/ui/sse2.rs # cpuid not supported, so sse2 not detected +rm tests/ui/intrinsics/const-eval-select-x86_64.rs # requires x86_64 vendor intrinsics +rm tests/ui/simd/array-type.rs # "Index argument for `simd_insert` is not a constant" +rm tests/ui/simd/intrinsic/generic-bitmask-pass.rs # simd_bitmask unimplemented +rm tests/ui/simd/intrinsic/generic-as.rs # simd_as unimplemented +rm tests/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs # simd_saturating_add unimplemented +rm tests/ui/simd/intrinsic/float-math-pass.rs # simd_fcos unimplemented +rm tests/ui/simd/intrinsic/generic-gather-pass.rs # simd_gather unimplemented +rm tests/ui/simd/intrinsic/generic-select-pass.rs # simd_select_bitmask unimplemented +rm tests/ui/simd/issue-85915-simd-ptrs.rs # simd_gather unimplemented +rm tests/ui/simd/issue-89193.rs # simd_gather unimplemented +rm tests/ui/simd/simd-bitmask.rs # simd_bitmask unimplemented # exotic linkages -rm src/test/ui/issues/issue-33992.rs # unsupported linkages -rm src/test/incremental/hashes/function_interfaces.rs # same -rm src/test/incremental/hashes/statics.rs # same +rm tests/ui/issues/issue-33992.rs # unsupported linkages +rm tests/incremental/hashes/function_interfaces.rs # same +rm tests/incremental/hashes/statics.rs # same # variadic arguments -rm src/test/ui/abi/mir/mir_codegen_calls_variadic.rs # requires float varargs -rm src/test/ui/abi/variadic-ffi.rs # requires callee side vararg support +rm tests/ui/abi/mir/mir_codegen_calls_variadic.rs # requires float varargs +rm tests/ui/abi/variadic-ffi.rs # requires callee side vararg support # unsized locals -rm -r src/test/run-pass-valgrind/unsized-locals +rm -r tests/run-pass-valgrind/unsized-locals # misc unimplemented things -rm src/test/ui/intrinsics/intrinsic-nearby.rs # unimplemented nearbyintf32 and nearbyintf64 intrinsics -rm src/test/ui/target-feature/missing-plusminus.rs # error not implemented -rm src/test/ui/fn/dyn-fn-alignment.rs # wants a 256 byte alignment -rm -r src/test/run-make/emit-named-files # requires full --emit support -rm src/test/ui/abi/stack-probes.rs # stack probes not yet implemented -rm src/test/ui/simd/intrinsic/ptr-cast.rs # simd_expose_addr intrinsic unimplemented -rm -r src/test/run-make/repr128-dwarf # debuginfo test -rm src/test/codegen-units/item-collection/asm-sym.rs # requires support for sym in asm!() +rm tests/ui/intrinsics/intrinsic-nearby.rs # unimplemented nearbyintf32 and nearbyintf64 intrinsics +rm tests/ui/target-feature/missing-plusminus.rs # error not implemented +rm tests/ui/fn/dyn-fn-alignment.rs # wants a 256 byte alignment +rm -r tests/run-make/emit-named-files # requires full --emit support +rm tests/ui/abi/stack-probes.rs # stack probes not yet implemented +rm tests/ui/simd/intrinsic/ptr-cast.rs # simd_expose_addr intrinsic unimplemented +rm -r tests/run-make/repr128-dwarf # debuginfo test +rm tests/codegen-units/item-collection/asm-sym.rs # requires support for sym in asm!() # optimization tests # ================== -rm src/test/ui/codegen/issue-28950.rs # depends on stack size optimizations -rm src/test/ui/codegen/init-large-type.rs # same -rm src/test/ui/issues/issue-40883.rs # same -rm -r src/test/run-make/fmt-write-bloat/ # tests an optimization +rm tests/ui/codegen/issue-28950.rs # depends on stack size optimizations +rm tests/ui/codegen/init-large-type.rs # same +rm tests/ui/issues/issue-40883.rs # same +rm -r tests/run-make/fmt-write-bloat/ # tests an optimization # backend specific tests # ====================== -rm src/test/incremental/thinlto/cgu_invalidated_when_import_{added,removed}.rs # requires LLVM -rm src/test/ui/abi/stack-protector.rs # requires stack protector support +rm tests/incremental/thinlto/cgu_invalidated_when_import_{added,removed}.rs # requires LLVM +rm tests/ui/abi/stack-protector.rs # requires stack protector support # giving different but possibly correct results # ============================================= -rm src/test/ui/mir/mir_misc_casts.rs # depends on deduplication of constants -rm src/test/ui/mir/mir_raw_fat_ptr.rs # same -rm src/test/ui/consts/issue-33537.rs # same -rm src/test/ui/layout/valid_range_oob.rs # different ICE message +rm tests/ui/mir/mir_misc_casts.rs # depends on deduplication of constants +rm tests/ui/mir/mir_raw_fat_ptr.rs # same +rm tests/ui/consts/issue-33537.rs # same +rm tests/ui/layout/valid_range_oob.rs # different ICE message # doesn't work due to the way the rustc test suite is invoked. # should work when using ./x.py test the way it is intended # ============================================================ -rm -r src/test/run-make/emit-shared-files # requires the rustdoc executable in dist/bin/ -rm -r src/test/run-make/unstable-flag-required # same -rm -r src/test/run-make/rustdoc-* # same -rm -r src/test/run-make/issue-88756-default-output # same -rm -r src/test/run-make/remap-path-prefix-dwarf # requires llvm-dwarfdump -rm -r src/test/ui/consts/missing_span_in_backtrace.rs # expects sysroot source to be elsewhere +rm -r tests/run-make/emit-shared-files # requires the rustdoc executable in dist/bin/ +rm -r tests/run-make/unstable-flag-required # same +rm -r tests/run-make/rustdoc-* # same +rm -r tests/run-make/issue-88756-default-output # same +rm -r tests/run-make/remap-path-prefix-dwarf # requires llvm-dwarfdump +rm -r tests/ui/consts/missing_span_in_backtrace.rs # expects sysroot source to be elsewhere # genuine bugs # ============ -rm src/test/incremental/spike-neg1.rs # errors out for some reason -rm src/test/incremental/spike-neg2.rs # same -rm src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs # gives a stackoverflow before the backend runs -rm src/test/ui/mir/ssa-analysis-regression-50041.rs # produces ICE -rm src/test/ui/type-alias-impl-trait/assoc-projection-ice.rs # produces ICE +rm tests/incremental/spike-neg1.rs # errors out for some reason +rm tests/incremental/spike-neg2.rs # same +rm tests/ui/issues/issue-74564-if-expr-stack-overflow.rs # gives a stackoverflow before the backend runs +rm tests/ui/mir/ssa-analysis-regression-50041.rs # produces ICE +rm tests/ui/type-alias-impl-trait/assoc-projection-ice.rs # produces ICE -rm src/test/ui/simd/intrinsic/generic-reduction-pass.rs # simd_reduce_add_unordered doesn't accept an accumulator for integer vectors +rm tests/ui/simd/intrinsic/generic-reduction-pass.rs # simd_reduce_add_unordered doesn't accept an accumulator for integer vectors -rm src/test/ui/runtime/out-of-stack.rs # SIGSEGV instead of SIGABRT for some reason (#1301) +rm tests/ui/runtime/out-of-stack.rs # SIGSEGV instead of SIGABRT for some reason (#1301) # bugs in the test suite # ====================== -rm src/test/ui/backtrace.rs # TODO warning -rm src/test/ui/simple_global_asm.rs # TODO add needs-asm-support -rm src/test/ui/test-attrs/test-type.rs # TODO panic message on stderr. correct stdout +rm tests/ui/backtrace.rs # TODO warning +rm tests/ui/simple_global_asm.rs # TODO add needs-asm-support +rm tests/ui/test-attrs/test-type.rs # TODO panic message on stderr. correct stdout # not sure if this is actually a bug in the test suite, but the symbol list shows the function without leading _ for some reason -rm -r src/test/run-make/native-link-modifier-bundle -rm src/test/ui/process/nofile-limit.rs # TODO some AArch64 linking issue -rm src/test/ui/dyn-star/dispatch-on-pin-mut.rs # TODO failed assertion in vtable::get_ptr_and_method_ref +rm -r tests/run-make/native-link-modifier-bundle +rm tests/ui/process/nofile-limit.rs # TODO some AArch64 linking issue +rm tests/ui/dyn-star/dispatch-on-pin-mut.rs # TODO failed assertion in vtable::get_ptr_and_method_ref -rm src/test/ui/stdio-is-blocking.rs # really slow with unoptimized libstd +rm tests/ui/stdio-is-blocking.rs # really slow with unoptimized libstd echo "[TEST] rustc test suite" -RUST_TEST_NOCAPTURE=1 COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 src/test/{codegen-units,run-make,run-pass-valgrind,ui,incremental} +RUST_TEST_NOCAPTURE=1 COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 tests/{codegen-units,run-make,run-pass-valgrind,ui,incremental} popd diff --git a/compiler/rustc_codegen_gcc/test.sh b/compiler/rustc_codegen_gcc/test.sh index 8b390f95a4b..c5ffb763673 100755 --- a/compiler/rustc_codegen_gcc/test.sh +++ b/compiler/rustc_codegen_gcc/test.sh @@ -253,25 +253,25 @@ rustc = "$HOME/.rustup/toolchains/$rust_toolchain-$TARGET_TRIPLE/bin/rustc" EOF rustc -V | cut -d' ' -f3 | tr -d '(' - git checkout $(rustc -V | cut -d' ' -f3 | tr -d '(') src/test + git checkout $(rustc -V | cut -d' ' -f3 | tr -d '(') tests - for test in $(rg -i --files-with-matches "//(\[\w+\])?~|// error-pattern:|// build-fail|// run-fail|-Cllvm-args" src/test/ui); do + for test in $(rg -i --files-with-matches "//(\[\w+\])?~|// error-pattern:|// build-fail|// run-fail|-Cllvm-args" tests/ui); do rm $test done - git checkout -- src/test/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed + git checkout -- tests/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed - rm -r src/test/ui/{abi*,extern/,panic-runtime/,panics/,unsized-locals/,proc-macro/,threads-sendsync/,thinlto/,borrowck/,test*,*lto*.rs} || true - for test in $(rg --files-with-matches "catch_unwind|should_panic|thread|lto" src/test/ui); do + rm -r tests/ui/{abi*,extern/,panic-runtime/,panics/,unsized-locals/,proc-macro/,threads-sendsync/,thinlto/,borrowck/,test*,*lto*.rs} || true + for test in $(rg --files-with-matches "catch_unwind|should_panic|thread|lto" tests/ui); do rm $test done - git checkout src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs - git checkout src/test/ui/type-alias-impl-trait/auxiliary/cross_crate_ice2.rs + git checkout tests/ui/type-alias-impl-trait/auxiliary/cross_crate_ice.rs + git checkout tests/ui/type-alias-impl-trait/auxiliary/cross_crate_ice2.rs RUSTC_ARGS="-Zpanic-abort-tests -Csymbol-mangling-version=v0 -Zcodegen-backend="$(pwd)"/../target/"$CHANNEL"/librustc_codegen_gcc."$dylib_ext" --sysroot "$(pwd)"/../build_sysroot/sysroot -Cpanic=abort" echo "[TEST] rustc test suite" - COMPILETEST_FORCE_STAGE0=1 ./x.py test --run always --stage 0 src/test/ui/ --rustc-args "$RUSTC_ARGS" + COMPILETEST_FORCE_STAGE0=1 ./x.py test --run always --stage 0 tests/ui/ --rustc-args "$RUSTC_ARGS" } function clean_ui_tests() { diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index a6fd2a7de6b..546540dfd76 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -34,13 +34,6 @@ pub trait ArgAttributesExt { ); } -fn should_use_mutable_noalias(cx: &CodegenCx<'_, '_>) -> bool { - // LLVM prior to version 12 had known miscompiles in the presence of - // noalias attributes (see #54878), but we don't support earlier - // versions at all anymore. We now enable mutable noalias by default. - cx.tcx.sess.opts.unstable_opts.mutable_noalias.unwrap_or(true) -} - const ABI_AFFECTING_ATTRIBUTES: [(ArgAttribute, llvm::AttributeKind); 1] = [(ArgAttribute::InReg, llvm::AttributeKind::InReg)]; @@ -88,9 +81,6 @@ fn get_attrs<'ll>(this: &ArgAttributes, cx: &CodegenCx<'ll, '_>) -> SmallVec<[&' attrs.push(llattr.create_attr(cx.llcx)); } } - if regular.contains(ArgAttribute::NoAliasMutRef) && should_use_mutable_noalias(cx) { - attrs.push(llvm::AttributeKind::NoAlias.create_attr(cx.llcx)); - } } else if cx.tcx.sess.opts.unstable_opts.sanitizer.contains(SanitizerSet::MEMORY) { // If we're not optimising, *but* memory sanitizer is on, emit noundef, since it affects // memory sanitizer's behavior. diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 1ce48f82e1c..680d810f78e 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -8,8 +8,8 @@ use crate::va_arg::emit_va_arg; use crate::value::Value; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh}; -use rustc_codegen_ssa::common::span_invalid_monomorphization_error; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; +use rustc_codegen_ssa::errors::{ExpectedPointerMutability, InvalidMonomorphization}; use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; @@ -284,15 +284,11 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { _ => bug!(), }, None => { - span_invalid_monomorphization_error( - tcx.sess, + tcx.sess.emit_err(InvalidMonomorphization::BasicIntegerType { span, - &format!( - "invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", - name, ty - ), - ); + name, + ty, + }); return; } } @@ -838,40 +834,24 @@ fn generic_simd_intrinsic<'ll, 'tcx>( llret_ty: &'ll Type, span: Span, ) -> Result<&'ll Value, ()> { - // macros for error handling: - #[allow(unused_macro_rules)] - macro_rules! emit_error { - ($msg: tt) => { - emit_error!($msg, ) - }; - ($msg: tt, $($fmt: tt)*) => { - span_invalid_monomorphization_error( - bx.sess(), span, - &format!(concat!("invalid monomorphization of `{}` intrinsic: ", $msg), - name, $($fmt)*)); - } - } - macro_rules! return_error { - ($($fmt: tt)*) => { - { - emit_error!($($fmt)*); - return Err(()); - } - } + ($diag: expr) => {{ + bx.sess().emit_err($diag); + return Err(()); + }}; } macro_rules! require { - ($cond: expr, $($fmt: tt)*) => { + ($cond: expr, $diag: expr) => { if !$cond { - return_error!($($fmt)*); + return_error!($diag); } }; } macro_rules! require_simd { - ($ty: expr, $position: expr) => { - require!($ty.is_simd(), "expected SIMD {} type, found non-SIMD `{}`", $position, $ty) + ($ty: expr, $diag: expr) => { + require!($ty.is_simd(), $diag) }; } @@ -881,7 +861,11 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let arg_tys = sig.inputs(); if name == sym::simd_select_bitmask { - require_simd!(arg_tys[1], "argument"); + require_simd!( + arg_tys[1], + InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] } + ); + let (len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); let expected_int_bits = (len.max(8) - 1).next_power_of_two(); @@ -902,12 +886,13 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let ptr = bx.pointercast(place.llval, bx.cx.type_ptr_to(int_ty)); bx.load(int_ty, ptr, Align::ONE) } - _ => return_error!( - "invalid bitmask `{}`, expected `u{}` or `[u8; {}]`", + _ => return_error!(InvalidMonomorphization::InvalidBitmask { + span, + name, mask_ty, expected_int_bits, expected_bytes - ), + }), }; let i1 = bx.type_i1(); @@ -919,7 +904,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } // every intrinsic below takes a SIMD vector as its first argument - require_simd!(arg_tys[0], "input"); + require_simd!(arg_tys[0], InvalidMonomorphization::SimdInput { span, name, ty: arg_tys[0] }); let in_ty = arg_tys[0]; let comparison = match name { @@ -934,23 +919,24 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx()); if let Some(cmp_op) = comparison { - require_simd!(ret_ty, "return"); + require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); + require!( in_len == out_len, - "expected return type with length {} (same as input type `{}`), \ - found `{}` with length {}", - in_len, - in_ty, - ret_ty, - out_len + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } ); require!( bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer, - "expected return type with integer elements, found `{}` with non-integer `{}`", - ret_ty, - out_ty + InvalidMonomorphization::ReturnIntegerType { span, name, ret_ty, out_ty } ); return Ok(compare_simd_types( @@ -975,10 +961,11 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span_bug!(span, "could not evaluate shuffle index array length") }) } - _ => return_error!( - "simd_shuffle index must be an array of `u32`, got `{}`", - args[2].layout.ty - ), + _ => return_error!(InvalidMonomorphization::SimdShuffle { + span, + name, + ty: args[2].layout.ty + }), } } else { stripped.parse().unwrap_or_else(|_| { @@ -986,23 +973,15 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }) }; - require_simd!(ret_ty, "return"); + require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); require!( out_len == n, - "expected return type of length {}, found `{}` with length {}", - n, - ret_ty, - out_len + InvalidMonomorphization::ReturnLength { span, name, in_len: n, ret_ty, out_len } ); require!( in_elem == out_ty, - "expected return element type `{}` (element of input `{}`), \ - found `{}` with element type `{}`", - in_elem, - in_ty, - ret_ty, - out_ty + InvalidMonomorphization::ReturnElement { span, name, in_elem, in_ty, ret_ty, out_ty } ); let total_len = u128::from(in_len) * 2; @@ -1015,15 +994,20 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let val = bx.const_get_elt(vector, i as u64); match bx.const_to_opt_u128(val, true) { None => { - emit_error!("shuffle index #{} is not a constant", arg_idx); + bx.sess().emit_err(InvalidMonomorphization::ShuffleIndexNotConstant { + span, + name, + arg_idx, + }); None } Some(idx) if idx >= total_len => { - emit_error!( - "shuffle index #{} is out of bounds (limit {})", + bx.sess().emit_err(InvalidMonomorphization::ShuffleIndexOutOfBounds { + span, + name, arg_idx, - total_len - ); + total_len, + }); None } Some(idx) => Some(bx.const_i32(idx as i32)), @@ -1044,10 +1028,13 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::simd_insert { require!( in_elem == arg_tys[2], - "expected inserted type `{}` (element of input `{}`), found `{}`", - in_elem, - in_ty, - arg_tys[2] + InvalidMonomorphization::InsertedType { + span, + name, + in_elem, + in_ty, + out_ty: arg_tys[2] + } ); return Ok(bx.insert_element( args[0].immediate(), @@ -1058,10 +1045,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::simd_extract { require!( ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, - in_ty, - ret_ty + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } ); return Ok(bx.extract_element(args[0].immediate(), args[1].immediate())); } @@ -1069,17 +1053,18 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::simd_select { let m_elem_ty = in_elem; let m_len = in_len; - require_simd!(arg_tys[1], "argument"); + require_simd!( + arg_tys[1], + InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] } + ); let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); require!( m_len == v_len, - "mismatched lengths: mask length `{}` != other vector length `{}`", - m_len, - v_len + InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len } ); match m_elem_ty.kind() { ty::Int(_) => {} - _ => return_error!("mask element type is `{}`, expected `i_`", m_elem_ty), + _ => return_error!(InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }), } // truncate the mask to a vector of i1s let i1 = bx.type_i1(); @@ -1111,11 +1096,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>( args[0].immediate(), i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), ), - _ => return_error!( - "vector argument `{}`'s element type `{}`, expected integer element type", + _ => return_error!(InvalidMonomorphization::VectorArgument { + span, + name, in_ty, in_elem - ), + }), }; // Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position. @@ -1150,12 +1136,13 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let ptr = bx.pointercast(ptr, bx.cx.type_ptr_to(array_ty)); return Ok(bx.load(array_ty, ptr, Align::ONE)); } - _ => return_error!( - "cannot return `{}`, expected `u{}` or `[u8; {}]`", + _ => return_error!(InvalidMonomorphization::CannotReturn { + span, + name, ret_ty, expected_int_bits, expected_bytes - ), + }), } } @@ -1168,25 +1155,11 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span: Span, args: &[OperandRef<'tcx, &'ll Value>], ) -> Result<&'ll Value, ()> { - #[allow(unused_macro_rules)] - macro_rules! emit_error { - ($msg: tt) => { - emit_error!($msg, ) - }; - ($msg: tt, $($fmt: tt)*) => { - span_invalid_monomorphization_error( - bx.sess(), span, - &format!(concat!("invalid monomorphization of `{}` intrinsic: ", $msg), - name, $($fmt)*)); - } - } macro_rules! return_error { - ($($fmt: tt)*) => { - { - emit_error!($($fmt)*); - return Err(()); - } - } + ($diag: expr) => {{ + bx.sess().emit_err($diag); + return Err(()); + }}; } let (elem_ty_str, elem_ty) = if let ty::Float(f) = in_elem.kind() { @@ -1194,16 +1167,15 @@ fn generic_simd_intrinsic<'ll, 'tcx>( match f.bit_width() { 32 => ("f32", elem_ty), 64 => ("f64", elem_ty), - _ => { - return_error!( - "unsupported element type `{}` of floating-point vector `{}`", - f.name_str(), - in_ty - ); - } + _ => return_error!(InvalidMonomorphization::FloatingPointVector { + span, + name, + f_ty: *f, + in_ty, + }), } } else { - return_error!("`{}` is not a floating-point type", in_ty); + return_error!(InvalidMonomorphization::FloatingPointType { span, name, in_ty }); }; let vec_ty = bx.type_vector(elem_ty, in_len); @@ -1225,7 +1197,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)), sym::simd_round => ("round", bx.type_func(&[vec_ty], vec_ty)), sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)), - _ => return_error!("unrecognized intrinsic `{}`", name), + _ => return_error!(InvalidMonomorphization::UnrecognizedIntrinsic { span, name }), }; let llvm_name = &format!("llvm.{0}.v{1}{2}", intr_name, in_len, elem_ty_str); let f = bx.declare_cfn(llvm_name, llvm::UnnamedAddr::No, fn_ty); @@ -1319,37 +1291,48 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // * M: any integer width is supported, will be truncated to i1 // All types must be simd vector types - require_simd!(in_ty, "first"); - require_simd!(arg_tys[1], "second"); - require_simd!(arg_tys[2], "third"); - require_simd!(ret_ty, "return"); + require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty }); + require_simd!( + arg_tys[1], + InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] } + ); + require_simd!( + arg_tys[2], + InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] } + ); + require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); // Of the same length: let (out_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); let (out_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx()); require!( in_len == out_len, - "expected {} argument with length {} (same as input type `{}`), \ - found `{}` with length {}", - "second", - in_len, - in_ty, - arg_tys[1], - out_len + InvalidMonomorphization::SecondArgumentLength { + span, + name, + in_len, + in_ty, + arg_ty: arg_tys[1], + out_len + } ); require!( in_len == out_len2, - "expected {} argument with length {} (same as input type `{}`), \ - found `{}` with length {}", - "third", - in_len, - in_ty, - arg_tys[2], - out_len2 + InvalidMonomorphization::ThirdArgumentLength { + span, + name, + in_len, + in_ty, + arg_ty: arg_tys[2], + out_len: out_len2 + } ); // The return type must match the first argument type - require!(ret_ty == in_ty, "expected return type `{}`, found `{}`", in_ty, ret_ty); + require!( + ret_ty == in_ty, + InvalidMonomorphization::ExpectedReturnType { span, name, in_ty, ret_ty } + ); // This counts how many pointers fn ptr_count(t: Ty<'_>) -> usize { @@ -1376,15 +1359,15 @@ fn generic_simd_intrinsic<'ll, 'tcx>( _ => { require!( false, - "expected element type `{}` of second argument `{}` \ - to be a pointer to the element type `{}` of the first \ - argument `{}`, found `{}` != `*_ {}`", - element_ty1, - arg_tys[1], - in_elem, - in_ty, - element_ty1, - in_elem + InvalidMonomorphization::ExpectedElementType { + span, + name, + expected_element: element_ty1, + second_arg: arg_tys[1], + in_elem, + in_ty, + mutability: ExpectedPointerMutability::Not, + } ); unreachable!(); } @@ -1400,10 +1383,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>( _ => { require!( false, - "expected element type `{}` of third argument `{}` \ - to be a signed integer type", - element_ty2, - arg_tys[2] + InvalidMonomorphization::ThirdArgElementType { + span, + name, + expected_element: element_ty2, + third_arg: arg_tys[2] + } ); } } @@ -1452,32 +1437,40 @@ fn generic_simd_intrinsic<'ll, 'tcx>( // * M: any integer width is supported, will be truncated to i1 // All types must be simd vector types - require_simd!(in_ty, "first"); - require_simd!(arg_tys[1], "second"); - require_simd!(arg_tys[2], "third"); + require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty }); + require_simd!( + arg_tys[1], + InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] } + ); + require_simd!( + arg_tys[2], + InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] } + ); // Of the same length: let (element_len1, _) = arg_tys[1].simd_size_and_type(bx.tcx()); let (element_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx()); require!( in_len == element_len1, - "expected {} argument with length {} (same as input type `{}`), \ - found `{}` with length {}", - "second", - in_len, - in_ty, - arg_tys[1], - element_len1 + InvalidMonomorphization::SecondArgumentLength { + span, + name, + in_len, + in_ty, + arg_ty: arg_tys[1], + out_len: element_len1 + } ); require!( in_len == element_len2, - "expected {} argument with length {} (same as input type `{}`), \ - found `{}` with length {}", - "third", - in_len, - in_ty, - arg_tys[2], - element_len2 + InvalidMonomorphization::ThirdArgumentLength { + span, + name, + in_len, + in_ty, + arg_ty: arg_tys[2], + out_len: element_len2 + } ); // This counts how many pointers @@ -1508,15 +1501,15 @@ fn generic_simd_intrinsic<'ll, 'tcx>( _ => { require!( false, - "expected element type `{}` of second argument `{}` \ - to be a pointer to the element type `{}` of the first \ - argument `{}`, found `{}` != `*mut {}`", - element_ty1, - arg_tys[1], - in_elem, - in_ty, - element_ty1, - in_elem + InvalidMonomorphization::ExpectedElementType { + span, + name, + expected_element: element_ty1, + second_arg: arg_tys[1], + in_elem, + in_ty, + mutability: ExpectedPointerMutability::Mut, + } ); unreachable!(); } @@ -1531,10 +1524,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>( _ => { require!( false, - "expected element type `{}` of third argument `{}` \ - be a signed integer type", - element_ty2, - arg_tys[2] + InvalidMonomorphization::ThirdArgElementType { + span, + name, + expected_element: element_ty2, + third_arg: arg_tys[2] + } ); } } @@ -1581,10 +1576,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( if name == sym::$name { require!( ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, - in_ty, - ret_ty + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } ); return match in_elem.kind() { ty::Int(_) | ty::Uint(_) => { @@ -1607,25 +1599,28 @@ fn generic_simd_intrinsic<'ll, 'tcx>( 32 => bx.const_real(bx.type_f32(), $identity), 64 => bx.const_real(bx.type_f64(), $identity), v => return_error!( - r#" -unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, - sym::$name, - in_ty, - in_elem, - v, - ret_ty + InvalidMonomorphization::UnsupportedSymbolOfSize { + span, + name, + symbol: sym::$name, + in_ty, + in_elem, + size: v, + ret_ty + } ), } }; Ok(bx.$float_reduce(acc, args[0].immediate())) } - _ => return_error!( - "unsupported {} from `{}` with element `{}` to `{}`", - sym::$name, + _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { + span, + name, + symbol: sym::$name, in_ty, in_elem, ret_ty - ), + }), }; } }; @@ -1653,22 +1648,20 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, if name == sym::$name { require!( ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, - in_ty, - ret_ty + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } ); return match in_elem.kind() { ty::Int(_i) => Ok(bx.$int_red(args[0].immediate(), true)), ty::Uint(_u) => Ok(bx.$int_red(args[0].immediate(), false)), ty::Float(_f) => Ok(bx.$float_red(args[0].immediate())), - _ => return_error!( - "unsupported {} from `{}` with element `{}` to `{}`", - sym::$name, + _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { + span, + name, + symbol: sym::$name, in_ty, in_elem, ret_ty - ), + }), }; } }; @@ -1686,22 +1679,20 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, let input = if !$boolean { require!( ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, - in_ty, - ret_ty + InvalidMonomorphization::ReturnType { span, name, in_elem, in_ty, ret_ty } ); args[0].immediate() } else { match in_elem.kind() { ty::Int(_) | ty::Uint(_) => {} - _ => return_error!( - "unsupported {} from `{}` with element `{}` to `{}`", - sym::$name, + _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { + span, + name, + symbol: sym::$name, in_ty, in_elem, ret_ty - ), + }), } // boolean reductions operate on vectors of i1s: @@ -1714,13 +1705,14 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, let r = bx.$red(input); Ok(if !$boolean { r } else { bx.zext(r, bx.type_bool()) }) } - _ => return_error!( - "unsupported {} from `{}` with element `{}` to `{}`", - sym::$name, + _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { + span, + name, + symbol: sym::$name, in_ty, in_elem, ret_ty - ), + }), }; } }; @@ -1733,16 +1725,18 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, bitwise_red!(simd_reduce_any: vector_reduce_or, true); if name == sym::simd_cast_ptr { - require_simd!(ret_ty, "return"); + require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); require!( in_len == out_len, - "expected return type with length {} (same as input type `{}`), \ - found `{}` with length {}", - in_len, - in_ty, - ret_ty, - out_len + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } ); match in_elem.kind() { @@ -1751,9 +1745,14 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); assert!(!check_sized); // we are in codegen, so we shouldn't see these types - require!(metadata.is_unit(), "cannot cast fat pointer `{}`", in_elem) + require!( + metadata.is_unit(), + InvalidMonomorphization::CastFatPointer { span, name, ty: in_elem } + ); + } + _ => { + return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem }) } - _ => return_error!("expected pointer, got `{}`", in_elem), } match out_elem.kind() { ty::RawPtr(p) => { @@ -1761,9 +1760,14 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty) }); assert!(!check_sized); // we are in codegen, so we shouldn't see these types - require!(metadata.is_unit(), "cannot cast to fat pointer `{}`", out_elem) + require!( + metadata.is_unit(), + InvalidMonomorphization::CastFatPointer { span, name, ty: out_elem } + ); + } + _ => { + return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem }) } - _ => return_error!("expected pointer, got `{}`", out_elem), } if in_elem == out_elem { @@ -1774,66 +1778,76 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, } if name == sym::simd_expose_addr { - require_simd!(ret_ty, "return"); + require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); require!( in_len == out_len, - "expected return type with length {} (same as input type `{}`), \ - found `{}` with length {}", - in_len, - in_ty, - ret_ty, - out_len + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } ); match in_elem.kind() { ty::RawPtr(_) => {} - _ => return_error!("expected pointer, got `{}`", in_elem), + _ => { + return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: in_elem }) + } } match out_elem.kind() { ty::Uint(ty::UintTy::Usize) => {} - _ => return_error!("expected `usize`, got `{}`", out_elem), + _ => return_error!(InvalidMonomorphization::ExpectedUsize { span, name, ty: out_elem }), } return Ok(bx.ptrtoint(args[0].immediate(), llret_ty)); } if name == sym::simd_from_exposed_addr { - require_simd!(ret_ty, "return"); + require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); require!( in_len == out_len, - "expected return type with length {} (same as input type `{}`), \ - found `{}` with length {}", - in_len, - in_ty, - ret_ty, - out_len + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } ); match in_elem.kind() { ty::Uint(ty::UintTy::Usize) => {} - _ => return_error!("expected `usize`, got `{}`", in_elem), + _ => return_error!(InvalidMonomorphization::ExpectedUsize { span, name, ty: in_elem }), } match out_elem.kind() { ty::RawPtr(_) => {} - _ => return_error!("expected pointer, got `{}`", out_elem), + _ => { + return_error!(InvalidMonomorphization::ExpectedPointer { span, name, ty: out_elem }) + } } return Ok(bx.inttoptr(args[0].immediate(), llret_ty)); } if name == sym::simd_cast || name == sym::simd_as { - require_simd!(ret_ty, "return"); + require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty }); let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); require!( in_len == out_len, - "expected return type with length {} (same as input type `{}`), \ - found `{}` with length {}", - in_len, - in_ty, - ret_ty, - out_len + InvalidMonomorphization::ReturnLengthInputType { + span, + name, + in_len, + in_ty, + ret_ty, + out_len + } ); // casting cares about nominal type, not just structural type if in_elem == out_elem { @@ -1912,11 +1926,14 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, } require!( false, - "unsupported cast from `{}` with element `{}` to `{}` with element `{}`", - in_ty, - in_elem, - ret_ty, - out_elem + InvalidMonomorphization::UnsupportedCast { + span, + name, + in_ty, + in_elem, + ret_ty, + out_elem + } ); } macro_rules! arith_binary { @@ -1928,10 +1945,10 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, })* _ => {}, } - require!(false, - "unsupported operation on `{}` with element `{}`", - in_ty, - in_elem) + require!( + false, + InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem } + ); })* } } @@ -1959,10 +1976,10 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, })* _ => {}, } - require!(false, - "unsupported operation on `{}` with element `{}`", - in_ty, - in_elem) + require!( + false, + InvalidMonomorphization::UnsupportedOperation { span, name, in_ty, in_elem } + ); })* } } @@ -2000,12 +2017,12 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_int_from_ty(i)), ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_uint_from_ty(i)), _ => { - return_error!( - "expected element type `{}` of vector type `{}` \ - to be a signed or unsigned integer type", - arg_tys[0].simd_size_and_type(bx.tcx()).1, - arg_tys[0] - ); + return_error!(InvalidMonomorphization::ExpectedVectorElementType { + span, + name, + expected_element: arg_tys[0].simd_size_and_type(bx.tcx()).1, + vector_type: arg_tys[0] + }); } }; let llvm_intrinsic = &format!( diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 7ada9c0e12a..0d2d2ec68a2 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -27,6 +27,7 @@ rustc_arena = { path = "../rustc_arena" } rustc_ast = { path = "../rustc_ast" } rustc_span = { path = "../rustc_span" } rustc_middle = { path = "../rustc_middle" } +rustc_type_ir = { path = "../rustc_type_ir" } rustc_attr = { path = "../rustc_attr" } rustc_symbol_mangling = { path = "../rustc_symbol_mangling" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 36f7e5954bf..342abf81f6a 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -11,7 +11,7 @@ use rustc_metadata::find_native_static_library; use rustc_metadata::fs::{emit_wrapper_file, METADATA_FILENAME}; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; -use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Lto, Strip}; +use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip}; use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind}; use rustc_session::cstore::DllImport; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; @@ -208,16 +208,16 @@ pub fn link_binary<'a>( Ok(()) } +// Crate type is not passed when calculating the dylibs to include for LTO. In that case all +// crate types must use the same dependency formats. pub fn each_linked_rlib( - sess: &Session, info: &CrateInfo, + crate_type: Option<CrateType>, f: &mut dyn FnMut(CrateNum, &Path), ) -> Result<(), errors::LinkRlibError> { let crates = info.used_crates.iter(); - let mut fmts = None; - let lto_active = matches!(sess.lto(), Lto::Fat | Lto::Thin); - if lto_active { + let fmts = if crate_type.is_none() { for combination in info.dependency_formats.iter().combinations(2) { let (ty1, list1) = &combination[0]; let (ty2, list2) = &combination[1]; @@ -230,27 +230,23 @@ pub fn each_linked_rlib( }); } } - } - - for (ty, list) in info.dependency_formats.iter() { - match ty { - CrateType::Executable - | CrateType::Staticlib - | CrateType::Cdylib - | CrateType::ProcMacro => { - fmts = Some(list); - break; - } - CrateType::Dylib if lto_active => { - fmts = Some(list); - break; - } - _ => {} + if info.dependency_formats.is_empty() { + return Err(errors::LinkRlibError::MissingFormat); } - } - let Some(fmts) = fmts else { - return Err(errors::LinkRlibError::MissingFormat); + &info.dependency_formats[0].1 + } else { + let fmts = info + .dependency_formats + .iter() + .find_map(|&(ty, ref list)| if Some(ty) == crate_type { Some(list) } else { None }); + + let Some(fmts) = fmts else { + return Err(errors::LinkRlibError::MissingFormat); + }; + + fmts }; + for &cnum in crates { match fmts.get(cnum.as_usize() - 1) { Some(&Linkage::NotLinked | &Linkage::Dynamic | &Linkage::IncludedFromDylib) => continue, @@ -516,64 +512,71 @@ fn link_staticlib<'a>( )?; let mut all_native_libs = vec![]; - let res = each_linked_rlib(sess, &codegen_results.crate_info, &mut |cnum, path| { - let name = codegen_results.crate_info.crate_name[&cnum]; - let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; - - // Here when we include the rlib into our staticlib we need to make a - // decision whether to include the extra object files along the way. - // These extra object files come from statically included native - // libraries, but they may be cfg'd away with #[link(cfg(..))]. - // - // This unstable feature, though, only needs liblibc to work. The only - // use case there is where musl is statically included in liblibc.rlib, - // so if we don't want the included version we just need to skip it. As - // a result the logic here is that if *any* linked library is cfg'd away - // we just skip all object files. - // - // Clearly this is not sufficient for a general purpose feature, and - // we'd want to read from the library's metadata to determine which - // object files come from where and selectively skip them. - let skip_object_files = native_libs.iter().any(|lib| { - matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. }) - && !relevant_lib(sess, lib) - }); + let res = each_linked_rlib( + &codegen_results.crate_info, + Some(CrateType::Staticlib), + &mut |cnum, path| { + let name = codegen_results.crate_info.crate_name[&cnum]; + let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; + + // Here when we include the rlib into our staticlib we need to make a + // decision whether to include the extra object files along the way. + // These extra object files come from statically included native + // libraries, but they may be cfg'd away with #[link(cfg(..))]. + // + // This unstable feature, though, only needs liblibc to work. The only + // use case there is where musl is statically included in liblibc.rlib, + // so if we don't want the included version we just need to skip it. As + // a result the logic here is that if *any* linked library is cfg'd away + // we just skip all object files. + // + // Clearly this is not sufficient for a general purpose feature, and + // we'd want to read from the library's metadata to determine which + // object files come from where and selectively skip them. + let skip_object_files = native_libs.iter().any(|lib| { + matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. }) + && !relevant_lib(sess, lib) + }); - let lto = are_upstream_rust_objects_already_included(sess) - && !ignored_for_lto(sess, &codegen_results.crate_info, cnum); + let lto = are_upstream_rust_objects_already_included(sess) + && !ignored_for_lto(sess, &codegen_results.crate_info, cnum); - // Ignoring obj file starting with the crate name - // as simple comparison is not enough - there - // might be also an extra name suffix - let obj_start = name.as_str().to_owned(); + // Ignoring obj file starting with the crate name + // as simple comparison is not enough - there + // might be also an extra name suffix + let obj_start = name.as_str().to_owned(); - ab.add_archive( - path, - Box::new(move |fname: &str| { - // Ignore metadata files, no matter the name. - if fname == METADATA_FILENAME { - return true; - } + ab.add_archive( + path, + Box::new(move |fname: &str| { + // Ignore metadata files, no matter the name. + if fname == METADATA_FILENAME { + return true; + } - // Don't include Rust objects if LTO is enabled - if lto && looks_like_rust_object_file(fname) { - return true; - } + // Don't include Rust objects if LTO is enabled + if lto && looks_like_rust_object_file(fname) { + return true; + } - // Otherwise if this is *not* a rust object and we're skipping - // objects then skip this file - if skip_object_files && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) { - return true; - } + // Otherwise if this is *not* a rust object and we're skipping + // objects then skip this file + if skip_object_files + && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) + { + return true; + } - // ok, don't skip this - false - }), - ) - .unwrap(); + // ok, don't skip this + false + }), + ) + .unwrap(); - all_native_libs.extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned()); - }); + all_native_libs + .extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned()); + }, + ); if let Err(e) = res { sess.emit_fatal(e); } @@ -1228,12 +1231,21 @@ pub fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) { sess.emit_fatal(errors::LinkerFileStem); }); + // Remove any version postfix. + let stem = stem + .rsplit_once('-') + .and_then(|(lhs, rhs)| rhs.chars().all(char::is_numeric).then_some(lhs)) + .unwrap_or(stem); + + // GCC can have an optional target prefix. let flavor = if stem == "emcc" { LinkerFlavor::EmCc } else if stem == "gcc" || stem.ends_with("-gcc") + || stem == "g++" + || stem.ends_with("-g++") || stem == "clang" - || stem.ends_with("-clang") + || stem == "clang++" { LinkerFlavor::from_cli(LinkerFlavorCli::Gcc, &sess.target) } else if stem == "wasm-ld" || stem.ends_with("-wasm-ld") { @@ -1354,7 +1366,8 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) { if !lib_args.is_empty() { sess.emit_note(errors::StaticLibraryNativeArtifacts); // Prefix for greppability - sess.emit_note(errors::NativeStaticLibs { arguments: lib_args.join(" ") }); + // Note: This must not be translated as tools are allowed to depend on this exact string. + sess.note_without_error(&format!("native-static-libs: {}", &lib_args.join(" "))); } } @@ -2612,7 +2625,7 @@ fn add_static_crate<'a>( sess.target.no_builtins || !codegen_results.crate_info.is_no_builtins.contains(&cnum); let mut archive = archive_builder_builder.new_archive_builder(sess); - if let Err(e) = archive.add_archive( + if let Err(error) = archive.add_archive( cratepath, Box::new(move |f| { if f == METADATA_FILENAME { @@ -2652,7 +2665,7 @@ fn add_static_crate<'a>( false }), ) { - sess.fatal(&format!("failed to build archive from rlib: {}", e)); + sess.emit_fatal(errors::RlibArchiveBuildFailure { error }); } if archive.build(&dst) { link_upstream(&dst); diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs index 237b8849a35..7d3c14fec5f 100644 --- a/compiler/rustc_codegen_ssa/src/back/metadata.rs +++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs @@ -171,11 +171,23 @@ pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static }; e_flags } - Architecture::Riscv64 if sess.target.options.features.contains("+d") => { - // copied from `riscv64-linux-gnu-gcc foo.c -c`, note though - // that the `+d` target feature represents whether the double - // float abi is enabled. - let e_flags = elf::EF_RISCV_RVC | elf::EF_RISCV_FLOAT_ABI_DOUBLE; + Architecture::Riscv32 | Architecture::Riscv64 => { + // Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc + let mut e_flags: u32 = 0x0; + let features = &sess.target.options.features; + // Check if compressed is enabled + if features.contains("+c") { + e_flags |= elf::EF_RISCV_RVC; + } + + // Select the appropriate floating-point ABI + if features.contains("+d") { + e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE; + } else if features.contains("+f") { + e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE; + } else { + e_flags |= elf::EF_RISCV_FLOAT_ABI_SOFT; + } e_flags } _ => 0, diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 1a7de1a184a..25dc88c535d 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1002,7 +1002,7 @@ fn start_executing_work<B: ExtraBackendMethods>( let sess = tcx.sess; let mut each_linked_rlib_for_lto = Vec::new(); - drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| { + drop(link::each_linked_rlib(crate_info, None, &mut |cnum, path| { if link::ignored_for_lto(sess, crate_info, cnum) { return; } @@ -1098,7 +1098,7 @@ fn start_executing_work<B: ExtraBackendMethods>( // There are a few environmental pre-conditions that shape how the system // is set up: // - // - Error reporting only can happen on the main thread because that's the + // - Error reporting can only happen on the main thread because that's the // only place where we have access to the compiler `Session`. // - LLVM work can be done on any thread. // - Codegen can only happen on the main thread. @@ -1110,16 +1110,16 @@ fn start_executing_work<B: ExtraBackendMethods>( // Error Reporting // =============== // The error reporting restriction is handled separately from the rest: We - // set up a `SharedEmitter` the holds an open channel to the main thread. + // set up a `SharedEmitter` that holds an open channel to the main thread. // When an error occurs on any thread, the shared emitter will send the // error message to the receiver main thread (`SharedEmitterMain`). The // main thread will periodically query this error message queue and emit // any error messages it has received. It might even abort compilation if - // has received a fatal error. In this case we rely on all other threads + // it has received a fatal error. In this case we rely on all other threads // being torn down automatically with the main thread. // Since the main thread will often be busy doing codegen work, error // reporting will be somewhat delayed, since the message queue can only be - // checked in between to work packages. + // checked in between two work packages. // // Work Processing Infrastructure // ============================== @@ -1133,7 +1133,7 @@ fn start_executing_work<B: ExtraBackendMethods>( // thread about what work to do when, and it will spawn off LLVM worker // threads as open LLVM WorkItems become available. // - // The job of the main thread is to codegen CGUs into LLVM work package + // The job of the main thread is to codegen CGUs into LLVM work packages // (since the main thread is the only thread that can do this). The main // thread will block until it receives a message from the coordinator, upon // which it will codegen one CGU, send it to the coordinator and block @@ -1142,10 +1142,10 @@ fn start_executing_work<B: ExtraBackendMethods>( // // The coordinator keeps a queue of LLVM WorkItems, and when a `Token` is // available, it will spawn off a new LLVM worker thread and let it process - // that a WorkItem. When a LLVM worker thread is done with its WorkItem, + // a WorkItem. When a LLVM worker thread is done with its WorkItem, // it will just shut down, which also frees all resources associated with // the given LLVM module, and sends a message to the coordinator that the - // has been completed. + // WorkItem has been completed. // // Work Scheduling // =============== @@ -1165,7 +1165,7 @@ fn start_executing_work<B: ExtraBackendMethods>( // // Doing LLVM Work on the Main Thread // ---------------------------------- - // Since the main thread owns the compiler processes implicit `Token`, it is + // Since the main thread owns the compiler process's implicit `Token`, it is // wasteful to keep it blocked without doing any work. Therefore, what we do // in this case is: We spawn off an additional LLVM worker thread that helps // reduce the queue. The work it is doing corresponds to the implicit @@ -1216,7 +1216,7 @@ fn start_executing_work<B: ExtraBackendMethods>( // ------------------------------ // // The final job the coordinator thread is responsible for is managing LTO - // and how that works. When LTO is requested what we'll to is collect all + // and how that works. When LTO is requested what we'll do is collect all // optimized LLVM modules into a local vector on the coordinator. Once all // modules have been codegened and optimized we hand this to the `lto` // module for further optimization. The `lto` module will return back a list diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 8b34be38580..f7312f6fcda 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -5,6 +5,7 @@ use crate::back::write::{ submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, ComputedLtoType, OngoingCodegen, }; use crate::common::{IntPredicate, RealPredicate, TypeKind}; +use crate::errors; use crate::meth; use crate::mir; use crate::mir::operand::OperandValue; @@ -152,9 +153,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ( &ty::Dynamic(ref data_a, _, src_dyn_kind), &ty::Dynamic(ref data_b, _, target_dyn_kind), - ) => { - assert_eq!(src_dyn_kind, target_dyn_kind); - + ) if src_dyn_kind == target_dyn_kind => { let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); if data_a.principal_def_id() == data_b.principal_def_id() { @@ -451,10 +450,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let Some(llfn) = cx.declare_c_main(llfty) else { // FIXME: We should be smart and show a better diagnostic here. let span = cx.tcx().def_span(rust_main_def_id); - cx.sess() - .struct_span_err(span, "entry symbol `main` declared multiple times") - .help("did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead") - .emit(); + cx.sess().emit_err(errors::MultipleMainFunctions { span }); cx.sess().abort_if_errors(); bug!(); }; @@ -595,8 +591,8 @@ pub fn codegen_crate<B: ExtraBackendMethods>( &metadata, &exported_symbols::metadata_symbol_name(tcx), ); - if let Err(err) = std::fs::write(&file_name, data) { - tcx.sess.fatal(&format!("error writing metadata object file: {}", err)); + if let Err(error) = std::fs::write(&file_name, data) { + tcx.sess.emit_fatal(errors::MetadataObjectFileWrite { error }); } Some(CompiledModule { name: metadata_cgu_name, @@ -815,11 +811,7 @@ impl CrateInfo { let subsystem = tcx.sess.first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem); let windows_subsystem = subsystem.map(|subsystem| { if subsystem != sym::windows && subsystem != sym::console { - tcx.sess.fatal(&format!( - "invalid windows subsystem `{}`, only \ - `windows` and `console` are allowed", - subsystem - )); + tcx.sess.emit_fatal(errors::InvalidWindowsSubsystem { subsystem }); } subsystem.to_string() }); diff --git a/compiler/rustc_codegen_ssa/src/common.rs b/compiler/rustc_codegen_ssa/src/common.rs index 71f9179d02c..e1abb73a504 100644 --- a/compiler/rustc_codegen_ssa/src/common.rs +++ b/compiler/rustc_codegen_ssa/src/common.rs @@ -1,10 +1,8 @@ #![allow(non_camel_case_types)] -use rustc_errors::struct_span_err; use rustc_hir::LangItem; use rustc_middle::mir::interpret::ConstValue; use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt}; -use rustc_session::Session; use rustc_span::Span; use crate::base; @@ -193,10 +191,6 @@ pub fn shift_mask_val<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } -pub fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) { - struct_span_err!(a, b, E0511, "{}", c).emit(); -} - pub fn asm_const_to_str<'tcx>( tcx: TyCtxt<'tcx>, sp: Span, diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 3ae6c531b44..1599ccbb259 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -93,6 +93,7 @@ fn push_debuginfo_type_name<'tcx>( Err(e) => { // Computing the layout can still fail here, e.g. if the target architecture // cannot represent the type. See https://github.com/rust-lang/rust/issues/94961. + // FIXME: migrate once `rustc_middle::mir::interpret::InterpError` is translatable. tcx.sess.fatal(&format!("{}", e)); } } diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 0620000201f..d81252653df 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -6,7 +6,9 @@ use rustc_errors::{ IntoDiagnosticArg, }; use rustc_macros::Diagnostic; +use rustc_middle::ty::Ty; use rustc_span::{Span, Symbol}; +use rustc_type_ir::FloatTy; use std::borrow::Cow; use std::io::Error; use std::path::{Path, PathBuf}; @@ -445,12 +447,6 @@ pub struct LinkerFileStem; pub struct StaticLibraryNativeArtifacts; #[derive(Diagnostic)] -#[diag(codegen_ssa_native_static_libs)] -pub struct NativeStaticLibs { - pub arguments: String, -} - -#[derive(Diagnostic)] #[diag(codegen_ssa_link_script_unavailable)] pub struct LinkScriptUnavailable; @@ -555,3 +551,432 @@ pub struct ExpectedUsedSymbol { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(codegen_ssa_multiple_main_functions)] +#[help] +pub struct MultipleMainFunctions { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_metadata_object_file_write)] +pub struct MetadataObjectFileWrite { + pub error: Error, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_invalid_windows_subsystem)] +pub struct InvalidWindowsSubsystem { + pub subsystem: Symbol, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_erroneous_constant)] +pub struct ErroneousConstant { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_polymorphic_constant_too_generic)] +pub struct PolymorphicConstantTooGeneric { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_shuffle_indices_evaluation)] +pub struct ShuffleIndicesEvaluation { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(codegen_ssa_missing_memory_ordering)] +pub struct MissingMemoryOrdering; + +#[derive(Diagnostic)] +#[diag(codegen_ssa_unknown_atomic_ordering)] +pub struct UnknownAtomicOrdering; + +#[derive(Diagnostic)] +#[diag(codegen_ssa_atomic_compare_exchange)] +pub struct AtomicCompareExchange; + +#[derive(Diagnostic)] +#[diag(codegen_ssa_unknown_atomic_operation)] +pub struct UnknownAtomicOperation; + +#[derive(Diagnostic)] +pub enum InvalidMonomorphization<'tcx> { + #[diag(codegen_ssa_invalid_monomorphization_basic_integer_type, code = "E0511")] + BasicIntegerType { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_basic_float_type, code = "E0511")] + BasicFloatType { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_float_to_int_unchecked, code = "E0511")] + FloatToIntUnchecked { + #[primary_span] + span: Span, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_floating_point_vector, code = "E0511")] + FloatingPointVector { + #[primary_span] + span: Span, + name: Symbol, + f_ty: FloatTy, + in_ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_floating_point_type, code = "E0511")] + FloatingPointType { + #[primary_span] + span: Span, + name: Symbol, + in_ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_unrecognized_intrinsic, code = "E0511")] + UnrecognizedIntrinsic { + #[primary_span] + span: Span, + name: Symbol, + }, + + #[diag(codegen_ssa_invalid_monomorphization_simd_argument, code = "E0511")] + SimdArgument { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_simd_input, code = "E0511")] + SimdInput { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_simd_first, code = "E0511")] + SimdFirst { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_simd_second, code = "E0511")] + SimdSecond { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_simd_third, code = "E0511")] + SimdThird { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_simd_return, code = "E0511")] + SimdReturn { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_invalid_bitmask, code = "E0511")] + InvalidBitmask { + #[primary_span] + span: Span, + name: Symbol, + mask_ty: Ty<'tcx>, + expected_int_bits: u64, + expected_bytes: u64, + }, + + #[diag(codegen_ssa_invalid_monomorphization_return_length_input_type, code = "E0511")] + ReturnLengthInputType { + #[primary_span] + span: Span, + name: Symbol, + in_len: u64, + in_ty: Ty<'tcx>, + ret_ty: Ty<'tcx>, + out_len: u64, + }, + + #[diag(codegen_ssa_invalid_monomorphization_second_argument_length, code = "E0511")] + SecondArgumentLength { + #[primary_span] + span: Span, + name: Symbol, + in_len: u64, + in_ty: Ty<'tcx>, + arg_ty: Ty<'tcx>, + out_len: u64, + }, + + #[diag(codegen_ssa_invalid_monomorphization_third_argument_length, code = "E0511")] + ThirdArgumentLength { + #[primary_span] + span: Span, + name: Symbol, + in_len: u64, + in_ty: Ty<'tcx>, + arg_ty: Ty<'tcx>, + out_len: u64, + }, + + #[diag(codegen_ssa_invalid_monomorphization_return_integer_type, code = "E0511")] + ReturnIntegerType { + #[primary_span] + span: Span, + name: Symbol, + ret_ty: Ty<'tcx>, + out_ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_simd_shuffle, code = "E0511")] + SimdShuffle { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_return_length, code = "E0511")] + ReturnLength { + #[primary_span] + span: Span, + name: Symbol, + in_len: u64, + ret_ty: Ty<'tcx>, + out_len: u64, + }, + + #[diag(codegen_ssa_invalid_monomorphization_return_element, code = "E0511")] + ReturnElement { + #[primary_span] + span: Span, + name: Symbol, + in_elem: Ty<'tcx>, + in_ty: Ty<'tcx>, + ret_ty: Ty<'tcx>, + out_ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_shuffle_index_not_constant, code = "E0511")] + ShuffleIndexNotConstant { + #[primary_span] + span: Span, + name: Symbol, + arg_idx: u64, + }, + + #[diag(codegen_ssa_invalid_monomorphization_shuffle_index_out_of_bounds, code = "E0511")] + ShuffleIndexOutOfBounds { + #[primary_span] + span: Span, + name: Symbol, + arg_idx: u64, + total_len: u128, + }, + + #[diag(codegen_ssa_invalid_monomorphization_inserted_type, code = "E0511")] + InsertedType { + #[primary_span] + span: Span, + name: Symbol, + in_elem: Ty<'tcx>, + in_ty: Ty<'tcx>, + out_ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_return_type, code = "E0511")] + ReturnType { + #[primary_span] + span: Span, + name: Symbol, + in_elem: Ty<'tcx>, + in_ty: Ty<'tcx>, + ret_ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_expected_return_type, code = "E0511")] + ExpectedReturnType { + #[primary_span] + span: Span, + name: Symbol, + in_ty: Ty<'tcx>, + ret_ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_mismatched_lengths, code = "E0511")] + MismatchedLengths { + #[primary_span] + span: Span, + name: Symbol, + m_len: u64, + v_len: u64, + }, + + #[diag(codegen_ssa_invalid_monomorphization_mask_type, code = "E0511")] + MaskType { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_vector_argument, code = "E0511")] + VectorArgument { + #[primary_span] + span: Span, + name: Symbol, + in_ty: Ty<'tcx>, + in_elem: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_cannot_return, code = "E0511")] + CannotReturn { + #[primary_span] + span: Span, + name: Symbol, + ret_ty: Ty<'tcx>, + expected_int_bits: u64, + expected_bytes: u64, + }, + + #[diag(codegen_ssa_invalid_monomorphization_expected_element_type, code = "E0511")] + ExpectedElementType { + #[primary_span] + span: Span, + name: Symbol, + expected_element: Ty<'tcx>, + second_arg: Ty<'tcx>, + in_elem: Ty<'tcx>, + in_ty: Ty<'tcx>, + mutability: ExpectedPointerMutability, + }, + + #[diag(codegen_ssa_invalid_monomorphization_third_arg_element_type, code = "E0511")] + ThirdArgElementType { + #[primary_span] + span: Span, + name: Symbol, + expected_element: Ty<'tcx>, + third_arg: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size, code = "E0511")] + UnsupportedSymbolOfSize { + #[primary_span] + span: Span, + name: Symbol, + symbol: Symbol, + in_ty: Ty<'tcx>, + in_elem: Ty<'tcx>, + size: u64, + ret_ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_unsupported_symbol, code = "E0511")] + UnsupportedSymbol { + #[primary_span] + span: Span, + name: Symbol, + symbol: Symbol, + in_ty: Ty<'tcx>, + in_elem: Ty<'tcx>, + ret_ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_cast_fat_pointer, code = "E0511")] + CastFatPointer { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_expected_pointer, code = "E0511")] + ExpectedPointer { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_expected_usize, code = "E0511")] + ExpectedUsize { + #[primary_span] + span: Span, + name: Symbol, + ty: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_unsupported_cast, code = "E0511")] + UnsupportedCast { + #[primary_span] + span: Span, + name: Symbol, + in_ty: Ty<'tcx>, + in_elem: Ty<'tcx>, + ret_ty: Ty<'tcx>, + out_elem: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_unsupported_operation, code = "E0511")] + UnsupportedOperation { + #[primary_span] + span: Span, + name: Symbol, + in_ty: Ty<'tcx>, + in_elem: Ty<'tcx>, + }, + + #[diag(codegen_ssa_invalid_monomorphization_expected_vector_element_type, code = "E0511")] + ExpectedVectorElementType { + #[primary_span] + span: Span, + name: Symbol, + expected_element: Ty<'tcx>, + vector_type: Ty<'tcx>, + }, +} + +pub enum ExpectedPointerMutability { + Mut, + Not, +} + +impl IntoDiagnosticArg for ExpectedPointerMutability { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + match self { + ExpectedPointerMutability::Mut => DiagnosticArgValue::Str(Cow::Borrowed("*mut")), + ExpectedPointerMutability::Not => DiagnosticArgValue::Str(Cow::Borrowed("*_")), + } + } +} diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index c7617d2e464..dd1ac2c74ae 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -261,6 +261,9 @@ impl CleanupKind { } } +/// MSVC requires unwinding code to be split to a tree of *funclets*, where each funclet can only +/// branch to itself or to its parent. Luckily, the code we generates matches this pattern. +/// Recover that structure in an analyze pass. pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKind> { fn discover_masters<'tcx>( result: &mut IndexVec<mir::BasicBlock, CleanupKind>, diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index 53ff3c24096..14fe84a146d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -1,3 +1,4 @@ +use crate::errors; use crate::mir::operand::OperandRef; use crate::traits::*; use rustc_middle::mir; @@ -44,10 +45,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.cx.tcx().const_eval_resolve(ty::ParamEnv::reveal_all(), uv, None).map_err(|err| { match err { ErrorHandled::Reported(_) => { - self.cx.tcx().sess.span_err(constant.span, "erroneous constant encountered"); + self.cx.tcx().sess.emit_err(errors::ErroneousConstant { span: constant.span }); } ErrorHandled::TooGeneric => { - span_bug!(constant.span, "codegen encountered polymorphic constant: {:?}", err); + self.cx + .tcx() + .sess + .diagnostic() + .emit_bug(errors::PolymorphicConstantTooGeneric { span: constant.span }); } } err @@ -87,7 +92,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (llval, c.ty()) }) .unwrap_or_else(|_| { - bx.tcx().sess.span_err(span, "could not evaluate shuffle_indices at compile time"); + bx.tcx().sess.emit_err(errors::ShuffleIndicesEvaluation { span }); // We've errored, so we don't have to produce working code. let ty = self.monomorphize(ty); let llty = bx.backend_type(bx.layout_of(ty)); diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index a75609260ed..766dc74cbbb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -1,7 +1,9 @@ use super::operand::{OperandRef, OperandValue}; use super::place::PlaceRef; use super::FunctionCx; -use crate::common::{span_invalid_monomorphization_error, IntPredicate}; +use crate::common::IntPredicate; +use crate::errors; +use crate::errors::InvalidMonomorphization; use crate::glue; use crate::meth; use crate::traits::*; @@ -305,15 +307,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => bug!(), }, None => { - span_invalid_monomorphization_error( - bx.tcx().sess, - span, - &format!( - "invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", - name, ty - ), - ); + bx.tcx().sess.emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty }); return; } } @@ -329,15 +323,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => bug!(), }, None => { - span_invalid_monomorphization_error( - bx.tcx().sess, - span, - &format!( - "invalid monomorphization of `{}` intrinsic: \ - expected basic float type, found `{}`", - name, arg_tys[0] - ), - ); + bx.tcx().sess.emit_err(InvalidMonomorphization::BasicFloatType { span, name, ty: arg_tys[0] }); return; } } @@ -345,29 +331,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { sym::float_to_int_unchecked => { if float_type_width(arg_tys[0]).is_none() { - span_invalid_monomorphization_error( - bx.tcx().sess, - span, - &format!( - "invalid monomorphization of `float_to_int_unchecked` \ - intrinsic: expected basic float type, \ - found `{}`", - arg_tys[0] - ), - ); + bx.tcx().sess.emit_err(InvalidMonomorphization::FloatToIntUnchecked { span, ty: arg_tys[0] }); return; } let Some((_width, signed)) = int_type_width_signed(ret_ty, bx.tcx()) else { - span_invalid_monomorphization_error( - bx.tcx().sess, - span, - &format!( - "invalid monomorphization of `float_to_int_unchecked` \ - intrinsic: expected basic integer type, \ - found `{}`", - ret_ty - ), - ); + bx.tcx().sess.emit_err(InvalidMonomorphization::FloatToIntUnchecked { span, ty: ret_ty }); return; }; if signed { @@ -402,7 +370,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { use crate::common::{AtomicRmwBinOp, SynchronizationScope}; let Some((instruction, ordering)) = atomic.split_once('_') else { - bx.sess().fatal("Atomic intrinsic missing memory ordering"); + bx.sess().emit_fatal(errors::MissingMemoryOrdering); }; let parse_ordering = |bx: &Bx, s| match s { @@ -412,25 +380,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { "release" => Release, "acqrel" => AcquireRelease, "seqcst" => SequentiallyConsistent, - _ => bx.sess().fatal("unknown ordering in atomic intrinsic"), + _ => bx.sess().emit_fatal(errors::UnknownAtomicOrdering), }; let invalid_monomorphization = |ty| { - span_invalid_monomorphization_error( - bx.tcx().sess, - span, - &format!( - "invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", - name, ty - ), - ); + bx.tcx().sess.emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty }); }; match instruction { "cxchg" | "cxchgweak" => { let Some((success, failure)) = ordering.split_once('_') else { - bx.sess().fatal("Atomic compare-exchange intrinsic missing failure memory ordering"); + bx.sess().emit_fatal(errors::AtomicCompareExchange); }; let ty = substs.type_at(0); if int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_unsafe_ptr() { @@ -529,7 +489,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { "min" => AtomicRmwBinOp::AtomicMin, "umax" => AtomicRmwBinOp::AtomicUMax, "umin" => AtomicRmwBinOp::AtomicUMin, - _ => bx.sess().fatal("unknown atomic operation"), + _ => bx.sess().emit_fatal(errors::UnknownAtomicOperation), }; let ty = substs.type_at(0); diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index da69fc8ecf7..739963fffd1 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -187,7 +187,7 @@ const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[ ("bmi2", None), ("cmpxchg16b", Some(sym::cmpxchg16b_target_feature)), ("ermsb", Some(sym::ermsb_target_feature)), - ("f16c", Some(sym::f16c_target_feature)), + ("f16c", None), ("fma", None), ("fxsr", None), ("gfni", Some(sym::avx512_target_feature)), @@ -396,7 +396,6 @@ pub fn from_target_feature( Some(sym::cmpxchg16b_target_feature) => rust_features.cmpxchg16b_target_feature, Some(sym::movbe_target_feature) => rust_features.movbe_target_feature, Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, - Some(sym::f16c_target_feature) => rust_features.f16c_target_feature, Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature, Some(sym::bpf_target_feature) => rust_features.bpf_target_feature, Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature, diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs index f1674d04f8d..351c701305a 100644 --- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs @@ -41,6 +41,7 @@ fn constness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Constness { }; if is_const { hir::Constness::Const } else { hir::Constness::NotConst } } + hir::Node::Expr(e) if let hir::ExprKind::Closure(c) = e.kind => c.constness, _ => { if let Some(fn_kind) = node.fn_kind() { if fn_kind.constness() == hir::Constness::Const { diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index e4f716c3194..a61d3ab40a5 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -40,12 +40,11 @@ where let index = index .try_into() .expect("more generic parameters than can fit into a `u32`"); - let is_used = unused_params.contains(index).map_or(true, |unused| !unused); // Only recurse when generic parameters in fns, closures and generators // are used and require substitution. // Just in case there are closures or generators within this subst, // recurse. - if is_used && subst.needs_subst() { + if unused_params.is_used(index) && subst.needs_subst() { return subst.visit_with(self); } } diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 443c01fdb90..46e7b09a55e 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -20,6 +20,7 @@ Rust MIR: a lowered representation of Rust. #![feature(trusted_step)] #![feature(try_blocks)] #![feature(yeet_expr)] +#![feature(if_let_guard)] #![feature(is_some_and)] #![recursion_limit = "256"] diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index 54213d55a2d..d4c75cd55ce 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -242,7 +242,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { // impl trait is gone in MIR, so check the return type of a const fn by its signature // instead of the type of the return place. self.span = body.local_decls[RETURN_PLACE].source_info.span; - let return_ty = tcx.fn_sig(def_id).output(); + let return_ty = self.ccx.fn_sig().output(); self.check_local_or_return_ty(return_ty.skip_binder(), RETURN_PLACE); } @@ -730,6 +730,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { substs, span: *fn_span, from_hir_call: *from_hir_call, + feature: Some(sym::const_trait_impl), }); return; } @@ -782,6 +783,20 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { ); return; } + Ok(Some(ImplSource::Closure(data))) => { + if !tcx.is_const_fn_raw(data.closure_def_id) { + self.check_op(ops::FnCallNonConst { + caller, + callee, + substs, + span: *fn_span, + from_hir_call: *from_hir_call, + feature: None, + }); + + return; + } + } Ok(Some(ImplSource::UserDefined(data))) => { let callee_name = tcx.item_name(callee); if let Some(&did) = tcx @@ -802,6 +817,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { substs, span: *fn_span, from_hir_call: *from_hir_call, + feature: None, }); return; } @@ -844,6 +860,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { substs, span: *fn_span, from_hir_call: *from_hir_call, + feature: None, }); return; } @@ -903,6 +920,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { substs, span: *fn_span, from_hir_call: *from_hir_call, + feature: None, }); return; } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs index 655ec345ed3..54868e418c4 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/mod.rs @@ -8,7 +8,7 @@ use rustc_attr as attr; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::mir; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, PolyFnSig, TyCtxt}; use rustc_span::Symbol; pub use self::qualifs::Qualif; @@ -64,6 +64,17 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { fn is_async(&self) -> bool { self.tcx.asyncness(self.def_id()).is_async() } + + pub fn fn_sig(&self) -> PolyFnSig<'tcx> { + let did = self.def_id().to_def_id(); + if self.tcx.is_closure(did) { + let ty = self.tcx.type_of(did); + let ty::Closure(_, substs) = ty.kind() else { bug!("type_of closure not ty::Closure") }; + substs.as_closure().sig() + } else { + self.tcx.fn_sig(did) + } + } } pub fn rustc_allow_const_fn_unstable( @@ -115,7 +126,7 @@ fn is_parent_const_stable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool { let local_def_id = def_id.expect_local(); let hir_id = tcx.local_def_id_to_hir_id(local_def_id); - let Some(parent) = tcx.hir().find_parent_node(hir_id) else { return false }; + let Some(parent) = tcx.hir().opt_parent_id(hir_id) else { return false }; let parent_def = tcx.hir().get(parent); if !matches!( diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index b19d270e610..0cb5d2ff8c7 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -111,6 +111,7 @@ pub struct FnCallNonConst<'tcx> { pub substs: SubstsRef<'tcx>, pub span: Span, pub from_hir_call: bool, + pub feature: Option<Symbol>, } impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { @@ -119,7 +120,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { ccx: &ConstCx<'_, 'tcx>, _: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let FnCallNonConst { caller, callee, substs, span, from_hir_call } = *self; + let FnCallNonConst { caller, callee, substs, span, from_hir_call, feature } = *self; let ConstCx { tcx, param_env, .. } = *ccx; let diag_trait = |err, self_ty: Ty<'_>, trait_id| { @@ -318,6 +319,13 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { ccx.const_kind(), )); + if let Some(feature) = feature && ccx.tcx.sess.is_nightly_build() { + err.help(&format!( + "add `#![feature({})]` to the crate attributes to enable", + feature, + )); + } + if let ConstContext::Static(_) = ccx.const_kind() { err.note("consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell"); } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs index d4570c59889..cf4e875c91f 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/post_drop_elaboration.rs @@ -95,7 +95,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> { } // Drop elaboration is not precise enough to accept code like - // `src/test/ui/consts/control-flow/drop-pass.rs`; e.g., when an `Option<Vec<T>>` is + // `tests/ui/consts/control-flow/drop-pass.rs`; e.g., when an `Option<Vec<T>>` is // initialized with `None` and never changed, it still emits drop glue. // Hence we additionally check the qualifs here to allow more code to pass. if self.qualifs.needs_non_const_drop(self.ccx, dropped_place.local, location) { diff --git a/compiler/rustc_const_eval/src/util/compare_types.rs b/compiler/rustc_const_eval/src/util/compare_types.rs index be786569cde..f5f3d5de6b5 100644 --- a/compiler/rustc_const_eval/src/util/compare_types.rs +++ b/compiler/rustc_const_eval/src/util/compare_types.rs @@ -58,6 +58,6 @@ pub fn is_subtype<'tcx>( // even if they're constrained in our current function. // // It seems very unlikely that this hides any bugs. - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + let _ = infcx.take_opaque_types(); errors.is_empty() } diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs index 94a8c1fc051..ea2a4388b92 100644 --- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs +++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs @@ -277,12 +277,12 @@ impl<Node: Idx> Dominators<Node> { } pub fn immediate_dominator(&self, node: Node) -> Node { - assert!(self.is_reachable(node), "node {:?} is not reachable", node); + assert!(self.is_reachable(node), "node {node:?} is not reachable"); self.immediate_dominators[node].unwrap() } pub fn dominators(&self, node: Node) -> Iter<'_, Node> { - assert!(self.is_reachable(node), "node {:?} is not reachable", node); + assert!(self.is_reachable(node), "node {node:?} is not reachable"); Iter { dominators: self, node: Some(node) } } diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index b31092eca98..c8e66eb672c 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -233,10 +233,9 @@ where .map(G::Node::new) .map(|node| match this.start_walk_from(node) { WalkReturn::Complete { scc_index } => scc_index, - WalkReturn::Cycle { min_depth } => panic!( - "`start_walk_node({:?})` returned cycle with depth {:?}", - node, min_depth - ), + WalkReturn::Cycle { min_depth } => { + panic!("`start_walk_node({node:?})` returned cycle with depth {min_depth:?}") + } }) .collect(); @@ -272,8 +271,7 @@ where NodeState::NotVisited => return None, NodeState::InCycleWith { parent } => panic!( - "`find_state` returned `InCycleWith({:?})`, which ought to be impossible", - parent + "`find_state` returned `InCycleWith({parent:?})`, which ought to be impossible" ), }) } @@ -369,7 +367,7 @@ where previous_node = previous; } // Only InCycleWith nodes were added to the reverse linked list. - other => panic!("Invalid previous link while compressing cycle: {:?}", other), + other => panic!("Invalid previous link while compressing cycle: {other:?}"), } debug!("find_state: parent_state = {:?}", node_state); @@ -394,7 +392,7 @@ where // NotVisited can not be part of a cycle since it should // have instead gotten explored. NodeState::NotVisited | NodeState::InCycleWith { .. } => { - panic!("invalid parent state: {:?}", node_state) + panic!("invalid parent state: {node_state:?}") } } } diff --git a/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs b/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs index 3a268e4b4f4..4b6aa116520 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs @@ -30,7 +30,7 @@ impl<O: ForestObligation> ObligationForest<O> { let counter = COUNTER.fetch_add(1, Ordering::AcqRel); - let file_path = dir.as_ref().join(format!("{:010}_{}.gv", counter, description)); + let file_path = dir.as_ref().join(format!("{counter:010}_{description}.gv")); let mut gv_file = BufWriter::new(File::create(file_path).unwrap()); @@ -47,7 +47,7 @@ impl<'a, O: ForestObligation + 'a> dot::Labeller<'a> for &'a ObligationForest<O> } fn node_id(&self, index: &Self::Node) -> dot::Id<'_> { - dot::Id::new(format!("obligation_{}", index)).unwrap() + dot::Id::new(format!("obligation_{index}")).unwrap() } fn node_label(&self, index: &Self::Node) -> dot::LabelText<'_> { diff --git a/compiler/rustc_data_structures/src/profiling.rs b/compiler/rustc_data_structures/src/profiling.rs index 16296b22489..393f1739081 100644 --- a/compiler/rustc_data_structures/src/profiling.rs +++ b/compiler/rustc_data_structures/src/profiling.rs @@ -545,7 +545,7 @@ impl SelfProfiler { // length can behave as a source of entropy for heap addresses, when // ASLR is disabled and the heap is otherwise determinic. let pid: u32 = process::id(); - let filename = format!("{}-{:07}.rustc_profile", crate_name, pid); + let filename = format!("{crate_name}-{pid:07}.rustc_profile"); let path = output_directory.join(&filename); let profiler = Profiler::with_counter(&path, measureme::counters::Counter::by_name(counter_name)?)?; diff --git a/compiler/rustc_data_structures/src/small_c_str.rs b/compiler/rustc_data_structures/src/small_c_str.rs index 3a8ab8ff991..719e4e3d974 100644 --- a/compiler/rustc_data_structures/src/small_c_str.rs +++ b/compiler/rustc_data_structures/src/small_c_str.rs @@ -30,7 +30,7 @@ impl SmallCStr { SmallVec::from_vec(data) }; if let Err(e) = ffi::CStr::from_bytes_with_nul(&data) { - panic!("The string \"{}\" cannot be converted into a CStr: {}", s, e); + panic!("The string \"{s}\" cannot be converted into a CStr: {e}"); } SmallCStr { data } } @@ -39,7 +39,7 @@ impl SmallCStr { pub fn new_with_nul(s: &str) -> SmallCStr { let b = s.as_bytes(); if let Err(e) = ffi::CStr::from_bytes_with_nul(b) { - panic!("The string \"{}\" cannot be converted into a CStr: {}", s, e); + panic!("The string \"{s}\" cannot be converted into a CStr: {e}"); } SmallCStr { data: SmallVec::from_slice(s.as_bytes()) } } @@ -74,7 +74,7 @@ impl<'a> FromIterator<&'a str> for SmallCStr { iter.into_iter().flat_map(|s| s.as_bytes()).copied().collect::<SmallVec<_>>(); data.push(0); if let Err(e) = ffi::CStr::from_bytes_with_nul(&data) { - panic!("The iterator {:?} cannot be converted into a CStr: {}", data, e); + panic!("The iterator {data:?} cannot be converted into a CStr: {e}"); } Self { data } } diff --git a/compiler/rustc_data_structures/src/steal.rs b/compiler/rustc_data_structures/src/steal.rs index a3ece655047..9a0fd52677d 100644 --- a/compiler/rustc_data_structures/src/steal.rs +++ b/compiler/rustc_data_structures/src/steal.rs @@ -41,6 +41,11 @@ impl<T> Steal<T> { } #[track_caller] + pub fn get_mut(&mut self) -> &mut T { + self.value.get_mut().as_mut().expect("attempt to read from stolen value") + } + + #[track_caller] pub fn steal(&self) -> T { let value_ref = &mut *self.value.try_write().expect("stealing value which is locked"); let value = value_ref.take(); diff --git a/compiler/rustc_data_structures/src/vec_map.rs b/compiler/rustc_data_structures/src/vec_map.rs index 2417df66bb9..d1a99bcaeb7 100644 --- a/compiler/rustc_data_structures/src/vec_map.rs +++ b/compiler/rustc_data_structures/src/vec_map.rs @@ -71,8 +71,7 @@ where // This should return just one element, otherwise it's a bug assert!( filter.next().is_none(), - "Collection {:#?} should have just one matching element", - self + "Collection {self:#?} should have just one matching element" ); Some(value) } diff --git a/compiler/rustc_driver/src/args.rs b/compiler/rustc_driver/src/args.rs index 01338359f1a..42c97cc6a9d 100644 --- a/compiler/rustc_driver/src/args.rs +++ b/compiler/rustc_driver/src/args.rs @@ -25,7 +25,7 @@ pub fn arg_expand_all(at_args: &[String]) -> Vec<String> { Ok(arg) => args.extend(arg), Err(err) => rustc_session::early_error( rustc_session::config::ErrorOutputType::default(), - &format!("Failed to load argument file: {}", err), + &format!("Failed to load argument file: {err}"), ), } } @@ -42,8 +42,8 @@ impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::Utf8Error(None) => write!(fmt, "Utf8 error"), - Error::Utf8Error(Some(path)) => write!(fmt, "Utf8 error in {}", path), - Error::IOError(path, err) => write!(fmt, "IO Error: {}: {}", path, err), + Error::Utf8Error(Some(path)) => write!(fmt, "Utf8 error in {path}"), + Error::IOError(path, err) => write!(fmt, "IO Error: {path}: {err}"), } } } diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 30179e97872..a62e5dec4b8 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -231,6 +231,10 @@ fn run_compiler( registry: diagnostics_registry(), }; + if !tracing::dispatcher::has_been_set() { + init_rustc_env_logger_with_backtrace_option(&config.opts.unstable_opts.log_backtrace); + } + match make_input(config.opts.error_format, &matches.free) { Err(reported) => return Err(reported), Ok(Some((input, input_file_path))) => { @@ -309,8 +313,8 @@ fn run_compiler( if let Some(ppm) = &sess.opts.pretty { if ppm.needs_ast_map() { - let expanded_crate = queries.expansion()?.peek().0.clone(); - queries.global_ctxt()?.peek_mut().enter(|tcx| { + let expanded_crate = queries.expansion()?.borrow().0.clone(); + queries.global_ctxt()?.enter(|tcx| { pretty::print_after_hir_lowering( tcx, compiler.input(), @@ -321,7 +325,7 @@ fn run_compiler( Ok(()) })?; } else { - let krate = queries.parse()?.take(); + let krate = queries.parse()?.steal(); pretty::print_after_parsing( sess, compiler.input(), @@ -343,7 +347,8 @@ fn run_compiler( } { - let (_, lint_store) = &*queries.register_plugins()?.peek(); + let plugins = queries.register_plugins()?; + let (_, lint_store) = &*plugins.borrow(); // Lint plugins are registered; now we can process command line flags. if sess.opts.describe_lints { @@ -371,7 +376,7 @@ fn run_compiler( return early_exit(); } - queries.global_ctxt()?.peek_mut().enter(|tcx| { + queries.global_ctxt()?.enter(|tcx| { let result = tcx.analysis(()); if sess.opts.unstable_opts.save_analysis { let crate_name = tcx.crate_name(LOCAL_CRATE); @@ -486,11 +491,8 @@ impl Compilation { fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { let upper_cased_code = code.to_ascii_uppercase(); - let normalised = if upper_cased_code.starts_with('E') { - upper_cased_code - } else { - format!("E{0:0>4}", code) - }; + let normalised = + if upper_cased_code.starts_with('E') { upper_cased_code } else { format!("E{code:0>4}") }; match registry.try_find_description(&normalised) { Ok(Some(description)) => { let mut is_in_code_block = false; @@ -513,14 +515,14 @@ fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) { if io::stdout().is_terminal() { show_content_with_pager(&text); } else { - print!("{}", text); + print!("{text}"); } } Ok(None) => { - early_error(output, &format!("no extended information for {}", code)); + early_error(output, &format!("no extended information for {code}")); } Err(InvalidErrorCode) => { - early_error(output, &format!("{} is not a valid error code", code)); + early_error(output, &format!("{code} is not a valid error code")); } } } @@ -552,7 +554,7 @@ fn show_content_with_pager(content: &str) { // If pager fails for whatever reason, we should still print the content // to standard output if fallback_to_println { - print!("{}", content); + print!("{content}"); } } @@ -672,7 +674,7 @@ fn print_crate_info( ); let id = rustc_session::output::find_crate_name(sess, attrs, input); if *req == PrintRequest::CrateName { - println!("{}", id); + println!("{id}"); continue; } let crate_types = collect_crate_types(sess, attrs); @@ -704,7 +706,7 @@ fn print_crate_info( } if let Some(value) = value { - Some(format!("{}=\"{}\"", name, value)) + Some(format!("{name}=\"{value}\"")) } else { Some(name.to_string()) } @@ -713,7 +715,7 @@ fn print_crate_info( cfgs.sort(); for cfg in cfgs { - println!("{}", cfg); + println!("{cfg}"); } } CallingConventions => { @@ -739,7 +741,7 @@ fn print_crate_info( let stable = sess.target.options.supported_split_debuginfo.contains(split); let unstable_ok = sess.unstable_options(); if stable || unstable_ok { - println!("{}", split); + println!("{split}"); } } } @@ -776,14 +778,14 @@ pub fn version_at_macro_invocation( ) { let verbose = matches.opt_present("verbose"); - println!("{} {}", binary, version); + println!("{binary} {version}"); if verbose { - println!("binary: {}", binary); - println!("commit-hash: {}", commit_hash); - println!("commit-date: {}", commit_date); + println!("binary: {binary}"); + println!("commit-hash: {commit_hash}"); + println!("commit-date: {commit_date}"); println!("host: {}", config::host_triple()); - println!("release: {}", release); + println!("release: {release}"); let debug_flags = matches.opt_strs("Z"); let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); @@ -1037,7 +1039,7 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> { .map(|&(name, ..)| ('C', name)) .chain(Z_OPTIONS.iter().map(|&(name, ..)| ('Z', name))) .find(|&(_, name)| *opt == name.replace('_', "-")) - .map(|(flag, _)| format!("{}. Did you mean `-{} {}`?", e, flag, opt)), + .map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")), _ => None, }; early_error(ErrorOutputType::default(), &msg.unwrap_or_else(|| e.to_string())); @@ -1148,7 +1150,7 @@ fn extra_compiler_flags() -> Option<(Vec<String>, bool)> { } else { result.push(a.to_string()); match ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.iter().find(|s| option == **s) { - Some(s) => result.push(format!("{}=[REDACTED]", s)), + Some(s) => result.push(format!("{s}=[REDACTED]")), None => result.push(content), } } @@ -1199,8 +1201,8 @@ static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + }; // Invoke the default handler, which prints the actual panic message and optionally a backtrace - // Don't do this for `GoodPathBug`, which already emits its own more useful backtrace. - if !info.payload().is::<rustc_errors::GoodPathBug>() { + // Don't do this for delayed bugs, which already emit their own more useful backtrace. + if !info.payload().is::<rustc_errors::DelayedBugPanic>() { (*DEFAULT_HOOK)(info); // Separate the output with an empty line @@ -1238,7 +1240,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // a .span_bug or .bug call has already printed what // it wants to print. if !info.payload().is::<rustc_errors::ExplicitBug>() - && !info.payload().is::<rustc_errors::GoodPathBug>() + && !info.payload().is::<rustc_errors::DelayedBugPanic>() { let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic"); handler.emit_diagnostic(&mut d); @@ -1246,7 +1248,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { let mut xs: Vec<Cow<'static, str>> = vec![ "the compiler unexpectedly panicked. this is a bug.".into(), - format!("we would appreciate a bug report: {}", bug_report_url).into(), + format!("we would appreciate a bug report: {bug_report_url}").into(), format!( "rustc {} running on {}", util::version_str!().unwrap_or("unknown_version"), @@ -1302,7 +1304,14 @@ pub fn install_ice_hook() { /// This allows tools to enable rust logging without having to magically match rustc's /// tracing crate version. pub fn init_rustc_env_logger() { - if let Err(error) = rustc_log::init_rustc_env_logger() { + init_rustc_env_logger_with_backtrace_option(&None); +} + +/// This allows tools to enable rust logging without having to magically match rustc's +/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to +/// choose a target module you wish to show backtraces along with its logging. +pub fn init_rustc_env_logger_with_backtrace_option(backtrace_target: &Option<String>) { + if let Err(error) = rustc_log::init_rustc_env_logger_with_backtrace_option(backtrace_target) { early_error(ErrorOutputType::default(), &error.to_string()); } } @@ -1368,7 +1377,6 @@ mod signal_handler { pub fn main() -> ! { let start_time = Instant::now(); let start_rss = get_resident_set_size(); - init_rustc_env_logger(); signal_handler::install(); let mut callbacks = TimePassesCallbacks::default(); install_ice_hook(); @@ -1379,7 +1387,7 @@ pub fn main() -> ! { arg.into_string().unwrap_or_else(|arg| { early_error( ErrorOutputType::default(), - &format!("argument {} is not valid Unicode: {:?}", i, arg), + &format!("argument {i} is not valid Unicode: {arg:?}"), ) }) }) diff --git a/compiler/rustc_driver/src/pretty.rs b/compiler/rustc_driver/src/pretty.rs index f9b1316d2eb..b2451bc730f 100644 --- a/compiler/rustc_driver/src/pretty.rs +++ b/compiler/rustc_driver/src/pretty.rs @@ -360,7 +360,7 @@ fn get_source(input: &Input, sess: &Session) -> (String, FileName) { fn write_or_print(out: &str, ofile: Option<&Path>, sess: &Session) { match ofile { - None => print!("{}", out), + None => print!("{out}"), Some(p) => { if let Err(e) = std::fs::write(p, out) { sess.emit_fatal(UnprettyDumpFail { @@ -402,7 +402,7 @@ pub fn print_after_parsing( } AstTree(PpAstTreeMode::Normal) => { debug!("pretty printing AST tree"); - format!("{:#?}", krate) + format!("{krate:#?}") } _ => unreachable!(), }; @@ -446,7 +446,7 @@ pub fn print_after_hir_lowering<'tcx>( AstTree(PpAstTreeMode::Expanded) => { debug!("pretty-printing expanded AST"); - format!("{:#?}", krate) + format!("{krate:#?}") } Hir(s) => call_with_pp_support_hir(&s, tcx, move |annotation, hir_map| { diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs index 3fba2cf5749..24258974bb9 100644 --- a/compiler/rustc_error_codes/src/error_codes.rs +++ b/compiler/rustc_error_codes/src/error_codes.rs @@ -110,6 +110,7 @@ E0204: include_str!("./error_codes/E0204.md"), E0205: include_str!("./error_codes/E0205.md"), E0206: include_str!("./error_codes/E0206.md"), E0207: include_str!("./error_codes/E0207.md"), +E0208: include_str!("./error_codes/E0208.md"), E0210: include_str!("./error_codes/E0210.md"), E0211: include_str!("./error_codes/E0211.md"), E0212: include_str!("./error_codes/E0212.md"), @@ -387,6 +388,7 @@ E0636: include_str!("./error_codes/E0636.md"), E0637: include_str!("./error_codes/E0637.md"), E0638: include_str!("./error_codes/E0638.md"), E0639: include_str!("./error_codes/E0639.md"), +E0640: include_str!("./error_codes/E0640.md"), E0641: include_str!("./error_codes/E0641.md"), E0642: include_str!("./error_codes/E0642.md"), E0643: include_str!("./error_codes/E0643.md"), @@ -434,6 +436,8 @@ E0713: include_str!("./error_codes/E0713.md"), E0714: include_str!("./error_codes/E0714.md"), E0715: include_str!("./error_codes/E0715.md"), E0716: include_str!("./error_codes/E0716.md"), +E0711: include_str!("./error_codes/E0711.md"), +E0717: include_str!("./error_codes/E0717.md"), E0718: include_str!("./error_codes/E0718.md"), E0719: include_str!("./error_codes/E0719.md"), E0720: include_str!("./error_codes/E0720.md"), @@ -540,7 +544,6 @@ E0791: include_str!("./error_codes/E0791.md"), // E0190, // deprecated: can only cast a &-pointer to an &-object // E0194, // merged into E0403 // E0196, // cannot determine a type for this closure - E0208, // internal error code // E0209, // builtin traits can only be implemented on structs or enums // E0213, // associated types are not accepted in this context // E0215, // angle-bracket notation is not stable with `Fn` @@ -571,7 +574,7 @@ E0791: include_str!("./error_codes/E0791.md"), // E0274, // on_unimplemented #2 // E0278, // requirement is not satisfied // E0279, - E0280, // requirement is not satisfied +// E0280, // changed to ICE // E0285, // overflow evaluation builtin bounds // E0296, // replaced with a generic attribute input check // E0298, // cannot compare constants @@ -579,8 +582,7 @@ E0791: include_str!("./error_codes/E0791.md"), // E0300, // unexpanded macro // E0304, // expected signed integer constant // E0305, // expected constant - E0313, // lifetime of borrowed pointer outlives lifetime of captured - // variable +// E0313, // removed: found unreachable // E0314, // closure outlives stack frame // E0315, // cannot invoke closure outside of its lifetime // E0319, // trait impls for defaulted traits allowed just for structs/enums @@ -616,7 +618,7 @@ E0791: include_str!("./error_codes/E0791.md"), // E0487, // unsafe use of destructor: destructor might be called while... // E0488, // lifetime of variable does not enclose its declaration // E0489, // type/lifetime parameter not in scope here - E0490, // a value of type `..` is borrowed for too long +// E0490, // removed: unreachable E0523, // two dependencies have same (crate-name, disambiguator) but different SVH // E0526, // shuffle indices are not constant // E0540, // multiple rustc_deprecated attributes @@ -634,14 +636,11 @@ E0791: include_str!("./error_codes/E0791.md"), // E0629, // missing 'feature' (rustc_const_unstable) // E0630, // rustc_const_unstable attribute must be paired with stable/unstable // attribute - E0640, // infer outlives requirements, internal error code // E0645, // trait aliases not finished // E0694, // an unknown tool name found in scoped attributes // E0702, // replaced with a generic attribute input check // E0707, // multiple elided lifetimes used in arguments of `async fn` // E0709, // multiple different lifetimes used in arguments of `async fn` - E0711, // a feature has been declared with conflicting stability attributes, internal error code - E0717, // rustc_promotable without stability attribute, internal error code // E0721, // `await` keyword // E0723, // unstable feature in `const` context // E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`. diff --git a/compiler/rustc_error_codes/src/error_codes/E0015.md b/compiler/rustc_error_codes/src/error_codes/E0015.md index 021a0219d13..ac78f66adad 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0015.md +++ b/compiler/rustc_error_codes/src/error_codes/E0015.md @@ -1,5 +1,4 @@ -A constant item was initialized with something that is not a constant -expression. +A non-`const` function was called in a `const` context. Erroneous code example: @@ -8,26 +7,20 @@ fn create_some() -> Option<u8> { Some(1) } -const FOO: Option<u8> = create_some(); // error! +// error: cannot call non-const fn `create_some` in constants +const FOO: Option<u8> = create_some(); ``` -The only functions that can be called in static or constant expressions are -`const` functions, and struct/enum constructors. +All functions used in a `const` context (constant or static expression) must +be marked `const`. To fix this error, you can declare `create_some` as a constant function: ``` -const fn create_some() -> Option<u8> { // declared as a const function +// declared as a `const` function: +const fn create_some() -> Option<u8> { Some(1) } -const FOO: Option<u8> = create_some(); // ok! - -// These are also working: -struct Bar { - x: u8, -} - -const OTHER_FOO: Option<u8> = Some(1); -const BAR: Bar = Bar {x: 1}; +const FOO: Option<u8> = create_some(); // no error! ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0208.md b/compiler/rustc_error_codes/src/error_codes/E0208.md new file mode 100644 index 00000000000..7edd93e56a9 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0208.md @@ -0,0 +1 @@ +#### This error code is internal to the compiler and will not be emitted with normal Rust code. diff --git a/compiler/rustc_error_codes/src/error_codes/E0588.md b/compiler/rustc_error_codes/src/error_codes/E0588.md index 040c7a02ef4..995d945f158 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0588.md +++ b/compiler/rustc_error_codes/src/error_codes/E0588.md @@ -11,7 +11,7 @@ struct Aligned(i32); struct Packed(Aligned); ``` -Just like you cannot have both `align` and `packed` representation hints on a +Just like you cannot have both `align` and `packed` representation hints on the same type, a `packed` type cannot contain another type with the `align` representation hint. However, you can do the opposite: diff --git a/compiler/rustc_error_codes/src/error_codes/E0640.md b/compiler/rustc_error_codes/src/error_codes/E0640.md new file mode 100644 index 00000000000..7edd93e56a9 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0640.md @@ -0,0 +1 @@ +#### This error code is internal to the compiler and will not be emitted with normal Rust code. diff --git a/compiler/rustc_error_codes/src/error_codes/E0711.md b/compiler/rustc_error_codes/src/error_codes/E0711.md new file mode 100644 index 00000000000..a2150037f78 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0711.md @@ -0,0 +1,30 @@ +#### This error code is internal to the compiler and will not be emitted with normal Rust code. + +Feature declared with conflicting stability requirements. + +```compile_fail,E0711 +// NOTE: this attribute is perma-unstable and should *never* be used outside of +// stdlib and the compiler. +#![feature(staged_api)] + +#![stable(feature = "...", since = "1.0.0")] + +#[stable(feature = "foo", since = "1.0.0")] +fn foo_stable_1_0_0() {} + +// error: feature `foo` is declared stable since 1.29.0 +#[stable(feature = "foo", since = "1.29.0")] +fn foo_stable_1_29_0() {} + +// error: feature `foo` is declared unstable +#[unstable(feature = "foo", issue = "none")] +fn foo_unstable() {} +``` + +In the above example, the `foo` feature is first defined to be stable since +1.0.0, but is then re-declared stable since 1.29.0. This discrepancy in +versions causes an error. Furthermore, `foo` is then re-declared as unstable, +again the conflict causes an error. + +This error can be fixed by splitting the feature, this allows any +stability requirements and removes any possibility of conflict. diff --git a/compiler/rustc_error_codes/src/error_codes/E0717.md b/compiler/rustc_error_codes/src/error_codes/E0717.md new file mode 100644 index 00000000000..7edd93e56a9 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0717.md @@ -0,0 +1 @@ +#### This error code is internal to the compiler and will not be emitted with normal Rust code. diff --git a/compiler/rustc_error_codes/src/error_codes/E0729.md b/compiler/rustc_error_codes/src/error_codes/E0729.md index 74f89080b91..3891745b500 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0729.md +++ b/compiler/rustc_error_codes/src/error_codes/E0729.md @@ -1,3 +1,5 @@ +#### Note: this error code is no longer emitted by the compiler + Support for Non-Lexical Lifetimes (NLL) has been included in the Rust compiler since 1.31, and has been enabled on the 2015 edition since 1.36. The new borrow checker for NLL uncovered some bugs in the old borrow checker, which in some diff --git a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl index db4c82b35c7..c8c7afb5f91 100644 --- a/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl +++ b/compiler/rustc_error_messages/locales/en-US/codegen_ssa.ftl @@ -157,8 +157,6 @@ codegen_ssa_linker_file_stem = couldn't extract file stem from specified linker codegen_ssa_static_library_native_artifacts = Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms. -codegen_ssa_native_static_libs = native-static-libs: {$arguments} - codegen_ssa_link_script_unavailable = can only use link script when linking with GNU-like linker codegen_ssa_link_script_write_failure = failed to write link script to {$path}: {$error} @@ -194,3 +192,102 @@ codegen_ssa_unknown_archive_kind = Don't know how to build archive of type: {$kind} codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` + +codegen_ssa_multiple_main_functions = entry symbol `main` declared multiple times + .help = did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead + +codegen_ssa_metadata_object_file_write = error writing metadata object file: {$error} + +codegen_ssa_invalid_windows_subsystem = invalid windows subsystem `{$subsystem}`, only `windows` and `console` are allowed + +codegen_ssa_erroneous_constant = erroneous constant encountered + +codegen_ssa_shuffle_indices_evaluation = could not evaluate shuffle_indices at compile time + +codegen_ssa_missing_memory_ordering = Atomic intrinsic missing memory ordering + +codegen_ssa_unknown_atomic_ordering = unknown ordering in atomic intrinsic + +codegen_ssa_atomic_compare_exchange = Atomic compare-exchange intrinsic missing failure memory ordering + +codegen_ssa_unknown_atomic_operation = unknown atomic operation + +codegen_ssa_invalid_monomorphization_basic_integer_type = invalid monomorphization of `{$name}` intrinsic: expected basic integer type, found `{$ty}` + +codegen_ssa_invalid_monomorphization_basic_float_type = invalid monomorphization of `{$name}` intrinsic: expected basic float type, found `{$ty}` + +codegen_ssa_invalid_monomorphization_float_to_int_unchecked = invalid monomorphization of `float_to_int_unchecked` intrinsic: expected basic float type, found `{$ty}` + +codegen_ssa_invalid_monomorphization_floating_point_vector = invalid monomorphization of `{$name}` intrinsic: unsupported element type `{$f_ty}` of floating-point vector `{$in_ty}` + +codegen_ssa_invalid_monomorphization_floating_point_type = invalid monomorphization of `{$name}` intrinsic: `{$in_ty}` is not a floating-point type + +codegen_ssa_invalid_monomorphization_unrecognized_intrinsic = invalid monomorphization of `{$name}` intrinsic: unrecognized intrinsic `{$name}` + +codegen_ssa_invalid_monomorphization_simd_argument = invalid monomorphization of `{$name}` intrinsic: expected SIMD argument type, found non-SIMD `{$ty}` + +codegen_ssa_invalid_monomorphization_simd_input = invalid monomorphization of `{$name}` intrinsic: expected SIMD input type, found non-SIMD `{$ty}` + +codegen_ssa_invalid_monomorphization_simd_first = invalid monomorphization of `{$name}` intrinsic: expected SIMD first type, found non-SIMD `{$ty}` + +codegen_ssa_invalid_monomorphization_simd_second = invalid monomorphization of `{$name}` intrinsic: expected SIMD second type, found non-SIMD `{$ty}` + +codegen_ssa_invalid_monomorphization_simd_third = invalid monomorphization of `{$name}` intrinsic: expected SIMD third type, found non-SIMD `{$ty}` + +codegen_ssa_invalid_monomorphization_simd_return = invalid monomorphization of `{$name}` intrinsic: expected SIMD return type, found non-SIMD `{$ty}` + +codegen_ssa_invalid_monomorphization_invalid_bitmask = invalid monomorphization of `{$name}` intrinsic: invalid bitmask `{$mask_ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]` + +codegen_ssa_polymorphic_constant_too_generic = codegen encountered polymorphic constant: TooGeneric + +codegen_ssa_invalid_monomorphization_return_length_input_type = invalid monomorphization of `{$name}` intrinsic: expected return type with length {$in_len} (same as input type `{$in_ty}`), found `{$ret_ty}` with length {$out_len} + +codegen_ssa_invalid_monomorphization_second_argument_length = invalid monomorphization of `{$name}` intrinsic: expected second argument with length {$in_len} (same as input type `{$in_ty}`), found `{$arg_ty}` with length {$out_len} + +codegen_ssa_invalid_monomorphization_third_argument_length = invalid monomorphization of `{$name}` intrinsic: expected third argument with length {$in_len} (same as input type `{$in_ty}`), found `{$arg_ty}` with length {$out_len} + +codegen_ssa_invalid_monomorphization_return_integer_type = invalid monomorphization of `{$name}` intrinsic: expected return type with integer elements, found `{$ret_ty}` with non-integer `{$out_ty}` + +codegen_ssa_invalid_monomorphization_simd_shuffle = invalid monomorphization of `{$name}` intrinsic: simd_shuffle index must be an array of `u32`, got `{$ty}` + +codegen_ssa_invalid_monomorphization_return_length = invalid monomorphization of `{$name}` intrinsic: expected return type of length {$in_len}, found `{$ret_ty}` with length {$out_len} + +codegen_ssa_invalid_monomorphization_return_element = invalid monomorphization of `{$name}` intrinsic: expected return element type `{$in_elem}` (element of input `{$in_ty}`), found `{$ret_ty}` with element type `{$out_ty}` + +codegen_ssa_invalid_monomorphization_shuffle_index_not_constant = invalid monomorphization of `{$name}` intrinsic: shuffle index #{$arg_idx} is not a constant + +codegen_ssa_invalid_monomorphization_shuffle_index_out_of_bounds = invalid monomorphization of `{$name}` intrinsic: shuffle index #{$arg_idx} is out of bounds (limit {$total_len}) + +codegen_ssa_invalid_monomorphization_inserted_type = invalid monomorphization of `{$name}` intrinsic: expected inserted type `{$in_elem}` (element of input `{$in_ty}`), found `{$out_ty}` + +codegen_ssa_invalid_monomorphization_return_type = invalid monomorphization of `{$name}` intrinsic: expected return type `{$in_elem}` (element of input `{$in_ty}`), found `{$ret_ty}` + +codegen_ssa_invalid_monomorphization_expected_return_type = invalid monomorphization of `{$name}` intrinsic: expected return type `{$in_ty}`, found `{$ret_ty}` + +codegen_ssa_invalid_monomorphization_mismatched_lengths = invalid monomorphization of `{$name}` intrinsic: mismatched lengths: mask length `{$m_len}` != other vector length `{$v_len}` + +codegen_ssa_invalid_monomorphization_mask_type = invalid monomorphization of `{$name}` intrinsic: mask element type is `{$ty}`, expected `i_` + +codegen_ssa_invalid_monomorphization_vector_argument = invalid monomorphization of `{$name}` intrinsic: vector argument `{$in_ty}`'s element type `{$in_elem}`, expected integer element type + +codegen_ssa_invalid_monomorphization_cannot_return = invalid monomorphization of `{$name}` intrinsic: cannot return `{$ret_ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]` + +codegen_ssa_invalid_monomorphization_expected_element_type = invalid monomorphization of `{$name}` intrinsic: expected element type `{$expected_element}` of second argument `{$second_arg}` to be a pointer to the element type `{$in_elem}` of the first argument `{$in_ty}`, found `{$expected_element}` != `{$mutability} {$in_elem}` + +codegen_ssa_invalid_monomorphization_third_arg_element_type = invalid monomorphization of `{$name}` intrinsic: expected element type `{$expected_element}` of third argument `{$third_arg}` to be a signed integer type + +codegen_ssa_invalid_monomorphization_unsupported_symbol_of_size = invalid monomorphization of `{$name}` intrinsic: unsupported {$symbol} from `{$in_ty}` with element `{$in_elem}` of size `{$size}` to `{$ret_ty}` + +codegen_ssa_invalid_monomorphization_unsupported_symbol = invalid monomorphization of `{$name}` intrinsic: unsupported {$symbol} from `{$in_ty}` with element `{$in_elem}` to `{$ret_ty}` + +codegen_ssa_invalid_monomorphization_cast_fat_pointer = invalid monomorphization of `{$name}` intrinsic: cannot cast fat pointer `{$ty}` + +codegen_ssa_invalid_monomorphization_expected_pointer = invalid monomorphization of `{$name}` intrinsic: expected pointer, got `{$ty}` + +codegen_ssa_invalid_monomorphization_expected_usize = invalid monomorphization of `{$name}` intrinsic: expected `usize`, got `{$ty}` + +codegen_ssa_invalid_monomorphization_unsupported_cast = invalid monomorphization of `{$name}` intrinsic: unsupported cast from `{$in_ty}` with element `{$in_elem}` to `{$ret_ty}` with element `{$out_elem}` + +codegen_ssa_invalid_monomorphization_unsupported_operation = invalid monomorphization of `{$name}` intrinsic: unsupported operation on `{$in_ty}` with element `{$in_elem}` + +codegen_ssa_invalid_monomorphization_expected_vector_element_type = invalid monomorphization of `{$name}` intrinsic: expected element type `{$expected_element}` of vector type `{$vector_type}` to be a signed or unsigned integer type diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl index 26cdf8a58f3..41f458f6c17 100644 --- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl +++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl @@ -120,3 +120,7 @@ hir_analysis_self_in_impl_self = hir_analysis_linkage_type = invalid type for variable with `#[linkage]` attribute + +hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit while auto-dereferencing `{$ty}` + .label = deref recursion limit reached + .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`) diff --git a/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl b/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl index 0612dbae0b6..ca72b7faa92 100644 --- a/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl +++ b/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl @@ -46,3 +46,14 @@ hir_typeck_add_missing_parentheses_in_range = you must surround the range in par hir_typeck_op_trait_generic_params = `{$method_name}` must not have any generic parameters + +hir_typeck_lang_start_incorrect_number_params = incorrect number of parameters for the `start` lang item +hir_typeck_lang_start_incorrect_number_params_note_expected_count = the `start` lang item should have four parameters, but found {$found_param_count} + +hir_typeck_lang_start_expected_sig_note = the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize` + +hir_typeck_lang_start_incorrect_param = parameter {$param_num} of the `start` lang item is incorrect + .suggestion = change the type from `{$found_ty}` to `{$expected_ty}` + +hir_typeck_lang_start_incorrect_ret_ty = the return type of the `start` lang item is incorrect + .suggestion = change the type from `{$found_ty}` to `{$expected_ty}` diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl index c9d83746d54..ae0091b0373 100644 --- a/compiler/rustc_error_messages/locales/en-US/infer.ftl +++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl @@ -101,7 +101,6 @@ infer_subtype_2 = ...so that {$requirement -> infer_reborrow = ...so that reference does not outlive borrowed content infer_reborrow_upvar = ...so that closure can access `{$name}` infer_relate_object_bound = ...so that it can be closed over into an object -infer_data_borrowed = ...so that the type `{$name}` is not borrowed for too long infer_reference_outlives_referent = ...so that the reference type `{$name}` does not outlive the data it points at infer_relate_param_bound = ...so that the type `{$name}` will meet its required lifetime bounds{$continues -> [true] ... @@ -172,3 +171,142 @@ infer_msl_unmet_req = because this has an unmet lifetime requirement infer_msl_trait_note = this has an implicit `'static` lifetime requirement infer_msl_trait_sugg = consider relaxing the implicit `'static` requirement infer_suggest_add_let_for_letchains = consider adding `let` + +infer_explicit_lifetime_required_with_ident = explicit lifetime required in the type of `{$simple_ident}` + .label = lifetime `{$named}` required + +infer_explicit_lifetime_required_with_param_type = explicit lifetime required in parameter type + .label = lifetime `{$named}` required + +infer_explicit_lifetime_required_sugg_with_ident = add explicit lifetime `{$named}` to the type of `{$simple_ident}` + +infer_explicit_lifetime_required_sugg_with_param_type = add explicit lifetime `{$named}` to type + +infer_actual_impl_expl_expected_signature_two = {$leading_ellipsis -> + [true] ... + *[false] {""} +}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... +infer_actual_impl_expl_expected_signature_any = {$leading_ellipsis -> + [true] ... + *[false] {""} +}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`... +infer_actual_impl_expl_expected_signature_some = {$leading_ellipsis -> + [true] ... + *[false] {""} +}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{lifetime_1}`... +infer_actual_impl_expl_expected_signature_nothing = {$leading_ellipsis -> + [true] ... + *[false] {""} +}closure with signature `{$ty_or_sig}` must implement `{$trait_path}` +infer_actual_impl_expl_expected_passive_two = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... +infer_actual_impl_expl_expected_passive_any = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any lifetime `'{$lifetime_1}`... +infer_actual_impl_expl_expected_passive_some = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for some specific lifetime `'{lifetime_1}`... +infer_actual_impl_expl_expected_passive_nothing = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}` +infer_actual_impl_expl_expected_other_two = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... +infer_actual_impl_expl_expected_other_any = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`... +infer_actual_impl_expl_expected_other_some = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{lifetime_1}`... +infer_actual_impl_expl_expected_other_nothing = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$ty_or_sig}` must implement `{$trait_path}` + +infer_actual_impl_expl_but_actually_implements_trait = ...but it actually implements `{$trait_path}`{$has_lifetime -> + [true] , for some specific lifetime `'{$lifetime}` + *[false] {""} +} +infer_actual_impl_expl_but_actually_implemented_for_ty = ...but `{$trait_path}` is actually implemented for the type `{$ty}`{$has_lifetime -> + [true] , for some specific lifetime `'{$lifetime}` + *[false] {""} +} +infer_actual_impl_expl_but_actually_ty_implements = ...but `{$ty}` actually implements `{$trait_path}`{$has_lifetime -> + [true] , for some specific lifetime `'{$lifetime}` + *[false] {""} +} + +infer_trait_placeholder_mismatch = implementation of `{$trait_def_id}` is not general enough + .label_satisfy = doesn't satisfy where-clause + .label_where = due to a where-clause on `{$def_id}`... + .label_dup = implementation of `{$trait_def_id}` is not general enough + +infer_trait_impl_diff = `impl` item signature doesn't match `trait` item signature + .found = found `{$found}` + .expected = expected `{$expected}` + .expected_found = expected signature `{$expected}` + {" "}found signature `{$found}` + +infer_tid_rel_help = verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output +infer_tid_consider_borrowing = consider borrowing this type parameter in the trait +infer_tid_param_help = the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + +infer_dtcs_has_lifetime_req_label = this has an implicit `'static` lifetime requirement +infer_dtcs_introduces_requirement = calling this method introduces the `impl`'s 'static` requirement +infer_dtcs_has_req_note = the used `impl` has a `'static` requirement +infer_dtcs_suggestion = consider relaxing the implicit `'static` requirement + +infer_but_calling_introduces = {$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$lifetime_kind -> + [named] lifetime `{lifetime}` + *[anon] an anonymous lifetime `'_` +} but calling `{assoc_item}` introduces an implicit `'static` lifetime requirement + .label1 = {$has_lifetime -> + [named] lifetime `{lifetime}` + *[anon] an anonymous lifetime `'_` + } + .label2 = ...is used and required to live as long as `'static` here because of an implicit lifetime bound on the {$has_impl_path -> + [named] `impl` of `{$impl_path}` + *[anon] inherent `impl` + } + +infer_but_needs_to_satisfy = {$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$has_lifetime -> + [named] lifetime `{lifetime}` + *[anon] an anonymous lifetime `'_` +} but it needs to satisfy a `'static` lifetime requirement + .influencer = this data with {$has_lifetime -> + [named] lifetime `{lifetime}` + *[anon] an anonymous lifetime `'_` + }... + .require = {$spans_empty -> + *[true] ...is used and required to live as long as `'static` here + [false] ...and is required to live as long as `'static` here + } + .used_here = ...is used here... + .introduced_by_bound = 'static` lifetime requirement introduced by this bound + +infer_more_targeted = {$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$has_lifetime -> + [named] lifetime `{lifetime}` + *[anon] an anonymous lifetime `'_` +} but calling `{$ident}` introduces an implicit `'static` lifetime requirement + +infer_ril_introduced_here = `'static` requirement introduced here +infer_ril_introduced_by = requirement introduced by this return type +infer_ril_because_of = because of this returned expression +infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type diff --git a/compiler/rustc_error_messages/locales/en-US/interface.ftl b/compiler/rustc_error_messages/locales/en-US/interface.ftl index bbcb8fc28cf..688b0447222 100644 --- a/compiler/rustc_error_messages/locales/en-US/interface.ftl +++ b/compiler/rustc_error_messages/locales/en-US/interface.ftl @@ -41,3 +41,6 @@ interface_rustc_error_unexpected_annotation = interface_failed_writing_file = failed to write file {$path}: {$error}" + +interface_proc_macro_crate_panic_abort = + building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic diff --git a/compiler/rustc_error_messages/locales/en-US/lint.ftl b/compiler/rustc_error_messages/locales/en-US/lint.ftl index 2eb409a5ddd..d63ff77d8e2 100644 --- a/compiler/rustc_error_messages/locales/en-US/lint.ftl +++ b/compiler/rustc_error_messages/locales/en-US/lint.ftl @@ -15,6 +15,43 @@ lint_enum_intrinsics_mem_variant = lint_expectation = this lint expectation is unfulfilled .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message + .rationale = {$rationale} + +lint_for_loops_over_fallibles = + for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement + .suggestion = consider using `if let` to clear intent + .remove_next = to iterate over `{$recv_snip}` remove the call to `next` + .use_while_let = to check pattern in a loop use `while let` + .use_question_mark = consider unwrapping the `Result` with `?` to iterate over its contents + +lint_non_binding_let_on_sync_lock = + non-binding let on a synchronization lock + +lint_non_binding_let_on_drop_type = + non-binding let on a type that implements `Drop` + +lint_non_binding_let_suggestion = + consider binding to an unused variable to avoid immediately dropping the value + +lint_non_binding_let_multi_suggestion = + consider immediately dropping the value + +lint_deprecated_lint_name = + lint name `{$name}` is deprecated and may not have an effect in the future. + .suggestion = change it to + +lint_renamed_or_removed_lint = {$msg} + .suggestion = use the new name + +lint_unknown_lint = + unknown lint: `{$name}` + .suggestion = did you mean + +lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level + +lint_unknown_gated_lint = + unknown lint: `{$name}` + .note = the `{$name}` lint is unstable lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label} .label = this {$label} contains {$count -> @@ -55,6 +92,8 @@ lint_diag_out_of_impl = lint_untranslatable_diag = diagnostics should be created using translatable messages +lint_bad_opt_access = {$msg} + lint_cstring_ptr = getting the inner pointer of a temporary `CString` .as_ptr_label = this pointer will be invalid .unwrap_label = this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime @@ -331,6 +370,8 @@ lint_builtin_anonymous_params = anonymous parameters are deprecated and will be .suggestion = try naming the parameter or explicitly ignoring it lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$reason}. See {$link} + .msg_suggestion = {$msg} + .default_suggestion = remove this attribute lint_builtin_deprecated_attr_used = use of deprecated attribute `{$name}`: no longer used. lint_builtin_deprecated_attr_default_suggestion = remove this attribute @@ -391,10 +432,16 @@ lint_builtin_incomplete_features = the feature `{$name}` is incomplete and may n .note = see issue #{$n} <https://github.com/rust-lang/rust/issues/{$n}> for more information .help = consider using `min_{$name}` instead, which is more stable and complete -lint_builtin_clashing_extern_same_name = `{$this_fi}` redeclared with a different signature +lint_builtin_unpermitted_type_init_zeroed = the type `{$ty}` does not permit zero-initialization +lint_builtin_unpermitted_type_init_unint = the type `{$ty}` does not permit being left uninitialized + +lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed +lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done + +lint_builtin_clashing_extern_same_name = `{$this}` redeclared with a different signature .previous_decl_label = `{$orig}` previously declared here .mismatch_label = this signature doesn't match the previous declaration -lint_builtin_clashing_extern_diff_name = `{$this_fi}` redeclares `{$orig}` with a different signature +lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature .previous_decl_label = `{$orig}` previously declared here .mismatch_label = this signature doesn't match the previous declaration @@ -403,6 +450,16 @@ lint_builtin_deref_nullptr = dereferencing a null pointer lint_builtin_asm_labels = avoid using named labels in inline assembly +lint_builtin_special_module_name_used_lib = found module declaration for lib.rs + .note = lib.rs is the root of this crate's library target + .help = to refer to it from other targets, use the library's name as the path + +lint_builtin_special_module_name_used_main = found module declaration for main.rs + .note = a binary crate cannot be used as library + +lint_supertrait_as_deref_target = `{$t}` implements `Deref` with supertrait `{$target_principal}` as target + .label = target type is set here + lint_overruled_attribute = {$lint_level}({$lint_source}) incompatible with previous forbid .label = overruled by previous forbid diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl index 60d3d3e69ab..a082c0b61fa 100644 --- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl +++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl @@ -206,6 +206,10 @@ mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper = .label = lower bound larger than upper bound .teach_note = When matching against a range, the compiler verifies that the range is non-empty. Range patterns include both end-points, so this is equivalent to requiring the start of the range to be less than or equal to the end of the range. +mir_build_literal_in_range_out_of_bounds = + literal out of range for `{$ty}` + .label = this value doesn't fit in `{$ty}` whose maximum value is `{$max}` + mir_build_lower_range_bound_must_be_less_than_upper = lower range bound must be less than upper mir_build_leading_irrefutable_let_patterns = leading irrefutable {$count -> @@ -299,3 +303,64 @@ mir_build_multiple_mut_borrows = cannot borrow value as mutable more than once a .mutable_borrow = another mutable borrow, by `{$name_mut}`, occurs here .immutable_borrow = also borrowed as immutable, by `{$name_immut}`, here .moved = also moved into `{$name_moved}` here + +mir_build_union_pattern = cannot use unions in constant patterns + +mir_build_type_not_structural = + to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]` + +mir_build_unsized_pattern = cannot use unsized non-slice type `{$non_sm_ty}` in constant patterns + +mir_build_invalid_pattern = `{$non_sm_ty}` cannot be used in patterns + +mir_build_float_pattern = floating-point types cannot be used in patterns + +mir_build_pointer_pattern = function pointers and unsized pointers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + +mir_build_indirect_structural_match = + to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]` + +mir_build_nontrivial_structural_match = + to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]` + +mir_build_overlapping_range_endpoints = multiple patterns overlap on their endpoints + .range = ... with this range + .note = you likely meant to write mutually exclusive ranges + +mir_build_non_exhaustive_omitted_pattern = some variants are not matched explicitly + .help = ensure that all variants are matched explicitly by adding the suggested match arms + .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found + +mir_build_uncovered = {$count -> + [1] pattern `{$witness_1}` + [2] patterns `{$witness_1}` and `{$witness_2}` + [3] patterns `{$witness_1}`, `{$witness_2}` and `{$witness_3}` + *[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more + } not covered + +mir_build_pattern_not_covered = refutable pattern in {$origin} + .pattern_ty = the matched value is of type `{$pattern_ty}` + +mir_build_inform_irrefutable = `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + +mir_build_more_information = for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + +mir_build_res_defined_here = {$res} defined here + +mir_build_adt_defined_here = `{$ty}` defined here + +mir_build_variant_defined_here = not covered + +mir_build_interpreted_as_const = introduce a variable instead + +mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as {$article} {$res} pattern, not a new variable + +mir_build_suggest_if_let = you might want to use `if let` to ignore the {$count -> + [one] variant that isn't + *[other] variants that aren't + } matched + +mir_build_suggest_let_else = you might want to use `let else` to handle the {$count -> + [one] variant that isn't + *[other] variants that aren't + } matched diff --git a/compiler/rustc_error_messages/locales/en-US/parse.ftl b/compiler/rustc_error_messages/locales/en-US/parse.ftl index 3401978caf5..8f063f5082c 100644 --- a/compiler/rustc_error_messages/locales/en-US/parse.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parse.ftl @@ -2,6 +2,10 @@ parse_struct_literal_body_without_path = struct literal body without path .suggestion = you might have forgotten to add the struct literal inside the block +parse_struct_literal_needing_parens = + invalid struct literal + .suggestion = you might need to surround the struct literal in parentheses + parse_maybe_report_ambiguous_plus = ambiguous `+` in a type .suggestion = use parentheses to disambiguate @@ -368,3 +372,9 @@ parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of parse_expected_fn_path_found_fn_keyword = expected identifier, found keyword `fn` .suggestion = use `Fn` to refer to the trait + +parse_where_clause_before_tuple_struct_body = where clauses are not allowed before tuple struct bodies + .label = unexpected where clause + .name_label = while parsing this tuple struct + .body_label = the struct body + .suggestion = move the body before the where clause diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl index 001e53d1d0e..91857dd227d 100644 --- a/compiler/rustc_error_messages/locales/en-US/passes.ftl +++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl @@ -4,6 +4,9 @@ -passes_see_issue = see issue #{$issue} <https://github.com/rust-lang/rust/issues/{$issue}> for more information +passes_incorrect_do_not_recommend_location = + `#[do_not_recommend]` can only be placed on trait implementations + passes_outer_crate_level_attr = crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_error_messages/locales/en-US/session.ftl index ab9e8b6baae..bc37d91a7c6 100644 --- a/compiler/rustc_error_messages/locales/en-US/session.ftl +++ b/compiler/rustc_error_messages/locales/en-US/session.ftl @@ -85,6 +85,7 @@ session_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float lite .help = valid suffixes are `f32` and `f64` session_int_literal_too_large = integer literal is too large + .note = value exceeds limit of `{$limit}` session_invalid_int_literal_width = invalid width `{$width}` for integer literal .help = valid widths are 8, 16, 32, 64 and 128 diff --git a/compiler/rustc_error_messages/locales/en-US/trait_selection.ftl b/compiler/rustc_error_messages/locales/en-US/trait_selection.ftl index 004e0ab1896..14eb4a5502d 100644 --- a/compiler/rustc_error_messages/locales/en-US/trait_selection.ftl +++ b/compiler/rustc_error_messages/locales/en-US/trait_selection.ftl @@ -2,10 +2,6 @@ trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entri trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated} -trait_selection_auto_deref_reached_recursion_limit = reached the recursion limit while auto-dereferencing `{$ty}` - .label = deref recursion limit reached - .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`) - trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]` .label = empty on-clause here diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index dee7a31ec20..cadd53fbd83 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -17,6 +17,7 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_target = { path = "../rustc_target" } rustc_hir = { path = "../rustc_hir" } rustc_lint_defs = { path = "../rustc_lint_defs" } +rustc_type_ir = { path = "../rustc_type_ir" } unicode-width = "0.1.4" termcolor = "1.0" annotate-snippets = "0.9" diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index a2ed988643f..cbfee582d87 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -1,7 +1,7 @@ use crate::diagnostic::IntoDiagnosticArg; use crate::{ Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed, - SubdiagnosticMessage, + ExplicitBug, SubdiagnosticMessage, }; use crate::{Handler, Level, MultiSpan, StashKey}; use rustc_lint_defs::Applicability; @@ -12,6 +12,7 @@ use std::borrow::Cow; use std::fmt::{self, Debug}; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; +use std::panic; use std::thread::panicking; /// Trait implemented by error types. This should not be implemented manually. Instead, use @@ -308,6 +309,58 @@ impl EmissionGuarantee for Noted { } } +/// Marker type which enables implementation of `create_bug` and `emit_bug` functions for +/// bug struct diagnostics. +#[derive(Copy, Clone)] +pub struct Bug; + +impl<'a> DiagnosticBuilder<'a, Bug> { + /// Convenience function for internal use, clients should use one of the + /// `struct_*` methods on [`Handler`]. + #[track_caller] + pub(crate) fn new_bug(handler: &'a Handler, message: impl Into<DiagnosticMessage>) -> Self { + let diagnostic = Diagnostic::new_with_code(Level::Bug, None, message); + Self::new_diagnostic_bug(handler, diagnostic) + } + + /// Creates a new `DiagnosticBuilder` with an already constructed + /// diagnostic. + pub(crate) fn new_diagnostic_bug(handler: &'a Handler, diagnostic: Diagnostic) -> Self { + debug!("Created new diagnostic bug"); + Self { + inner: DiagnosticBuilderInner { + state: DiagnosticBuilderState::Emittable(handler), + diagnostic: Box::new(diagnostic), + }, + _marker: PhantomData, + } + } +} + +impl EmissionGuarantee for Bug { + fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self { + match db.inner.state { + // First `.emit()` call, the `&Handler` is still available. + DiagnosticBuilderState::Emittable(handler) => { + db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation; + + handler.emit_diagnostic(&mut db.inner.diagnostic); + } + // `.emit()` was previously called, disallowed from repeating it. + DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {} + } + // Then panic. No need to return the marker type. + panic::panic_any(ExplicitBug); + } + + fn make_diagnostic_builder( + handler: &Handler, + msg: impl Into<DiagnosticMessage>, + ) -> DiagnosticBuilder<'_, Self> { + DiagnosticBuilder::new_bug(handler, msg) + } +} + impl<'a> DiagnosticBuilder<'a, !> { /// Convenience function for internal use, clients should use one of the /// `struct_*` methods on [`Handler`]. diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 628cb90903f..dad5e98aac0 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -9,6 +9,7 @@ use rustc_span::edition::Edition; use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol}; use rustc_target::abi::TargetDataLayoutErrors; use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple}; +use rustc_type_ir as type_ir; use std::borrow::Cow; use std::fmt; use std::num::ParseIntError; @@ -170,18 +171,15 @@ impl IntoDiagnosticArg for ast::token::TokenKind { } } +impl IntoDiagnosticArg for type_ir::FloatTy { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Borrowed(self.name_str())) + } +} + impl IntoDiagnosticArg for Level { fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - DiagnosticArgValue::Str(Cow::Borrowed(match self { - Level::Allow => "-A", - Level::Warn => "-W", - Level::ForceWarn(_) => "--force-warn", - Level::Deny => "-D", - Level::Forbid => "-F", - Level::Expect(_) => { - unreachable!("lints with the level of `expect` should not run this code"); - } - })) + DiagnosticArgValue::Str(Cow::Borrowed(self.to_cmd_flag())) } } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 0ca200abe19..7f01df32101 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -28,6 +28,7 @@ use rustc_error_messages::{FluentArgs, SpanLabel}; use rustc_span::hygiene::{ExpnKind, MacroKind}; use std::borrow::Cow; use std::cmp::{max, min, Reverse}; +use std::error::Report; use std::io::prelude::*; use std::io::{self, IsTerminal}; use std::iter; @@ -250,7 +251,7 @@ pub trait Emitter: Translate { let mut primary_span = diag.span.clone(); let suggestions = diag.suggestions.as_deref().unwrap_or(&[]); if let Some((sugg, rest)) = suggestions.split_first() { - let msg = self.translate_message(&sugg.msg, fluent_args); + let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap(); if rest.is_empty() && // ^ if there is only one suggestion // don't display multi-suggestions as labels @@ -1325,7 +1326,7 @@ impl EmitterWriter { // very *weird* formats // see? for (text, style) in msg.iter() { - let text = self.translate_message(text, args); + let text = self.translate_message(text, args).map_err(Report::new).unwrap(); let lines = text.split('\n').collect::<Vec<_>>(); if lines.len() > 1 { for (i, line) in lines.iter().enumerate() { @@ -1387,7 +1388,7 @@ impl EmitterWriter { label_width += 2; } for (text, _) in msg.iter() { - let text = self.translate_message(text, args); + let text = self.translate_message(text, args).map_err(Report::new).unwrap(); // Account for newlines to align output to its label. for (line, text) in normalize_whitespace(&text).lines().enumerate() { buffer.append( @@ -2301,7 +2302,9 @@ impl FileWithAnnotatedLines { hi.col_display += 1; } - let label = label.as_ref().map(|m| emitter.translate_message(m, args).to_string()); + let label = label.as_ref().map(|m| { + emitter.translate_message(m, args).map_err(Report::new).unwrap().to_string() + }); if lo.line != hi.line { let ml = MultilineAnnotation { diff --git a/compiler/rustc_errors/src/error.rs b/compiler/rustc_errors/src/error.rs new file mode 100644 index 00000000000..ec0a2fe8cd8 --- /dev/null +++ b/compiler/rustc_errors/src/error.rs @@ -0,0 +1,137 @@ +use rustc_error_messages::{ + fluent_bundle::resolver::errors::{ReferenceKind, ResolverError}, + FluentArgs, FluentError, +}; +use std::borrow::Cow; +use std::error::Error; +use std::fmt; + +#[derive(Debug)] +pub enum TranslateError<'args> { + One { + id: &'args Cow<'args, str>, + args: &'args FluentArgs<'args>, + kind: TranslateErrorKind<'args>, + }, + Two { + primary: Box<TranslateError<'args>>, + fallback: Box<TranslateError<'args>>, + }, +} + +impl<'args> TranslateError<'args> { + pub fn message(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self { + Self::One { id, args, kind: TranslateErrorKind::MessageMissing } + } + pub fn primary(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self { + Self::One { id, args, kind: TranslateErrorKind::PrimaryBundleMissing } + } + pub fn attribute( + id: &'args Cow<'args, str>, + args: &'args FluentArgs<'args>, + attr: &'args str, + ) -> Self { + Self::One { id, args, kind: TranslateErrorKind::AttributeMissing { attr } } + } + pub fn value(id: &'args Cow<'args, str>, args: &'args FluentArgs<'args>) -> Self { + Self::One { id, args, kind: TranslateErrorKind::ValueMissing } + } + + pub fn fluent( + id: &'args Cow<'args, str>, + args: &'args FluentArgs<'args>, + errs: Vec<FluentError>, + ) -> Self { + Self::One { id, args, kind: TranslateErrorKind::Fluent { errs } } + } + + pub fn and(self, fallback: TranslateError<'args>) -> TranslateError<'args> { + Self::Two { primary: Box::new(self), fallback: Box::new(fallback) } + } +} + +#[derive(Debug)] +pub enum TranslateErrorKind<'args> { + MessageMissing, + PrimaryBundleMissing, + AttributeMissing { attr: &'args str }, + ValueMissing, + Fluent { errs: Vec<FluentError> }, +} + +impl fmt::Display for TranslateError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use TranslateErrorKind::*; + + match self { + Self::One { id, args, kind } => { + writeln!(f, "failed while formatting fluent string `{id}`: ")?; + match kind { + MessageMissing => writeln!(f, "message was missing")?, + PrimaryBundleMissing => writeln!(f, "the primary bundle was missing")?, + AttributeMissing { attr } => { + writeln!(f, "the attribute `{attr}` was missing")?; + writeln!(f, "help: add `.{attr} = <message>`")?; + } + ValueMissing => writeln!(f, "the value was missing")?, + Fluent { errs } => { + for err in errs { + match err { + FluentError::ResolverError(ResolverError::Reference( + ReferenceKind::Message { id, .. } + | ReferenceKind::Variable { id, .. }, + )) => { + if args.iter().any(|(arg_id, _)| arg_id == id) { + writeln!( + f, + "argument `{id}` exists but was not referenced correctly" + )?; + writeln!(f, "help: try using `{{${id}}}` instead")?; + } else { + writeln!( + f, + "the fluent string has an argument `{id}` that was not found." + )?; + let vars: Vec<&str> = + args.iter().map(|(a, _v)| a).collect(); + match &*vars { + [] => writeln!(f, "help: no arguments are available")?, + [one] => writeln!( + f, + "help: the argument `{one}` is available" + )?, + [first, middle @ .., last] => { + write!(f, "help: the arguments `{first}`")?; + for a in middle { + write!(f, ", `{a}`")?; + } + writeln!(f, " and `{last}` are available")?; + } + } + } + } + _ => writeln!(f, "{err}")?, + } + } + } + } + } + // If someone cares about primary bundles, they'll probably notice it's missing + // regardless or will be using `debug_assertions` + // so we skip the arm below this one to avoid confusing the regular user. + Self::Two { primary: box Self::One { kind: PrimaryBundleMissing, .. }, fallback } => { + fmt::Display::fmt(fallback, f)?; + } + Self::Two { primary, fallback } => { + writeln!( + f, + "first, fluent formatting using the primary bundle failed:\n {primary}\n \ + while attempting to recover by using the fallback bundle instead, another error occurred:\n{fallback}" + )?; + } + } + Ok(()) + } +} + +impl Error for TranslateError<'_> {} diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index a37073d8fa3..dc38b8725ad 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -24,6 +24,7 @@ use rustc_data_structures::sync::Lrc; use rustc_error_messages::FluentArgs; use rustc_span::hygiene::ExpnData; use rustc_span::Span; +use std::error::Report; use std::io::{self, Write}; use std::path::Path; use std::sync::{Arc, Mutex}; @@ -321,7 +322,8 @@ impl Diagnostic { fn from_errors_diagnostic(diag: &crate::Diagnostic, je: &JsonEmitter) -> Diagnostic { let args = to_fluent_args(diag.args()); let sugg = diag.suggestions.iter().flatten().map(|sugg| { - let translated_message = je.translate_message(&sugg.msg, &args); + let translated_message = + je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap(); Diagnostic { message: translated_message.to_string(), code: None, @@ -411,7 +413,10 @@ impl DiagnosticSpan { Self::from_span_etc( span.span, span.is_primary, - span.label.as_ref().map(|m| je.translate_message(m, args)).map(|m| m.to_string()), + span.label + .as_ref() + .map(|m| je.translate_message(m, args).unwrap()) + .map(|m| m.to_string()), suggestion, je, ) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 136c360201e..535812fb0e2 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -11,6 +11,10 @@ #![feature(never_type)] #![feature(result_option_inspect)] #![feature(rustc_attrs)] +#![feature(yeet_expr)] +#![feature(try_blocks)] +#![feature(box_patterns)] +#![feature(error_reporter)] #![allow(incomplete_features)] #[macro_use] @@ -40,8 +44,8 @@ use rustc_span::source_map::SourceMap; use rustc_span::HashStableContext; use rustc_span::{Loc, Span}; -use std::any::Any; use std::borrow::Cow; +use std::error::Report; use std::fmt; use std::hash::Hash; use std::num::NonZeroUsize; @@ -55,11 +59,14 @@ mod diagnostic; mod diagnostic_builder; mod diagnostic_impls; pub mod emitter; +pub mod error; pub mod json; mod lock; pub mod registry; mod snippet; mod styled_buffer; +#[cfg(test)] +mod tests; pub mod translation; pub use diagnostic_builder::IntoDiagnostic; @@ -364,9 +371,9 @@ pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; /// or `.span_bug` rather than a failed assertion, etc. pub struct ExplicitBug; -/// Signifies that the compiler died with an explicit call to `.delay_good_path_bug` +/// Signifies that the compiler died with an explicit call to `.delay_*_bug` /// rather than a failed assertion, etc. -pub struct GoodPathBug; +pub struct DelayedBugPanic; pub use diagnostic::{ AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId, @@ -399,7 +406,7 @@ struct HandlerInner { warn_count: usize, deduplicated_err_count: usize, emitter: Box<dyn Emitter + sync::Send>, - delayed_span_bugs: Vec<Diagnostic>, + delayed_span_bugs: Vec<DelayedDiagnostic>, delayed_good_path_bugs: Vec<DelayedDiagnostic>, /// This flag indicates that an expected diagnostic was emitted and suppressed. /// This is used for the `delayed_good_path_bugs` check. @@ -505,11 +512,7 @@ impl Drop for HandlerInner { if !self.has_errors() { let bugs = std::mem::replace(&mut self.delayed_span_bugs, Vec::new()); - self.flush_delayed( - bugs, - "no errors encountered even though `delay_span_bug` issued", - ExplicitBug, - ); + self.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued"); } // FIXME(eddyb) this explains what `delayed_good_path_bugs` are! @@ -520,9 +523,8 @@ impl Drop for HandlerInner { if !self.has_any_message() && !self.suppressed_expected_diag { let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new()); self.flush_delayed( - bugs.into_iter().map(DelayedDiagnostic::decorate), + bugs, "no warnings or errors encountered even though `delayed_good_path_bugs` issued", - GoodPathBug, ); } @@ -622,7 +624,14 @@ impl Handler { ) -> SubdiagnosticMessage { let inner = self.inner.borrow(); let args = crate::translation::to_fluent_args(args); - SubdiagnosticMessage::Eager(inner.emitter.translate_message(&message, &args).to_string()) + SubdiagnosticMessage::Eager( + inner + .emitter + .translate_message(&message, &args) + .map_err(Report::new) + .unwrap() + .to_string(), + ) } // This is here to not allow mutation of flags; @@ -978,6 +987,7 @@ impl Handler { self.inner.borrow_mut().span_bug(span, msg) } + /// For documentation on this, see `Session::delay_span_bug`. #[track_caller] pub fn delay_span_bug( &self, @@ -1132,6 +1142,20 @@ impl Handler { self.create_fatal(fatal).emit() } + pub fn create_bug<'a>( + &'a self, + bug: impl IntoDiagnostic<'a, diagnostic_builder::Bug>, + ) -> DiagnosticBuilder<'a, diagnostic_builder::Bug> { + bug.into_diagnostic(self) + } + + pub fn emit_bug<'a>( + &'a self, + bug: impl IntoDiagnostic<'a, diagnostic_builder::Bug>, + ) -> diagnostic_builder::Bug { + self.create_bug(bug).emit() + } + fn emit_diag_at_span( &self, mut diag: Diagnostic, @@ -1208,11 +1232,7 @@ impl Handler { pub fn flush_delayed(&self) { let mut inner = self.inner.lock(); let bugs = std::mem::replace(&mut inner.delayed_span_bugs, Vec::new()); - inner.flush_delayed( - bugs, - "no errors encountered even though `delay_span_bug` issued", - ExplicitBug, - ); + inner.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued"); } } @@ -1272,7 +1292,9 @@ impl HandlerInner { // once *any* errors were emitted (and truncate `delayed_span_bugs` // when an error is first emitted, also), but maybe there's a case // in which that's not sound? otherwise this is really inefficient. - self.delayed_span_bugs.push(diagnostic.clone()); + let backtrace = std::backtrace::Backtrace::force_capture(); + self.delayed_span_bugs + .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace)); if !self.flags.report_delayed_bugs { return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); @@ -1529,6 +1551,7 @@ impl HandlerInner { self.emit_diagnostic(diag.set_span(sp)); } + /// For documentation on this, see `Session::delay_span_bug`. #[track_caller] fn delay_span_bug( &mut self, @@ -1546,7 +1569,6 @@ impl HandlerInner { } let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg); diagnostic.set_span(sp.into()); - diagnostic.note(&format!("delayed at {}", std::panic::Location::caller())); self.emit_diagnostic(&mut diagnostic).unwrap() } @@ -1589,12 +1611,13 @@ impl HandlerInner { fn flush_delayed( &mut self, - bugs: impl IntoIterator<Item = Diagnostic>, + bugs: impl IntoIterator<Item = DelayedDiagnostic>, explanation: impl Into<DiagnosticMessage> + Copy, - panic_with: impl Any + Send + 'static, ) { let mut no_bugs = true; - for mut bug in bugs { + for bug in bugs { + let mut bug = bug.decorate(); + if no_bugs { // Put the overall explanation before the `DelayedBug`s, to // frame them better (e.g. separate warnings from them). @@ -1617,9 +1640,9 @@ impl HandlerInner { self.emit_diagnostic(&mut bug); } - // Panic with `ExplicitBug` to avoid "unexpected panic" messages. + // Panic with `DelayedBugPanic` to avoid "unexpected panic" messages. if !no_bugs { - panic::panic_any(panic_with); + panic::panic_any(DelayedBugPanic); } } diff --git a/compiler/rustc_errors/src/tests.rs b/compiler/rustc_errors/src/tests.rs new file mode 100644 index 00000000000..52103e46097 --- /dev/null +++ b/compiler/rustc_errors/src/tests.rs @@ -0,0 +1,188 @@ +use crate::error::{TranslateError, TranslateErrorKind}; +use crate::fluent_bundle::*; +use crate::translation::Translate; +use crate::FluentBundle; +use rustc_data_structures::sync::Lrc; +use rustc_error_messages::fluent_bundle::resolver::errors::{ReferenceKind, ResolverError}; +use rustc_error_messages::langid; +use rustc_error_messages::DiagnosticMessage; + +struct Dummy { + bundle: FluentBundle, +} + +impl Translate for Dummy { + fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> { + None + } + + fn fallback_fluent_bundle(&self) -> &FluentBundle { + &self.bundle + } +} + +fn make_dummy(ftl: &'static str) -> Dummy { + let resource = FluentResource::try_new(ftl.into()).expect("Failed to parse an FTL string."); + + let langid_en = langid!("en-US"); + + #[cfg(parallel_compiler)] + let mut bundle = FluentBundle::new_concurrent(vec![langid_en]); + + #[cfg(not(parallel_compiler))] + let mut bundle = FluentBundle::new(vec![langid_en]); + + bundle.add_resource(resource).expect("Failed to add FTL resources to the bundle."); + + Dummy { bundle } +} + +#[test] +fn wellformed_fluent() { + let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value + .label = value moved into `{$name}` here + .occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait + .value_borrowed_label = value borrowed here after move + .suggestion = borrow this binding in the pattern to avoid moving the value"); + + let mut args = FluentArgs::new(); + args.set("name", "Foo"); + args.set("ty", "std::string::String"); + { + let message = DiagnosticMessage::FluentIdentifier( + "mir_build_borrow_of_moved_value".into(), + Some("suggestion".into()), + ); + + assert_eq!( + dummy.translate_message(&message, &args).unwrap(), + "borrow this binding in the pattern to avoid moving the value" + ); + } + + { + let message = DiagnosticMessage::FluentIdentifier( + "mir_build_borrow_of_moved_value".into(), + Some("value_borrowed_label".into()), + ); + + assert_eq!( + dummy.translate_message(&message, &args).unwrap(), + "value borrowed here after move" + ); + } + + { + let message = DiagnosticMessage::FluentIdentifier( + "mir_build_borrow_of_moved_value".into(), + Some("occurs_because_label".into()), + ); + + assert_eq!( + dummy.translate_message(&message, &args).unwrap(), + "move occurs because `\u{2068}Foo\u{2069}` has type `\u{2068}std::string::String\u{2069}` which does not implement the `Copy` trait" + ); + + { + let message = DiagnosticMessage::FluentIdentifier( + "mir_build_borrow_of_moved_value".into(), + Some("label".into()), + ); + + assert_eq!( + dummy.translate_message(&message, &args).unwrap(), + "value moved into `\u{2068}Foo\u{2069}` here" + ); + } + } +} + +#[test] +fn misformed_fluent() { + let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value + .label = value moved into `{name}` here + .occurs_because_label = move occurs because `{$oops}` has type `{$ty}` which does not implement the `Copy` trait + .suggestion = borrow this binding in the pattern to avoid moving the value"); + + let mut args = FluentArgs::new(); + args.set("name", "Foo"); + args.set("ty", "std::string::String"); + { + let message = DiagnosticMessage::FluentIdentifier( + "mir_build_borrow_of_moved_value".into(), + Some("value_borrowed_label".into()), + ); + + let err = dummy.translate_message(&message, &args).unwrap_err(); + assert!( + matches!( + &err, + TranslateError::Two { + primary: box TranslateError::One { + kind: TranslateErrorKind::PrimaryBundleMissing, + .. + }, + fallback: box TranslateError::One { + kind: TranslateErrorKind::AttributeMissing { attr: "value_borrowed_label" }, + .. + } + } + ), + "{err:#?}" + ); + assert_eq!( + format!("{err}"), + "failed while formatting fluent string `mir_build_borrow_of_moved_value`: \nthe attribute `value_borrowed_label` was missing\nhelp: add `.value_borrowed_label = <message>`\n" + ); + } + + { + let message = DiagnosticMessage::FluentIdentifier( + "mir_build_borrow_of_moved_value".into(), + Some("label".into()), + ); + + let err = dummy.translate_message(&message, &args).unwrap_err(); + if let TranslateError::Two { + primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. }, + fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. }, + } = &err + && let [FluentError::ResolverError(ResolverError::Reference( + ReferenceKind::Message { id, .. } + | ReferenceKind::Variable { id, .. }, + ))] = &**errs + && id == "name" + {} else { + panic!("{err:#?}") + }; + assert_eq!( + format!("{err}"), + "failed while formatting fluent string `mir_build_borrow_of_moved_value`: \nargument `name` exists but was not referenced correctly\nhelp: try using `{$name}` instead\n" + ); + } + + { + let message = DiagnosticMessage::FluentIdentifier( + "mir_build_borrow_of_moved_value".into(), + Some("occurs_because_label".into()), + ); + + let err = dummy.translate_message(&message, &args).unwrap_err(); + if let TranslateError::Two { + primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. }, + fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. }, + } = &err + && let [FluentError::ResolverError(ResolverError::Reference( + ReferenceKind::Message { id, .. } + | ReferenceKind::Variable { id, .. }, + ))] = &**errs + && id == "oops" + {} else { + panic!("{err:#?}") + }; + assert_eq!( + format!("{err}"), + "failed while formatting fluent string `mir_build_borrow_of_moved_value`: \nthe fluent string has an argument `oops` that was not found.\nhelp: the arguments `name` and `ty` are available\n" + ); + } +} diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index afd660ff1bf..addfc9726ca 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -1,11 +1,10 @@ +use crate::error::TranslateError; use crate::snippet::Style; use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle}; use rustc_data_structures::sync::Lrc; -use rustc_error_messages::{ - fluent_bundle::resolver::errors::{ReferenceKind, ResolverError}, - FluentArgs, FluentError, -}; +use rustc_error_messages::FluentArgs; use std::borrow::Cow; +use std::error::Report; /// Convert diagnostic arguments (a rustc internal type that exists to implement /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. @@ -46,7 +45,10 @@ pub trait Translate { args: &FluentArgs<'_>, ) -> Cow<'_, str> { Cow::Owned( - messages.iter().map(|(m, _)| self.translate_message(m, args)).collect::<String>(), + messages + .iter() + .map(|(m, _)| self.translate_message(m, args).map_err(Report::new).unwrap()) + .collect::<String>(), ) } @@ -55,83 +57,56 @@ pub trait Translate { &'a self, message: &'a DiagnosticMessage, args: &'a FluentArgs<'_>, - ) -> Cow<'_, str> { + ) -> Result<Cow<'_, str>, TranslateError<'_>> { trace!(?message, ?args); let (identifier, attr) = match message { DiagnosticMessage::Str(msg) | DiagnosticMessage::Eager(msg) => { - return Cow::Borrowed(msg); + return Ok(Cow::Borrowed(msg)); } DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr), }; + let translate_with_bundle = + |bundle: &'a FluentBundle| -> Result<Cow<'_, str>, TranslateError<'_>> { + let message = bundle + .get_message(identifier) + .ok_or(TranslateError::message(identifier, args))?; + let value = match attr { + Some(attr) => message + .get_attribute(attr) + .ok_or(TranslateError::attribute(identifier, args, attr))? + .value(), + None => message.value().ok_or(TranslateError::value(identifier, args))?, + }; + debug!(?message, ?value); - let translate_with_bundle = |bundle: &'a FluentBundle| -> Option<(Cow<'_, str>, Vec<_>)> { - let message = bundle.get_message(identifier)?; - let value = match attr { - Some(attr) => message.get_attribute(attr)?.value(), - None => message.value()?, + let mut errs = vec![]; + let translated = bundle.format_pattern(value, Some(args), &mut errs); + debug!(?translated, ?errs); + if errs.is_empty() { + Ok(translated) + } else { + Err(TranslateError::fluent(identifier, args, errs)) + } }; - debug!(?message, ?value); - - let mut errs = vec![]; - let translated = bundle.format_pattern(value, Some(args), &mut errs); - debug!(?translated, ?errs); - Some((translated, errs)) - }; - self.fluent_bundle() - .and_then(|bundle| translate_with_bundle(bundle)) - // If `translate_with_bundle` returns `None` with the primary bundle, this is likely - // just that the primary bundle doesn't contain the message being translated, so - // proceed to the fallback bundle. - // - // However, when errors are produced from translation, then that means the translation - // is broken (e.g. `{$foo}` exists in a translation but `foo` isn't provided). - // - // In debug builds, assert so that compiler devs can spot the broken translation and - // fix it.. - .inspect(|(_, errs)| { - debug_assert!( - errs.is_empty(), - "identifier: {:?}, attr: {:?}, args: {:?}, errors: {:?}", - identifier, - attr, - args, - errs - ); - }) - // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so - // just hide it and try with the fallback bundle. - .filter(|(_, errs)| errs.is_empty()) - .or_else(|| translate_with_bundle(self.fallback_fluent_bundle())) - .map(|(translated, errs)| { - // Always bail out for errors with the fallback bundle. + try { + match self.fluent_bundle().map(|b| translate_with_bundle(b)) { + // The primary bundle was present and translation succeeded + Some(Ok(t)) => t, - let mut help_messages = vec![]; + // Always yeet out for errors on debug + Some(Err(primary)) if cfg!(debug_assertions) => do yeet primary, - if !errs.is_empty() { - for error in &errs { - match error { - FluentError::ResolverError(ResolverError::Reference( - ReferenceKind::Message { id, .. }, - )) if args.iter().any(|(arg_id, _)| arg_id == id) => { - help_messages.push(format!("Argument `{id}` exists but was not referenced correctly. Try using `{{${id}}}` instead")); - } - _ => {} - } - } + // If `translate_with_bundle` returns `Err` with the primary bundle, this is likely + // just that the primary bundle doesn't contain the message being translated or + // something else went wrong) so proceed to the fallback bundle. + Some(Err(primary)) => translate_with_bundle(self.fallback_fluent_bundle()) + .map_err(|fallback| primary.and(fallback))?, - panic!( - "Encountered errors while formatting message for `{identifier}`\n\ - help: {}\n\ - attr: `{attr:?}`\n\ - args: `{args:?}`\n\ - errors: `{errs:?}`", - help_messages.join("\nhelp: ") - ); - } - - translated - }) - .expect("failed to find message in primary or fallback fluent bundles") + // The primary bundle is missing, proceed to the fallback bundle + None => translate_with_bundle(self.fallback_fluent_bundle()) + .map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?, + } + } } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 00453f78287..ffde8480c02 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -31,11 +31,11 @@ use rustc_span::edition::Edition; use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{BytePos, FileName, RealFileName, Span, DUMMY_SP}; +use rustc_span::{BytePos, FileName, Span, DUMMY_SP}; use smallvec::{smallvec, SmallVec}; use std::iter; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::rc::Rc; pub(crate) use rustc_span::hygiene::MacroKind; @@ -1423,8 +1423,10 @@ fn pretty_printing_compatibility_hack(item: &Item, sess: &ParseSess) -> bool { if let [variant] = &*enum_def.variants { if variant.ident.name == sym::Input { let filename = sess.source_map().span_to_filename(item.ident.span); - if let FileName::Real(RealFileName::LocalPath(path)) = filename { - if let Some(c) = path + if let FileName::Real(real) = filename { + if let Some(c) = real + .local_path() + .unwrap_or(Path::new("")) .components() .flat_map(|c| c.as_os_str().to_str()) .find(|c| c.starts_with("rental") || c.starts_with("allsorts-rental")) diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 93b3af4ab97..9b16e79d49a 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -533,6 +533,7 @@ impl<'a> ExtCtxt<'a> { ast::ExprKind::Closure(Box::new(ast::Closure { binder: ast::ClosureBinder::NotPresent, capture_clause: ast::CaptureBy::Ref, + constness: ast::Const::No, asyncness: ast::Async::No, movability: ast::Movability::Movable, fn_decl, diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 3a38d7a9669..f469b2daef5 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -114,6 +114,12 @@ impl BestFailure { } impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, 'matcher> { + type Failure = (Token, usize, &'static str); + + fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure { + (tok, position, msg) + } + fn before_match_loc(&mut self, parser: &TtParser, matcher: &'matcher MatcherLoc) { if self.remaining_matcher.is_none() || (parser.has_no_remaining_items_for_step() && *matcher != MatcherLoc::Eof) @@ -122,7 +128,7 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, } } - fn after_arm(&mut self, result: &NamedParseResult) { + fn after_arm(&mut self, result: &NamedParseResult<Self::Failure>) { match result { Success(_) => { // Nonterminal parser recovery might turn failed matches into successful ones, @@ -132,7 +138,7 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, "should not collect detailed info for successful macro match", ); } - Failure(token, approx_position, msg) => { + Failure((token, approx_position, msg)) => { debug!(?token, ?msg, "a new failure of an arm"); if self @@ -175,6 +181,21 @@ impl<'a, 'cx> CollectTrackerAndEmitter<'a, 'cx, '_> { } } +/// Currently used by macro_rules! compilation to extract a little information from the `Failure` case. +pub struct FailureForwarder; + +impl<'matcher> Tracker<'matcher> for FailureForwarder { + type Failure = (Token, usize, &'static str); + + fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure { + (tok, position, msg) + } + + fn description() -> &'static str { + "failure-forwarder" + } +} + pub(super) fn emit_frag_parse_err( mut e: DiagnosticBuilder<'_, rustc_errors::ErrorGuaranteed>, parser: &Parser<'_>, diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index df1c1834c1d..2e199541b92 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -305,13 +305,13 @@ enum EofMatcherPositions { } /// Represents the possible results of an attempted parse. -pub(crate) enum ParseResult<T> { +pub(crate) enum ParseResult<T, F> { /// Parsed successfully. Success(T), /// Arm failed to match. If the second parameter is `token::Eof`, it indicates an unexpected /// end of macro invocation. Otherwise, it indicates that no rules expected the given token. /// The usize is the approximate position of the token in the input token stream. - Failure(Token, usize, &'static str), + Failure(F), /// Fatal error (malformed macro?). Abort compilation. Error(rustc_span::Span, String), ErrorReported(ErrorGuaranteed), @@ -320,7 +320,7 @@ pub(crate) enum ParseResult<T> { /// A `ParseResult` where the `Success` variant contains a mapping of /// `MacroRulesNormalizedIdent`s to `NamedMatch`es. This represents the mapping /// of metavars to the token trees they bind to. -pub(crate) type NamedParseResult = ParseResult<NamedMatches>; +pub(crate) type NamedParseResult<F> = ParseResult<NamedMatches, F>; /// Contains a mapping of `MacroRulesNormalizedIdent`s to `NamedMatch`es. /// This represents the mapping of metavars to the token trees they bind to. @@ -458,7 +458,7 @@ impl TtParser { token: &Token, approx_position: usize, track: &mut T, - ) -> Option<NamedParseResult> { + ) -> Option<NamedParseResult<T::Failure>> { // Matcher positions that would be valid if the macro invocation was over now. Only // modified if `token == Eof`. let mut eof_mps = EofMatcherPositions::None; @@ -595,14 +595,14 @@ impl TtParser { EofMatcherPositions::Multiple => { Error(token.span, "ambiguity: multiple successful parses".to_string()) } - EofMatcherPositions::None => Failure( + EofMatcherPositions::None => Failure(T::build_failure( Token::new( token::Eof, if token.span.is_dummy() { token.span } else { token.span.shrink_to_hi() }, ), approx_position, "missing tokens in macro arguments", - ), + )), }) } else { None @@ -615,7 +615,7 @@ impl TtParser { parser: &mut Cow<'_, Parser<'_>>, matcher: &'matcher [MatcherLoc], track: &mut T, - ) -> NamedParseResult { + ) -> NamedParseResult<T::Failure> { // A queue of possible matcher positions. We initialize it with the matcher position in // which the "dot" is before the first token of the first token tree in `matcher`. // `parse_tt_inner` then processes all of these possible matcher positions and produces @@ -648,11 +648,11 @@ impl TtParser { (0, 0) => { // There are no possible next positions AND we aren't waiting for the black-box // parser: syntax error. - return Failure( + return Failure(T::build_failure( parser.token.clone(), parser.approx_token_stream_pos(), "no rules expected this token in macro call", - ); + )); } (_, 0) => { @@ -711,11 +711,11 @@ impl TtParser { } } - fn ambiguity_error( + fn ambiguity_error<F>( &self, matcher: &[MatcherLoc], token_span: rustc_span::Span, - ) -> NamedParseResult { + ) -> NamedParseResult<F> { let nts = self .bb_mps .iter() @@ -741,11 +741,11 @@ impl TtParser { ) } - fn nameize<I: Iterator<Item = NamedMatch>>( + fn nameize<I: Iterator<Item = NamedMatch>, F>( &self, matcher: &[MatcherLoc], mut res: I, - ) -> NamedParseResult { + ) -> NamedParseResult<F> { // Make that each metavar has _exactly one_ binding. If so, insert the binding into the // `NamedParseResult`. Otherwise, it's an error. let mut ret_val = FxHashMap::default(); diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index fbb806fe81b..c0489f68633 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -141,31 +141,40 @@ fn trace_macros_note(cx_expansions: &mut FxIndexMap<Span, Vec<String>>, sp: Span } pub(super) trait Tracker<'matcher> { + /// The contents of `ParseResult::Failure`. + type Failure; + + /// Arm failed to match. If the token is `token::Eof`, it indicates an unexpected + /// end of macro invocation. Otherwise, it indicates that no rules expected the given token. + /// The usize is the approximate position of the token in the input token stream. + fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure; + /// This is called before trying to match next MatcherLoc on the current token. - fn before_match_loc(&mut self, parser: &TtParser, matcher: &'matcher MatcherLoc); + fn before_match_loc(&mut self, _parser: &TtParser, _matcher: &'matcher MatcherLoc) {} /// This is called after an arm has been parsed, either successfully or unsuccessfully. When this is called, /// `before_match_loc` was called at least once (with a `MatcherLoc::Eof`). - fn after_arm(&mut self, result: &NamedParseResult); + fn after_arm(&mut self, _result: &NamedParseResult<Self::Failure>) {} /// For tracing. fn description() -> &'static str; - fn recovery() -> Recovery; + fn recovery() -> Recovery { + Recovery::Forbidden + } } /// A noop tracker that is used in the hot path of the expansion, has zero overhead thanks to monomorphization. pub(super) struct NoopTracker; impl<'matcher> Tracker<'matcher> for NoopTracker { - fn before_match_loc(&mut self, _: &TtParser, _: &'matcher MatcherLoc) {} - fn after_arm(&mut self, _: &NamedParseResult) {} + type Failure = (); + + fn build_failure(_tok: Token, _position: usize, _msg: &'static str) -> Self::Failure {} + fn description() -> &'static str { "none" } - fn recovery() -> Recovery { - Recovery::Forbidden - } } /// Expands the rules based macro defined by `lhses` and `rhses` for a given @@ -326,8 +335,8 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>( return Ok((i, named_matches)); } - Failure(_, reached_position, _) => { - trace!(%reached_position, "Failed to match arm, trying the next one"); + Failure(_) => { + trace!("Failed to match arm, trying the next one"); // Try the next arm. } Error(_, _) => { @@ -381,11 +390,13 @@ pub fn compile_declarative_macro( let rhs_nm = Ident::new(sym::rhs, def.span); let tt_spec = Some(NonterminalKind::TT); - // Parse the macro_rules! invocation - let (macro_rules, body) = match &def.kind { - ast::ItemKind::MacroDef(def) => (def.macro_rules, def.body.tokens.clone()), + let macro_def = match &def.kind { + ast::ItemKind::MacroDef(def) => def, _ => unreachable!(), }; + let macro_rules = macro_def.macro_rules; + + // Parse the macro_rules! invocation // The pattern that macro_rules matches. // The grammar for macro_rules! is: @@ -426,13 +437,32 @@ pub fn compile_declarative_macro( // Convert it into `MatcherLoc` form. let argument_gram = mbe::macro_parser::compute_locs(&argument_gram); - let parser = Parser::new(&sess.parse_sess, body, true, rustc_parse::MACRO_ARGUMENTS); + let create_parser = || { + let body = macro_def.body.tokens.clone(); + Parser::new(&sess.parse_sess, body, true, rustc_parse::MACRO_ARGUMENTS) + }; + + let parser = create_parser(); let mut tt_parser = TtParser::new(Ident::with_dummy_span(if macro_rules { kw::MacroRules } else { kw::Macro })); let argument_map = match tt_parser.parse_tt(&mut Cow::Owned(parser), &argument_gram, &mut NoopTracker) { Success(m) => m, - Failure(token, _, msg) => { + Failure(()) => { + // The fast `NoopTracker` doesn't have any info on failure, so we need to retry it with another one + // that gives us the information we need. + // For this we need to reclone the macro body as the previous parser consumed it. + let retry_parser = create_parser(); + + let parse_result = tt_parser.parse_tt( + &mut Cow::Owned(retry_parser), + &argument_gram, + &mut diagnostics::FailureForwarder, + ); + let Failure((token, _, msg)) = parse_result else { + unreachable!("matcher returned something other than Failure after retry"); + }; + let s = parse_failure_msg(&token); let sp = token.span.substitute_dummy(def.span); let mut err = sess.parse_sess.span_diagnostic.struct_span_err(sp, &s); diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index e2f30fb89b9..aab4b604fad 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -48,6 +48,8 @@ declare_features! ( /// Allows `#[target_feature(...)]` on aarch64 platforms (accepted, aarch64_target_feature, "1.61.0", Some(44839), None), + /// Allows using the `efiapi` ABI. + (accepted, abi_efiapi, "CURRENT_RUSTC_VERSION", Some(65815), None), /// Allows the sysV64 ABI to be specified on all platforms /// instead of just the platforms on which it is the C ABI. (accepted, abi_sysv64, "1.24.0", Some(36167), None), @@ -161,6 +163,8 @@ declare_features! ( (accepted, extern_crate_self, "1.34.0", Some(56409), None), /// Allows access to crate names passed via `--extern` through prelude. (accepted, extern_prelude, "1.30.0", Some(44660), None), + /// Allows using F16C intrinsics from `core::arch::{x86, x86_64}`. + (accepted, f16c_target_feature, "CURRENT_RUSTC_VERSION", Some(44839), None), /// Allows field shorthands (`x` meaning `x: x`) in struct literal expressions. (accepted, field_init_shorthand, "1.17.0", Some(37340), None), /// Allows `#[must_use]` on functions, and introduces must-use operators (RFC 1940). diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index beade4d44da..196c31302a0 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -254,7 +254,6 @@ declare_features! ( (active, bpf_target_feature, "1.54.0", Some(44839), None), (active, cmpxchg16b_target_feature, "1.32.0", Some(44839), None), (active, ermsb_target_feature, "1.49.0", Some(44839), None), - (active, f16c_target_feature, "1.36.0", Some(44839), None), (active, hexagon_target_feature, "1.27.0", Some(44839), None), (active, mips_target_feature, "1.27.0", Some(44839), None), (active, movbe_target_feature, "1.34.0", Some(44839), None), @@ -282,8 +281,6 @@ declare_features! ( (active, abi_avr_interrupt, "1.45.0", Some(69664), None), /// Allows `extern "C-cmse-nonsecure-call" fn()`. (active, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391), None), - /// Allows using the `efiapi` ABI. - (active, abi_efiapi, "1.40.0", Some(65815), None), /// Allows `extern "msp430-interrupt" fn()`. (active, abi_msp430_interrupt, "1.16.0", Some(38487), None), /// Allows `extern "ptx-*" fn()`. @@ -342,7 +339,9 @@ declare_features! ( (active, collapse_debuginfo, "1.65.0", Some(100758), None), /// Allows `async {}` expressions in const contexts. (active, const_async_blocks, "1.53.0", Some(85368), None), - // Allows limiting the evaluation steps of const expressions + /// Allows `const || {}` closures in const contexts. + (incomplete, const_closures, "CURRENT_RUSTC_VERSION", Some(106003), None), + /// Allows limiting the evaluation steps of const expressions (active, const_eval_limit, "1.43.0", Some(67217), None), /// Allows the definition of `const extern fn` and `const unsafe extern fn`. (active, const_extern_fn, "1.40.0", Some(64926), None), @@ -374,6 +373,8 @@ declare_features! ( (active, deprecated_safe, "1.61.0", Some(94978), None), /// Allows having using `suggestion` in the `#[deprecated]` attribute. (active, deprecated_suggestion, "1.61.0", Some(94785), None), + /// Controls errors in trait implementations. + (active, do_not_recommend, "1.67.0", Some(51992), None), /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`. (active, doc_auto_cfg, "1.58.0", Some(43781), None), /// Allows `#[doc(cfg(...))]`. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 6d8e78a0f18..af56a0b2459 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -70,7 +70,7 @@ impl std::fmt::Debug for AttributeGate { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match *self { Self::Gated(ref stab, name, expl, _) => { - write!(fmt, "Gated({:?}, {}, {})", stab, name, expl) + write!(fmt, "Gated({stab:?}, {name}, {expl})") } Self::Ungated => write!(fmt, "Ungated"), } @@ -487,6 +487,9 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ experimental!(collapse_debuginfo) ), + // RFC 2397 + gated!(do_not_recommend, Normal, template!(Word), WarnFollowing, experimental!(do_not_recommend)), + // ========================================================================== // Internal attributes: Stability, deprecation, and unsafe: // ========================================================================== diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs index bdaa0ee88eb..8e2a13a6c0a 100644 --- a/compiler/rustc_feature/src/lib.rs +++ b/compiler/rustc_feature/src/lib.rs @@ -120,7 +120,7 @@ fn find_lang_feature_issue(feature: Symbol) -> Option<NonZeroU32> { .find(|t| t.name == feature); match found { Some(found) => found.issue, - None => panic!("feature `{}` is not declared anywhere", feature), + None => panic!("feature `{feature}` is not declared anywhere"), } } } diff --git a/compiler/rustc_graphviz/src/lib.rs b/compiler/rustc_graphviz/src/lib.rs index 434f0a53b78..b70a55e8953 100644 --- a/compiler/rustc_graphviz/src/lib.rs +++ b/compiler/rustc_graphviz/src/lib.rs @@ -516,7 +516,7 @@ impl<'a> LabelText<'a> { match *self { LabelStr(ref s) => format!("\"{}\"", s.escape_default()), EscStr(ref s) => format!("\"{}\"", LabelText::escape_str(s)), - HtmlStr(ref s) => format!("<{}>", s), + HtmlStr(ref s) => format!("<{s}>"), } } @@ -622,7 +622,7 @@ where if let Some(fontname) = options.iter().find_map(|option| { if let RenderOption::Fontname(fontname) = option { Some(fontname) } else { None } }) { - font = format!(r#"fontname="{}""#, fontname); + font = format!(r#"fontname="{fontname}""#); graph_attrs.push(&font[..]); content_attrs.push(&font[..]); } @@ -635,8 +635,8 @@ where if !(graph_attrs.is_empty() && content_attrs.is_empty()) { writeln!(w, r#" graph[{}];"#, graph_attrs.join(" "))?; let content_attrs_str = content_attrs.join(" "); - writeln!(w, r#" node[{}];"#, content_attrs_str)?; - writeln!(w, r#" edge[{}];"#, content_attrs_str)?; + writeln!(w, r#" node[{content_attrs_str}];"#)?; + writeln!(w, r#" edge[{content_attrs_str}];"#)?; } let mut text = Vec::new(); @@ -649,7 +649,7 @@ where write!(text, "{}", id.as_slice()).unwrap(); if !options.contains(&RenderOption::NoNodeLabels) { - write!(text, "[label={}]", escaped).unwrap(); + write!(text, "[label={escaped}]").unwrap(); } let style = g.node_style(n); @@ -678,7 +678,7 @@ where write!(text, "{} -> {}", source_id.as_slice(), target_id.as_slice()).unwrap(); if !options.contains(&RenderOption::NoEdgeLabels) { - write!(text, "[label={}]", escaped_label).unwrap(); + write!(text, "[label={escaped_label}]").unwrap(); } let style = g.edge_style(e); diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 149cf4ece37..92103979786 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -597,8 +597,7 @@ impl<Id> Res<Id> { where Id: Debug, { - self.opt_def_id() - .unwrap_or_else(|| panic!("attempted .def_id() on invalid res: {:?}", self)) + self.opt_def_id().unwrap_or_else(|| panic!("attempted .def_id() on invalid res: {self:?}")) } /// Return `Some(..)` with the `DefId` of this `Res` if it has a ID, else `None`. diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs index dd37efb6983..21cf214e47c 100644 --- a/compiler/rustc_hir/src/definitions.rs +++ b/compiler/rustc_hir/src/definitions.rs @@ -53,9 +53,8 @@ impl DefPathTable { // // See the documentation for DefPathHash for more information. panic!( - "found DefPathHash collision between {:?} and {:?}. \ - Compilation cannot continue.", - def_path1, def_path2 + "found DefPathHash collision between {def_path1:?} and {def_path2:?}. \ + Compilation cannot continue." ); } @@ -224,7 +223,7 @@ impl DefPath { let mut s = String::with_capacity(self.data.len() * 16); for component in &self.data { - write!(s, "::{}", component).unwrap(); + write!(s, "::{component}").unwrap(); } s @@ -240,7 +239,7 @@ impl DefPath { for component in &self.data { s.extend(opt_delimiter); opt_delimiter = Some('-'); - write!(s, "{}", component).unwrap(); + write!(s, "{component}").unwrap(); } s @@ -433,7 +432,7 @@ impl fmt::Display for DefPathData { match self.name() { DefPathDataName::Named(name) => f.write_str(name.as_str()), // FIXME(#70334): this will generate legacy {{closure}}, {{impl}}, etc - DefPathDataName::Anon { namespace } => write!(f, "{{{{{}}}}}", namespace), + DefPathDataName::Anon { namespace } => write!(f, "{{{{{namespace}}}}}"), } } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 034f06bb889..60f5b79de10 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -938,6 +938,7 @@ pub struct Crate<'hir> { pub struct Closure<'hir> { pub def_id: LocalDefId, pub binder: ClosureBinder, + pub constness: Constness, pub capture_clause: CaptureBy, pub bound_generic_params: &'hir [GenericParam<'hir>], pub fn_decl: &'hir FnDecl<'hir>, @@ -3460,7 +3461,7 @@ impl<'hir> Node<'hir> { /// ```ignore (illustrative) /// ctor /// .ctor_hir_id() - /// .and_then(|ctor_id| tcx.hir().find(tcx.hir().get_parent_node(ctor_id))) + /// .and_then(|ctor_id| tcx.hir().find_parent(ctor_id)) /// .and_then(|parent| parent.ident()) /// ``` pub fn ident(&self) -> Option<Ident> { diff --git a/compiler/rustc_hir/src/hir_id.rs b/compiler/rustc_hir/src/hir_id.rs index 5d05adfb556..404abe2b068 100644 --- a/compiler/rustc_hir/src/hir_id.rs +++ b/compiler/rustc_hir/src/hir_id.rs @@ -119,7 +119,7 @@ impl HirId { impl fmt::Display for HirId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self) + write!(f, "{self:?}") } } @@ -148,7 +148,7 @@ rustc_index::newtype_index! { /// that is, within a `hir::Item`, `hir::TraitItem`, or `hir::ImplItem`. There is no /// guarantee that the numerical value of a given `ItemLocalId` corresponds to /// the node's position within the owning item in any way, but there is a - /// guarantee that the `LocalItemId`s within an owner occupy a dense range of + /// guarantee that the `ItemLocalId`s within an owner occupy a dense range of /// integers starting at zero, so a mapping that maps all or most nodes within /// an "item-like" to something else can be implemented by a `Vec` instead of a /// tree or hash map. @@ -161,7 +161,7 @@ impl ItemLocalId { pub const INVALID: ItemLocalId = ItemLocalId::MAX; } -// Safety: Ord is implement as just comparing the LocalItemId's numerical +// Safety: Ord is implement as just comparing the ItemLocalId's numerical // values and these are not changed by (de-)serialization. unsafe impl StableOrd for ItemLocalId {} diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 6c475b659eb..02641b7cf8f 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -742,6 +742,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) fn_decl_span: _, fn_arg_span: _, movability: _, + constness: _, }) => { walk_list!(visitor, visit_generic_param, bound_generic_params); visitor.visit_fn(FnKind::Closure, fn_decl, body, expression.span, expression.hir_id) diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index e6465d641f1..5368dc0735b 100644 --- a/compiler/rustc_hir_analysis/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs @@ -267,7 +267,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // segments, even though `trait_ref.path.segments` is of length `1`. Work // around that bug here, even though it should be fixed elsewhere. // This would otherwise cause an invalid suggestion. For an example, look at - // `src/test/ui/issues/issue-28344.rs` where instead of the following: + // `tests/ui/issues/issue-28344.rs` where instead of the following: // // error[E0191]: the value of the associated type `Output` // (from trait `std::ops::BitXor`) must be specified diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index f64d65cc6ad..ce3682a8f2d 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -1,6 +1,6 @@ use super::IsMethodCall; use crate::astconv::{ - AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, + CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, }; use crate::errors::AssocTypeBindingNotAllowed; @@ -18,642 +18,624 @@ use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; use rustc_span::{symbol::kw, Span}; use smallvec::SmallVec; -impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { - /// Report an error that a generic argument did not match the generic parameter that was - /// expected. - fn generic_arg_mismatch_err( - tcx: TyCtxt<'_>, - arg: &GenericArg<'_>, - param: &GenericParamDef, - possible_ordering_error: bool, - help: Option<&str>, - ) { - let sess = tcx.sess; - let mut err = struct_span_err!( - sess, - arg.span(), - E0747, - "{} provided when a {} was expected", - arg.descr(), - param.kind.descr(), - ); - - if let GenericParamDefKind::Const { .. } = param.kind { - if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) { - err.help("const arguments cannot yet be inferred with `_`"); - if sess.is_nightly_build() { - err.help( - "add `#![feature(generic_arg_infer)]` to the crate attributes to enable", - ); - } +/// Report an error that a generic argument did not match the generic parameter that was +/// expected. +fn generic_arg_mismatch_err( + tcx: TyCtxt<'_>, + arg: &GenericArg<'_>, + param: &GenericParamDef, + possible_ordering_error: bool, + help: Option<&str>, +) { + let sess = tcx.sess; + let mut err = struct_span_err!( + sess, + arg.span(), + E0747, + "{} provided when a {} was expected", + arg.descr(), + param.kind.descr(), + ); + + if let GenericParamDefKind::Const { .. } = param.kind { + if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) { + err.help("const arguments cannot yet be inferred with `_`"); + if sess.is_nightly_build() { + err.help("add `#![feature(generic_arg_infer)]` to the crate attributes to enable"); } } + } - let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diagnostic| { - let suggestions = vec![ - (arg.span().shrink_to_lo(), String::from("{ ")), - (arg.span().shrink_to_hi(), String::from(" }")), - ]; - err.multipart_suggestion( - "if this generic argument was intended as a const parameter, \ + let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diagnostic| { + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + err.multipart_suggestion( + "if this generic argument was intended as a const parameter, \ surround it with braces", - suggestions, - Applicability::MaybeIncorrect, - ); - }; - - // Specific suggestion set for diagnostics - match (arg, ¶m.kind) { - ( - GenericArg::Type(hir::Ty { - kind: hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)), - .. - }), - GenericParamDefKind::Const { .. }, - ) => match path.res { - Res::Err => { - add_braces_suggestion(arg, &mut err); - err.set_primary_message( - "unresolved item provided when a constant was expected", - ) + suggestions, + Applicability::MaybeIncorrect, + ); + }; + + // Specific suggestion set for diagnostics + match (arg, ¶m.kind) { + ( + GenericArg::Type(hir::Ty { + kind: hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)), + .. + }), + GenericParamDefKind::Const { .. }, + ) => match path.res { + Res::Err => { + add_braces_suggestion(arg, &mut err); + err.set_primary_message("unresolved item provided when a constant was expected") .emit(); - return; - } - Res::Def(DefKind::TyParam, src_def_id) => { - if let Some(param_local_id) = param.def_id.as_local() { - let param_name = tcx.hir().ty_param_name(param_local_id); - let param_type = tcx.type_of(param.def_id); - if param_type.is_suggestable(tcx, false) { - err.span_suggestion( - tcx.def_span(src_def_id), - "consider changing this type parameter to be a `const` generic", - format!("const {}: {}", param_name, param_type), - Applicability::MaybeIncorrect, - ); - }; - } - } - _ => add_braces_suggestion(arg, &mut err), - }, - ( - GenericArg::Type(hir::Ty { kind: hir::TyKind::Path(_), .. }), - GenericParamDefKind::Const { .. }, - ) => add_braces_suggestion(arg, &mut err), - ( - GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), - GenericParamDefKind::Const { .. }, - ) if tcx.type_of(param.def_id) == tcx.types.usize => { - let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id())); - if let Ok(snippet) = snippet { - err.span_suggestion( - arg.span(), - "array type provided where a `usize` was expected, try", - format!("{{ {} }}", snippet), - Applicability::MaybeIncorrect, - ); + return; + } + Res::Def(DefKind::TyParam, src_def_id) => { + if let Some(param_local_id) = param.def_id.as_local() { + let param_name = tcx.hir().ty_param_name(param_local_id); + let param_type = tcx.type_of(param.def_id); + if param_type.is_suggestable(tcx, false) { + err.span_suggestion( + tcx.def_span(src_def_id), + "consider changing this type parameter to be a `const` generic", + format!("const {}: {}", param_name, param_type), + Applicability::MaybeIncorrect, + ); + }; } } - (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => { - let body = tcx.hir().body(cnst.value.body); - if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = - body.value.kind - { - if let Res::Def(DefKind::Fn { .. }, id) = path.res { - err.help(&format!( - "`{}` is a function item, not a type", - tcx.item_name(id) - )); - err.help("function item types cannot be named directly"); - } + _ => add_braces_suggestion(arg, &mut err), + }, + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Path(_), .. }), + GenericParamDefKind::Const { .. }, + ) => add_braces_suggestion(arg, &mut err), + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), + GenericParamDefKind::Const { .. }, + ) if tcx.type_of(param.def_id) == tcx.types.usize => { + let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id())); + if let Ok(snippet) = snippet { + err.span_suggestion( + arg.span(), + "array type provided where a `usize` was expected, try", + format!("{{ {} }}", snippet), + Applicability::MaybeIncorrect, + ); + } + } + (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => { + let body = tcx.hir().body(cnst.value.body); + if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body.value.kind + { + if let Res::Def(DefKind::Fn { .. }, id) = path.res { + err.help(&format!("`{}` is a function item, not a type", tcx.item_name(id))); + err.help("function item types cannot be named directly"); } } - _ => {} } + _ => {} + } - let kind_ord = param.kind.to_ord(); - let arg_ord = arg.to_ord(); + let kind_ord = param.kind.to_ord(); + let arg_ord = arg.to_ord(); - // This note is only true when generic parameters are strictly ordered by their kind. - if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { - let (first, last) = if kind_ord < arg_ord { - (param.kind.descr(), arg.descr()) - } else { - (arg.descr(), param.kind.descr()) - }; - err.note(&format!("{} arguments must be provided before {} arguments", first, last)); - if let Some(help) = help { - err.help(help); - } + // This note is only true when generic parameters are strictly ordered by their kind. + if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { + let (first, last) = if kind_ord < arg_ord { + (param.kind.descr(), arg.descr()) + } else { + (arg.descr(), param.kind.descr()) + }; + err.note(&format!("{} arguments must be provided before {} arguments", first, last)); + if let Some(help) = help { + err.help(help); } + } + + err.emit(); +} - err.emit(); +/// Creates the relevant generic argument substitutions +/// corresponding to a set of generic parameters. This is a +/// rather complex function. Let us try to explain the role +/// of each of its parameters: +/// +/// To start, we are given the `def_id` of the thing we are +/// creating the substitutions for, and a partial set of +/// substitutions `parent_substs`. In general, the substitutions +/// for an item begin with substitutions for all the "parents" of +/// that item -- e.g., for a method it might include the +/// parameters from the impl. +/// +/// Therefore, the method begins by walking down these parents, +/// starting with the outermost parent and proceed inwards until +/// it reaches `def_id`. For each parent `P`, it will check `parent_substs` +/// first to see if the parent's substitutions are listed in there. If so, +/// we can append those and move on. Otherwise, it invokes the +/// three callback functions: +/// +/// - `args_for_def_id`: given the `DefId` `P`, supplies back the +/// generic arguments that were given to that parent from within +/// the path; so e.g., if you have `<T as Foo>::Bar`, the `DefId` +/// might refer to the trait `Foo`, and the arguments might be +/// `[T]`. The boolean value indicates whether to infer values +/// for arguments whose values were not explicitly provided. +/// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`, +/// instantiate a `GenericArg`. +/// - `inferred_kind`: if no parameter was provided, and inference is enabled, then +/// creates a suitable inference variable. +pub fn create_substs_for_generic_args<'tcx, 'a>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + parent_substs: &[subst::GenericArg<'tcx>], + has_self: bool, + self_ty: Option<Ty<'tcx>>, + arg_count: &GenericArgCountResult, + ctx: &mut impl CreateSubstsForGenericArgsCtxt<'a, 'tcx>, +) -> SubstsRef<'tcx> { + // Collect the segments of the path; we need to substitute arguments + // for parameters throughout the entire path (wherever there are + // generic parameters). + let mut parent_defs = tcx.generics_of(def_id); + let count = parent_defs.count(); + let mut stack = vec![(def_id, parent_defs)]; + while let Some(def_id) = parent_defs.parent { + parent_defs = tcx.generics_of(def_id); + stack.push((def_id, parent_defs)); } - /// Creates the relevant generic argument substitutions - /// corresponding to a set of generic parameters. This is a - /// rather complex function. Let us try to explain the role - /// of each of its parameters: - /// - /// To start, we are given the `def_id` of the thing we are - /// creating the substitutions for, and a partial set of - /// substitutions `parent_substs`. In general, the substitutions - /// for an item begin with substitutions for all the "parents" of - /// that item -- e.g., for a method it might include the - /// parameters from the impl. - /// - /// Therefore, the method begins by walking down these parents, - /// starting with the outermost parent and proceed inwards until - /// it reaches `def_id`. For each parent `P`, it will check `parent_substs` - /// first to see if the parent's substitutions are listed in there. If so, - /// we can append those and move on. Otherwise, it invokes the - /// three callback functions: - /// - /// - `args_for_def_id`: given the `DefId` `P`, supplies back the - /// generic arguments that were given to that parent from within - /// the path; so e.g., if you have `<T as Foo>::Bar`, the `DefId` - /// might refer to the trait `Foo`, and the arguments might be - /// `[T]`. The boolean value indicates whether to infer values - /// for arguments whose values were not explicitly provided. - /// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`, - /// instantiate a `GenericArg`. - /// - `inferred_kind`: if no parameter was provided, and inference is enabled, then - /// creates a suitable inference variable. - pub fn create_substs_for_generic_args<'a>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - parent_substs: &[subst::GenericArg<'tcx>], - has_self: bool, - self_ty: Option<Ty<'tcx>>, - arg_count: &GenericArgCountResult, - ctx: &mut impl CreateSubstsForGenericArgsCtxt<'a, 'tcx>, - ) -> SubstsRef<'tcx> { - // Collect the segments of the path; we need to substitute arguments - // for parameters throughout the entire path (wherever there are - // generic parameters). - let mut parent_defs = tcx.generics_of(def_id); - let count = parent_defs.count(); - let mut stack = vec![(def_id, parent_defs)]; - while let Some(def_id) = parent_defs.parent { - parent_defs = tcx.generics_of(def_id); - stack.push((def_id, parent_defs)); + // We manually build up the substitution, rather than using convenience + // methods in `subst.rs`, so that we can iterate over the arguments and + // parameters in lock-step linearly, instead of trying to match each pair. + let mut substs: SmallVec<[subst::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count); + // Iterate over each segment of the path. + while let Some((def_id, defs)) = stack.pop() { + let mut params = defs.params.iter().peekable(); + + // If we have already computed substitutions for parents, we can use those directly. + while let Some(¶m) = params.peek() { + if let Some(&kind) = parent_substs.get(param.index as usize) { + substs.push(kind); + params.next(); + } else { + break; + } } - // We manually build up the substitution, rather than using convenience - // methods in `subst.rs`, so that we can iterate over the arguments and - // parameters in lock-step linearly, instead of trying to match each pair. - let mut substs: SmallVec<[subst::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count); - // Iterate over each segment of the path. - while let Some((def_id, defs)) = stack.pop() { - let mut params = defs.params.iter().peekable(); - - // If we have already computed substitutions for parents, we can use those directly. - while let Some(¶m) = params.peek() { - if let Some(&kind) = parent_substs.get(param.index as usize) { - substs.push(kind); - params.next(); - } else { - break; + // `Self` is handled first, unless it's been handled in `parent_substs`. + if has_self { + if let Some(¶m) = params.peek() { + if param.index == 0 { + if let GenericParamDefKind::Type { .. } = param.kind { + substs.push( + self_ty + .map(|ty| ty.into()) + .unwrap_or_else(|| ctx.inferred_kind(None, param, true)), + ); + params.next(); + } } } + } - // `Self` is handled first, unless it's been handled in `parent_substs`. - if has_self { - if let Some(¶m) = params.peek() { - if param.index == 0 { - if let GenericParamDefKind::Type { .. } = param.kind { - substs.push( - self_ty - .map(|ty| ty.into()) - .unwrap_or_else(|| ctx.inferred_kind(None, param, true)), - ); + // Check whether this segment takes generic arguments and the user has provided any. + let (generic_args, infer_args) = ctx.args_for_def_id(def_id); + + let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter()); + let mut args = args_iter.clone().peekable(); + + // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. + // If we later encounter a lifetime, we know that the arguments were provided in the + // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be + // inferred, so we can use it for diagnostics later. + let mut force_infer_lt = None; + + loop { + // We're going to iterate through the generic arguments that the user + // provided, matching them with the generic parameters we expect. + // Mismatches can occur as a result of elided lifetimes, or for malformed + // input. We try to handle both sensibly. + match (args.peek(), params.peek()) { + (Some(&arg), Some(¶m)) => { + match (arg, ¶m.kind, arg_count.explicit_late_bound) { + (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) + | ( + GenericArg::Type(_) | GenericArg::Infer(_), + GenericParamDefKind::Type { .. }, + _, + ) + | ( + GenericArg::Const(_) | GenericArg::Infer(_), + GenericParamDefKind::Const { .. }, + _, + ) => { + substs.push(ctx.provided_kind(param, arg)); + args.next(); params.next(); } - } - } - } - - // Check whether this segment takes generic arguments and the user has provided any. - let (generic_args, infer_args) = ctx.args_for_def_id(def_id); - - let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter()); - let mut args = args_iter.clone().peekable(); - - // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. - // If we later encounter a lifetime, we know that the arguments were provided in the - // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be - // inferred, so we can use it for diagnostics later. - let mut force_infer_lt = None; - - loop { - // We're going to iterate through the generic arguments that the user - // provided, matching them with the generic parameters we expect. - // Mismatches can occur as a result of elided lifetimes, or for malformed - // input. We try to handle both sensibly. - match (args.peek(), params.peek()) { - (Some(&arg), Some(¶m)) => { - match (arg, ¶m.kind, arg_count.explicit_late_bound) { - (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) - | ( - GenericArg::Type(_) | GenericArg::Infer(_), - GenericParamDefKind::Type { .. }, - _, - ) - | ( - GenericArg::Const(_) | GenericArg::Infer(_), - GenericParamDefKind::Const { .. }, - _, - ) => { - substs.push(ctx.provided_kind(param, arg)); - args.next(); - params.next(); - } - ( - GenericArg::Infer(_) | GenericArg::Type(_) | GenericArg::Const(_), - GenericParamDefKind::Lifetime, - _, - ) => { - // We expected a lifetime argument, but got a type or const - // argument. That means we're inferring the lifetimes. - substs.push(ctx.inferred_kind(None, param, infer_args)); - force_infer_lt = Some((arg, param)); - params.next(); - } - (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { - // We've come across a lifetime when we expected something else in - // the presence of explicit late bounds. This is most likely - // due to the presence of the explicit bound so we're just going to - // ignore it. - args.next(); - } - (_, _, _) => { - // We expected one kind of parameter, but the user provided - // another. This is an error. However, if we already know that - // the arguments don't match up with the parameters, we won't issue - // an additional error, as the user already knows what's wrong. - if arg_count.correct.is_ok() { - // We're going to iterate over the parameters to sort them out, and - // show that order to the user as a possible order for the parameters - let mut param_types_present = defs - .params - .iter() - .map(|param| (param.kind.to_ord(), param.clone())) - .collect::<Vec<(ParamKindOrd, GenericParamDef)>>(); - param_types_present.sort_by_key(|(ord, _)| *ord); - let (mut param_types_present, ordered_params): ( - Vec<ParamKindOrd>, - Vec<GenericParamDef>, - ) = param_types_present.into_iter().unzip(); - param_types_present.dedup(); - - Self::generic_arg_mismatch_err( - tcx, - arg, - param, - !args_iter.clone().is_sorted_by_key(|arg| arg.to_ord()), - Some(&format!( - "reorder the arguments: {}: `<{}>`", - param_types_present - .into_iter() - .map(|ord| format!("{}s", ord)) - .collect::<Vec<String>>() - .join(", then "), - ordered_params - .into_iter() - .filter_map(|param| { - if param.name == kw::SelfUpper { - None - } else { - Some(param.name.to_string()) - } - }) - .collect::<Vec<String>>() - .join(", ") - )), - ); - } - - // We've reported the error, but we want to make sure that this - // problem doesn't bubble down and create additional, irrelevant - // errors. In this case, we're simply going to ignore the argument - // and any following arguments. The rest of the parameters will be - // inferred. - while args.next().is_some() {} - } + ( + GenericArg::Infer(_) | GenericArg::Type(_) | GenericArg::Const(_), + GenericParamDefKind::Lifetime, + _, + ) => { + // We expected a lifetime argument, but got a type or const + // argument. That means we're inferring the lifetimes. + substs.push(ctx.inferred_kind(None, param, infer_args)); + force_infer_lt = Some((arg, param)); + params.next(); } - } - - (Some(&arg), None) => { - // We should never be able to reach this point with well-formed input. - // There are three situations in which we can encounter this issue. - // - // 1. The number of arguments is incorrect. In this case, an error - // will already have been emitted, and we can ignore it. - // 2. There are late-bound lifetime parameters present, yet the - // lifetime arguments have also been explicitly specified by the - // user. - // 3. We've inferred some lifetimes, which have been provided later (i.e. - // after a type or const). We want to throw an error in this case. - - if arg_count.correct.is_ok() - && arg_count.explicit_late_bound == ExplicitLateBound::No - { - let kind = arg.descr(); - assert_eq!(kind, "lifetime"); - let (provided_arg, param) = - force_infer_lt.expect("lifetimes ought to have been inferred"); - Self::generic_arg_mismatch_err(tcx, provided_arg, param, false, None); + (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { + // We've come across a lifetime when we expected something else in + // the presence of explicit late bounds. This is most likely + // due to the presence of the explicit bound so we're just going to + // ignore it. + args.next(); } + (_, _, _) => { + // We expected one kind of parameter, but the user provided + // another. This is an error. However, if we already know that + // the arguments don't match up with the parameters, we won't issue + // an additional error, as the user already knows what's wrong. + if arg_count.correct.is_ok() { + // We're going to iterate over the parameters to sort them out, and + // show that order to the user as a possible order for the parameters + let mut param_types_present = defs + .params + .iter() + .map(|param| (param.kind.to_ord(), param.clone())) + .collect::<Vec<(ParamKindOrd, GenericParamDef)>>(); + param_types_present.sort_by_key(|(ord, _)| *ord); + let (mut param_types_present, ordered_params): ( + Vec<ParamKindOrd>, + Vec<GenericParamDef>, + ) = param_types_present.into_iter().unzip(); + param_types_present.dedup(); + + generic_arg_mismatch_err( + tcx, + arg, + param, + !args_iter.clone().is_sorted_by_key(|arg| arg.to_ord()), + Some(&format!( + "reorder the arguments: {}: `<{}>`", + param_types_present + .into_iter() + .map(|ord| format!("{}s", ord)) + .collect::<Vec<String>>() + .join(", then "), + ordered_params + .into_iter() + .filter_map(|param| { + if param.name == kw::SelfUpper { + None + } else { + Some(param.name.to_string()) + } + }) + .collect::<Vec<String>>() + .join(", ") + )), + ); + } - break; + // We've reported the error, but we want to make sure that this + // problem doesn't bubble down and create additional, irrelevant + // errors. In this case, we're simply going to ignore the argument + // and any following arguments. The rest of the parameters will be + // inferred. + while args.next().is_some() {} + } } + } - (None, Some(¶m)) => { - // If there are fewer arguments than parameters, it means - // we're inferring the remaining arguments. - substs.push(ctx.inferred_kind(Some(&substs), param, infer_args)); - params.next(); + (Some(&arg), None) => { + // We should never be able to reach this point with well-formed input. + // There are three situations in which we can encounter this issue. + // + // 1. The number of arguments is incorrect. In this case, an error + // will already have been emitted, and we can ignore it. + // 2. There are late-bound lifetime parameters present, yet the + // lifetime arguments have also been explicitly specified by the + // user. + // 3. We've inferred some lifetimes, which have been provided later (i.e. + // after a type or const). We want to throw an error in this case. + + if arg_count.correct.is_ok() + && arg_count.explicit_late_bound == ExplicitLateBound::No + { + let kind = arg.descr(); + assert_eq!(kind, "lifetime"); + let (provided_arg, param) = + force_infer_lt.expect("lifetimes ought to have been inferred"); + generic_arg_mismatch_err(tcx, provided_arg, param, false, None); } - (None, None) => break, + break; + } + + (None, Some(¶m)) => { + // If there are fewer arguments than parameters, it means + // we're inferring the remaining arguments. + substs.push(ctx.inferred_kind(Some(&substs), param, infer_args)); + params.next(); } + + (None, None) => break, } } - - tcx.intern_substs(&substs) } - /// Checks that the correct number of generic arguments have been provided. - /// Used specifically for function calls. - pub fn check_generic_arg_count_for_call( - tcx: TyCtxt<'_>, - span: Span, - def_id: DefId, - generics: &ty::Generics, - seg: &hir::PathSegment<'_>, - is_method_call: IsMethodCall, - ) -> GenericArgCountResult { - let empty_args = hir::GenericArgs::none(); - let gen_args = seg.args.unwrap_or(&empty_args); - let gen_pos = if is_method_call == IsMethodCall::Yes { - GenericArgPosition::MethodCall + tcx.intern_substs(&substs) +} + +/// Checks that the correct number of generic arguments have been provided. +/// Used specifically for function calls. +pub fn check_generic_arg_count_for_call( + tcx: TyCtxt<'_>, + span: Span, + def_id: DefId, + generics: &ty::Generics, + seg: &hir::PathSegment<'_>, + is_method_call: IsMethodCall, +) -> GenericArgCountResult { + let empty_args = hir::GenericArgs::none(); + let gen_args = seg.args.unwrap_or(&empty_args); + let gen_pos = if is_method_call == IsMethodCall::Yes { + GenericArgPosition::MethodCall + } else { + GenericArgPosition::Value + }; + let has_self = generics.parent.is_none() && generics.has_self; + + check_generic_arg_count( + tcx, + span, + def_id, + seg, + generics, + gen_args, + gen_pos, + has_self, + seg.infer_args, + ) +} + +/// Checks that the correct number of generic arguments have been provided. +/// This is used both for datatypes and function calls. +#[instrument(skip(tcx, gen_pos), level = "debug")] +pub(crate) fn check_generic_arg_count( + tcx: TyCtxt<'_>, + span: Span, + def_id: DefId, + seg: &hir::PathSegment<'_>, + gen_params: &ty::Generics, + gen_args: &hir::GenericArgs<'_>, + gen_pos: GenericArgPosition, + has_self: bool, + infer_args: bool, +) -> GenericArgCountResult { + let default_counts = gen_params.own_defaults(); + let param_counts = gen_params.own_counts(); + + // Subtracting from param count to ensure type params synthesized from `impl Trait` + // cannot be explicitly specified. + let synth_type_param_count = gen_params + .params + .iter() + .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. })) + .count(); + let named_type_param_count = param_counts.types - has_self as usize - synth_type_param_count; + let infer_lifetimes = + (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params(); + + if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() { + prohibit_assoc_ty_binding(tcx, b.span); + } + + let explicit_late_bound = + prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos); + + let mut invalid_args = vec![]; + + let mut check_lifetime_args = |min_expected_args: usize, + max_expected_args: usize, + provided_args: usize, + late_bounds_ignore: bool| { + if (min_expected_args..=max_expected_args).contains(&provided_args) { + return Ok(()); + } + + if late_bounds_ignore { + return Ok(()); + } + + if provided_args > max_expected_args { + invalid_args.extend( + gen_args.args[max_expected_args..provided_args].iter().map(|arg| arg.span()), + ); + }; + + let gen_args_info = if provided_args > min_expected_args { + invalid_args.extend( + gen_args.args[min_expected_args..provided_args].iter().map(|arg| arg.span()), + ); + let num_redundant_args = provided_args - min_expected_args; + GenericArgsInfo::ExcessLifetimes { num_redundant_args } } else { - GenericArgPosition::Value + let num_missing_args = min_expected_args - provided_args; + GenericArgsInfo::MissingLifetimes { num_missing_args } }; - let has_self = generics.parent.is_none() && generics.has_self; - Self::check_generic_arg_count( + let reported = WrongNumberOfGenericArgs::new( tcx, - span, - def_id, + gen_args_info, seg, - generics, + gen_params, + has_self as usize, gen_args, - gen_pos, - has_self, - seg.infer_args, + def_id, ) - } - - /// Checks that the correct number of generic arguments have been provided. - /// This is used both for datatypes and function calls. - #[instrument(skip(tcx, gen_pos), level = "debug")] - pub(crate) fn check_generic_arg_count( - tcx: TyCtxt<'_>, - span: Span, - def_id: DefId, - seg: &hir::PathSegment<'_>, - gen_params: &ty::Generics, - gen_args: &hir::GenericArgs<'_>, - gen_pos: GenericArgPosition, - has_self: bool, - infer_args: bool, - ) -> GenericArgCountResult { - let default_counts = gen_params.own_defaults(); - let param_counts = gen_params.own_counts(); - - // Subtracting from param count to ensure type params synthesized from `impl Trait` - // cannot be explicitly specified. - let synth_type_param_count = gen_params - .params - .iter() - .filter(|param| { - matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. }) - }) - .count(); - let named_type_param_count = - param_counts.types - has_self as usize - synth_type_param_count; - let infer_lifetimes = - (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params(); - - if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() { - Self::prohibit_assoc_ty_binding(tcx, b.span); + .diagnostic() + .emit(); + + Err(reported) + }; + + let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes }; + let max_expected_lifetime_args = param_counts.lifetimes; + let num_provided_lifetime_args = gen_args.num_lifetime_params(); + + let lifetimes_correct = check_lifetime_args( + min_expected_lifetime_args, + max_expected_lifetime_args, + num_provided_lifetime_args, + explicit_late_bound == ExplicitLateBound::Yes, + ); + + let mut check_types_and_consts = |expected_min, + expected_max, + expected_max_with_synth, + provided, + params_offset, + args_offset| { + debug!( + ?expected_min, + ?expected_max, + ?provided, + ?params_offset, + ?args_offset, + "check_types_and_consts" + ); + if (expected_min..=expected_max).contains(&provided) { + return Ok(()); } - let explicit_late_bound = - Self::prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos); - - let mut invalid_args = vec![]; + let num_default_params = expected_max - expected_min; - let mut check_lifetime_args = - |min_expected_args: usize, - max_expected_args: usize, - provided_args: usize, - late_bounds_ignore: bool| { - if (min_expected_args..=max_expected_args).contains(&provided_args) { - return Ok(()); - } - - if late_bounds_ignore { - return Ok(()); - } + let gen_args_info = if provided > expected_max { + invalid_args.extend( + gen_args.args[args_offset + expected_max..args_offset + provided] + .iter() + .map(|arg| arg.span()), + ); + let num_redundant_args = provided - expected_max; - if provided_args > max_expected_args { - invalid_args.extend( - gen_args.args[max_expected_args..provided_args] - .iter() - .map(|arg| arg.span()), - ); - }; - - let gen_args_info = if provided_args > min_expected_args { - invalid_args.extend( - gen_args.args[min_expected_args..provided_args] - .iter() - .map(|arg| arg.span()), - ); - let num_redundant_args = provided_args - min_expected_args; - GenericArgsInfo::ExcessLifetimes { num_redundant_args } - } else { - let num_missing_args = min_expected_args - provided_args; - GenericArgsInfo::MissingLifetimes { num_missing_args } - }; - - let reported = WrongNumberOfGenericArgs::new( - tcx, - gen_args_info, - seg, - gen_params, - has_self as usize, - gen_args, - def_id, - ) - .diagnostic() - .emit(); - - Err(reported) - }; - - let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes }; - let max_expected_lifetime_args = param_counts.lifetimes; - let num_provided_lifetime_args = gen_args.num_lifetime_params(); - - let lifetimes_correct = check_lifetime_args( - min_expected_lifetime_args, - max_expected_lifetime_args, - num_provided_lifetime_args, - explicit_late_bound == ExplicitLateBound::Yes, - ); + // Provide extra note if synthetic arguments like `impl Trait` are specified. + let synth_provided = provided <= expected_max_with_synth; - let mut check_types_and_consts = |expected_min, - expected_max, - expected_max_with_synth, - provided, - params_offset, - args_offset| { - debug!( - ?expected_min, - ?expected_max, - ?provided, - ?params_offset, - ?args_offset, - "check_types_and_consts" - ); - if (expected_min..=expected_max).contains(&provided) { - return Ok(()); + GenericArgsInfo::ExcessTypesOrConsts { + num_redundant_args, + num_default_params, + args_offset, + synth_provided, } + } else { + let num_missing_args = expected_max - provided; - let num_default_params = expected_max - expected_min; - - let gen_args_info = if provided > expected_max { - invalid_args.extend( - gen_args.args[args_offset + expected_max..args_offset + provided] - .iter() - .map(|arg| arg.span()), - ); - let num_redundant_args = provided - expected_max; + GenericArgsInfo::MissingTypesOrConsts { + num_missing_args, + num_default_params, + args_offset, + } + }; - // Provide extra note if synthetic arguments like `impl Trait` are specified. - let synth_provided = provided <= expected_max_with_synth; + debug!(?gen_args_info); - GenericArgsInfo::ExcessTypesOrConsts { - num_redundant_args, - num_default_params, - args_offset, - synth_provided, - } - } else { - let num_missing_args = expected_max - provided; + let reported = WrongNumberOfGenericArgs::new( + tcx, + gen_args_info, + seg, + gen_params, + params_offset, + gen_args, + def_id, + ) + .diagnostic() + .emit_unless(gen_args.has_err()); - GenericArgsInfo::MissingTypesOrConsts { - num_missing_args, - num_default_params, - args_offset, - } - }; - - debug!(?gen_args_info); - - let reported = WrongNumberOfGenericArgs::new( - tcx, - gen_args_info, - seg, - gen_params, - params_offset, - gen_args, - def_id, - ) - .diagnostic() - .emit_unless(gen_args.has_err()); - - Err(reported) - }; + Err(reported) + }; - let args_correct = { - let expected_min = if infer_args { - 0 - } else { - param_counts.consts + named_type_param_count - - default_counts.types - - default_counts.consts - }; - debug!(?expected_min); - debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params()); - - check_types_and_consts( - expected_min, - param_counts.consts + named_type_param_count, - param_counts.consts + named_type_param_count + synth_type_param_count, - gen_args.num_generic_params(), - param_counts.lifetimes + has_self as usize, - gen_args.num_lifetime_params(), - ) + let args_correct = { + let expected_min = if infer_args { + 0 + } else { + param_counts.consts + named_type_param_count + - default_counts.types + - default_counts.consts }; + debug!(?expected_min); + debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params()); + + check_types_and_consts( + expected_min, + param_counts.consts + named_type_param_count, + param_counts.consts + named_type_param_count + synth_type_param_count, + gen_args.num_generic_params(), + param_counts.lifetimes + has_self as usize, + gen_args.num_lifetime_params(), + ) + }; - GenericArgCountResult { - explicit_late_bound, - correct: lifetimes_correct.and(args_correct).map_err(|reported| { - GenericArgCountMismatch { reported: Some(reported), invalid_args } - }), - } + GenericArgCountResult { + explicit_late_bound, + correct: lifetimes_correct + .and(args_correct) + .map_err(|reported| GenericArgCountMismatch { reported: Some(reported), invalid_args }), } +} - /// Emits an error regarding forbidden type binding associations - pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { - tcx.sess.emit_err(AssocTypeBindingNotAllowed { span }); - } +/// Emits an error regarding forbidden type binding associations +pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { + tcx.sess.emit_err(AssocTypeBindingNotAllowed { span }); +} - /// Prohibits explicit lifetime arguments if late-bound lifetime parameters - /// are present. This is used both for datatypes and function calls. - pub(crate) fn prohibit_explicit_late_bound_lifetimes( - tcx: TyCtxt<'_>, - def: &ty::Generics, - args: &hir::GenericArgs<'_>, - position: GenericArgPosition, - ) -> ExplicitLateBound { - let param_counts = def.own_counts(); - let infer_lifetimes = position != GenericArgPosition::Type && !args.has_lifetime_params(); - - if infer_lifetimes { - return ExplicitLateBound::No; - } +/// Prohibits explicit lifetime arguments if late-bound lifetime parameters +/// are present. This is used both for datatypes and function calls. +pub(crate) fn prohibit_explicit_late_bound_lifetimes( + tcx: TyCtxt<'_>, + def: &ty::Generics, + args: &hir::GenericArgs<'_>, + position: GenericArgPosition, +) -> ExplicitLateBound { + let param_counts = def.own_counts(); + let infer_lifetimes = position != GenericArgPosition::Type && !args.has_lifetime_params(); + + if infer_lifetimes { + return ExplicitLateBound::No; + } - if let Some(span_late) = def.has_late_bound_regions { - let msg = "cannot specify lifetime arguments explicitly \ + if let Some(span_late) = def.has_late_bound_regions { + let msg = "cannot specify lifetime arguments explicitly \ if late bound lifetime parameters are present"; - let note = "the late bound lifetime parameter is introduced here"; - let span = args.args[0].span(); - - if position == GenericArgPosition::Value - && args.num_lifetime_params() != param_counts.lifetimes - { - let mut err = tcx.sess.struct_span_err(span, msg); - err.span_note(span_late, note); - err.emit(); - } else { - let mut multispan = MultiSpan::from_span(span); - multispan.push_span_label(span_late, note); - tcx.struct_span_lint_hir( - LATE_BOUND_LIFETIME_ARGUMENTS, - args.args[0].hir_id(), - multispan, - msg, - |lint| lint, - ); - } - - ExplicitLateBound::Yes + let note = "the late bound lifetime parameter is introduced here"; + let span = args.args[0].span(); + + if position == GenericArgPosition::Value + && args.num_lifetime_params() != param_counts.lifetimes + { + let mut err = tcx.sess.struct_span_err(span, msg); + err.span_note(span_late, note); + err.emit(); } else { - ExplicitLateBound::No + let mut multispan = MultiSpan::from_span(span); + multispan.push_span_label(span_late, note); + tcx.struct_span_lint_hir( + LATE_BOUND_LIFETIME_ARGUMENTS, + args.args[0].hir_id(), + multispan, + msg, + |lint| lint, + ); } + + ExplicitLateBound::Yes + } else { + ExplicitLateBound::No } } diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index d7ab942665b..3521911b055 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -3,8 +3,11 @@ //! instance of `AstConv`. mod errors; -mod generics; +pub mod generics; +use crate::astconv::generics::{ + check_generic_arg_count, create_substs_for_generic_args, prohibit_assoc_ty_binding, +}; use crate::bounds::Bounds; use crate::collect::HirPlaceholderCollector; use crate::errors::{ @@ -24,6 +27,7 @@ use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{walk_generics, Visitor as _}; use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; @@ -106,11 +110,12 @@ pub trait AstConv<'tcx> { poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx>; - /// Normalize an associated type coming from the user. - /// - /// This should only be used by astconv. Use `FnCtxt::normalize` - /// or `ObligationCtxt::normalize` in downstream crates. - fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>; + /// Returns `AdtDef` if `ty` is an ADT. + /// Note that `ty` might be a projection type that needs normalization. + /// This used to get the enum variants in scope of the type. + /// For example, `Self::A` could refer to an associated type + /// or to an enum variant depending on the result of this function. + fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>>; /// Invoked when we encounter an error from some prior pass /// (e.g., resolve) that is translated into a ty-error. This is @@ -119,6 +124,13 @@ pub trait AstConv<'tcx> { fn set_tainted_by_errors(&self, e: ErrorGuaranteed); fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span); + + fn astconv(&self) -> &dyn AstConv<'tcx> + where + Self: Sized, + { + self + } } #[derive(Debug)] @@ -278,7 +290,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::BoundConstness::NotConst, ); if let Some(b) = item_segment.args().bindings.first() { - Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span); } substs @@ -348,7 +360,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { assert!(self_ty.is_none()); } - let arg_count = Self::check_generic_arg_count( + let arg_count = check_generic_arg_count( tcx, span, def_id, @@ -485,14 +497,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Avoid ICE #86756 when type error recovery goes awry. return tcx.ty_error().into(); } - self.astconv - .normalize_ty( - self.span, - tcx.at(self.span) - .bound_type_of(param.def_id) - .subst(tcx, substs), - ) - .into() + tcx.at(self.span).bound_type_of(param.def_id).subst(tcx, substs).into() } else if infer_args { self.astconv.ty_infer(Some(param), self.span).into() } else { @@ -530,7 +535,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { inferred_params: vec![], infer_args, }; - let substs = Self::create_substs_for_generic_args( + let substs = create_substs_for_generic_args( tcx, def_id, parent_substs, @@ -616,7 +621,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); if let Some(b) = item_segment.args().bindings.first() { - Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span); } args @@ -682,7 +687,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::Binder::bind_with_vars(tcx.mk_trait_ref(trait_def_id, substs), bound_vars); debug!(?poly_trait_ref, ?assoc_bindings); - bounds.trait_bounds.push((poly_trait_ref, span, constness)); + bounds.push_trait_bound(tcx, poly_trait_ref, span, constness); let mut dup_bindings = FxHashMap::default(); for binding in &assoc_bindings { @@ -810,7 +815,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { constness, ); if let Some(b) = trait_segment.args().bindings.first() { - Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span); } self.tcx().mk_trait_ref(trait_def_id, substs) } @@ -853,18 +858,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } /// Sets `implicitly_sized` to true on `Bounds` if necessary - pub(crate) fn add_implicitly_sized<'hir>( + pub(crate) fn add_implicitly_sized( &self, - bounds: &mut Bounds<'hir>, - ast_bounds: &'hir [hir::GenericBound<'hir>], - self_ty_where_predicates: Option<(LocalDefId, &'hir [hir::WherePredicate<'hir>])>, + bounds: &mut Bounds<'tcx>, + self_ty: Ty<'tcx>, + ast_bounds: &'tcx [hir::GenericBound<'tcx>], + self_ty_where_predicates: Option<(LocalDefId, &'tcx [hir::WherePredicate<'tcx>])>, span: Span, ) { let tcx = self.tcx(); // Try to find an unbound in bounds. let mut unbound = None; - let mut search_bounds = |ast_bounds: &'hir [hir::GenericBound<'hir>]| { + let mut search_bounds = |ast_bounds: &'tcx [hir::GenericBound<'tcx>]| { for ab in ast_bounds { if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab { if unbound.is_none() { @@ -912,7 +918,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // No lang item for `Sized`, so we can't add it as a bound. return; } - bounds.implicitly_sized = Some(span); + bounds.push_sized(tcx, self_ty, span); } /// This helper takes a *converted* parameter type (`param_ty`) @@ -963,10 +969,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } hir::GenericBound::Outlives(lifetime) => { let region = self.ast_region_to_region(lifetime, None); - bounds.region_bounds.push(( - ty::Binder::bind_with_vars(region, bound_vars), + bounds.push_region_bound( + self.tcx(), + ty::Binder::bind_with_vars( + ty::OutlivesPredicate(param_ty, region), + bound_vars, + ), lifetime.ident.span, - )); + ); } } } @@ -1199,17 +1209,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { (_, _) => { let got = if let Some(_) = term.ty() { "type" } else { "constant" }; let expected = def_kind.descr(assoc_item_def_id); - let reported = tcx - .sess - .struct_span_err( + let mut err = tcx.sess.struct_span_err( + binding.span, + &format!("expected {expected} bound, found {got}"), + ); + err.span_note( + tcx.def_span(assoc_item_def_id), + &format!("{expected} defined here"), + ); + + if let hir::def::DefKind::AssocConst = def_kind + && let Some(t) = term.ty() && (t.is_enum() || t.references_error()) + && tcx.features().associated_const_equality { + err.span_suggestion( binding.span, - &format!("expected {expected} bound, found {got}"), - ) - .span_note( - tcx.def_span(assoc_item_def_id), - &format!("{expected} defined here"), - ) - .emit(); + "if equating a const, try wrapping with braces", + format!("{} = {{ const }}", binding.item_name), + Applicability::HasPlaceholders, + ); + } + let reported = err.emit(); term = match def_kind { hir::def::DefKind::AssocTy => { tcx.ty_error_with_guaranteed(reported).into() @@ -1225,13 +1244,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }; } } - bounds.projection_bounds.push(( - projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate { - projection_ty, - term: term, - }), + bounds.push_projection_bound( + tcx, + projection_ty + .map_bound(|projection_ty| ty::ProjectionPredicate { projection_ty, term }), binding.span, - )); + ); } ConvertedBindingKind::Constraint(ast_bounds) => { // "Desugar" a constraint like `T: Iterator<Item: Debug>` to @@ -1254,13 +1272,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { item_segment: &hir::PathSegment<'_>, ) -> Ty<'tcx> { let substs = self.ast_path_substs_for_ty(span, did, item_segment); - self.normalize_ty(span, self.tcx().at(span).bound_type_of(did).subst(self.tcx(), substs)) + self.tcx().at(span).bound_type_of(did).subst(self.tcx(), substs) } fn conv_object_ty_poly_trait_ref( &self, span: Span, - trait_bounds: &[hir::PolyTraitRef<'_>], + hir_trait_bounds: &[hir::PolyTraitRef<'_>], lifetime: &hir::Lifetime, borrowed: bool, representation: DynKind, @@ -1270,7 +1288,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut bounds = Bounds::default(); let mut potential_assoc_types = Vec::new(); let dummy_self = self.tcx().types.trait_object_dummy_self; - for trait_bound in trait_bounds.iter().rev() { + for trait_bound in hir_trait_bounds.iter().rev() { if let GenericArgCountResult { correct: Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }), @@ -1287,10 +1305,45 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } + let mut trait_bounds = vec![]; + let mut projection_bounds = vec![]; + for (pred, span) in bounds.predicates() { + let bound_pred = pred.kind(); + match bound_pred.skip_binder() { + ty::PredicateKind::Clause(clause) => match clause { + ty::Clause::Trait(trait_pred) => { + assert_eq!(trait_pred.polarity, ty::ImplPolarity::Positive); + trait_bounds.push(( + bound_pred.rebind(trait_pred.trait_ref), + span, + trait_pred.constness, + )); + } + ty::Clause::Projection(proj) => { + projection_bounds.push((bound_pred.rebind(proj), span)); + } + ty::Clause::TypeOutlives(_) => { + // Do nothing, we deal with regions separately + } + ty::Clause::RegionOutlives(_) => bug!(), + }, + ty::PredicateKind::WellFormed(_) + | ty::PredicateKind::ObjectSafe(_) + | ty::PredicateKind::ClosureKind(_, _, _) + | ty::PredicateKind::Subtype(_) + | ty::PredicateKind::Coerce(_) + | ty::PredicateKind::ConstEvaluatable(_) + | ty::PredicateKind::ConstEquate(_, _) + | ty::PredicateKind::TypeWellFormedFromEnv(_) + | ty::PredicateKind::Ambiguous => bug!(), + } + } + // Expand trait aliases recursively and check that only one regular (non-auto) trait // is used and no 'maybe' bounds are used. let expanded_traits = - traits::expand_trait_aliases(tcx, bounds.trait_bounds.iter().map(|&(a, b, _)| (a, b))); + traits::expand_trait_aliases(tcx, trait_bounds.iter().map(|&(a, b, _)| (a, b))); + let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits .filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self) .partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); @@ -1327,8 +1380,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } if regular_traits.is_empty() && auto_traits.is_empty() { - let trait_alias_span = bounds - .trait_bounds + let trait_alias_span = trait_bounds .iter() .map(|&(trait_ref, _, _)| trait_ref.def_id()) .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) @@ -1359,8 +1411,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Use a `BTreeSet` to keep output in a more consistent order. let mut associated_types: FxHashMap<Span, BTreeSet<DefId>> = FxHashMap::default(); - let regular_traits_refs_spans = bounds - .trait_bounds + let regular_traits_refs_spans = trait_bounds .into_iter() .filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id())); @@ -1414,7 +1465,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // the discussion in #56288 for alternatives. if !references_self { // Include projections defined on supertraits. - bounds.projection_bounds.push((pred, span)); + projection_bounds.push((pred, span)); } } _ => (), @@ -1422,7 +1473,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } - for (projection_bound, _) in &bounds.projection_bounds { + for (projection_bound, _) in &projection_bounds { for def_ids in associated_types.values_mut() { def_ids.remove(&projection_bound.projection_def_id()); } @@ -1431,7 +1482,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self.complain_about_missing_associated_types( associated_types, potential_assoc_types, - trait_bounds, + hir_trait_bounds, ); // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as @@ -1473,7 +1524,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let substs = tcx.intern_substs(&substs[..]); let span = i.bottom().1; - let empty_generic_args = trait_bounds.iter().any(|hir_bound| { + let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| { hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id) && hir_bound.span.contains(span) }); @@ -1505,7 +1556,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }) }); - let existential_projections = bounds.projection_bounds.iter().map(|(bound, _)| { + let existential_projections = projection_bounds.iter().map(|(bound, _)| { bound.map_bound(|mut b| { assert_eq!(b.projection_ty.self_ty(), dummy_self); @@ -1593,8 +1644,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn report_ambiguous_associated_type( &self, span: Span, - type_str: &str, - trait_str: &str, + types: &[String], + traits: &[String], name: Symbol, ) -> ErrorGuaranteed { let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type"); @@ -1605,19 +1656,92 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .keys() .any(|full_span| full_span.contains(span)) { - err.span_suggestion( + err.span_suggestion_verbose( span.shrink_to_lo(), "you are looking for the module in `std`, not the primitive type", "std::", Applicability::MachineApplicable, ); } else { - err.span_suggestion( - span, - "use fully-qualified syntax", - format!("<{} as {}>::{}", type_str, trait_str, name), - Applicability::HasPlaceholders, - ); + match (types, traits) { + ([], []) => { + err.span_suggestion_verbose( + span, + &format!( + "if there were a type named `Type` that implements a trait named \ + `Trait` with associated type `{name}`, you could use the \ + fully-qualified path", + ), + format!("<Type as Trait>::{name}"), + Applicability::HasPlaceholders, + ); + } + ([], [trait_str]) => { + err.span_suggestion_verbose( + span, + &format!( + "if there were a type named `Example` that implemented `{trait_str}`, \ + you could use the fully-qualified path", + ), + format!("<Example as {trait_str}>::{name}"), + Applicability::HasPlaceholders, + ); + } + ([], traits) => { + err.span_suggestions( + span, + &format!( + "if there were a type named `Example` that implemented one of the \ + traits with associated type `{name}`, you could use the \ + fully-qualified path", + ), + traits + .iter() + .map(|trait_str| format!("<Example as {trait_str}>::{name}")) + .collect::<Vec<_>>(), + Applicability::HasPlaceholders, + ); + } + ([type_str], []) => { + err.span_suggestion_verbose( + span, + &format!( + "if there were a trait named `Example` with associated type `{name}` \ + implemented for `{type_str}`, you could use the fully-qualified path", + ), + format!("<{type_str} as Example>::{name}"), + Applicability::HasPlaceholders, + ); + } + (types, []) => { + err.span_suggestions( + span, + &format!( + "if there were a trait named `Example` with associated type `{name}` \ + implemented for one of the types, you could use the fully-qualified \ + path", + ), + types + .into_iter() + .map(|type_str| format!("<{type_str} as Example>::{name}")), + Applicability::HasPlaceholders, + ); + } + (types, traits) => { + let mut suggestions = vec![]; + for type_str in types { + for trait_str in traits { + suggestions.push(format!("<{type_str} as {trait_str}>::{name}")); + } + } + err.span_suggestions( + span, + "use the fully-qualified path", + suggestions, + Applicability::MachineApplicable, + ); + } + } } err.emit() } @@ -1786,7 +1910,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Ok(bound) } - // Create a type from a path to an associated type. + // Create a type from a path to an associated type or to an enum variant. // For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C` // and item_segment is the path segment for `D`. We return a type and a def for // the whole path. @@ -1814,7 +1938,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Check if we have an enum variant. let mut variant_resolution = None; - if let ty::Adt(adt_def, adt_substs) = qself_ty.kind() { + if let Some(adt_def) = self.probe_adt(span, qself_ty) { if adt_def.is_enum() { let variant_def = adt_def .variants() @@ -1916,6 +2040,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let Some(assoc_ty_did) = self.lookup_assoc_ty(assoc_ident, hir_ref_id, span, impl_) else { continue; }; + let ty::Adt(_, adt_substs) = qself_ty.kind() else { + // FIXME(inherent_associated_types) + bug!("unimplemented: non-adt self of inherent assoc ty"); + }; let item_substs = self.create_substs_for_associated_item( span, assoc_ty_did, @@ -1923,7 +2051,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { adt_substs, ); let ty = tcx.bound_type_of(assoc_ty_did).subst(tcx, item_substs); - let ty = self.normalize_ty(span, ty); return Ok((ty, DefKind::AssocTy, assoc_ty_did)); } } @@ -1997,12 +2124,64 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.emit() } else if let Err(reported) = qself_ty.error_reported() { reported + } else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() { + // `<impl Trait as OtherTrait>::Assoc` makes no sense. + struct_span_err!( + tcx.sess, + tcx.def_span(alias_ty.def_id), + E0667, + "`impl Trait` is not allowed in path parameters" + ) + .emit() // Already reported in an earlier stage. } else { + // Find all the `impl`s that `qself_ty` has for any trait that has the + // associated type, so that we suggest the right one. + let infcx = tcx.infer_ctxt().build(); + // We create a fresh `ty::ParamEnv` instead of the one for `self.item_def_id()` + // to avoid a cycle error in `src/test/ui/resolve/issue-102946.rs`. + let param_env = ty::ParamEnv::empty(); + let traits: Vec<_> = self + .tcx() + .all_traits() + .filter(|trait_def_id| { + // Consider only traits with the associated type + tcx.associated_items(*trait_def_id) + .in_definition_order() + .any(|i| { + i.kind.namespace() == Namespace::TypeNS + && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident + && matches!(i.kind, ty::AssocKind::Type) + }) + // Consider only accessible traits + && tcx.visibility(*trait_def_id) + .is_accessible_from(self.item_def_id(), tcx) + && tcx.all_impls(*trait_def_id) + .any(|impl_def_id| { + let trait_ref = tcx.bound_impl_trait_ref(impl_def_id); + trait_ref.map_or(false, |trait_ref| { + let impl_ = trait_ref.subst( + tcx, + infcx.fresh_substs_for_item(span, impl_def_id), + ); + infcx + .can_eq( + param_env, + tcx.erase_regions(impl_.self_ty()), + tcx.erase_regions(qself_ty), + ) + .is_ok() + }) + && tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative + }) + }) + .map(|trait_def_id| tcx.def_path_str(trait_def_id)) + .collect(); + // Don't print `TyErr` to the user. self.report_ambiguous_associated_type( span, - &qself_ty.to_string(), - "Trait", + &[qself_ty.to_string()], + &traits, assoc_ident.name, ) }; @@ -2020,7 +2199,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }; let ty = self.projected_ty_from_poly_trait_ref(span, assoc_ty_did, assoc_segment, bound); - let ty = self.normalize_ty(span, ty); if let Some(variant_def_id) = variant_resolution { tcx.struct_span_lint_hir( @@ -2121,16 +2299,30 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let is_part_of_self_trait_constraints = def_id == trait_def_id; let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id); - let type_name = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { - "Self" + let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait { + vec!["Self".to_string()] } else { - "Type" + // Find all the types that have an `impl` for the trait. + tcx.all_impls(trait_def_id) + .filter(|impl_def_id| { + // Consider only accessible traits + tcx.visibility(*impl_def_id).is_accessible_from(self.item_def_id(), tcx) + && tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative + }) + .filter_map(|impl_def_id| tcx.impl_trait_ref(impl_def_id)) + .map(|impl_| impl_.self_ty()) + // We don't care about blanket impls. + .filter(|self_ty| !self_ty.has_non_region_param()) + .map(|self_ty| tcx.erase_regions(self_ty).to_string()) + .collect() }; - + // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that + // references the trait. Relevant for the first case in + // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs` let reported = self.report_ambiguous_associated_type( span, - type_name, - &path_str, + &type_names, + &[path_str], item_segment.ident.name, ); return tcx.ty_error_with_guaranteed(reported) @@ -2156,7 +2348,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!("qpath_to_ty: trait_ref={:?}", trait_ref); - self.normalize_ty(span, tcx.mk_projection(item_def_id, item_substs)) + tcx.mk_projection(item_def_id, item_substs) } pub fn prohibit_generics<'a>( @@ -2259,7 +2451,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for segment in segments { // Only emit the first error to avoid overloading the user with error messages. if let Some(b) = segment.args().bindings.first() { - Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span); return true; } } @@ -2273,6 +2465,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty: Option<Ty<'tcx>>, kind: DefKind, def_id: DefId, + span: Span, ) -> Vec<PathSeg> { // We need to extract the type parameters supplied by the user in // the path `path`. Due to the current setup, this is a bit of a @@ -2340,8 +2533,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Case 2. Reference to a variant constructor. DefKind::Ctor(CtorOf::Variant, ..) | DefKind::Variant => { - let adt_def = self_ty.map(|t| t.ty_adt_def().unwrap()); - let (generics_def_id, index) = if let Some(adt_def) = adt_def { + let (generics_def_id, index) = if let Some(self_ty) = self_ty { + let adt_def = self.probe_adt(span, self_ty).unwrap(); debug_assert!(adt_def.is_enum()); (adt_def.did(), last) } else if last >= 1 && segments[last - 1].args.is_some() { @@ -2417,7 +2610,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { err.note("`impl Trait` types can't have type parameters"); }); let substs = self.ast_path_substs_for_ty(span, did, item_segment.0); - self.normalize_ty(span, tcx.mk_opaque(did, substs)) + tcx.mk_opaque(did, substs) } Res::Def( DefKind::Enum @@ -2437,7 +2630,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { assert_eq!(opt_self_ty, None); let path_segs = - self.def_ids_for_value_path_segments(path.segments, None, kind, def_id); + self.def_ids_for_value_path_segments(path.segments, None, kind, def_id, span); let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); self.prohibit_generics( @@ -2577,7 +2770,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } tcx.ty_error_with_guaranteed(err.emit()) } else { - self.normalize_ty(span, ty) + ty } } Res::Def(DefKind::AssocTy, def_id) => { @@ -2720,8 +2913,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { None, ty::BoundConstness::NotConst, ); - EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id))) - .subst(tcx, substs) + EarlyBinder(tcx.at(span).type_of(def_id)).subst(tcx, substs) } hir::TyKind::Array(ref ty, ref length) => { let length = match length { @@ -2731,8 +2923,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } }; - let array_ty = tcx.mk_ty(ty::Array(self.ast_ty_to_ty(ty), length)); - self.normalize_ty(ast_ty.span, array_ty) + tcx.mk_ty(ty::Array(self.ast_ty_to_ty(ty), length)) } hir::TyKind::Typeof(ref e) => { let ty_erased = tcx.type_of(e.def_id); @@ -2936,7 +3127,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) = hir.get(fn_hir_id) else { return None }; let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) = - hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") }; + hir.get_parent(fn_hir_id) else { bug!("ImplItem should have Impl parent") }; let trait_ref = self.instantiate_mono_trait_ref( i.of_trait.as_ref()?, diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index e988c77a064..730560cc686 100644 --- a/compiler/rustc_trait_selection/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -178,6 +178,10 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { self.state.obligations } + pub fn current_obligations(&self) -> Vec<traits::PredicateObligation<'tcx>> { + self.state.obligations.clone() + } + pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] { &self.state.steps } diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 3e3544ce666..0880c8c15f2 100644 --- a/compiler/rustc_hir_analysis/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs @@ -1,6 +1,7 @@ //! Bounds are restrictions applied to some types after they've been converted into the //! `ty` form from the HIR. +use rustc_hir::LangItem; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_span::Span; @@ -15,73 +16,53 @@ use rustc_span::Span; /// ^^^^^^^^^ bounding the type parameter `T` /// /// impl dyn Bar + Baz -/// ^^^^^^^^^ bounding the forgotten dynamic type +/// ^^^^^^^^^ bounding the type-erased dynamic type /// ``` /// /// Our representation is a bit mixed here -- in some cases, we /// include the self type (e.g., `trait_bounds`) but in others we do not #[derive(Default, PartialEq, Eq, Clone, Debug)] pub struct Bounds<'tcx> { - /// A list of region bounds on the (implicit) self type. So if you - /// had `T: 'a + 'b` this might would be a list `['a, 'b]` (but - /// the `T` is not explicitly included). - pub region_bounds: Vec<(ty::Binder<'tcx, ty::Region<'tcx>>, Span)>, - - /// A list of trait bounds. So if you had `T: Debug` this would be - /// `T: Debug`. Note that the self-type is explicit here. - pub trait_bounds: Vec<(ty::PolyTraitRef<'tcx>, Span, ty::BoundConstness)>, - - /// A list of projection equality bounds. So if you had `T: - /// Iterator<Item = u32>` this would include `<T as - /// Iterator>::Item => u32`. Note that the self-type is explicit - /// here. - pub projection_bounds: Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>, - - /// `Some` if there is *no* `?Sized` predicate. The `span` - /// is the location in the source of the `T` declaration which can - /// be cited as the source of the `T: Sized` requirement. - pub implicitly_sized: Option<Span>, + pub predicates: Vec<(ty::Predicate<'tcx>, Span)>, } impl<'tcx> Bounds<'tcx> { - /// Converts a bounds list into a flat set of predicates (like - /// where-clauses). Because some of our bounds listings (e.g., - /// regions) don't include the self-type, you must supply the - /// self-type here (the `param_ty` parameter). - pub fn predicates<'out, 's>( - &'s self, + pub fn push_region_bound( + &mut self, tcx: TyCtxt<'tcx>, - param_ty: Ty<'tcx>, - // the output must live shorter than the duration of the borrow of self and 'tcx. - ) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> + 'out - where - 'tcx: 'out, - 's: 'out, - { - // If it could be sized, and is, add the `Sized` predicate. - let sized_predicate = self.implicitly_sized.and_then(|span| { - // FIXME: use tcx.at(span).mk_trait_ref(LangItem::Sized) here? This may make no-core code harder to write. - let sized = tcx.lang_items().sized_trait()?; - let trait_ref = ty::Binder::dummy(tcx.mk_trait_ref(sized, [param_ty])); - Some((trait_ref.without_const().to_predicate(tcx), span)) - }); + region: ty::PolyTypeOutlivesPredicate<'tcx>, + span: Span, + ) { + self.predicates.push((region.to_predicate(tcx), span)); + } - let region_preds = self.region_bounds.iter().map(move |&(region_bound, span)| { - let pred = region_bound - .map_bound(|region_bound| ty::OutlivesPredicate(param_ty, region_bound)) - .to_predicate(tcx); - (pred, span) - }); - let trait_bounds = - self.trait_bounds.iter().map(move |&(bound_trait_ref, span, constness)| { - let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx); - (predicate, span) - }); - let projection_bounds = self - .projection_bounds - .iter() - .map(move |&(projection, span)| (projection.to_predicate(tcx), span)); + pub fn push_trait_bound( + &mut self, + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + span: Span, + constness: ty::BoundConstness, + ) { + self.predicates.push((trait_ref.with_constness(constness).to_predicate(tcx), span)); + } + + pub fn push_projection_bound( + &mut self, + tcx: TyCtxt<'tcx>, + projection: ty::PolyProjectionPredicate<'tcx>, + span: Span, + ) { + self.predicates.push((projection.to_predicate(tcx), span)); + } + + pub fn push_sized(&mut self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) { + let sized_def_id = tcx.require_lang_item(LangItem::Sized, Some(span)); + let trait_ref = ty::Binder::dummy(tcx.mk_trait_ref(sized_def_id, [ty])); + // Preferrable to put this obligation first, since we report better errors for sized ambiguity. + self.predicates.insert(0, (trait_ref.without_const().to_predicate(tcx), span)); + } - sized_predicate.into_iter().chain(region_preds).chain(trait_bounds).chain(projection_bounds) + pub fn predicates(&self) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> + '_ { + self.predicates.iter().cloned() } } diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 28cd18bbb8e..43795cfba3f 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -468,14 +468,14 @@ fn check_opaque_meets_bounds<'tcx>( // Can have different predicates to their defining use hir::OpaqueTyOrigin::TyAlias => { let outlives_environment = OutlivesEnvironment::new(param_env); - let _ = infcx.check_region_obligations_and_report_errors( + let _ = infcx.err_ctxt().check_region_obligations_and_report_errors( defining_use_anchor, &outlives_environment, ); } } // Clean up after ourselves - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + let _ = infcx.take_opaque_types(); } fn is_enum_of_nonnullable_ptr<'tcx>( diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index a767338ab85..2cdf7579471 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -2,7 +2,9 @@ use super::potentially_plural_count; use crate::errors::LifetimesOrBoundsMismatchOnTrait; use hir::def_id::{DefId, LocalDefId}; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed}; +use rustc_errors::{ + pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed, MultiSpan, +}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit; @@ -270,8 +272,8 @@ fn compare_method_predicate_entailment<'tcx>( let unnormalized_impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(unnormalized_impl_sig)); let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id); - let impl_fty = ocx.normalize(&norm_cause, param_env, unnormalized_impl_fty); - debug!("compare_impl_method: impl_fty={:?}", impl_fty); + let impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_impl_sig); + debug!("compare_impl_method: impl_fty={:?}", impl_sig); let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs); let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig); @@ -294,18 +296,17 @@ fn compare_method_predicate_entailment<'tcx>( // type would be more appropriate. In other places we have a `Vec<Span>` // corresponding to their `Vec<Predicate>`, but we don't have that here. // Fixing this would improve the output of test `issue-83765.rs`. - let result = ocx.sup(&cause, param_env, trait_fty, impl_fty); + let result = ocx.sup(&cause, param_env, trait_sig, impl_sig); if let Err(terr) = result { - debug!(?terr, "sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); + debug!(?impl_sig, ?trait_sig, ?terr, "sub_types failed"); let emitted = report_trait_method_mismatch( &infcx, cause, terr, - (trait_m, trait_fty), - (impl_m, impl_fty), - trait_sig, + (trait_m, trait_sig), + (impl_m, impl_sig), impl_trait_ref, ); return Err(emitted); @@ -321,15 +322,6 @@ fn compare_method_predicate_entailment<'tcx>( ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_impl_fty.into())), )); } - let emit_implied_wf_lint = || { - infcx.tcx.struct_span_lint_hir( - rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT, - impl_m_hir_id, - infcx.tcx.def_span(impl_m.def_id), - "impl method assumes more implied bounds than the corresponding trait method", - |lint| lint, - ); - }; // Check that all obligations are satisfied by the implementation's // version. @@ -347,7 +339,7 @@ fn compare_method_predicate_entailment<'tcx>( ) .map(|()| { // If the skip-mode was successful, emit a lint. - emit_implied_wf_lint(); + emit_implied_wf_lint(infcx.tcx, impl_m, impl_m_hir_id, vec![]); }); } CheckImpliedWfMode::Skip => { @@ -383,8 +375,16 @@ fn compare_method_predicate_entailment<'tcx>( CheckImpliedWfMode::Skip, ) .map(|()| { + let bad_args = extract_bad_args_for_implies_lint( + tcx, + &errors, + (trait_m, trait_sig), + // Unnormalized impl sig corresponds to the HIR types written + (impl_m, unnormalized_impl_sig), + impl_m_hir_id, + ); // If the skip-mode was successful, emit a lint. - emit_implied_wf_lint(); + emit_implied_wf_lint(tcx, impl_m, impl_m_hir_id, bad_args); }); } CheckImpliedWfMode::Skip => { @@ -401,6 +401,141 @@ fn compare_method_predicate_entailment<'tcx>( Ok(()) } +fn extract_bad_args_for_implies_lint<'tcx>( + tcx: TyCtxt<'tcx>, + errors: &[infer::RegionResolutionError<'tcx>], + (trait_m, trait_sig): (&ty::AssocItem, ty::FnSig<'tcx>), + (impl_m, impl_sig): (&ty::AssocItem, ty::FnSig<'tcx>), + hir_id: hir::HirId, +) -> Vec<(Span, Option<String>)> { + let mut blame_generics = vec![]; + for error in errors { + // Look for the subregion origin that contains an input/output type + let origin = match error { + infer::RegionResolutionError::ConcreteFailure(o, ..) => o, + infer::RegionResolutionError::GenericBoundFailure(o, ..) => o, + infer::RegionResolutionError::SubSupConflict(_, _, o, ..) => o, + infer::RegionResolutionError::UpperBoundUniverseConflict(.., o, _) => o, + }; + // Extract (possible) input/output types from origin + match origin { + infer::SubregionOrigin::Subtype(trace) => { + if let Some((a, b)) = trace.values.ty() { + blame_generics.extend([a, b]); + } + } + infer::SubregionOrigin::RelateParamBound(_, ty, _) => blame_generics.push(*ty), + infer::SubregionOrigin::ReferenceOutlivesReferent(ty, _) => blame_generics.push(*ty), + _ => {} + } + } + + let fn_decl = tcx.hir().fn_decl_by_hir_id(hir_id).unwrap(); + let opt_ret_ty = match fn_decl.output { + hir::FnRetTy::DefaultReturn(_) => None, + hir::FnRetTy::Return(ty) => Some(ty), + }; + + // Map late-bound regions from trait to impl, so the names are right. + let mapping = std::iter::zip( + tcx.fn_sig(trait_m.def_id).bound_vars(), + tcx.fn_sig(impl_m.def_id).bound_vars(), + ) + .filter_map(|(impl_bv, trait_bv)| { + if let ty::BoundVariableKind::Region(impl_bv) = impl_bv + && let ty::BoundVariableKind::Region(trait_bv) = trait_bv + { + Some((impl_bv, trait_bv)) + } else { + None + } + }) + .collect(); + + // For each arg, see if it was in the "blame" of any of the region errors. + // If so, then try to produce a suggestion to replace the argument type with + // one from the trait. + let mut bad_args = vec![]; + for (idx, (ty, hir_ty)) in + std::iter::zip(impl_sig.inputs_and_output, fn_decl.inputs.iter().chain(opt_ret_ty)) + .enumerate() + { + let expected_ty = trait_sig.inputs_and_output[idx] + .fold_with(&mut RemapLateBound { tcx, mapping: &mapping }); + if blame_generics.iter().any(|blame| ty.contains(*blame)) { + let expected_ty_sugg = expected_ty.to_string(); + bad_args.push(( + hir_ty.span, + // Only suggest something if it actually changed. + (expected_ty_sugg != ty.to_string()).then_some(expected_ty_sugg), + )); + } + } + + bad_args +} + +struct RemapLateBound<'a, 'tcx> { + tcx: TyCtxt<'tcx>, + mapping: &'a FxHashMap<ty::BoundRegionKind, ty::BoundRegionKind>, +} + +impl<'tcx> TypeFolder<'tcx> for RemapLateBound<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + if let ty::ReFree(fr) = *r { + self.tcx.mk_region(ty::ReFree(ty::FreeRegion { + bound_region: self + .mapping + .get(&fr.bound_region) + .copied() + .unwrap_or(fr.bound_region), + ..fr + })) + } else { + r + } + } +} + +fn emit_implied_wf_lint<'tcx>( + tcx: TyCtxt<'tcx>, + impl_m: &ty::AssocItem, + hir_id: hir::HirId, + bad_args: Vec<(Span, Option<String>)>, +) { + let span: MultiSpan = if bad_args.is_empty() { + tcx.def_span(impl_m.def_id).into() + } else { + bad_args.iter().map(|(span, _)| *span).collect::<Vec<_>>().into() + }; + tcx.struct_span_lint_hir( + rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT, + hir_id, + span, + "impl method assumes more implied bounds than the corresponding trait method", + |lint| { + let bad_args: Vec<_> = + bad_args.into_iter().filter_map(|(span, sugg)| Some((span, sugg?))).collect(); + if !bad_args.is_empty() { + lint.multipart_suggestion( + format!( + "replace {} type{} to make the impl signature compatible", + pluralize!("this", bad_args.len()), + pluralize!(bad_args.len()) + ), + bad_args, + Applicability::MaybeIncorrect, + ); + } + lint + }, + ); +} + #[derive(Debug, PartialEq, Eq)] enum CheckImpliedWfMode { /// Checks implied well-formedness of the impl method. If it fails, we will @@ -425,7 +560,7 @@ fn compare_asyncness<'tcx>( ty::Alias(ty::Opaque, ..) => { // allow both `async fn foo()` and `fn foo() -> impl Future` } - ty::Error(rustc_errors::ErrorGuaranteed { .. }) => { + ty::Error(_) => { // We don't know if it's ok, but at least it's already an error. } _ => { @@ -484,7 +619,8 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( let impl_trait_ref = tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap(); let param_env = tcx.param_env(def_id); - // First, check a few of the same thing as `compare_impl_method`, just so we don't ICE during substitutions later. + // First, check a few of the same things as `compare_impl_method`, + // just so we don't ICE during substitution later. compare_number_of_generics(tcx, impl_m, trait_m, tcx.hir().span_if_local(impl_m.def_id), true)?; compare_generic_param_kinds(tcx, impl_m, trait_m, true)?; check_region_bounds_on_impl_item(tcx, impl_m, trait_m, true)?; @@ -577,14 +713,11 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( debug!(?trait_sig, ?impl_sig, "equating function signatures"); - let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)); - let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)); - // Unify the whole function signature. We need to do this to fully infer // the lifetimes of the return type, but do this after unifying just the // return types, since we want to avoid duplicating errors from // `compare_method_predicate_entailment`. - match ocx.eq(&cause, param_env, trait_fty, impl_fty) { + match ocx.eq(&cause, param_env, trait_sig, impl_sig) { Ok(()) => {} Err(terr) => { // This function gets called during `compare_method_predicate_entailment` when normalizing a @@ -595,9 +728,8 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( infcx, cause, terr, - (trait_m, trait_fty), - (impl_m, impl_fty), - trait_sig, + (trait_m, trait_sig), + (impl_m, impl_sig), impl_trait_ref, ); return Err(emitted); @@ -619,7 +751,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( Some(infcx), infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys), ); - infcx.check_region_obligations_and_report_errors( + infcx.err_ctxt().check_region_obligations_and_report_errors( impl_m.def_id.expect_local(), &outlives_environment, )?; @@ -771,9 +903,8 @@ fn report_trait_method_mismatch<'tcx>( infcx: &InferCtxt<'tcx>, mut cause: ObligationCause<'tcx>, terr: TypeError<'tcx>, - (trait_m, trait_fty): (&ty::AssocItem, Ty<'tcx>), - (impl_m, impl_fty): (&ty::AssocItem, Ty<'tcx>), - trait_sig: ty::FnSig<'tcx>, + (trait_m, trait_sig): (&ty::AssocItem, ty::FnSig<'tcx>), + (impl_m, impl_sig): (&ty::AssocItem, ty::FnSig<'tcx>), impl_trait_ref: ty::TraitRef<'tcx>, ) -> ErrorGuaranteed { let tcx = infcx.tcx; @@ -858,10 +989,7 @@ fn report_trait_method_mismatch<'tcx>( &mut diag, &cause, trait_err_span.map(|sp| (sp, "type in trait".to_owned())), - Some(infer::ValuePairs::Terms(ExpectedFound { - expected: trait_fty.into(), - found: impl_fty.into(), - })), + Some(infer::ValuePairs::Sigs(ExpectedFound { expected: trait_sig, found: impl_sig })), terr, false, false, @@ -1651,8 +1779,9 @@ pub(super) fn compare_impl_const_raw( } let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors(impl_const_item_def, &outlives_environment)?; - + infcx + .err_ctxt() + .check_region_obligations_and_report_errors(impl_const_item_def, &outlives_environment)?; Ok(()) } @@ -1760,7 +1889,7 @@ fn compare_type_predicate_entailment<'tcx>( // Finally, resolve all regions. This catches wily misuses of // lifetime parameters. let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors( + infcx.err_ctxt().check_region_obligations_and_report_errors( impl_ty.def_id.expect_local(), &outlives_environment, )?; @@ -1974,27 +2103,11 @@ pub(super) fn check_type_bounds<'tcx>( let outlives_environment = OutlivesEnvironment::with_bounds(param_env, Some(&infcx), implied_bounds); - infcx.check_region_obligations_and_report_errors( + infcx.err_ctxt().check_region_obligations_and_report_errors( impl_ty.def_id.expect_local(), &outlives_environment, )?; - let constraints = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - for (key, value) in constraints { - infcx - .err_ctxt() - .report_mismatched_types( - &ObligationCause::misc( - value.hidden_type.span, - tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()), - ), - tcx.mk_opaque(key.def_id.to_def_id(), key.substs), - value.hidden_type.ty, - TypeError::Mismatch, - ) - .emit(); - } - Ok(()) } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 0d1aa39c5d9..92fd4625ee8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1,4 +1,6 @@ +use crate::autoderef::Autoderef; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; + use hir::def::DefKind; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; @@ -22,7 +24,6 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -97,25 +98,28 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( let infcx = &tcx.infer_ctxt().build(); let ocx = ObligationCtxt::new(infcx); - let assumed_wf_types = ocx.assumed_wf_types(param_env, span, body_def_id); - let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env }; if !tcx.features().trivial_bounds { wfcx.check_false_global_bounds() } f(&mut wfcx); + + let assumed_wf_types = wfcx.ocx.assumed_wf_types(param_env, span, body_def_id); + let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types); + let errors = wfcx.select_all_or_error(); if !errors.is_empty() { infcx.err_ctxt().report_fulfillment_errors(&errors, None); return; } - let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types); let outlives_environment = OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds); - let _ = infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment); + let _ = infcx + .err_ctxt() + .check_region_obligations_and_report_errors(body_def_id, &outlives_environment); } fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) { @@ -1250,7 +1254,11 @@ fn check_impl<'tcx>( // therefore don't need to be WF (the trait's `Self: Trait` predicate // won't hold). let trait_ref = tcx.impl_trait_ref(item.owner_id).unwrap(); - let trait_ref = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref); + let trait_ref = wfcx.normalize( + ast_trait_ref.path.span, + Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), + trait_ref, + ); let trait_pred = ty::TraitPredicate { trait_ref, constness: match constness { @@ -1259,7 +1267,7 @@ fn check_impl<'tcx>( }, polarity: ty::ImplPolarity::Positive, }; - let obligations = traits::wf::trait_obligations( + let mut obligations = traits::wf::trait_obligations( wfcx.infcx, wfcx.param_env, wfcx.body_id, @@ -1267,6 +1275,13 @@ fn check_impl<'tcx>( ast_trait_ref.path.span, item, ); + for obligation in &mut obligations { + if let Some(pred) = obligation.predicate.to_opt_poly_trait_pred() + && pred.self_ty().skip_binder() == trait_ref.self_ty() + { + obligation.cause.span = ast_self_ty.span; + } + } debug!(?obligations); wfcx.register_obligations(obligations); } @@ -1489,54 +1504,38 @@ fn check_fn_or_method<'tcx>( def_id: LocalDefId, ) { let tcx = wfcx.tcx(); - let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig); + let mut sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig); // Normalize the input and output types one at a time, using a different // `WellFormedLoc` for each. We cannot call `normalize_associated_types` // on the entire `FnSig`, since this would use the same `WellFormedLoc` // for each type, preventing the HIR wf check from generating // a nice error message. - let ty::FnSig { mut inputs_and_output, c_variadic, unsafety, abi } = sig; - inputs_and_output = tcx.mk_type_list(inputs_and_output.iter().enumerate().map(|(i, ty)| { - wfcx.normalize( - span, - Some(WellFormedLoc::Param { - function: def_id, - // Note that the `param_idx` of the output type is - // one greater than the index of the last input type. - param_idx: i.try_into().unwrap(), - }), - ty, - ) - })); - // Manually call `normalize_associated_types_in` on the other types - // in `FnSig`. This ensures that if the types of these fields - // ever change to include projections, we will start normalizing - // them automatically. - let sig = ty::FnSig { - inputs_and_output, - c_variadic: wfcx.normalize(span, None, c_variadic), - unsafety: wfcx.normalize(span, None, unsafety), - abi: wfcx.normalize(span, None, abi), - }; + let arg_span = + |idx| hir_decl.inputs.get(idx).map_or(hir_decl.output.span(), |arg: &hir::Ty<'_>| arg.span); + + sig.inputs_and_output = + tcx.mk_type_list(sig.inputs_and_output.iter().enumerate().map(|(idx, ty)| { + wfcx.normalize( + arg_span(idx), + Some(WellFormedLoc::Param { + function: def_id, + // Note that the `param_idx` of the output type is + // one greater than the index of the last input type. + param_idx: idx.try_into().unwrap(), + }), + ty, + ) + })); - for (i, (&input_ty, ty)) in iter::zip(sig.inputs(), hir_decl.inputs).enumerate() { + for (idx, ty) in sig.inputs_and_output.iter().enumerate() { wfcx.register_wf_obligation( - ty.span, - Some(WellFormedLoc::Param { function: def_id, param_idx: i.try_into().unwrap() }), - input_ty.into(), + arg_span(idx), + Some(WellFormedLoc::Param { function: def_id, param_idx: idx.try_into().unwrap() }), + ty.into(), ); } - wfcx.register_wf_obligation( - hir_decl.output.span(), - Some(WellFormedLoc::Param { - function: def_id, - param_idx: sig.inputs().len().try_into().unwrap(), - }), - sig.output().into(), - ); - check_where_clauses(wfcx, span, def_id); check_return_position_impl_trait_in_trait_bounds( @@ -1912,7 +1911,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { } let pred = obligation.predicate; // Match the existing behavior. - if pred.is_global() && !pred.has_late_bound_regions() { + if pred.is_global() && !pred.has_late_bound_vars() { let pred = self.normalize(span, None, pred); let hir_node = tcx.hir().find(self.body_id); diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index bfedf63da97..2e2c1591e9b 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -325,7 +325,9 @@ fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDef // Finally, resolve all regions. let outlives_env = OutlivesEnvironment::new(param_env); - let _ = infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env); + let _ = infcx + .err_ctxt() + .check_region_obligations_and_report_errors(impl_did, &outlives_env); } } _ => { @@ -565,7 +567,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn // Finally, resolve all regions. let outlives_env = OutlivesEnvironment::new(param_env); - let _ = infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env); + let _ = infcx.err_ctxt().check_region_obligations_and_report_errors(impl_did, &outlives_env); CoerceUnsizedInfo { custom_kind: kind } } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 1ff7429e415..cd745ee8cab 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -17,6 +17,7 @@ use crate::astconv::AstConv; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::errors; +use hir::def::DefKind; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey}; @@ -24,8 +25,8 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{GenericParamKind, Node}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::ObligationCause; use rustc_middle::hir::nested_filter; use rustc_middle::ty::query::Providers; use rustc_middle::ty::util::{Discr, IntTypeExt}; @@ -212,7 +213,7 @@ pub(crate) fn placeholder_type_error_diag<'tcx>( is_fn = true; // Check if parent is const or static - let parent_id = tcx.hir().get_parent_node(hir_ty.hir_id); + let parent_id = tcx.hir().parent_id(hir_ty.hir_id); let parent_node = tcx.hir().get(parent_id); is_const_or_static = matches!( @@ -350,7 +351,7 @@ impl<'tcx> ItemCtxt<'tcx> { } pub fn to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { - <dyn AstConv<'_>>::ast_ty_to_ty(self, ast_ty) + self.astconv().ast_ty_to_ty(ast_ty) } pub fn hir_id(&self) -> hir::HirId { @@ -412,8 +413,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx> { if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { - let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item( - self, + let item_substs = self.astconv().create_substs_for_associated_item( span, item_def_id, item_segment, @@ -504,9 +504,9 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { } } - fn normalize_ty(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - // Types in item signatures are not normalized to avoid undue dependencies. - ty + fn probe_adt(&self, _span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> { + // FIXME(#103640): Should we handle the case where `ty` is a projection? + ty.ty_adt_def() } fn set_tainted_by_errors(&self, _: ErrorGuaranteed) { @@ -1108,11 +1108,10 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => { // Do not try to infer the return type for a impl method coming from a trait if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) = - tcx.hir().get(tcx.hir().get_parent_node(hir_id)) + tcx.hir().get_parent(hir_id) && i.of_trait.is_some() { - <dyn AstConv<'_>>::ty_of_fn( - &icx, + icx.astconv().ty_of_fn( hir_id, sig.header.unsafety, sig.header.abi, @@ -1129,15 +1128,9 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _), generics, .. - }) => <dyn AstConv<'_>>::ty_of_fn( - &icx, - hir_id, - header.unsafety, - header.abi, - decl, - Some(generics), - None, - ), + }) => { + icx.astconv().ty_of_fn(hir_id, header.unsafety, header.abi, decl, Some(generics), None) + } ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { let abi = tcx.hir().get_foreign_abi(hir_id); @@ -1195,12 +1188,11 @@ fn infer_return_ty_for_fn_sig<'tcx>( ty::ReErased => tcx.lifetimes.re_static, _ => r, }); - let fn_sig = ty::Binder::dummy(fn_sig); let mut visitor = HirPlaceholderCollector::default(); visitor.visit_ty(ty); let mut diag = bad_placeholder(tcx, visitor.0, "return type"); - let ret_ty = fn_sig.skip_binder().output(); + let ret_ty = fn_sig.output(); if ret_ty.is_suggestable(tcx, false) { diag.span_suggestion( ty.span, @@ -1223,29 +1215,28 @@ fn infer_return_ty_for_fn_sig<'tcx>( Applicability::MachineApplicable, ); } + } else if let Some(sugg) = suggest_impl_trait(tcx, ret_ty, ty.span, hir_id, def_id) { + diag.span_suggestion( + ty.span, + "replace with an appropriate return type", + sugg, + Applicability::MachineApplicable, + ); } else if ret_ty.is_closure() { - // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds - // to prevent the user from getting a papercut while trying to use the unique closure - // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`). diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound"); + } + // Also note how `Fn` traits work just in case! + if ret_ty.is_closure() { diag.note( "for more information on `Fn` traits and closure types, see \ https://doc.rust-lang.org/book/ch13-01-closures.html", ); - } else if let Some(i_ty) = suggest_impl_iterator(tcx, ret_ty, ty.span, hir_id, def_id) { - diag.span_suggestion( - ty.span, - "replace with an appropriate return type", - format!("impl Iterator<Item = {}>", i_ty), - Applicability::MachineApplicable, - ); } diag.emit(); - fn_sig + ty::Binder::dummy(fn_sig) } - None => <dyn AstConv<'_>>::ty_of_fn( - icx, + None => icx.astconv().ty_of_fn( hir_id, sig.header.unsafety, sig.header.abi, @@ -1256,47 +1247,94 @@ fn infer_return_ty_for_fn_sig<'tcx>( } } -fn suggest_impl_iterator<'tcx>( +fn suggest_impl_trait<'tcx>( tcx: TyCtxt<'tcx>, ret_ty: Ty<'tcx>, span: Span, hir_id: hir::HirId, def_id: LocalDefId, -) -> Option<Ty<'tcx>> { - let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) else { return None; }; - let Some(iterator_item) = tcx.get_diagnostic_item(sym::IteratorItem) else { return None; }; - if !tcx - .infer_ctxt() - .build() - .type_implements_trait(iter_trait, [ret_ty], tcx.param_env(def_id)) - .must_apply_modulo_regions() - { - return None; - } - let infcx = tcx.infer_ctxt().build(); - let ocx = ObligationCtxt::new_in_snapshot(&infcx); - // Find the type of `Iterator::Item`. - let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }; - let ty_var = infcx.next_ty_var(origin); - let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection( - ty::ProjectionPredicate { - projection_ty: tcx.mk_alias_ty(iterator_item, tcx.mk_substs([ret_ty.into()].iter())), - term: ty_var.into(), - }, - ))); - // Add `<ret_ty as Iterator>::Item = _` obligation. - ocx.register_obligation(crate::traits::Obligation::misc( - tcx, - span, - hir_id, - tcx.param_env(def_id), - projection, - )); - if ocx.select_where_possible().is_empty() - && let item_ty = infcx.resolve_vars_if_possible(ty_var) - && item_ty.is_suggestable(tcx, false) - { - return Some(item_ty); +) -> Option<String> { + let format_as_assoc: fn(_, _, _, _, _) -> _ = + |tcx: TyCtxt<'tcx>, + _: ty::SubstsRef<'tcx>, + trait_def_id: DefId, + assoc_item_def_id: DefId, + item_ty: Ty<'tcx>| { + let trait_name = tcx.item_name(trait_def_id); + let assoc_name = tcx.item_name(assoc_item_def_id); + Some(format!("impl {trait_name}<{assoc_name} = {item_ty}>")) + }; + let format_as_parenthesized: fn(_, _, _, _, _) -> _ = + |tcx: TyCtxt<'tcx>, + substs: ty::SubstsRef<'tcx>, + trait_def_id: DefId, + _: DefId, + item_ty: Ty<'tcx>| { + let trait_name = tcx.item_name(trait_def_id); + let args_tuple = substs.type_at(1); + let ty::Tuple(types) = *args_tuple.kind() else { return None; }; + if !types.is_suggestable(tcx, false) { + return None; + } + let maybe_ret = + if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") }; + Some(format!( + "impl {trait_name}({}){maybe_ret}", + types.iter().map(|ty| ty.to_string()).collect::<Vec<_>>().join(", ") + )) + }; + + for (trait_def_id, assoc_item_def_id, formatter) in [ + ( + tcx.get_diagnostic_item(sym::Iterator), + tcx.get_diagnostic_item(sym::IteratorItem), + format_as_assoc, + ), + ( + tcx.lang_items().future_trait(), + tcx.get_diagnostic_item(sym::FutureOutput), + format_as_assoc, + ), + (tcx.lang_items().fn_trait(), tcx.lang_items().fn_once_output(), format_as_parenthesized), + ( + tcx.lang_items().fn_mut_trait(), + tcx.lang_items().fn_once_output(), + format_as_parenthesized, + ), + ( + tcx.lang_items().fn_once_trait(), + tcx.lang_items().fn_once_output(), + format_as_parenthesized, + ), + ] { + let Some(trait_def_id) = trait_def_id else { continue; }; + let Some(assoc_item_def_id) = assoc_item_def_id else { continue; }; + if tcx.def_kind(assoc_item_def_id) != DefKind::AssocTy { + continue; + } + let param_env = tcx.param_env(def_id); + let infcx = tcx.infer_ctxt().build(); + let substs = ty::InternalSubsts::for_item(tcx, trait_def_id, |param, _| { + if param.index == 0 { ret_ty.into() } else { infcx.var_for_def(span, param) } + }); + if !infcx.type_implements_trait(trait_def_id, substs, param_env).must_apply_modulo_regions() + { + continue; + } + let ocx = ObligationCtxt::new_in_snapshot(&infcx); + let item_ty = ocx.normalize( + &ObligationCause::misc(span, hir_id), + param_env, + tcx.mk_projection(assoc_item_def_id, substs), + ); + // FIXME(compiler-errors): We may benefit from resolving regions here. + if ocx.select_where_possible().is_empty() + && let item_ty = infcx.resolve_vars_if_possible(item_ty) + && item_ty.is_suggestable(tcx, false) + && let Some(sugg) = formatter(tcx, infcx.resolve_vars_if_possible(substs), trait_def_id, assoc_item_def_id, item_ty) + { + return Some(sugg); + } } None } @@ -1307,8 +1345,7 @@ fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> { match item.kind { hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| { let selfty = tcx.type_of(def_id); - <dyn AstConv<'_>>::instantiate_mono_trait_ref( - &icx, + icx.astconv().instantiate_mono_trait_ref( ast_trait_ref, selfty, check_impl_constness(tcx, impl_.constness, ast_trait_ref), @@ -1438,15 +1475,8 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( hir::Unsafety::Unsafe }; let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let fty = <dyn AstConv<'_>>::ty_of_fn( - &ItemCtxt::new(tcx, def_id), - hir_id, - unsafety, - abi, - decl, - None, - None, - ); + let fty = + ItemCtxt::new(tcx, def_id).astconv().ty_of_fn(hir_id, unsafety, abi, decl, None, None); // Feature gate SIMD types in FFI, since I am not sure that the // ABIs are handled at all correctly. -huonw diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index cb4c35c0ce1..9a5f447c260 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -4,6 +4,7 @@ use hir::{ GenericParamKind, HirId, Node, }; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint; @@ -103,7 +104,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { // `min_const_generics`. Some(parent_def_id.to_def_id()) } else { - let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); + let parent_node = tcx.hir().get_parent(hir_id); match parent_node { // HACK(eddyb) this provides the correct generics for repeat // expressions' count (i.e. `N` in `[x; N]`), and explicit @@ -142,7 +143,20 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { Some(tcx.typeck_root_def_id(def_id)) } Node::Item(item) => match item.kind { - ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => { + ItemKind::OpaqueTy(hir::OpaqueTy { + origin: + hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id), + in_trait, + .. + }) => { + if in_trait { + assert!(matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn)) + } else { + assert!(matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn | DefKind::Fn)) + } + Some(fn_def_id.to_def_id()) + } + ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => { let parent_id = tcx.hir().get_parent_item(hir_id); assert_ne!(parent_id, hir::CRATE_OWNER_ID); debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id); @@ -320,7 +334,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { // provide junk type parameter defs for const blocks. if let Node::AnonConst(_) = node { - let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); + let parent_node = tcx.hir().get_parent(hir_id); if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node { params.push(ty::GenericParamDef { index: next_index(), diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 0542e2c8f50..62eef710ba4 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -26,9 +26,9 @@ fn associated_type_bounds<'tcx>( ); let icx = ItemCtxt::new(tcx, assoc_item_def_id); - let mut bounds = <dyn AstConv<'_>>::compute_bounds(&icx, item_ty, ast_bounds); + let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds); // Associated types are implicitly sized unless a `?Sized` bound is found - <dyn AstConv<'_>>::add_implicitly_sized(&icx, &mut bounds, ast_bounds, None, span); + icx.astconv().add_implicitly_sized(&mut bounds, item_ty, ast_bounds, None, span); let trait_def_id = tcx.parent(assoc_item_def_id); let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id.expect_local()); @@ -44,9 +44,7 @@ fn associated_type_bounds<'tcx>( } }); - let all_bounds = tcx - .arena - .alloc_from_iter(bounds.predicates(tcx, item_ty).into_iter().chain(bounds_from_parent)); + let all_bounds = tcx.arena.alloc_from_iter(bounds.predicates().chain(bounds_from_parent)); debug!("associated_type_bounds({}) = {:?}", tcx.def_path_str(assoc_item_def_id), all_bounds); all_bounds } @@ -72,12 +70,12 @@ fn opaque_type_bounds<'tcx>( }; let icx = ItemCtxt::new(tcx, opaque_def_id); - let mut bounds = <dyn AstConv<'_>>::compute_bounds(&icx, item_ty, ast_bounds); + let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds); // Opaque types are implicitly sized unless a `?Sized` bound is found - <dyn AstConv<'_>>::add_implicitly_sized(&icx, &mut bounds, ast_bounds, None, span); + icx.astconv().add_implicitly_sized(&mut bounds, item_ty, ast_bounds, None, span); debug!(?bounds); - tcx.arena.alloc_from_iter(bounds.predicates(tcx, item_ty)) + tcx.arena.alloc_from_iter(bounds.predicates()) }) } diff --git a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs index fb519d6731d..35f10dc8737 100644 --- a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs +++ b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs @@ -682,7 +682,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { }; let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); // Ensure that the parent of the def is an item, not HRTB - let parent_id = self.tcx.hir().get_parent_node(hir_id); + let parent_id = self.tcx.hir().parent_id(hir_id); if !parent_id.is_owner() { struct_span_err!( self.tcx.sess, @@ -1195,8 +1195,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { // Fresh lifetimes in APIT used to be allowed in async fns and forbidden in // regular fns. if let Some(hir::PredicateOrigin::ImplTrait) = where_bound_origin - && let hir::LifetimeName::Param(_) = lifetime_ref.res - && lifetime_ref.is_anonymous() + && let hir::LifetimeName::Param(param_id) = lifetime_ref.res + && let Some(generics) = self.tcx.hir().get_generics(self.tcx.local_parent(param_id)) + && let Some(param) = generics.params.iter().find(|p| p.def_id == param_id) + && param.is_elided_lifetime() && let hir::IsAsync::NotAsync = self.tcx.asyncness(lifetime_ref.hir_id.owner.def_id) && !self.tcx.features().anonymous_lifetime_in_impl_trait { diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 0943350e2d4..23425355684 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -162,15 +162,15 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP let mut bounds = Bounds::default(); // Params are implicitly sized unless a `?Sized` bound is found - <dyn AstConv<'_>>::add_implicitly_sized( - &icx, + icx.astconv().add_implicitly_sized( &mut bounds, + param_ty, &[], Some((param.def_id, ast_generics.predicates)), param.span, ); trace!(?bounds); - predicates.extend(bounds.predicates(tcx, param_ty)); + predicates.extend(bounds.predicates()); trace!(?predicates); } GenericParamKind::Const { .. } => { @@ -210,22 +210,16 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } let mut bounds = Bounds::default(); - <dyn AstConv<'_>>::add_bounds( - &icx, - ty, - bound_pred.bounds.iter(), - &mut bounds, - bound_vars, - ); - predicates.extend(bounds.predicates(tcx, ty)); + icx.astconv().add_bounds(ty, bound_pred.bounds.iter(), &mut bounds, bound_vars); + predicates.extend(bounds.predicates()); } hir::WherePredicate::RegionPredicate(region_pred) => { - let r1 = <dyn AstConv<'_>>::ast_region_to_region(&icx, ®ion_pred.lifetime, None); + let r1 = icx.astconv().ast_region_to_region(®ion_pred.lifetime, None); predicates.extend(region_pred.bounds.iter().map(|bound| { let (r2, span) = match bound { hir::GenericBound::Outlives(lt) => { - (<dyn AstConv<'_>>::ast_region_to_region(&icx, lt, None), lt.ident.span) + (icx.astconv().ast_region_to_region(lt, None), lt.ident.span) } _ => bug!(), }; @@ -270,7 +264,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP // We create bi-directional Outlives predicates between the original // and the duplicated parameter, to ensure that they do not get out of sync. if let Node::Item(&Item { kind: ItemKind::OpaqueTy(..), .. }) = node { - let opaque_ty_id = tcx.hir().get_parent_node(hir_id); + let opaque_ty_id = tcx.hir().parent_id(hir_id); let opaque_ty_node = tcx.hir().get(opaque_ty_id); let Node::Ty(&Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node else { bug!("unexpected {opaque_ty_node:?}") @@ -278,7 +272,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP debug!(?lifetimes); for (arg, duplicate) in std::iter::zip(lifetimes, ast_generics.params) { let hir::GenericArg::Lifetime(arg) = arg else { bug!() }; - let orig_region = <dyn AstConv<'_>>::ast_region_to_region(&icx, &arg, None); + let orig_region = icx.astconv().ast_region_to_region(&arg, None); if !matches!(orig_region.kind(), ty::ReEarlyBound(..)) { // Only early-bound regions can point to the original generic parameter. continue; @@ -526,17 +520,12 @@ pub(super) fn super_predicates_that_define_assoc_type( // Convert the bounds that follow the colon, e.g., `Bar + Zed` in `trait Foo: Bar + Zed`. let self_param_ty = tcx.types.self_param; let superbounds1 = if let Some(assoc_name) = assoc_name { - <dyn AstConv<'_>>::compute_bounds_that_match_assoc_type( - &icx, - self_param_ty, - bounds, - assoc_name, - ) + icx.astconv().compute_bounds_that_match_assoc_type(self_param_ty, bounds, assoc_name) } else { - <dyn AstConv<'_>>::compute_bounds(&icx, self_param_ty, bounds) + icx.astconv().compute_bounds(self_param_ty, bounds) }; - let superbounds1 = superbounds1.predicates(tcx, self_param_ty); + let superbounds1 = superbounds1.predicates(); // Convert any explicit superbounds in the where-clause, // e.g., `trait Foo where Self: Bar`. @@ -745,5 +734,5 @@ fn predicates_from_bound<'tcx>( ) -> Vec<(ty::Predicate<'tcx>, Span)> { let mut bounds = Bounds::default(); astconv.add_bounds(param_ty, [bound].into_iter(), &mut bounds, bound_vars); - bounds.predicates(astconv.tcx(), param_ty).collect() + bounds.predicates().collect() } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 4bd55a54831..1f9a9f80302 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -28,7 +28,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option< _ => return None, }; - let parent_node_id = tcx.hir().get_parent_node(hir_id); + let parent_node_id = tcx.hir().parent_id(hir_id); let parent_node = tcx.hir().get(parent_node_id); let (generics, arg_idx) = match parent_node { @@ -402,7 +402,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } Node::AnonConst(_) => { - let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); + let parent_node = tcx.hir().get_parent(hir_id); match parent_node { Node::Ty(&Ty { kind: TyKind::Array(_, ref constant), .. }) | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) @@ -445,7 +445,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { .. }, ) if let Node::TraitRef(trait_ref) = - tcx.hir().get(tcx.hir().get_parent_node(binding_id)) + tcx.hir().get_parent(binding_id) && e.hir_id == hir_id => { let Some(trait_def_id) = trait_ref.trait_def_id() else { @@ -472,7 +472,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { Node::TypeBinding( binding @ &TypeBinding { hir_id: binding_id, gen_args, ref kind, .. }, ) if let Node::TraitRef(trait_ref) = - tcx.hir().get(tcx.hir().get_parent_node(binding_id)) + tcx.hir().get_parent(binding_id) && let Some((idx, _)) = gen_args.args.iter().enumerate().find(|(_, arg)| { if let GenericArg::Const(ct) = arg { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index d383fcacb3a..04f5f3f6276 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -300,3 +300,15 @@ pub(crate) struct LinkageType { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[help] +#[diag(hir_analysis_auto_deref_reached_recursion_limit, code = "E0055")] +pub struct AutoDerefReachedRecursionLimit<'a> { + #[primary_span] + #[label] + pub span: Span, + pub ty: Ty<'a>, + pub suggested_limit: rustc_session::Limit, + pub crate_name: Symbol, +} diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 4f9d5826583..2dbfc1bc9a2 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -114,34 +114,46 @@ fn diagnostic_hir_wf_check<'tcx>( // Get the starting `hir::Ty` using our `WellFormedLoc`. // We will walk 'into' this type to try to find // a more precise span for our predicate. - let ty = match loc { + let tys = match loc { WellFormedLoc::Ty(_) => match hir.get(hir_id) { hir::Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Type(ty) => Some(ty), - hir::ImplItemKind::Const(ty, _) => Some(ty), + hir::ImplItemKind::Type(ty) => vec![ty], + hir::ImplItemKind::Const(ty, _) => vec![ty], ref item => bug!("Unexpected ImplItem {:?}", item), }, hir::Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Type(_, ty) => ty, - hir::TraitItemKind::Const(ty, _) => Some(ty), + hir::TraitItemKind::Type(_, ty) => ty.into_iter().collect(), + hir::TraitItemKind::Const(ty, _) => vec![ty], ref item => bug!("Unexpected TraitItem {:?}", item), }, hir::Node::Item(item) => match item.kind { - hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _) => Some(ty), - hir::ItemKind::Impl(ref impl_) => { - assert!(impl_.of_trait.is_none(), "Unexpected trait impl: {:?}", impl_); - Some(impl_.self_ty) - } + hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _) => vec![ty], + hir::ItemKind::Impl(ref impl_) => match &impl_.of_trait { + Some(t) => t + .path + .segments + .last() + .iter() + .flat_map(|seg| seg.args().args) + .filter_map(|arg| { + if let hir::GenericArg::Type(ty) = arg { Some(*ty) } else { None } + }) + .chain([impl_.self_ty]) + .collect(), + None => { + vec![impl_.self_ty] + } + }, ref item => bug!("Unexpected item {:?}", item), }, - hir::Node::Field(field) => Some(field.ty), + hir::Node::Field(field) => vec![field.ty], hir::Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Static(ty, _), .. - }) => Some(*ty), + }) => vec![*ty], hir::Node::GenericParam(hir::GenericParam { kind: hir::GenericParamKind::Type { default: Some(ty), .. }, .. - }) => Some(*ty), + }) => vec![*ty], ref node => bug!("Unexpected node {:?}", node), }, WellFormedLoc::Param { function: _, param_idx } => { @@ -149,16 +161,16 @@ fn diagnostic_hir_wf_check<'tcx>( // Get return type if param_idx as usize == fn_decl.inputs.len() { match fn_decl.output { - hir::FnRetTy::Return(ty) => Some(ty), + hir::FnRetTy::Return(ty) => vec![ty], // The unit type `()` is always well-formed - hir::FnRetTy::DefaultReturn(_span) => None, + hir::FnRetTy::DefaultReturn(_span) => vec![], } } else { - Some(&fn_decl.inputs[param_idx as usize]) + vec![&fn_decl.inputs[param_idx as usize]] } } }; - if let Some(ty) = ty { + for ty in tys { visitor.visit_ty(ty); } visitor.cause diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 1aae9e0bd20..8b9034d9620 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -181,7 +181,8 @@ fn get_impl_substs( let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_hir_id, assumed_wf_types); let outlives_env = OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds); - let _ = infcx.check_region_obligations_and_report_errors(impl1_def_id, &outlives_env); + let _ = + infcx.err_ctxt().check_region_obligations_and_report_errors(impl1_def_id, &outlives_env); let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else { let span = tcx.def_span(impl1_def_id); tcx.sess.emit_err(SubstsOnOverriddenImpl { span }); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 2058832d5fd..ddc5b766881 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -84,6 +84,7 @@ extern crate rustc_middle; pub mod check; pub mod astconv; +pub mod autoderef; mod bounds; mod check_unused; mod coherence; @@ -544,7 +545,7 @@ pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { // scope. This is derived from the enclosing item-like thing. let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id); let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); - <dyn AstConv<'_>>::ast_ty_to_ty(&item_cx, hir_ty) + item_cx.astconv().ast_ty_to_ty(hir_ty) } pub fn hir_trait_to_predicates<'tcx>( @@ -558,8 +559,7 @@ pub fn hir_trait_to_predicates<'tcx>( let env_def_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id); let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); let mut bounds = Bounds::default(); - let _ = <dyn AstConv<'_>>::instantiate_poly_trait_ref( - &item_cx, + let _ = &item_cx.astconv().instantiate_poly_trait_ref( hir_trait, DUMMY_SP, ty::BoundConstness::NotConst, diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 4451db19f5c..574b1e8b485 100644 --- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs @@ -597,11 +597,15 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let span = self.path_segment.ident.span; // insert a suggestion of the form "Y<'a, 'b>" - let ident = self.path_segment.ident.name.to_ident_string(); - let sugg = format!("{}<{}>", ident, suggested_args); + let sugg = format!("<{}>", suggested_args); debug!("sugg: {:?}", sugg); - err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders); + err.span_suggestion_verbose( + span.shrink_to_hi(), + &msg, + sugg, + Applicability::HasPlaceholders, + ); } AngleBrackets::Available => { @@ -643,11 +647,15 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { let span = self.path_segment.ident.span; // insert a suggestion of the form "Y<T, U>" - let ident = self.path_segment.ident.name.to_ident_string(); - let sugg = format!("{}<{}>", ident, suggested_args); + let sugg = format!("<{}>", suggested_args); debug!("sugg: {:?}", sugg); - err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders); + err.span_suggestion_verbose( + span.shrink_to_hi(), + &msg, + sugg, + Applicability::HasPlaceholders, + ); } AngleBrackets::Available => { let gen_args_span = self.gen_args.span().unwrap(); @@ -716,7 +724,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { num = num_trait_generics_except_self, ); - if let Some(parent_node) = self.tcx.hir().find_parent_node(self.path_segment.hir_id) + if let Some(parent_node) = self.tcx.hir().opt_parent_id(self.path_segment.hir_id) && let Some(parent_node) = self.tcx.hir().find(parent_node) && let hir::Node::Expr(expr) = parent_node { match expr.kind { diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 24a67cc14c4..f74c551a45b 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1268,7 +1268,7 @@ impl<'a> State<'a> { hir::InlineAsmOperand::In { reg, ref expr } => { s.word("in"); s.popen(); - s.word(format!("{}", reg)); + s.word(format!("{reg}")); s.pclose(); s.space(); s.print_expr(expr); @@ -1276,7 +1276,7 @@ impl<'a> State<'a> { hir::InlineAsmOperand::Out { reg, late, ref expr } => { s.word(if late { "lateout" } else { "out" }); s.popen(); - s.word(format!("{}", reg)); + s.word(format!("{reg}")); s.pclose(); s.space(); match expr { @@ -1287,7 +1287,7 @@ impl<'a> State<'a> { hir::InlineAsmOperand::InOut { reg, late, ref expr } => { s.word(if late { "inlateout" } else { "inout" }); s.popen(); - s.word(format!("{}", reg)); + s.word(format!("{reg}")); s.pclose(); s.space(); s.print_expr(expr); @@ -1295,7 +1295,7 @@ impl<'a> State<'a> { hir::InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => { s.word(if late { "inlateout" } else { "inout" }); s.popen(); - s.word(format!("{}", reg)); + s.word(format!("{reg}")); s.pclose(); s.space(); s.print_expr(in_expr); @@ -1464,6 +1464,7 @@ impl<'a> State<'a> { } hir::ExprKind::Closure(&hir::Closure { binder, + constness, capture_clause, bound_generic_params, fn_decl, @@ -1474,6 +1475,7 @@ impl<'a> State<'a> { def_id: _, }) => { self.print_closure_binder(binder, bound_generic_params); + self.print_constness(constness); self.print_capture_clause(capture_clause); self.print_closure_params(fn_decl, body); @@ -2272,10 +2274,7 @@ impl<'a> State<'a> { } pub fn print_fn_header_info(&mut self, header: hir::FnHeader) { - match header.constness { - hir::Constness::NotConst => {} - hir::Constness::Const => self.word_nbsp("const"), - } + self.print_constness(header.constness); match header.asyncness { hir::IsAsync::NotAsync => {} @@ -2292,6 +2291,13 @@ impl<'a> State<'a> { self.word("fn") } + pub fn print_constness(&mut self, s: hir::Constness) { + match s { + hir::Constness::NotConst => {} + hir::Constness::Const => self.word_nbsp("const"), + } + } + pub fn print_unsafety(&mut self, s: hir::Unsafety) { match s { hir::Unsafety::Normal => {} diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index ab12cae4e2b..b6f19d3cc68 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -224,14 +224,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut ret_span: MultiSpan = semi_span.into(); ret_span.push_span_label( expr.span, - "this could be implicitly returned but it is a statement, not a \ - tail expression", + "this could be implicitly returned but it is a statement, not a tail expression", ); ret_span.push_span_label(ret, "the `match` arms can conform to this return type"); ret_span.push_span_label( semi_span, - "the `match` is a statement because of this semicolon, consider \ - removing it", + "the `match` is a statement because of this semicolon, consider removing it", ); diag.span_note(ret_span, "you might have meant to return the `match` expression"); diag.tool_only_span_suggestion( @@ -289,15 +287,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, sp: Span) -> Option<(Span, String)> { let node = { - let rslt = self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(hir_id)); + let rslt = self.tcx.hir().parent_id(self.tcx.hir().parent_id(hir_id)); self.tcx.hir().get(rslt) }; if let hir::Node::Block(block) = node { // check that the body's parent is an fn - let parent = self - .tcx - .hir() - .get(self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(block.hir_id))); + let parent = self.tcx.hir().get_parent(self.tcx.hir().parent_id(block.hir_id)); if let (Some(expr), hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })) = (&block.expr, parent) { @@ -526,7 +521,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } })?; - let opaque_ty = self.tcx.mk_opaque(rpit_def_id, substs); if !self.can_coerce(first_ty, expected) || !self.can_coerce(second_ty, expected) { return None; @@ -540,13 +534,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let pred = pred.kind().rebind(match pred.kind().skip_binder() { ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => { - assert_eq!(trait_pred.trait_ref.self_ty(), opaque_ty); + // FIXME(rpitit): This will need to be fixed when we move to associated types + assert!(matches!( + *trait_pred.trait_ref.self_ty().kind(), + ty::Alias(_, ty::AliasTy { def_id, substs, .. }) + if def_id == rpit_def_id && substs == substs + )); ty::PredicateKind::Clause(ty::Clause::Trait( trait_pred.with_self_ty(self.tcx, ty), )) } ty::PredicateKind::Clause(ty::Clause::Projection(mut proj_pred)) => { - assert_eq!(proj_pred.projection_ty.self_ty(), opaque_ty); + assert!(matches!( + *proj_pred.projection_ty.self_ty().kind(), + ty::Alias(_, ty::AliasTy { def_id, substs, .. }) + if def_id == rpit_def_id && substs == substs + )); proj_pred = proj_pred.with_self_ty(self.tcx, ty); ty::PredicateKind::Clause(ty::Clause::Projection(proj_pred)) } diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 41b52a4c4a9..7873257c4e3 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -2,11 +2,11 @@ use super::method::MethodCallee; use super::{FnCtxt, PlaceOp}; +use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind}; use rustc_infer::infer::InferOk; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; -use rustc_trait_selection::autoderef::{Autoderef, AutoderefKind}; use std::iter; diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 829913d278d..8d417290407 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -1,4 +1,4 @@ -use super::method::probe::{IsSuggestion, Mode, ProbeScope}; +use super::method::probe::ProbeScope; use super::method::MethodCallee; use super::{Expectation, FnCtxt, TupleArgumentsFlag}; @@ -8,6 +8,7 @@ use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; +use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::{ infer, traits::{self, Obligation}, @@ -25,7 +26,6 @@ use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_target::spec::abi; -use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -288,7 +288,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_span: Span, ) { let hir = self.tcx.hir(); - let parent_hir_id = hir.get_parent_node(hir_id); + let parent_hir_id = hir.parent_id(hir_id); let parent_node = hir.get(parent_hir_id); if let ( hir::Node::Expr(hir::Expr { @@ -303,7 +303,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { // Actually need to unwrap a few more layers of HIR to get to // the _real_ closure... - let async_closure = hir.get_parent_node(hir.get_parent_node(parent_hir_id)); + let async_closure = hir.parent_id(hir.parent_id(parent_hir_id)); if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), .. @@ -336,7 +336,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, callee_expr: &'tcx hir::Expr<'tcx>, ) -> bool { - let hir_id = self.tcx.hir().get_parent_node(call_expr.hir_id); + let hir_id = self.tcx.hir().parent_id(call_expr.hir_id); let parent_node = self.tcx.hir().get(hir_id); if let ( hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Array(_), .. }), @@ -496,15 +496,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // any strange errors. If it's successful, then we'll do a true // method lookup. let Ok(pick) = self - .probe_for_name( - Mode::MethodCall, + .lookup_probe_for_diagnostic( segment.ident, - IsSuggestion(true), callee_ty, - call_expr.hir_id, + call_expr, // We didn't record the in scope traits during late resolution // so we need to probe AllTraits unfortunately ProbeScope::AllTraits, + expected.only_has_type(self), ) else { return None; }; diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 32f86b8042c..57feefbcab6 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -1,4 +1,7 @@ use crate::coercion::CoerceMany; +use crate::errors::{ + LangStartIncorrectNumberArgs, LangStartIncorrectParam, LangStartIncorrectRetTy, +}; use crate::gather_locals::GatherLocalsVisitor; use crate::FnCtxt; use crate::GeneratorTypes; @@ -9,8 +12,9 @@ use rustc_hir::lang_items::LangItem; use rustc_hir_analysis::check::fn_maybe_err; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::RegionVariableOrigin; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Binder, Ty, TyCtxt}; use rustc_span::def_id::LocalDefId; +use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use std::cell::RefCell; @@ -168,6 +172,10 @@ pub(super) fn check_fn<'a, 'tcx>( check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty); } + if let Some(lang_start_defid) = tcx.lang_items().start_fn() && lang_start_defid == hir.local_def_id(fn_id).to_def_id() { + check_lang_start_fn(tcx, fn_sig, decl, fn_def_id); + } + gen_ty } @@ -223,3 +231,126 @@ fn check_panic_info_fn( tcx.sess.span_err(span, "should have no const parameters"); } } + +fn check_lang_start_fn<'tcx>( + tcx: TyCtxt<'tcx>, + fn_sig: ty::FnSig<'tcx>, + decl: &'tcx hir::FnDecl<'tcx>, + def_id: LocalDefId, +) { + let inputs = fn_sig.inputs(); + + let arg_count = inputs.len(); + if arg_count != 4 { + tcx.sess.emit_err(LangStartIncorrectNumberArgs { + params_span: tcx.def_span(def_id), + found_param_count: arg_count, + }); + } + + // only check args if they should exist by checking the count + // note: this does not handle args being shifted or their order swapped very nicely + // but it's a lang item, users shouldn't frequently encounter this + + // first arg is `main: fn() -> T` + if let Some(&main_arg) = inputs.get(0) { + // make a Ty for the generic on the fn for diagnostics + // FIXME: make the lang item generic checks check for the right generic *kind* + // for example `start`'s generic should be a type parameter + let generics = tcx.generics_of(def_id); + let fn_generic = generics.param_at(0, tcx); + let generic_tykind = + ty::Param(ty::ParamTy { index: fn_generic.index, name: fn_generic.name }); + let generic_ty = tcx.mk_ty(generic_tykind); + let expected_fn_sig = + tcx.mk_fn_sig([].iter(), &generic_ty, false, hir::Unsafety::Normal, Abi::Rust); + let expected_ty = tcx.mk_fn_ptr(Binder::dummy(expected_fn_sig)); + + // we emit the same error to suggest changing the arg no matter what's wrong with the arg + let emit_main_fn_arg_err = || { + tcx.sess.emit_err(LangStartIncorrectParam { + param_span: decl.inputs[0].span, + param_num: 1, + expected_ty: expected_ty, + found_ty: main_arg, + }); + }; + + if let ty::FnPtr(main_fn_sig) = main_arg.kind() { + let main_fn_inputs = main_fn_sig.inputs(); + if main_fn_inputs.iter().count() != 0 { + emit_main_fn_arg_err(); + } + + let output = main_fn_sig.output(); + output.map_bound(|ret_ty| { + // if the output ty is a generic, it's probably the right one + if !matches!(ret_ty.kind(), ty::Param(_)) { + emit_main_fn_arg_err(); + } + }); + } else { + emit_main_fn_arg_err(); + } + } + + // second arg is isize + if let Some(&argc_arg) = inputs.get(1) { + if argc_arg != tcx.types.isize { + tcx.sess.emit_err(LangStartIncorrectParam { + param_span: decl.inputs[1].span, + param_num: 2, + expected_ty: tcx.types.isize, + found_ty: argc_arg, + }); + } + } + + // third arg is `*const *const u8` + if let Some(&argv_arg) = inputs.get(2) { + let mut argv_is_okay = false; + if let ty::RawPtr(outer_ptr) = argv_arg.kind() { + if outer_ptr.mutbl.is_not() { + if let ty::RawPtr(inner_ptr) = outer_ptr.ty.kind() { + if inner_ptr.mutbl.is_not() && inner_ptr.ty == tcx.types.u8 { + argv_is_okay = true; + } + } + } + } + + if !argv_is_okay { + let inner_ptr_ty = + tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: tcx.types.u8 }); + let expected_ty = + tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: inner_ptr_ty }); + tcx.sess.emit_err(LangStartIncorrectParam { + param_span: decl.inputs[2].span, + param_num: 3, + expected_ty, + found_ty: argv_arg, + }); + } + } + + // fourth arg is `sigpipe: u8` + if let Some(&sigpipe_arg) = inputs.get(3) { + if sigpipe_arg != tcx.types.u8 { + tcx.sess.emit_err(LangStartIncorrectParam { + param_span: decl.inputs[3].span, + param_num: 4, + expected_ty: tcx.types.u8, + found_ty: sigpipe_arg, + }); + } + } + + // output type is isize + if fn_sig.output() != tcx.types.isize { + tcx.sess.emit_err(LangStartIncorrectRetTy { + ret_span: decl.output.span(), + expected_ty: tcx.types.isize, + found_ty: fn_sig.output(), + }); + } +} diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 5bd02dff73b..399702fd41a 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -13,7 +13,7 @@ use rustc_infer::infer::{InferOk, InferResult}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TypeSuperVisitable, TypeVisitor}; use rustc_span::source_map::Span; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; @@ -21,6 +21,7 @@ use rustc_trait_selection::traits::error_reporting::ArgKind; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use std::cmp; use std::iter; +use std::ops::ControlFlow; /// What signature do we *expect* the closure to have from context? #[derive(Debug, Clone, TypeFoldable, TypeVisitable)] @@ -54,7 +55,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // closure sooner rather than later, so first examine the expected // type, and see if can glean a closure kind from there. let (expected_sig, expected_kind) = match expected.to_option(self) { - Some(ty) => self.deduce_expectations_from_expected_type(ty), + Some(ty) => self.deduce_closure_signature(ty), None => (None, None), }; let body = self.tcx.hir().body(closure.body); @@ -162,13 +163,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Given the expected type, figures out what it can about this closure we /// are about to type check: #[instrument(skip(self), level = "debug")] - fn deduce_expectations_from_expected_type( + fn deduce_closure_signature( &self, expected_ty: Ty<'tcx>, ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) { match *expected_ty.kind() { ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => self - .deduce_signature_from_predicates( + .deduce_closure_signature_from_predicates( + expected_ty, self.tcx.bound_explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs), ), ty::Dynamic(ref object_type, ..) => { @@ -181,7 +183,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .and_then(|did| self.tcx.fn_trait_kind_from_def_id(did)); (sig, kind) } - ty::Infer(ty::TyVar(vid)) => self.deduce_signature_from_predicates( + ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates( + self.tcx.mk_ty_var(self.root_var(vid)), self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)), ), ty::FnPtr(sig) => { @@ -192,8 +195,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn deduce_signature_from_predicates( + fn deduce_closure_signature_from_predicates( &self, + expected_ty: Ty<'tcx>, predicates: impl DoubleEndedIterator<Item = (ty::Predicate<'tcx>, Span)>, ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) { let mut expected_sig = None; @@ -214,13 +218,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if expected_sig.is_none() && let ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) = bound_predicate.skip_binder() { - expected_sig = self.normalize( + let inferred_sig = self.normalize( obligation.cause.span, self.deduce_sig_from_projection( Some(obligation.cause.span), bound_predicate.rebind(proj_predicate), ), ); + // Make sure that we didn't infer a signature that mentions itself. + // This can happen when we elaborate certain supertrait bounds that + // mention projections containing the `Self` type. See #105401. + struct MentionsTy<'tcx> { + expected_ty: Ty<'tcx>, + } + impl<'tcx> TypeVisitor<'tcx> for MentionsTy<'tcx> { + type BreakTy = (); + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + if t == self.expected_ty { + ControlFlow::BREAK + } else { + t.super_visit_with(self) + } + } + } + if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { + expected_sig = inferred_sig; + } } // Even if we can't infer the full signature, we may be able to @@ -623,14 +647,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), bound_vars, ); - // Astconv can't normalize inputs or outputs with escaping bound vars, - // so normalize them here, after we've wrapped them in a binder. - let result = self.normalize(self.tcx.hir().span(hir_id), result); let c_result = self.inh.infcx.canonicalize_response(result); self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result); - result + // Normalize only after registering in `user_provided_sigs`. + self.normalize(self.tcx.hir().span(hir_id), result) } /// Invoked when we are translating the generator that results diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 3fb14e31ea1..7e1c0faa453 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -13,7 +13,7 @@ //! useful for freezing mut things (that is, when the expected type is &T //! but you have &mut T) and also for avoiding the linearity //! of mut things (when the expected is &mut T and you have &mut T). See -//! the various `src/test/ui/coerce/*.rs` tests for +//! the various `tests/ui/coerce/*.rs` tests for //! examples of where this is useful. //! //! ## Subtle note @@ -1547,7 +1547,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { err.span_label(cause.span, "return type is not `()`"); } ObligationCauseCode::BlockTailExpression(blk_id) => { - let parent_id = fcx.tcx.hir().get_parent_node(blk_id); + let parent_id = fcx.tcx.hir().parent_id(blk_id); err = self.report_return_mismatched_types( cause, expected, @@ -1578,7 +1578,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { None, ); if !fcx.tcx.features().unsized_locals { - let id = fcx.tcx.hir().get_parent_node(id); + let id = fcx.tcx.hir().parent_id(id); unsized_return = self.is_return_ty_unsized(fcx, id); } } @@ -1668,7 +1668,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { let mut pointing_at_return_type = false; let mut fn_output = None; - let parent_id = fcx.tcx.hir().get_parent_node(id); + let parent_id = fcx.tcx.hir().parent_id(id); let parent = fcx.tcx.hir().get(parent_id); if let Some(expr) = expression && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { body, .. }), .. }) = parent @@ -1807,7 +1807,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // Get the return type. && let hir::TyKind::OpaqueDef(..) = ty.kind { - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(fcx, ty); + let ty = fcx.astconv().ast_ty_to_ty( ty); // Get the `impl Trait`'s `DefId`. if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind() // Get the `impl Trait`'s `Item` so that we can get its trait bounds and @@ -1866,7 +1866,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fn is_return_ty_unsized<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool { if let Some((fn_decl, _)) = fcx.get_fn_decl(blk_id) && let hir::FnRetTy::Return(ty) = fn_decl.output - && let ty = <dyn AstConv<'_>>::ast_ty_to_ty(fcx, ty) + && let ty = fcx.astconv().ast_ty_to_ty( ty) && let ty::Dynamic(..) = ty.kind() { return true; diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 1360383a75a..6c128d0aa1a 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,9 +1,11 @@ use crate::FnCtxt; use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::MultiSpan; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::CtorKind; +use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; use rustc_infer::infer::InferOk; @@ -11,11 +13,14 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut}; +use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder}; +use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; +use rustc_middle::ty::relate::TypeRelation; +use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut, TypeVisitable}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span}; use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::method_chain::CollectAllMismatches; use rustc_trait_selection::traits::ObligationCause; use super::method::probe; @@ -40,7 +45,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.annotate_alternative_method_deref(err, expr, error); // Use `||` to give these suggestions a precedence - let _ = self.suggest_missing_parentheses(err, expr) + let suggested = self.suggest_missing_parentheses(err, expr) || self.suggest_remove_last_method_call(err, expr, expected) || self.suggest_associated_const(err, expr, expected) || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr) @@ -52,8 +57,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty) || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected) || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) + || self.suggest_clone_for_ref(err, expr, expr_ty, expected) || self.suggest_into(err, expr, expr_ty, expected) || self.suggest_floating_point_literal(err, expr, expected); + if !suggested { + self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected); + } } pub fn emit_coerce_suggestions( @@ -75,6 +84,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.note_need_for_fn_pointer(err, expected, expr_ty); self.note_internal_mutation_in_method(err, expr, expected, expr_ty); self.check_for_range_as_method_call(err, expr, expr_ty, expected); + self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected); } /// Requires that the two types unify, and prints an error message if @@ -205,13 +215,223 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (expected, Some(err)) } + pub fn point_at_expr_source_of_inferred_type( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + found: Ty<'tcx>, + expected: Ty<'tcx>, + ) -> bool { + let map = self.tcx.hir(); + + let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind else { return false; }; + let [hir::PathSegment { ident, args: None, .. }] = p.segments else { return false; }; + let hir::def::Res::Local(hir_id) = p.res else { return false; }; + let Some(hir::Node::Pat(pat)) = map.find(hir_id) else { return false; }; + let Some(hir::Node::Local(hir::Local { + ty: None, + init: Some(init), + .. + })) = map.find_parent(pat.hir_id) else { return false; }; + let Some(ty) = self.node_ty_opt(init.hir_id) else { return false; }; + if ty.is_closure() || init.span.overlaps(expr.span) || pat.span.from_expansion() { + return false; + } + + // Locate all the usages of the relevant binding. + struct FindExprs<'hir> { + hir_id: hir::HirId, + uses: Vec<&'hir hir::Expr<'hir>>, + } + impl<'v> Visitor<'v> for FindExprs<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = ex.kind + && let hir::def::Res::Local(hir_id) = path.res + && hir_id == self.hir_id + { + self.uses.push(ex); + } + hir::intravisit::walk_expr(self, ex); + } + } + + let mut expr_finder = FindExprs { hir_id, uses: vec![] }; + let id = map.get_parent_item(hir_id); + let hir_id: hir::HirId = id.into(); + + let Some(node) = map.find(hir_id) else { return false; }; + let Some(body_id) = node.body_id() else { return false; }; + let body = map.body(body_id); + expr_finder.visit_expr(body.value); + // Hack to make equality checks on types with inference variables and regions useful. + let mut eraser = BottomUpFolder { + tcx: self.tcx, + lt_op: |_| self.tcx.lifetimes.re_erased, + ct_op: |c| c, + ty_op: |t| match *t.kind() { + ty::Infer(ty::TyVar(vid)) => self.tcx.mk_ty_infer(ty::TyVar(self.root_var(vid))), + ty::Infer(ty::IntVar(_)) => { + self.tcx.mk_ty_infer(ty::IntVar(ty::IntVid { index: 0 })) + } + ty::Infer(ty::FloatVar(_)) => { + self.tcx.mk_ty_infer(ty::FloatVar(ty::FloatVid { index: 0 })) + } + _ => t, + }, + }; + let mut prev = eraser.fold_ty(ty); + let mut prev_span = None; + + for binding in expr_finder.uses { + // In every expression where the binding is referenced, we will look at that + // expression's type and see if it is where the incorrect found type was fully + // "materialized" and point at it. We will also try to provide a suggestion there. + if let Some(hir::Node::Expr(expr) + | hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr), + .. + })) = &map.find_parent(binding.hir_id) + && let hir::ExprKind::MethodCall(segment, rcvr, args, _span) = expr.kind + && rcvr.hir_id == binding.hir_id + && let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(expr.hir_id) + { + // We special case methods, because they can influence inference through the + // call's arguments and we can provide a more explicit span. + let sig = self.tcx.fn_sig(def_id); + let def_self_ty = sig.input(0).skip_binder(); + let rcvr_ty = self.node_ty(rcvr.hir_id); + // Get the evaluated type *after* calling the method call, so that the influence + // of the arguments can be reflected in the receiver type. The receiver + // expression has the type *before* theis analysis is done. + let ty = match self.lookup_probe_for_diagnostic( + segment.ident, + rcvr_ty, + expr, + probe::ProbeScope::TraitsInScope, + None, + ) { + Ok(pick) => pick.self_ty, + Err(_) => rcvr_ty, + }; + // Remove one layer of references to account for `&mut self` and + // `&self`, so that we can compare it against the binding. + let (ty, def_self_ty) = match (ty.kind(), def_self_ty.kind()) { + (ty::Ref(_, ty, a), ty::Ref(_, self_ty, b)) if a == b => (*ty, *self_ty), + _ => (ty, def_self_ty), + }; + let mut param_args = FxHashMap::default(); + let mut param_expected = FxHashMap::default(); + let mut param_found = FxHashMap::default(); + if self.can_eq(self.param_env, ty, found).is_ok() { + // We only point at the first place where the found type was inferred. + for (i, param_ty) in sig.inputs().skip_binder().iter().skip(1).enumerate() { + if def_self_ty.contains(*param_ty) && let ty::Param(_) = param_ty.kind() { + // We found an argument that references a type parameter in `Self`, + // so we assume that this is the argument that caused the found + // type, which we know already because of `can_eq` above was first + // inferred in this method call. + let arg = &args[i]; + let arg_ty = self.node_ty(arg.hir_id); + err.span_label( + arg.span, + &format!( + "this is of type `{arg_ty}`, which causes `{ident}` to be \ + inferred as `{ty}`", + ), + ); + param_args.insert(param_ty, (arg, arg_ty)); + } + } + } + + // Here we find, for a type param `T`, the type that `T` is in the current + // method call *and* in the original expected type. That way, we can see if we + // can give any structured suggestion for the function argument. + let mut c = CollectAllMismatches { + infcx: &self.infcx, + param_env: self.param_env, + errors: vec![], + }; + let _ = c.relate(def_self_ty, ty); + for error in c.errors { + if let TypeError::Sorts(error) = error { + param_found.insert(error.expected, error.found); + } + } + c.errors = vec![]; + let _ = c.relate(def_self_ty, expected); + for error in c.errors { + if let TypeError::Sorts(error) = error { + param_expected.insert(error.expected, error.found); + } + } + for (param, (arg, arg_ty)) in param_args.iter() { + let Some(expected) = param_expected.get(param) else { continue; }; + let Some(found) = param_found.get(param) else { continue; }; + if self.can_eq(self.param_env, *arg_ty, *found).is_err() { continue; } + self.emit_coerce_suggestions(err, arg, *found, *expected, None, None); + } + + let ty = eraser.fold_ty(ty); + if ty.references_error() { + break; + } + if ty != prev + && param_args.is_empty() + && self.can_eq(self.param_env, ty, found).is_ok() + { + // We only point at the first place where the found type was inferred. + err.span_label( + segment.ident.span, + with_forced_trimmed_paths!(format!( + "here the type of `{ident}` is inferred to be `{ty}`", + )), + ); + break; + } else if !param_args.is_empty() { + break; + } + prev = ty; + } else { + let ty = eraser.fold_ty(self.node_ty(binding.hir_id)); + if ty.references_error() { + break; + } + if ty != prev + && let Some(span) = prev_span + && self.can_eq(self.param_env, ty, found).is_ok() + { + // We only point at the first place where the found type was inferred. + // We use the *previous* span because if the type is known *here* it means + // it was *evaluated earlier*. We don't do this for method calls because we + // evaluate the method's self type eagerly, but not in any other case. + err.span_label( + span, + with_forced_trimmed_paths!(format!( + "here the type of `{ident}` is inferred to be `{ty}`", + )), + ); + break; + } + prev = ty; + } + if binding.hir_id == expr.hir_id { + // Do not look at expressions that come after the expression we were originally + // evaluating and had a type error. + break; + } + prev_span = Some(binding.span); + } + true + } + fn annotate_expected_due_to_let_ty( &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, error: Option<TypeError<'tcx>>, ) { - let parent = self.tcx.hir().get_parent_node(expr.hir_id); + let parent = self.tcx.hir().parent_id(expr.hir_id); match (self.tcx.hir().find(parent), error) { (Some(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. })), _) if init.hir_id == expr.hir_id => @@ -258,10 +478,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Path { res: hir::def::Res::Local(hir_id), .. }, )) => { if let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(*hir_id) { - let parent = self.tcx.hir().get_parent_node(pat.hir_id); primary_span = pat.span; secondary_span = pat.span; - match self.tcx.hir().find(parent) { + match self.tcx.hir().find_parent(pat.hir_id) { Some(hir::Node::Local(hir::Local { ty: Some(ty), .. })) => { primary_span = ty.span; post_message = " type"; @@ -326,7 +545,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, error: Option<TypeError<'tcx>>, ) { - let parent = self.tcx.hir().get_parent_node(expr.hir_id); + let parent = self.tcx.hir().parent_id(expr.hir_id); let Some(TypeError::Sorts(ExpectedFound { expected, .. })) = error else {return;}; let Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(lhs, rhs, _), .. @@ -339,19 +558,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(self_ty) = self.typeck_results.borrow().expr_ty_adjusted_opt(base) else { return; }; let Ok(pick) = self - .probe_for_name( - probe::Mode::MethodCall, + .lookup_probe_for_diagnostic( path.ident, - probe::IsSuggestion(true), self_ty, - deref.hir_id, + deref, probe::ProbeScope::TraitsInScope, + None, ) else { return; }; let in_scope_methods = self.probe_for_name_many( probe::Mode::MethodCall, path.ident, + Some(expected), probe::IsSuggestion(true), self_ty, deref.hir_id, @@ -363,6 +582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let all_methods = self.probe_for_name_many( probe::Mode::MethodCall, path.ident, + Some(expected), probe::IsSuggestion(true), self_ty, deref.hir_id, @@ -510,7 +730,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Unroll desugaring, to make sure this works for `for` loops etc. loop { - parent = self.tcx.hir().get_parent_node(id); + parent = self.tcx.hir().parent_id(id); if let Some(parent_span) = self.tcx.hir().opt_span(parent) { if parent_span.find_ancestor_inside(expr.span).is_some() { // The parent node is part of the same span, so is the result of the @@ -790,12 +1010,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - let local_parent = self.tcx.hir().get_parent_node(local_id); + let local_parent = self.tcx.hir().parent_id(local_id); let Some(Node::Param(hir::Param { hir_id: param_hir_id, .. })) = self.tcx.hir().find(local_parent) else { return None; }; - let param_parent = self.tcx.hir().get_parent_node(*param_hir_id); + let param_parent = self.tcx.hir().parent_id(*param_hir_id); let Some(Node::Expr(hir::Expr { hir_id: expr_hir_id, kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }), @@ -804,7 +1024,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; }; - let expr_parent = self.tcx.hir().get_parent_node(*expr_hir_id); + let expr_parent = self.tcx.hir().parent_id(*expr_hir_id); let hir = self.tcx.hir().find(expr_parent); let closure_params_len = closure_fn_decl.inputs.len(); let ( @@ -857,7 +1077,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }?; - match hir.find(hir.get_parent_node(expr.hir_id))? { + match hir.find_parent(expr.hir_id)? { Node::ExprField(field) => { if field.ident.name == local.name && field.is_shorthand { return Some(local.name); @@ -883,7 +1103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns whether the given expression is an `else if`. pub(crate) fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool { if let hir::ExprKind::If(..) = expr.kind { - let parent_id = self.tcx.hir().get_parent_node(expr.hir_id); + let parent_id = self.tcx.hir().parent_id(expr.hir_id); if let Some(Node::Expr(hir::Expr { kind: hir::ExprKind::If(_, _, Some(else_expr)), .. @@ -1040,7 +1260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(..), .. - })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) + })) = self.tcx.hir().find_parent(expr.hir_id) { if mutability.is_mut() { // Suppressing this diagnostic, we'll properly print it in `check_expr_assign` @@ -1162,7 +1382,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } // If we've reached our target type with just removing `&`, then just print now. - if steps == 0 { + if steps == 0 && !remove.trim().is_empty() { return Some(( prefix_span, format!("consider removing the `{}`", remove.trim()), @@ -1221,6 +1441,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { (prefix_span, format!("{}{}", prefix, "*".repeat(steps))) }; + if suggestion.trim().is_empty() { + return None; + } return Some(( span, @@ -1267,9 +1490,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut sugg = vec![]; - if let Some(hir::Node::ExprField(field)) = - self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) - { + if let Some(hir::Node::ExprField(field)) = self.tcx.hir().find_parent(expr.hir_id) { // `expr` is a literal field for a struct, only suggest if appropriate if field.is_shorthand { // This is a field literal @@ -1613,7 +1834,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn check_for_range_as_method_call( &self, err: &mut Diagnostic, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, ) { @@ -1625,16 +1846,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [start, end], _, ) = expr.kind else { return; }; - let parent = self.tcx.hir().get_parent_node(expr.hir_id); + let parent = self.tcx.hir().parent_id(expr.hir_id); if let Some(hir::Node::ExprField(_)) = self.tcx.hir().find(parent) { // Ignore `Foo { field: a..Default::default() }` return; } let mut expr = end.expr; + let mut expectation = Some(expected_ty); while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind { // Getting to the root receiver and asserting it is a fn call let's us ignore cases in - // `src/test/ui/methods/issues/issue-90315.stderr`. + // `tests/ui/methods/issues/issue-90315.stderr`. expr = rcvr; + // If we have more than one layer of calls, then the expected ty + // cannot guide the method probe. + expectation = None; } let hir::ExprKind::Call(method_name, _) = expr.kind else { return; }; let ty::Adt(adt, _) = checked_ty.kind() else { return; }; @@ -1650,13 +1875,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else { return; }; let [hir::PathSegment { ident, .. }] = p.segments else { return; }; let self_ty = self.typeck_results.borrow().expr_ty(start.expr); - let Ok(_pick) = self.probe_for_name( - probe::Mode::MethodCall, + let Ok(_pick) = self.lookup_probe_for_diagnostic( *ident, - probe::IsSuggestion(true), self_ty, - expr.hir_id, + expr, probe::ProbeScope::AllTraits, + expectation, ) else { return; }; let mut sugg = "."; let mut span = start.expr.span.between(end.expr.span); @@ -1673,4 +1897,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } + + /// Identify when the type error is because `()` is found in a binding that was assigned a + /// block without a tail expression. + fn check_for_binding_assigned_block_without_tail_expression( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + checked_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) { + if !checked_ty.is_unit() { + return; + } + let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else { return; }; + let hir::def::Res::Local(hir_id) = path.res else { return; }; + let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else { + return; + }; + let Some(hir::Node::Local(hir::Local { + ty: None, + init: Some(init), + .. + })) = self.tcx.hir().find_parent(pat.hir_id) else { return; }; + let hir::ExprKind::Block(block, None) = init.kind else { return; }; + if block.expr.is_some() { + return; + } + let [.., stmt] = block.stmts else { + err.span_label(block.span, "this empty block is missing a tail expression"); + return; + }; + let hir::StmtKind::Semi(tail_expr) = stmt.kind else { return; }; + let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else { return; }; + if self.can_eq(self.param_env, expected_ty, ty).is_ok() { + err.span_suggestion_short( + stmt.span.with_lo(tail_expr.span.hi()), + "remove this semicolon", + "", + Applicability::MachineApplicable, + ); + } else { + err.span_label(block.span, "this block is missing a tail expression"); + } + } } diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 507272fdec5..5b4fd5e4a52 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -172,3 +172,36 @@ impl AddToDiagnostic for TypeMismatchFruTypo { ); } } + +#[derive(Diagnostic)] +#[diag(hir_typeck_lang_start_incorrect_number_params)] +#[note(hir_typeck_lang_start_incorrect_number_params_note_expected_count)] +#[note(hir_typeck_lang_start_expected_sig_note)] +pub struct LangStartIncorrectNumberArgs { + #[primary_span] + pub params_span: Span, + pub found_param_count: usize, +} + +#[derive(Diagnostic)] +#[diag(hir_typeck_lang_start_incorrect_param)] +pub struct LangStartIncorrectParam<'tcx> { + #[primary_span] + #[suggestion(style = "short", code = "{expected_ty}", applicability = "machine-applicable")] + pub param_span: Span, + + pub param_num: usize, + pub expected_ty: Ty<'tcx>, + pub found_ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag(hir_typeck_lang_start_incorrect_ret_ty)] +pub struct LangStartIncorrectRetTy<'tcx> { + #[primary_span] + #[suggestion(style = "short", code = "{expected_ty}", applicability = "machine-applicable")] + pub ret_span: Span, + + pub expected_ty: Ty<'tcx>, + pub found_ty: Ty<'tcx>, +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index b8b4e873663..ba1a5a0cb03 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -234,6 +234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) => self.check_expr_path(qpath, expr, args), _ => self.check_expr_kind(expr, expected), }); + let ty = self.resolve_vars_if_possible(ty); // Warn for non-block expressions with diverging children. match expr.kind { @@ -350,7 +351,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ExprKind::Struct(qpath, fields, ref base_expr) => { self.check_expr_struct(expr, expected, qpath, fields, base_expr) } - ExprKind::Field(base, field) => self.check_field(expr, &base, field), + ExprKind::Field(base, field) => self.check_field(expr, &base, field, expected), ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr), ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src), hir::ExprKind::Err => tcx.ty_error(), @@ -920,7 +921,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { original_expr_id: HirId, then: impl FnOnce(&hir::Expr<'_>), ) { - let mut parent = self.tcx.hir().get_parent_node(original_expr_id); + let mut parent = self.tcx.hir().parent_id(original_expr_id); while let Some(node) = self.tcx.hir().find(parent) { match node { hir::Node::Expr(hir::Expr { @@ -943,7 +944,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) => { // Check if our original expression is a child of the condition of a while loop let expr_is_ancestor = std::iter::successors(Some(original_expr_id), |id| { - self.tcx.hir().find_parent_node(*id) + self.tcx.hir().opt_parent_id(*id) }) .take_while(|id| *id != parent) .any(|id| id == expr.hir_id); @@ -959,7 +960,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | hir::Node::TraitItem(_) | hir::Node::Crate(_) => break, _ => { - parent = self.tcx.hir().get_parent_node(parent); + parent = self.tcx.hir().parent_id(parent); } } } @@ -1083,7 +1084,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Do not suggest `if let x = y` as `==` is way more likely to be the intention. let hir = self.tcx.hir(); if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) = - hir.get(hir.get_parent_node(hir.get_parent_node(expr.hir_id))) + hir.get_parent(hir.parent_id(expr.hir_id)) { err.span_suggestion_verbose( expr.span.shrink_to_lo(), @@ -1243,6 +1244,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { SelfSource::MethodCall(rcvr), error, Some((rcvr, args)), + expected, ) { err.emit(); } @@ -2185,6 +2187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, base: &'tcx hir::Expr<'tcx>, field: Ident, + expected: Expectation<'tcx>, ) -> Ty<'tcx> { debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field); let base_ty = self.check_expr(base); @@ -2216,7 +2219,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None); return field_ty; } - private_candidate = Some((adjustments, base_def.did(), field_ty)); + private_candidate = Some((adjustments, base_def.did())); } } ty::Tuple(tys) => { @@ -2239,16 +2242,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - if let Some((adjustments, did, field_ty)) = private_candidate { + if let Some((adjustments, did)) = private_candidate { // (#90483) apply adjustments to avoid ExprUseVisitor from // creating erroneous projection. self.apply_adjustments(base, adjustments); - self.ban_private_field_access(expr, base_ty, field, did); - return field_ty; + self.ban_private_field_access(expr, base_ty, field, did, expected.only_has_type(self)); + return self.tcx().ty_error(); } if field.name == kw::Empty { - } else if self.method_exists(field, base_ty, expr.hir_id, true) { + } else if self.method_exists( + field, + base_ty, + expr.hir_id, + true, + expected.only_has_type(self), + ) { self.ban_take_value_of_method(expr, base_ty, field); } else if !base_ty.is_primitive_ty() { self.ban_nonexisting_field(field, base, expr, base_ty); @@ -2422,10 +2431,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn ban_private_field_access( &self, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, expr_t: Ty<'tcx>, field: Ident, base_did: DefId, + return_ty: Option<Ty<'tcx>>, ) { let struct_path = self.tcx().def_path_str(base_did); let kind_name = self.tcx().def_kind(base_did).descr(base_did); @@ -2437,7 +2447,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(field.span, "private field"); // Also check if an accessible method exists, which is often what is meant. - if self.method_exists(field, expr_t, expr.hir_id, false) && !self.expr_in_place(expr.hir_id) + if self.method_exists(field, expr_t, expr.hir_id, false, return_ty) + && !self.expr_in_place(expr.hir_id) { self.suggest_method_call( &mut err, @@ -2451,7 +2462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } - fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: Ident) { + fn ban_take_value_of_method(&self, expr: &hir::Expr<'tcx>, expr_t: Ty<'tcx>, field: Ident) { let mut err = type_error_struct!( self.tcx().sess, field.span, @@ -2462,7 +2473,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(field.span, "method, not a field"); let expr_is_call = if let hir::Node::Expr(hir::Expr { kind: ExprKind::Call(callee, _args), .. }) = - self.tcx.hir().get(self.tcx.hir().get_parent_node(expr.hir_id)) + self.tcx.hir().get_parent(expr.hir_id) { expr.hir_id == callee.hir_id } else { diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index ac6b0924ab5..2cc7b357c0a 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -308,7 +308,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { if relationship.self_in_trait && relationship.output { // This case falls back to () to ensure that the code pattern in - // src/test/ui/never_type/fallback-closure-ret.rs continues to + // tests/ui/never_type/fallback-closure-ret.rs continues to // compile when never_type_fallback is enabled. // // This rule is not readily explainable from first principles, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 150e917c739..8570715b41e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1,7 +1,7 @@ use crate::callee::{self, DeferredCallResolution}; use crate::method::{self, MethodCallee, SelfSource}; use crate::rvalue_scopes; -use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; +use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, RawTy}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; @@ -10,6 +10,9 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, GenericArg, Node, QPath}; +use rustc_hir_analysis::astconv::generics::{ + check_generic_arg_count_for_call, create_substs_for_generic_args, +}; use rustc_hir_analysis::astconv::{ AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, IsMethodCall, PathSeg, @@ -24,7 +27,7 @@ use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, UserType, }; -use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts}; +use rustc_middle::ty::{GenericArgKind, SubstsRef, UserSelfTy, UserSubsts}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; @@ -161,47 +164,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) { self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id))); self.write_substs(hir_id, method.substs); - - // When the method is confirmed, the `method.substs` includes - // parameters from not just the method, but also the impl of - // the method -- in particular, the `Self` type will be fully - // resolved. However, those are not something that the "user - // specified" -- i.e., those types come from the inferred type - // of the receiver, not something the user wrote. So when we - // create the user-substs, we want to replace those earlier - // types with just the types that the user actually wrote -- - // that is, those that appear on the *method itself*. - // - // As an example, if the user wrote something like - // `foo.bar::<u32>(...)` -- the `Self` type here will be the - // type of `foo` (possibly adjusted), but we don't want to - // include that. We want just the `[_, u32]` part. - if !method.substs.is_empty() { - let method_generics = self.tcx.generics_of(method.def_id); - if !method_generics.params.is_empty() { - let user_type_annotation = self.probe(|_| { - let user_substs = UserSubsts { - substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| { - let i = param.index as usize; - if i < method_generics.parent_count { - self.var_for_def(DUMMY_SP, param) - } else { - method.substs[i] - } - }), - user_self_ty: None, // not relevant here - }; - - self.canonicalize_user_type_annotation(UserType::TypeOf( - method.def_id, - user_substs, - )) - }); - - debug!("write_method_call: user_type_annotation={:?}", user_type_annotation); - self.write_user_type_annotation(hir_id, user_type_annotation); - } - } } pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) { @@ -410,23 +372,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> { - let t = <dyn AstConv<'_>>::ast_ty_to_ty(self, ast_t); + pub fn handle_raw_ty(&self, span: Span, ty: Ty<'tcx>) -> RawTy<'tcx> { + RawTy { raw: ty, normalized: self.normalize(span, ty) } + } + + pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> RawTy<'tcx> { + let t = self.astconv().ast_ty_to_ty(ast_t); self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None)); - t + self.handle_raw_ty(ast_t.span, t) } pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { let ty = self.to_ty(ast_ty); debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); - if Self::can_contain_user_lifetime_bounds(ty) { - let c_ty = self.canonicalize_response(UserType::Ty(ty)); + if Self::can_contain_user_lifetime_bounds(ty.raw) { + let c_ty = self.canonicalize_response(UserType::Ty(ty.raw)); debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); } - ty + ty.normalized + } + + pub(super) fn user_substs_for_adt(ty: RawTy<'tcx>) -> UserSubsts<'tcx> { + match (ty.raw.kind(), ty.normalized.kind()) { + (ty::Adt(_, substs), _) => UserSubsts { substs, user_self_ty: None }, + (_, ty::Adt(adt, substs)) => UserSubsts { + substs, + user_self_ty: Some(UserSelfTy { impl_def_id: adt.did(), self_ty: ty.raw }), + }, + _ => bug!("non-adt type {:?}", ty), + } } pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> { @@ -696,8 +673,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Note: this check is pessimistic, as the inference type could be matched with something other // than the opaque type, but then we need a new `TypeRelation` just for this specific case and // can't re-use `sup` below. - // See src/test/ui/impl-trait/hidden-type-is-opaque.rs and - // src/test/ui/impl-trait/hidden-type-is-opaque-2.rs for examples that hit this path. + // See tests/ui/impl-trait/hidden-type-is-opaque.rs and + // tests/ui/impl-trait/hidden-type-is-opaque-2.rs for examples that hit this path. if formal_ret.has_infer_types() { for ty in ret_ty.walk() { if let ty::subst::GenericArgKind::Type(ty) = ty.unpack() @@ -780,7 +757,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { qpath: &'tcx QPath<'tcx>, hir_id: hir::HirId, span: Span, - ) -> (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) { + ) -> (Res, Option<RawTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) { debug!( "resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}", qpath, hir_id, span @@ -803,7 +780,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // to be object-safe. // We manually call `register_wf_obligation` in the success path // below. - (<dyn AstConv<'_>>::ast_ty_to_ty_in_path(self, qself), qself, segment) + let ty = self.astconv().ast_ty_to_ty_in_path(qself); + (self.handle_raw_ty(span, ty), qself, segment) } QPath::LangItem(..) => { bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`") @@ -811,7 +789,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id) { - self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None)); + self.register_wf_obligation(ty.raw.into(), qself.span, traits::WellFormed(None)); // Return directly on cache hit. This is useful to avoid doubly reporting // errors with default match binding modes. See #44614. let def = cached_result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)); @@ -819,7 +797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let item_name = item_segment.ident; let result = self - .resolve_fully_qualified_call(span, item_name, ty, qself.span, hir_id) + .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id) .or_else(|error| { let result = match error { method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), @@ -830,17 +808,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // a WF obligation for `dyn MyTrait` when method lookup fails. Otherwise, // register a WF obligation so that we can detect any additional // errors in the self type. - if !(matches!(error, method::MethodError::NoMatch(_)) && ty.is_trait()) { - self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None)); + if !(matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait()) { + self.register_wf_obligation( + ty.raw.into(), + qself.span, + traits::WellFormed(None), + ); } if item_name.name != kw::Empty { if let Some(mut e) = self.report_method_error( span, - ty, + ty.normalized, item_name, SelfSource::QPath(qself), error, None, + Expectation::NoExpectation, ) { e.emit(); } @@ -849,7 +832,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); if result.is_ok() { - self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None)); + self.register_wf_obligation(ty.raw.into(), qself.span, traits::WellFormed(None)); } // Write back the new resolution. @@ -986,7 +969,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn instantiate_value_path( &self, segments: &[hir::PathSegment<'_>], - self_ty: Option<Ty<'tcx>>, + self_ty: Option<RawTy<'tcx>>, res: Res, span: Span, hir_id: hir::HirId, @@ -995,8 +978,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let path_segs = match res { Res::Local(_) | Res::SelfCtor(_) => vec![], - Res::Def(kind, def_id) => <dyn AstConv<'_>>::def_ids_for_value_path_segments( - self, segments, self_ty, kind, def_id, + Res::Def(kind, def_id) => self.astconv().def_ids_for_value_path_segments( + segments, + self_ty.map(|ty| ty.raw), + kind, + def_id, + span, ), _ => bug!("instantiate_value_path on {:?}", res), }; @@ -1007,8 +994,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) if let Some(self_ty) = self_ty => { - let adt_def = self_ty.ty_adt_def().unwrap(); - user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did(), self_ty }); + let adt_def = self_ty.normalized.ty_adt_def().unwrap(); + user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did(), self_ty: self_ty.raw }); is_alias_variant_ctor = true; } Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => { @@ -1027,7 +1014,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // inherent impl, we need to record the // `T` for posterity (see `UserSelfTy` for // details). - let self_ty = self_ty.expect("UFCS sugared assoc missing Self"); + let self_ty = self_ty.expect("UFCS sugared assoc missing Self").raw; user_self_ty = Some(UserSelfTy { impl_def_id: container_id, self_ty }); } } @@ -1042,8 +1029,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // errors if type parameters are provided in an inappropriate place. let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); - let generics_has_err = <dyn AstConv<'_>>::prohibit_generics( - self, + let generics_has_err = self.astconv().prohibit_generics( segments.iter().enumerate().filter_map(|(index, seg)| { if !generic_segs.contains(&index) || is_alias_variant_ctor { Some(seg) @@ -1084,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // parameter internally, but we don't allow users to specify the // parameter's value explicitly, so we have to do some error- // checking here. - let arg_count = <dyn AstConv<'_>>::check_generic_arg_count_for_call( + let arg_count = check_generic_arg_count_for_call( tcx, span, def_id, @@ -1109,19 +1095,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .unwrap_or(false); let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res { - let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id)); - match *ty.kind() { - ty::Adt(adt_def, substs) if adt_def.has_ctor() => { - let variant = adt_def.non_enum_variant(); - let (ctor_kind, ctor_def_id) = variant.ctor.unwrap(); - (Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id), Some(substs)) + let ty = self.handle_raw_ty(span, tcx.at(span).type_of(impl_def_id)); + match ty.normalized.ty_adt_def() { + Some(adt_def) if adt_def.has_ctor() => { + let (ctor_kind, ctor_def_id) = adt_def.non_enum_variant().ctor.unwrap(); + let new_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); + let user_substs = Self::user_substs_for_adt(ty); + user_self_ty = user_substs.user_self_ty; + (new_res, Some(user_substs.substs)) } _ => { let mut err = tcx.sess.struct_span_err( span, "the `Self` constructor can only be used with tuple or unit structs", ); - if let Some(adt_def) = ty.ty_adt_def() { + if let Some(adt_def) = ty.normalized.ty_adt_def() { match adt_def.adt_kind() { AdtKind::Enum => { err.help("did you mean to use one of the enum's variants?"); @@ -1190,10 +1178,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - <dyn AstConv<'_>>::ast_region_to_region(self.fcx, lt, Some(param)).into() + self.fcx.astconv().ast_region_to_region(lt, Some(param)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.fcx.to_ty(ty).into() + self.fcx.to_ty(ty).raw.into() } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { self.fcx.const_arg_to_const(&ct.value, param.def_id).into() @@ -1225,10 +1213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we have a default, then we it doesn't matter that we're not // inferring the type arguments: we provide the default where any // is missing. - let default = tcx.bound_type_of(param.def_id); - self.fcx - .normalize_ty(self.span, default.subst(tcx, substs.unwrap())) - .into() + tcx.bound_type_of(param.def_id).subst(tcx, substs.unwrap()).into() } else { // If no type arguments were provided, we have to infer them. // This case also occurs as a result of some malformed input, e.g. @@ -1250,13 +1235,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let substs = self_ctor_substs.unwrap_or_else(|| { - <dyn AstConv<'_>>::create_substs_for_generic_args( + let substs_raw = self_ctor_substs.unwrap_or_else(|| { + create_substs_for_generic_args( tcx, def_id, &[], has_self, - self_ty, + self_ty.map(|s| s.raw), &arg_count, &mut CreateCtorSubstsContext { fcx: self, @@ -1269,7 +1254,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); // First, store the "user substs" for later. - self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty); + self.write_user_type_annotation_from_substs(hir_id, def_id, substs_raw, user_self_ty); + + // Normalize only after registering type annotations. + let substs = self.normalize(span, substs_raw); self.add_required_obligations_for_hir(span, def_id, &substs, hir_id); @@ -1287,6 +1275,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // with the substituted impl type. // This also occurs for an enum variant on a type alias. let impl_ty = self.normalize(span, tcx.bound_type_of(impl_def_id).subst(tcx, substs)); + let self_ty = self.normalize(span, self_ty); match self.at(&self.misc(span), self.param_env).eq(impl_ty, self_ty) { Ok(ok) => self.register_infer_ok_obligations(ok), Err(_) => { @@ -1435,9 +1424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool { let mut contained_in_place = false; - while let hir::Node::Expr(parent_expr) = - self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id)) - { + while let hir::Node::Expr(parent_expr) = self.tcx.hir().get_parent(expr_id) { match &parent_expr.kind { hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => { if lhs.hir_id == expr_id { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index d342d96a10f..b9e13fd2009 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -5,11 +5,11 @@ use crate::method::MethodCallee; use crate::Expectation::*; use crate::TupleArgumentsFlag::*; use crate::{ - struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, + struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, RawTy, TupleArgumentsFlag, }; use rustc_ast as ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -473,7 +473,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &hir::Expr<'tcx>, ) { // Next, let's construct the error - let (error_span, full_call_span, ctor_of, is_method) = match &call_expr.kind { + let (error_span, full_call_span, call_name, is_method) = match &call_expr.kind { hir::ExprKind::Call( hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, _, @@ -481,12 +481,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Res::Def(DefKind::Ctor(of, _), _) = self.typeck_results.borrow().qpath_res(qpath, *hir_id) { - (call_span, *span, Some(of), false) + let name = match of { + CtorOf::Struct => "struct", + CtorOf::Variant => "enum variant", + }; + (call_span, *span, name, false) } else { - (call_span, *span, None, false) + (call_span, *span, "function", false) } } - hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None, false), + hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, "function", false), hir::ExprKind::MethodCall(path_segment, _, _, span) => { let ident_span = path_segment.ident.span; let ident_span = if let Some(args) = path_segment.args { @@ -494,17 +498,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { ident_span }; - // methods are never ctors - (*span, ident_span, None, true) + (*span, ident_span, "method", true) } k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), }; let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span); - let call_name = match ctor_of { - Some(CtorOf::Struct) => "struct", - Some(CtorOf::Variant) => "enum variant", - None => "function", - }; // Don't print if it has error types or is just plain `_` fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool { @@ -690,8 +688,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err = tcx.sess.struct_span_err_with_code( full_call_span, &format!( - "this {} takes {}{} but {} {} supplied", - call_name, + "{call_name} takes {}{} but {} {} supplied", if c_variadic { "at least " } else { "" }, potentially_plural_count( formal_and_expected_inputs.len(), @@ -801,6 +798,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { full_call_span, format!("arguments to this {} are incorrect", call_name), ); + if let (Some(callee_ty), hir::ExprKind::MethodCall(_, rcvr, _, _)) = + (callee_ty, &call_expr.kind) + { + // Type that would have accepted this argument if it hadn't been inferred earlier. + // FIXME: We leave an inference variable for now, but it'd be nice to get a more + // specific type to increase the accuracy of the diagnostic. + let expected = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: full_call_span, + }); + self.point_at_expr_source_of_inferred_type(&mut err, rcvr, expected, callee_ty); + } // Call out where the function is defined self.label_fn_like( &mut err, @@ -1222,31 +1231,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); return None; } - Res::Def(DefKind::Variant, _) => match ty.kind() { - ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did(), substs)), - _ => bug!("unexpected type: {:?}", ty), + Res::Def(DefKind::Variant, _) => match ty.normalized.ty_adt_def() { + Some(adt) => { + Some((adt.variant_of_res(def), adt.did(), Self::user_substs_for_adt(ty))) + } + _ => bug!("unexpected type: {:?}", ty.normalized), }, Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) | Res::SelfTyParam { .. } - | Res::SelfTyAlias { .. } => match ty.kind() { - ty::Adt(adt, substs) if !adt.is_enum() => { - Some((adt.non_enum_variant(), adt.did(), substs)) + | Res::SelfTyAlias { .. } => match ty.normalized.ty_adt_def() { + Some(adt) if !adt.is_enum() => { + Some((adt.non_enum_variant(), adt.did(), Self::user_substs_for_adt(ty))) } _ => None, }, _ => bug!("unexpected definition: {:?}", def), }; - if let Some((variant, did, substs)) = variant { + if let Some((variant, did, ty::UserSubsts { substs, user_self_ty })) = variant { debug!("check_struct_path: did={:?} substs={:?}", did, substs); - self.write_user_type_annotation_from_substs(hir_id, did, substs, None); + + // Register type annotation. + self.write_user_type_annotation_from_substs(hir_id, did, substs, user_self_ty); // Check bounds on type arguments used in the path. self.add_required_obligations_for_hir(path_span, did, substs, hir_id); - Some((variant, ty)) + Some((variant, ty.normalized)) } else { - match ty.kind() { + match ty.normalized.kind() { ty::Error(_) => { // E0071 might be caused by a spelling error, which will have // already caused an error message and probably a suggestion @@ -1259,7 +1272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { path_span, E0071, "expected struct, variant or union type, found {}", - ty.sort_string(self.tcx) + ty.normalized.sort_string(self.tcx) ) .span_label(path_span, "not a struct") .emit(); @@ -1647,20 +1660,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { qpath: &QPath<'_>, path_span: Span, hir_id: hir::HirId, - ) -> (Res, Ty<'tcx>) { + ) -> (Res, RawTy<'tcx>) { match *qpath { QPath::Resolved(ref maybe_qself, ref path) => { - let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); - let ty = <dyn AstConv<'_>>::res_to_ty(self, self_ty, path, true); - (path.res, ty) + let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself).raw); + let ty = self.astconv().res_to_ty(self_ty, path, true); + (path.res, self.handle_raw_ty(path_span, ty)) } QPath::TypeRelative(ref qself, ref segment) => { let ty = self.to_ty(qself); - let result = <dyn AstConv<'_>>::associated_path_to_ty( - self, hir_id, path_span, ty, qself, segment, true, - ); + let result = self + .astconv() + .associated_path_to_ty(hir_id, path_span, ty.raw, qself, segment, true); let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); + let ty = self.handle_raw_ty(path_span, ty); let result = result.map(|(_, kind, def_id)| (kind, def_id)); // Write back the new resolution. @@ -1669,7 +1683,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty) } QPath::LangItem(lang_item, span, id) => { - self.resolve_lang_item_path(lang_item, span, hir_id, id) + let (res, ty) = self.resolve_lang_item_path(lang_item, span, hir_id, id); + (res, self.handle_raw_ty(path_span, ty)) } } } @@ -1689,7 +1704,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // even if their `ObligationCauseCode` isn't an `Expr*Obligation` kind. // This is important since if we adjust one span but not the other, then // we will have "duplicated" the error on the UI side. - let mut remap_cause = FxHashSet::default(); + let mut remap_cause = FxIndexSet::default(); let mut not_adjusted = vec![]; for error in errors { @@ -1717,6 +1732,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + // Adjust any other errors that come from other cause codes, when these + // errors are of the same predicate as one we successfully adjusted, and + // when their spans overlap (suggesting they're due to the same root cause). + // + // This is because due to normalization, we often register duplicate + // obligations with misc obligations that are basically impossible to + // line back up with a useful ExprBindingObligation. for error in not_adjusted { for (span, predicate, cause) in &remap_cause { if *predicate == error.obligation.predicate @@ -1803,7 +1825,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir_id: call_hir_id, span: call_span, .. - }) = hir.get(hir.get_parent_node(expr.hir_id)) + }) = hir.get_parent(expr.hir_id) && callee.hir_id == expr.hir_id { if self.closure_span_overlaps_error(error, *call_span) { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 30b59da7852..428fde642bc 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -17,11 +17,10 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, Const, Ty, TyCtxt}; +use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitable}; use rustc_session::Session; use rustc_span::symbol::Ident; -use rustc_span::{self, Span}; +use rustc_span::{self, Span, DUMMY_SP}; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use std::cell::{Cell, RefCell}; @@ -176,6 +175,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_sig }) }), + autoderef_steps: Box::new(|ty| { + let mut autoderef = self.autoderef(DUMMY_SP, ty).silence_errors(); + let mut steps = vec![]; + while let Some((ty, _)) = autoderef.next() { + steps.push((ty, autoderef.current_obligations())); + } + steps + }), } } @@ -287,8 +294,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { poly_trait_ref, ); - let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item( - self, + let item_substs = self.astconv().create_substs_for_associated_item( span, item_def_id, item_segment, @@ -298,11 +304,14 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { self.tcx().mk_projection(item_def_id, item_substs) } - fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_escaping_bound_vars() { - ty // FIXME: normalization and escaping regions - } else { - self.normalize(span, ty) + fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> { + match ty.kind() { + ty::Adt(adt_def, _) => Some(*adt_def), + // FIXME(#104767): Should we handle bound regions here? + ty::Alias(ty::Projection, _) if !ty.has_escaping_bound_vars() => { + self.normalize(span, ty).ty_adt_def() + } + _ => None, } } @@ -310,7 +319,21 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { self.infcx.set_tainted_by_errors(e) } - fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { + fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span) { + // FIXME: normalization and escaping regions + let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty }; self.write_ty(hir_id, ty) } } + +/// Represents a user-provided type in the raw form (never normalized). +/// +/// This is a bridge between the interface of `AstConv`, which outputs a raw `Ty`, +/// and the API in this module, which expect `Ty` to be fully normalized. +#[derive(Clone, Copy, Debug)] +pub struct RawTy<'tcx> { + pub raw: Ty<'tcx>, + + /// The normalized form of `raw`, stored here for efficiency. + pub normalized: Ty<'tcx>, +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 322e11c978f..005bd164065 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -5,7 +5,7 @@ use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, @@ -32,7 +32,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results .borrow() .liberated_fn_sigs() - .get(self.tcx.hir().get_parent_node(self.body_id)) + .get(self.tcx.hir().parent_id(self.body_id)) .copied() } @@ -417,10 +417,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if self.suggest_else_fn_with_closure(err, expr, found, expected) { return true; } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) - && let ty::FnDef(def_id, ..) = &found.kind() - && let Some(sp) = self.tcx.hir().span_if_local(*def_id) + && let ty::FnDef(def_id, ..) = *found.kind() + && let Some(sp) = self.tcx.hir().span_if_local(def_id) { - err.span_label(sp, format!("{found} defined here")); + let name = self.tcx.item_name(def_id); + let kind = self.tcx.def_kind(def_id); + if let DefKind::Ctor(of, CtorKind::Fn) = kind { + err.span_label(sp, format!("`{name}` defines {} constructor here, which should be called", match of { + CtorOf::Struct => "a struct", + CtorOf::Variant => "an enum variant", + })); + } else { + let descr = kind.descr(def_id); + err.span_label(sp, format!("{descr} `{name}` defined here")); + } return true; } else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) { return true; @@ -642,7 +652,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check if the parent expression is a call to Pin::new. If it // is and we were expecting a Box, ergo Pin<Box<expected>>, we // can suggest Box::pin. - let parent = self.tcx.hir().get_parent_node(expr.hir_id); + let parent = self.tcx.hir().parent_id(expr.hir_id); let Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) = self.tcx.hir().find(parent) else { return false; }; @@ -783,7 +793,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // are not, the expectation must have been caused by something else. debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); let span = ty.span; - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty); + let ty = self.astconv().ast_ty_to_ty(ty); debug!("suggest_missing_return_type: return type {:?}", ty); debug!("suggest_missing_return_type: expected type {:?}", ty); let bound_vars = self.tcx.late_bound_vars(fn_id); @@ -854,7 +864,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .. }) => { // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below) - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, bounded_ty); + let ty = self.astconv().ast_ty_to_ty(bounded_ty); Some((ty, bounds)) } _ => None, @@ -892,7 +902,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let all_bounds_str = all_matching_bounds_strs.join(" + "); let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| { - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, param); + let ty = self.astconv().ast_ty_to_ty( param); matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param) }); @@ -946,7 +956,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let hir::FnRetTy::Return(ty) = fn_decl.output { - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty); + let ty = self.astconv().ast_ty_to_ty(ty); let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); let ty = match self.tcx.asyncness(fn_id.owner) { @@ -1014,6 +1024,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + pub(crate) fn suggest_clone_for_ref( + &self, + diag: &mut Diagnostic, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) -> bool { + if let ty::Ref(_, inner_ty, hir::Mutability::Not) = expr_ty.kind() + && let Some(clone_trait_def) = self.tcx.lang_items().clone_trait() + && expected_ty == *inner_ty + && self + .infcx + .type_implements_trait( + clone_trait_def, + [self.tcx.erase_regions(expected_ty)], + self.param_env + ) + .must_apply_modulo_regions() + { + diag.span_suggestion_verbose( + expr.span.shrink_to_hi(), + "consider using clone here", + ".clone()", + Applicability::MachineApplicable, + ); + return true; + } + false + } + pub(crate) fn suggest_copied_or_cloned( &self, diag: &mut Diagnostic, @@ -1309,10 +1349,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Path { segments: [segment], .. }, )) | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => { - let self_ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty); + let self_ty = self.astconv().ast_ty_to_ty(ty); if let Ok(pick) = self.probe_for_name( Mode::Path, Ident::new(capitalized_name, segment.ident.span), + Some(expected_ty), IsSuggestion(true), self_ty, expr.hir_id, diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 9a096f24fac..15dd3412c34 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -77,7 +77,8 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { Some(ref ty) => { let o_ty = self.fcx.to_ty(&ty); - let c_ty = self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty)); + let c_ty = + self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty.raw)); debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty); self.fcx .typeck_results @@ -85,7 +86,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { .user_provided_types_mut() .insert(ty.hir_id, c_ty); - Some(LocalTy { decl_ty: o_ty, revealed_ty: o_ty }) + Some(LocalTy { decl_ty: o_ty.normalized, revealed_ty: o_ty.normalized }) } None => None, }; diff --git a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs index bfe95852aa7..472205be7b5 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -140,7 +140,7 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { diag_expr_id: HirId, ) { let hir = self.tcx.hir(); - let parent = match hir.find_parent_node(place_with_id.hir_id) { + let parent = match hir.opt_parent_id(place_with_id.hir_id) { Some(parent) => parent, None => place_with_id.hir_id, }; diff --git a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs index e4ac91befac..7990d95310b 100644 --- a/compiler/rustc_hir_typeck/src/generator_interior/mod.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs @@ -448,7 +448,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { // the yield, even if it's not borrowed or referenced after the yield. Ideally this would // *only* happen for types with observable drop, not all types which wrap them, but that // doesn't match the behavior of MIR borrowck and causes ICEs. See the FIXME comment in - // src/test/ui/generator/drop-tracking-parent-expression.rs. + // tests/ui/generator/drop-tracking-parent-expression.rs. let scope = if self.drop_ranges.is_borrowed_temporary(expr) || ty.map_or(true, |ty| { // Avoid ICEs in needs_drop. diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index cecf3d3f1e0..7ddf9eaa4d8 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -205,7 +205,7 @@ fn typeck_with_fallback<'tcx>( if let Some(hir::FnSig { header, decl, .. }) = fn_sig { let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() { - <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None) + fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None) } else { tcx.fn_sig(def_id) }; @@ -220,11 +220,11 @@ fn typeck_with_fallback<'tcx>( } else { let expected_type = body_ty .and_then(|ty| match ty.kind { - hir::TyKind::Infer => Some(<dyn AstConv<'_>>::ast_ty_to_ty(&fcx, ty)), + hir::TyKind::Infer => Some(fcx.astconv().ast_ty_to_ty(ty)), _ => None, }) .unwrap_or_else(|| match tcx.hir().get(id) { - Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) { + Node::AnonConst(_) => match tcx.hir().get(tcx.hir().parent_id(id)) { Node::Expr(&hir::Expr { kind: hir::ExprKind::ConstBlock(ref anon_const), .. @@ -297,7 +297,7 @@ fn typeck_with_fallback<'tcx>( fcx.resolve_generator_interiors(def_id.to_def_id()); for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { - let ty = fcx.normalize_ty(span, ty); + let ty = fcx.normalize(span, ty); fcx.require_type_is_sized(ty, span, code); } diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 218c54688aa..4a33a791e1b 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -4,6 +4,9 @@ use crate::{callee, FnCtxt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; +use rustc_hir_analysis::astconv::generics::{ + check_generic_arg_count_for_call, create_substs_for_generic_args, +}; use rustc_hir_analysis::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; use rustc_infer::infer::{self, InferOk}; use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; @@ -12,7 +15,8 @@ use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutabili use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::subst::{self, SubstsRef}; use rustc_middle::ty::{self, GenericParamDefKind, Ty}; -use rustc_span::Span; +use rustc_middle::ty::{InternalSubsts, UserSubsts, UserType}; +use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::traits; use std::iter; @@ -89,7 +93,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that // something which derefs to `Self` actually implements the trait and the caller // wanted to make a static dispatch on it but forgot to import the trait. - // See test `src/test/ui/issue-35976.rs`. + // See test `tests/ui/issue-35976.rs`. // // In that case, we'll error anyway, but we'll also re-run the search with all traits // in scope, and if we find another method which can be used, we'll output an @@ -330,7 +334,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // variables. let generics = self.tcx.generics_of(pick.item.def_id); - let arg_count_correct = <dyn AstConv<'_>>::check_generic_arg_count_for_call( + let arg_count_correct = check_generic_arg_count_for_call( self.tcx, self.span, pick.item.def_id, @@ -368,11 +372,10 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> subst::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - <dyn AstConv<'_>>::ast_region_to_region(self.cfcx.fcx, lt, Some(param)) - .into() + self.cfcx.fcx.astconv().ast_region_to_region(lt, Some(param)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.cfcx.to_ty(ty).into() + self.cfcx.to_ty(ty).raw.into() } (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { self.cfcx.const_arg_to_const(&ct.value, param.def_id).into() @@ -397,7 +400,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.cfcx.var_for_def(self.cfcx.span, param) } } - <dyn AstConv<'_>>::create_substs_for_generic_args( + + let substs = create_substs_for_generic_args( self.tcx, pick.item.def_id, parent_substs, @@ -405,7 +409,47 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { None, &arg_count_correct, &mut MethodSubstsCtxt { cfcx: self, pick, seg }, - ) + ); + + // When the method is confirmed, the `substs` includes + // parameters from not just the method, but also the impl of + // the method -- in particular, the `Self` type will be fully + // resolved. However, those are not something that the "user + // specified" -- i.e., those types come from the inferred type + // of the receiver, not something the user wrote. So when we + // create the user-substs, we want to replace those earlier + // types with just the types that the user actually wrote -- + // that is, those that appear on the *method itself*. + // + // As an example, if the user wrote something like + // `foo.bar::<u32>(...)` -- the `Self` type here will be the + // type of `foo` (possibly adjusted), but we don't want to + // include that. We want just the `[_, u32]` part. + if !substs.is_empty() && !generics.params.is_empty() { + let user_type_annotation = self.probe(|_| { + let user_substs = UserSubsts { + substs: InternalSubsts::for_item(self.tcx, pick.item.def_id, |param, _| { + let i = param.index as usize; + if i < generics.parent_count { + self.fcx.var_for_def(DUMMY_SP, param) + } else { + substs[i] + } + }), + user_self_ty: None, // not relevant here + }; + + self.fcx.canonicalize_user_type_annotation(UserType::TypeOf( + pick.item.def_id, + user_substs, + )) + }); + + debug!("instantiate_method_substs: user_type_annotation={:?}", user_type_annotation); + self.fcx.write_user_type_annotation(self.call_expr.hir_id, user_type_annotation); + } + + self.normalize(self.span, substs) } fn unify_receivers( diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index b9b27e8627a..146d5e60c2f 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -57,7 +57,12 @@ pub enum MethodError<'tcx> { PrivateMatch(DefKind, DefId, Vec<DefId>), // Found a `Self: Sized` bound where `Self` is a trait object. - IllegalSizedBound(Vec<DefId>, bool, Span), + IllegalSizedBound { + candidates: Vec<DefId>, + needs_mut: bool, + bound_span: Span, + self_expr: &'tcx hir::Expr<'tcx>, + }, // Found a match, but the return type is wrong BadReturnType, @@ -92,10 +97,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, call_expr_id: hir::HirId, allow_private: bool, + return_type: Option<Ty<'tcx>>, ) -> bool { match self.probe_for_name( probe::Mode::MethodCall, method_name, + return_type, IsSuggestion(false), self_ty, call_expr_id, @@ -112,8 +119,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(NoMatch(..)) => false, Err(Ambiguity(..)) => true, Err(PrivateMatch(..)) => allow_private, - Err(IllegalSizedBound(..)) => true, - Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"), + Err(IllegalSizedBound { .. }) => true, + Err(BadReturnType) => false, } } @@ -125,17 +132,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { msg: &str, method_name: Ident, self_ty: Ty<'tcx>, - call_expr: &hir::Expr<'_>, + call_expr: &hir::Expr<'tcx>, span: Option<Span>, ) { let params = self - .probe_for_name( - probe::Mode::MethodCall, + .lookup_probe_for_diagnostic( method_name, - IsSuggestion(true), self_ty, - call_expr.hir_id, + call_expr, ProbeScope::TraitsInScope, + None, ) .map(|pick| { let sig = self.tcx.fn_sig(pick.item.def_id); @@ -216,27 +222,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // We probe again, taking all traits into account (not only those in scope). - let candidates = - match self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::AllTraits) { - // If we find a different result the caller probably forgot to import a trait. - Ok(ref new_pick) if pick.differs_from(new_pick) => { - vec![new_pick.item.container_id(self.tcx)] - } - Err(Ambiguity(ref sources)) => sources - .iter() - .filter_map(|source| { - match *source { - // Note: this cannot come from an inherent impl, - // because the first probing succeeded. - CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def), - CandidateSource::Trait(_) => None, - } - }) - .collect(), - _ => Vec::new(), - }; - - return Err(IllegalSizedBound(candidates, needs_mut, span)); + let candidates = match self.lookup_probe_for_diagnostic( + segment.ident, + self_ty, + call_expr, + ProbeScope::AllTraits, + None, + ) { + // If we find a different result the caller probably forgot to import a trait. + Ok(ref new_pick) if pick.differs_from(new_pick) => { + vec![new_pick.item.container_id(self.tcx)] + } + Err(Ambiguity(ref sources)) => sources + .iter() + .filter_map(|source| { + match *source { + // Note: this cannot come from an inherent impl, + // because the first probing succeeded. + CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def), + CandidateSource::Trait(_) => None, + } + }) + .collect(), + _ => Vec::new(), + }; + + return Err(IllegalSizedBound { candidates, needs_mut, bound_span: span, self_expr }); } Ok(result.callee) @@ -247,12 +258,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, method_name: Ident, self_ty: Ty<'tcx>, - call_expr: &'tcx hir::Expr<'tcx>, + call_expr: &hir::Expr<'_>, scope: ProbeScope, ) -> probe::PickResult<'tcx> { let pick = self.probe_for_name( probe::Mode::MethodCall, method_name, + None, IsSuggestion(false), self_ty, call_expr.hir_id, @@ -262,6 +274,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(pick) } + pub fn lookup_probe_for_diagnostic( + &self, + method_name: Ident, + self_ty: Ty<'tcx>, + call_expr: &hir::Expr<'_>, + scope: ProbeScope, + return_type: Option<Ty<'tcx>>, + ) -> probe::PickResult<'tcx> { + let pick = self.probe_for_name( + probe::Mode::MethodCall, + method_name, + return_type, + IsSuggestion(true), + self_ty, + call_expr.hir_id, + scope, + )?; + Ok(pick) + } + pub(super) fn obligation_for_method( &self, cause: ObligationCause<'tcx>, @@ -479,6 +511,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pick = self.probe_for_name( probe::Mode::Path, method_name, + None, IsSuggestion(false), self_ty, expr_id, diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 2daf1979ee5..dd827777df9 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -9,6 +9,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; +use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -29,7 +30,6 @@ use rustc_span::lev_distance::{ }; use rustc_span::symbol::sym; use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; -use rustc_trait_selection::autoderef::{self, Autoderef}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy; use rustc_trait_selection::traits::query::method_autoderef::{ @@ -304,6 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, mode: Mode, item_name: Ident, + return_type: Option<Ty<'tcx>>, is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, scope_expr_id: hir::HirId, @@ -313,7 +314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name.span, mode, Some(item_name), - None, + return_type, is_suggestion, self_ty, scope_expr_id, @@ -327,6 +328,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, mode: Mode, item_name: Ident, + return_type: Option<Ty<'tcx>>, is_suggestion: IsSuggestion, self_ty: Ty<'tcx>, scope_expr_id: hir::HirId, @@ -336,7 +338,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name.span, mode, Some(item_name), - None, + return_type, is_suggestion, self_ty, scope_expr_id, @@ -1540,7 +1542,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let InferOk { value: normalized_xform_ret_ty, obligations: normalization_obligations, - } = self.fcx.at(&cause, self.param_env).normalize(probe.xform_ret_ty); + } = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty); xform_ret_ty = normalized_xform_ret_ty; debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty); @@ -1554,7 +1556,23 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // Convert the bounds into obligations. let impl_obligations = traits::predicates_for_generics( - move |_, _| cause.clone(), + |_idx, span| { + let misc = traits::ObligationCause::misc(span, self.body_id); + let parent_trait_pred = ty::Binder::dummy(ty::TraitPredicate { + trait_ref: ty::TraitRef::from_method(self.tcx, impl_def_id, substs), + constness: ty::BoundConstness::NotConst, + polarity: ty::ImplPolarity::Positive, + }); + misc.derived_cause(parent_trait_pred, |derived| { + traits::ImplDerivedObligation(Box::new( + traits::ImplDerivedObligationCause { + derived, + impl_def_id, + span, + }, + )) + }) + }, self.param_env, impl_bounds, ); @@ -1597,7 +1615,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx); parent_pred = Some(predicate); let obligation = - traits::Obligation::new(self.tcx, cause, self.param_env, predicate); + traits::Obligation::new(self.tcx, cause.clone(), self.param_env, predicate); if !self.predicate_may_hold(&obligation) { result = ProbeResult::NoMatch; if self.probe(|_| { @@ -1656,22 +1674,48 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - if let ProbeResult::Match = result { - if let (Some(return_ty), Some(xform_ret_ty)) = (self.return_type, xform_ret_ty) { - let xform_ret_ty = self.resolve_vars_if_possible(xform_ret_ty); - debug!( - "comparing return_ty {:?} with xform ret ty {:?}", - return_ty, probe.xform_ret_ty - ); - if self - .at(&ObligationCause::dummy(), self.param_env) - .define_opaque_types(false) - .sup(return_ty, xform_ret_ty) - .is_err() - { - return ProbeResult::BadReturnType; + if let ProbeResult::Match = result + && let Some(return_ty) = self.return_type + && let Some(mut xform_ret_ty) = xform_ret_ty + { + // `xform_ret_ty` has only been normalized for `InherentImplCandidate`. + // We don't normalize the other candidates for perf/backwards-compat reasons... + // but `self.return_type` is only set on the diagnostic-path, so we + // should be okay doing it here. + if !matches!(probe.kind, InherentImplCandidate(..)) { + let InferOk { + value: normalized_xform_ret_ty, + obligations: normalization_obligations, + } = self.fcx.at(&cause, self.param_env).normalize(xform_ret_ty); + xform_ret_ty = normalized_xform_ret_ty; + debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty); + // Evaluate those obligations to see if they might possibly hold. + for o in normalization_obligations { + let o = self.resolve_vars_if_possible(o); + if !self.predicate_may_hold(&o) { + result = ProbeResult::NoMatch; + possibly_unsatisfied_predicates.push(( + o.predicate, + None, + Some(o.cause), + )); + } } } + + debug!( + "comparing return_ty {:?} with xform ret ty {:?}", + return_ty, xform_ret_ty + ); + if let ProbeResult::Match = result + && self + .at(&ObligationCause::dummy(), self.param_env) + .define_opaque_types(false) + .sup(return_ty, xform_ret_ty) + .is_err() + { + result = ProbeResult::BadReturnType; + } } result @@ -1685,7 +1729,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// probe. This will result in a pending obligation so when more type-info is available we can /// make the final decision. /// - /// Example (`src/test/ui/method-two-trait-defer-resolution-1.rs`): + /// Example (`tests/ui/method-two-trait-defer-resolution-1.rs`): /// /// ```ignore (illustrative) /// trait Foo { ... } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index b04ef55a994..8166eb82990 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2,6 +2,7 @@ //! found or is otherwise invalid. use crate::errors; +use crate::Expectation; use crate::FnCtxt; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -100,6 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) } + #[instrument(level = "debug", skip(self))] pub fn report_method_error( &self, span: Span, @@ -108,6 +110,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source: SelfSource<'tcx>, error: MethodError<'tcx>, args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, + expected: Expectation<'tcx>, ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { // Avoid suggestions when we don't know what's going on. if rcvr_ty.references_error() { @@ -116,7 +119,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let sugg_span = if let SelfSource::MethodCall(expr) = source { // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing. - self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)).span + self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)).span } else { span }; @@ -131,6 +134,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args, sugg_span, &mut no_match_data, + expected, ); } @@ -176,10 +180,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.emit(); } - MethodError::IllegalSizedBound(candidates, needs_mut, bound_span) => { - let msg = format!("the `{}` method cannot be invoked on a trait object", item_name); + MethodError::IllegalSizedBound { candidates, needs_mut, bound_span, self_expr } => { + let msg = if needs_mut { + with_forced_trimmed_paths!(format!( + "the `{item_name}` method cannot be invoked on `{rcvr_ty}`" + )) + } else { + format!("the `{item_name}` method cannot be invoked on a trait object") + }; let mut err = self.sess().struct_span_err(span, &msg); - err.span_label(bound_span, "this has a `Sized` requirement"); + if !needs_mut { + err.span_label(bound_span, "this has a `Sized` requirement"); + } if !candidates.is_empty() { let help = format!( "{an}other candidate{s} {were} found in the following trait{s}, perhaps \ @@ -197,7 +209,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { *region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, ); - err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty)); + let msg = format!("you need `{}` instead of `{}`", trait_type, rcvr_ty); + let mut kind = &self_expr.kind; + while let hir::ExprKind::AddrOf(_, _, expr) + | hir::ExprKind::Unary(hir::UnOp::Deref, expr) = kind + { + kind = &expr.kind; + } + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = kind + && let hir::def::Res::Local(hir_id) = path.res + && let Some(hir::Node::Pat(b)) = self.tcx.hir().find(hir_id) + && let Some(hir::Node::Param(p)) = self.tcx.hir().find_parent(b.hir_id) + && let Some(node) = self.tcx.hir().find_parent(p.hir_id) + && let Some(decl) = node.fn_decl() + && let Some(ty) = decl.inputs.iter().find(|ty| ty.span == p.ty_span) + && let hir::TyKind::Ref(_, mut_ty) = &ty.kind + && let hir::Mutability::Not = mut_ty.mutbl + { + err.span_suggestion_verbose( + mut_ty.ty.span.shrink_to_lo(), + &msg, + "mut ", + Applicability::MachineApplicable, + ); + } else { + err.help(&msg); + } } } err.emit(); @@ -217,6 +254,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, sugg_span: Span, no_match_data: &mut NoMatchData<'tcx>, + expected: Expectation<'tcx>, ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { let mode = no_match_data.mode; let tcx = self.tcx; @@ -287,7 +325,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { self.suggest_await_before_method( - &mut err, item_name, rcvr_ty, cal, span, + &mut err, item_name, rcvr_ty, cal, span, expected.only_has_type(self), ); } if let Some(span) = @@ -332,9 +370,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let SelfSource::MethodCall(rcvr_expr) = source { self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { let call_expr = - self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id)); - let probe = - self.lookup_probe(item_name, output_ty, call_expr, ProbeScope::AllTraits); + self.tcx.hir().expect_expr(self.tcx.hir().parent_id(rcvr_expr.hir_id)); + let probe = self.lookup_probe_for_diagnostic( + item_name, + output_ty, + call_expr, + ProbeScope::AllTraits, + expected.only_has_type(self), + ); probe.is_ok() }); } @@ -544,22 +587,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Find all the requirements that come from a local `impl` block. let mut skip_list: FxHashSet<_> = Default::default(); - let mut spanned_predicates: FxHashMap<MultiSpan, _> = Default::default(); - for (data, p, parent_p, impl_def_id, cause) in unsatisfied_predicates + let mut spanned_predicates = FxHashMap::default(); + for (p, parent_p, impl_def_id, cause) in unsatisfied_predicates .iter() .filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c))) .filter_map(|(p, parent, c)| match c.code() { - ObligationCauseCode::ImplDerivedObligation(data) => { - Some((&data.derived, p, parent, data.impl_def_id, data)) + ObligationCauseCode::ImplDerivedObligation(data) + if matches!(p.kind().skip_binder(), ty::PredicateKind::Clause(_)) => + { + Some((p, parent, data.impl_def_id, data)) } _ => None, }) { - let parent_trait_ref = data.parent_trait_pred; - let path = parent_trait_ref.print_modifiers_and_trait_path(); - let tr_self_ty = parent_trait_ref.skip_binder().self_ty(); - let unsatisfied_msg = "unsatisfied trait bound introduced here"; - let derive_msg = "unsatisfied trait bound introduced in this `derive` macro"; match self.tcx.hir().get_if_local(impl_def_id) { // Unmet obligation comes from a `derive` macro, point at it once to // avoid multiple span labels pointing at the same place. @@ -575,10 +615,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) => { let span = self_ty.span.ctxt().outer_expn_data().call_site; - let mut spans: MultiSpan = span.into(); - spans.push_span_label(span, derive_msg); - let entry = spanned_predicates.entry(spans); - entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); + let entry = spanned_predicates.entry(span); + let entry = entry.or_insert_with(|| { + (FxHashSet::default(), FxHashSet::default(), Vec::new()) + }); + entry.0.insert(span); + entry.1.insert(( + span, + "unsatisfied trait bound introduced in this `derive` macro", + )); + entry.2.push(p); + skip_list.insert(p); } // Unmet obligation coming from an `impl`. @@ -605,8 +652,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; err.span_suggestion_verbose( sp, - "consider relaxing the type parameter's implicit \ - `Sized` bound", + "consider relaxing the type parameter's implicit `Sized` bound", sugg, Applicability::MachineApplicable, ); @@ -617,25 +663,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let _ = format_pred(*pred); } skip_list.insert(p); - let mut spans = if cause.span != *item_span { - let mut spans: MultiSpan = cause.span.into(); - spans.push_span_label(cause.span, unsatisfied_msg); - spans + let entry = spanned_predicates.entry(self_ty.span); + let entry = entry.or_insert_with(|| { + (FxHashSet::default(), FxHashSet::default(), Vec::new()) + }); + entry.2.push(p); + if cause.span != *item_span { + entry.0.insert(cause.span); + entry.1.insert((cause.span, "unsatisfied trait bound introduced here")); } else { - let mut spans = Vec::with_capacity(2); if let Some(trait_ref) = of_trait { - spans.push(trait_ref.path.span); + entry.0.insert(trait_ref.path.span); } - spans.push(self_ty.span); - spans.into() + entry.0.insert(self_ty.span); }; if let Some(trait_ref) = of_trait { - spans.push_span_label(trait_ref.path.span, ""); + entry.1.insert((trait_ref.path.span, "")); } - spans.push_span_label(self_ty.span, ""); - - let entry = spanned_predicates.entry(spans); - entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); + entry.1.insert((self_ty.span, "")); } Some(Node::Item(hir::Item { kind: hir::ItemKind::Trait(rustc_ast::ast::IsAuto::Yes, ..), @@ -652,11 +697,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); - spanned_predicates.sort_by_key(|(span, (_, _, _))| span.primary_span()); - for (span, (_path, _self_ty, preds)) in spanned_predicates { - let mut preds: Vec<_> = preds - .into_iter() - .filter_map(|pred| format_pred(*pred)) + spanned_predicates.sort_by_key(|(span, _)| *span); + for (_, (primary_spans, span_labels, predicates)) in spanned_predicates { + let mut preds: Vec<_> = predicates + .iter() + .filter_map(|pred| format_pred(**pred)) .map(|(p, _)| format!("`{}`", p)) .collect(); preds.sort(); @@ -666,6 +711,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),) }; + let mut span: MultiSpan = primary_spans.into_iter().collect::<Vec<_>>().into(); + for (sp, label) in span_labels { + span.push_span_label(sp, label); + } err.span_note(span, &msg); unsatisfied_bounds = true; } @@ -865,7 +914,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Don't suggest (for example) `expr.field.clone()` if `expr.clone()` // can't be called due to `typeof(expr): Clone` not holding. if unsatisfied_predicates.is_empty() { - self.suggest_calling_method_on_field(&mut err, source, span, rcvr_ty, item_name); + self.suggest_calling_method_on_field( + &mut err, + source, + span, + rcvr_ty, + item_name, + expected.only_has_type(self), + ); } self.check_for_inner_self(&mut err, source, rcvr_ty, item_name); @@ -889,6 +945,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &unsatisfied_predicates, &static_candidates, unsatisfied_bounds, + expected.only_has_type(self), ); } @@ -914,8 +971,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let msg = "remove this method call"; let mut fallback_span = true; if let SelfSource::MethodCall(expr) = source { - let call_expr = - self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); + let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)); if let Some(span) = call_expr.span.trim_start(expr.span) { err.span_suggestion(span, msg, "", Applicability::MachineApplicable); fallback_span = false; @@ -955,7 +1011,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.check_for_deref_method(&mut err, source, rcvr_ty, item_name); + self.check_for_deref_method(&mut err, source, rcvr_ty, item_name, expected); return Some(err); } @@ -1268,7 +1324,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } else { - let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); + let call_expr = tcx.hir().expect_expr(tcx.hir().parent_id(expr.hir_id)); if let Some(span) = call_expr.span.trim_start(item_name.span) { err.span_suggestion( @@ -1342,13 +1398,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let range_ty = self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); - let pick = self.probe_for_name( - Mode::MethodCall, + let pick = self.lookup_probe_for_diagnostic( item_name, - IsSuggestion(true), range_ty, - expr.hir_id, + expr, ProbeScope::AllTraits, + None, ); if pick.is_ok() { let range_span = parent_expr.span.with_hi(expr.span.hi()); @@ -1450,7 +1505,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let filename = tcx.sess.source_map().span_to_filename(span); let parent_node = - self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)); + self.tcx.hir().get_parent(hir_id); let msg = format!( "you must specify a type for this binding, like `{}`", concrete_type, @@ -1523,16 +1578,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; visitor.visit_body(&body); - let parent = self.tcx.hir().get_parent_node(seg1.hir_id); + let parent = self.tcx.hir().parent_id(seg1.hir_id); if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) && let Some(expr) = visitor.result && let Some(self_ty) = self.node_ty_opt(expr.hir_id) { - let probe = self.lookup_probe( + let probe = self.lookup_probe_for_diagnostic( seg2.ident, self_ty, call_expr, ProbeScope::TraitsInScope, + None, ); if probe.is_ok() { let sm = self.infcx.tcx.sess.source_map(); @@ -1555,13 +1611,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, actual: Ty<'tcx>, item_name: Ident, + return_type: Option<Ty<'tcx>>, ) { if let SelfSource::MethodCall(expr) = source && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id() && let Some((fields, substs)) = self.get_field_candidates_considering_privacy(span, actual, mod_id) { - let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); + let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)); let lang_items = self.tcx.lang_items(); let never_mention_traits = [ @@ -1578,11 +1635,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_for_nested_field_satisfying( span, &|_, field_ty| { - self.lookup_probe( + self.lookup_probe_for_diagnostic( item_name, field_ty, call_expr, ProbeScope::TraitsInScope, + return_type, ) .map_or(false, |pick| { !never_mention_traits @@ -1631,7 +1689,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let tcx = self.tcx; let SelfSource::MethodCall(expr) = source else { return; }; - let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); + let call_expr = tcx.hir().expect_expr(tcx.hir().parent_id(expr.hir_id)); let ty::Adt(kind, substs) = actual.kind() else { return; }; match kind.adt_kind() { @@ -1648,9 +1706,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } - self.lookup_probe(item_name, field_ty, call_expr, ProbeScope::TraitsInScope) - .ok() - .map(|pick| (variant, field, pick)) + self.lookup_probe_for_diagnostic( + item_name, + field_ty, + call_expr, + ProbeScope::TraitsInScope, + None, + ) + .ok() + .map(|pick| (variant, field, pick)) }) .collect(); @@ -1714,11 +1778,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::AdtKind::Struct | ty::AdtKind::Union => { let [first] = ***substs else { return; }; let ty::GenericArgKind::Type(ty) = first.unpack() else { return; }; - let Ok(pick) = self.lookup_probe( + let Ok(pick) = self.lookup_probe_for_diagnostic( item_name, ty, call_expr, ProbeScope::TraitsInScope, + None, ) else { return; }; let name = self.ty_to_value_string(actual); @@ -1978,12 +2043,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_source: SelfSource<'tcx>, rcvr_ty: Ty<'tcx>, item_name: Ident, + expected: Expectation<'tcx>, ) { let SelfSource::QPath(ty) = self_source else { return; }; for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) { if let Ok(pick) = self.probe_for_name( Mode::Path, item_name, + expected.only_has_type(self), IsSuggestion(true), deref_ty, ty.hir_id, @@ -2048,12 +2115,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty: Ty<'tcx>, call: &hir::Expr<'_>, span: Span, + return_type: Option<Ty<'tcx>>, ) { let output_ty = match self.get_impl_future_output_ty(ty) { Some(output_ty) => self.resolve_vars_if_possible(output_ty), _ => return, }; - let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true); + let method_exists = + self.method_exists(item_name, output_ty, call.hir_id, true, return_type); debug!("suggest_await_before_method: is_method_exist={}", method_exists); if method_exists { err.span_suggestion_verbose( @@ -2167,6 +2236,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )], static_candidates: &[CandidateSource], unsatisfied_bounds: bool, + return_type: Option<Ty<'tcx>>, ) { let mut alt_rcvr_sugg = false; if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) { @@ -2189,7 +2259,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "), (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"), ] { - match self.lookup_probe(item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) { + match self.lookup_probe_for_diagnostic( + item_name, + *rcvr_ty, + rcvr, + ProbeScope::AllTraits, + return_type, + ) { Ok(pick) => { // If the method is defined for the receiver we have, it likely wasn't `use`d. // We point at the method, but we just skip the rest of the check for arbitrary @@ -2222,11 +2298,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"), ] { if let Some(new_rcvr_t) = *rcvr_ty - && let Ok(pick) = self.lookup_probe( + && let Ok(pick) = self.lookup_probe_for_diagnostic( item_name, new_rcvr_t, rcvr, ProbeScope::AllTraits, + return_type, ) { debug!("try_alt_rcvr: pick candidate {:?}", pick); @@ -2592,7 +2669,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; } - let parent = self.tcx.hir().get_parent_node(expr.hir_id); + let parent = self.tcx.hir().parent_id(expr.hir_id); if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) && let hir::ExprKind::MethodCall( hir::PathSegment { ident: method_name, .. }, @@ -2605,11 +2682,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { name: Symbol::intern(&format!("{}_else", method_name.as_str())), span: method_name.span, }; - let probe = self.lookup_probe( + let probe = self.lookup_probe_for_diagnostic( new_name, self_ty, self_expr, ProbeScope::TraitsInScope, + Some(expected), ); // check the method arguments number diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 28e959b7c6a..46799245222 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1,4 +1,4 @@ -use crate::FnCtxt; +use crate::{FnCtxt, RawTy}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ @@ -553,6 +553,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (lhs, Some((true, rhs_ty, rhs_sp))) => one_side_err(rhs_sp, rhs_ty, lhs), _ => span_bug!(span, "Impossible, verified above."), } + if (lhs, rhs).references_error() { + err.downgrade_to_delayed_bug(); + } if self.tcx.sess.teach(&err.get_code().unwrap()) { err.note( "In a match expression, only numbers and characters can be matched \ @@ -692,7 +695,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; if let PatKind::Ref(inner, mutbl) = pat.kind && let PatKind::Binding(_, _, binding, ..) = inner.kind { - let binding_parent_id = tcx.hir().get_parent_node(pat.hir_id); + let binding_parent_id = tcx.hir().parent_id(pat.hir_id); let binding_parent = tcx.hir().get(binding_parent_id); debug!(?inner, ?pat, ?binding_parent); @@ -758,6 +761,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_note(sp, format!("{msg}: `{sugg}`")); } } + hir::Node::Pat(pt) if let PatKind::TupleStruct(_, pat_arr, _) = pt.kind => { + for i in pat_arr.iter() { + if let PatKind::Ref(the_ref, _) = i.kind + && let PatKind::Binding(mt, _, ident, _) = the_ref.kind { + let hir::BindingAnnotation(_, mtblty) = mt; + err.span_suggestion_verbose( + i.span, + format!("consider removing `&{mutability}` from the pattern"), + mtblty.prefix_str().to_string() + &ident.name.to_string(), + Applicability::MaybeIncorrect, + ); + } + } + if let Some((sp, msg, sugg)) = mut_var_suggestion { + err.span_note(sp, format!("{msg}: `{sugg}`")); + } + } hir::Node::Param(_) | hir::Node::Arm(_) | hir::Node::Pat(_) => { // rely on match ergonomics or it might be nested `&&pat` err.span_suggestion_verbose( @@ -839,7 +859,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, pat: &Pat<'tcx>, qpath: &hir::QPath<'_>, - path_resolution: (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]), + path_resolution: (Res, Option<RawTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]), expected: Ty<'tcx>, ti: TopInfo<'tcx>, ) -> Ty<'tcx> { @@ -936,7 +956,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { res.descr(), ), ); - match self.tcx.hir().get(self.tcx.hir().get_parent_node(pat.hir_id)) { + match self.tcx.hir().get_parent(pat.hir_id) { hir::Node::PatField(..) => { e.span_suggestion_verbose( ident.span.shrink_to_hi(), diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index a0f048fc09b..ae0df5aa8f1 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -3,6 +3,7 @@ use crate::{has_expected_num_generic_args, FnCtxt, PlaceOp}; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::InferOk; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast}; @@ -10,7 +11,6 @@ use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutabili use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; -use rustc_trait_selection::autoderef::Autoderef; use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index a9347991e7f..e12a575d5ac 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -663,7 +663,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // fields of some type, the observable drop order will remain the same as it previously // was even though we're dropping each capture individually. // See https://github.com/rust-lang/project-rfc-2229/issues/42 and - // `src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs`. + // `tests/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs`. for (_, captures) in &mut root_var_min_capture_list { captures.sort_by(|capture1, capture2| { for (p1, p2) in capture1.place.projections.iter().zip(&capture2.place.projections) { diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index bb956ddc780..8c24b600644 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -534,8 +534,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { #[instrument(skip(self), level = "debug")] fn visit_opaque_types(&mut self) { - let opaque_types = - self.fcx.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + let opaque_types = self.fcx.infcx.take_opaque_types(); for (opaque_type_key, decl) in opaque_types { let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span); let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span); diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 686cb6dac49..15179392c88 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -1091,7 +1091,7 @@ impl<T: Idx> ToString for BitSet<T> { assert!(mask <= 0xFF); let byte = word & mask; - result.push_str(&format!("{}{:02x}", sep, byte)); + result.push_str(&format!("{sep}{byte:02x}")); if remain <= 8 { break; diff --git a/compiler/rustc_index/src/interval.rs b/compiler/rustc_index/src/interval.rs index 3592fb33077..d809740c6ab 100644 --- a/compiler/rustc_index/src/interval.rs +++ b/compiler/rustc_index/src/interval.rs @@ -135,10 +135,7 @@ impl<I: Idx> IntervalSet<I> { }; debug_assert!( self.check_invariants(), - "wrong intervals after insert {:?}..={:?} to {:?}", - start, - end, - self + "wrong intervals after insert {start:?}..={end:?} to {self:?}" ); result } diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 4430acf34db..033a1842edb 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1,15 +1,18 @@ use hir::GenericParamKind; use rustc_errors::{ fluent, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString, - MultiSpan, SubdiagnosticMessage, + IntoDiagnosticArg, MultiSpan, SubdiagnosticMessage, }; use rustc_hir as hir; -use rustc_hir::{FnRetTy, Ty}; +use rustc_hir::FnRetTy; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_middle::ty::{Region, TyCtxt}; +use rustc_middle::ty::print::TraitRefPrintOnlyTraitPath; +use rustc_middle::ty::{Binder, FnSig, Region, Ty, TyCtxt}; use rustc_span::symbol::kw; +use rustc_span::Symbol; use rustc_span::{symbol::Ident, BytePos, Span}; +use crate::infer::error_reporting::nice_region_error::placeholder_error::Highlighted; use crate::infer::error_reporting::{ need_type_info::{GeneratorKindAsDiagArg, UnderspecifiedArgKind}, ObligationCauseAsDiagArg, @@ -357,8 +360,8 @@ impl AddToDiagnostic for LifetimeMismatchLabels { pub struct AddLifetimeParamsSuggestion<'a> { pub tcx: TyCtxt<'a>, pub sub: Region<'a>, - pub ty_sup: &'a Ty<'a>, - pub ty_sub: &'a Ty<'a>, + pub ty_sup: &'a hir::Ty<'a>, + pub ty_sub: &'a hir::Ty<'a>, pub add_note: bool, } @@ -520,3 +523,411 @@ pub struct MismatchedStaticLifetime<'a> { #[subdiagnostic] pub implicit_static_lifetimes: Vec<ImplicitStaticLifetimeSubdiag>, } + +#[derive(Diagnostic)] +pub enum ExplicitLifetimeRequired<'a> { + #[diag(infer_explicit_lifetime_required_with_ident, code = "E0621")] + WithIdent { + #[primary_span] + #[label] + span: Span, + simple_ident: Ident, + named: String, + #[suggestion( + infer_explicit_lifetime_required_sugg_with_ident, + code = "{new_ty}", + applicability = "unspecified" + )] + new_ty_span: Span, + #[skip_arg] + new_ty: Ty<'a>, + }, + #[diag(infer_explicit_lifetime_required_with_param_type, code = "E0621")] + WithParamType { + #[primary_span] + #[label] + span: Span, + named: String, + #[suggestion( + infer_explicit_lifetime_required_sugg_with_param_type, + code = "{new_ty}", + applicability = "unspecified" + )] + new_ty_span: Span, + #[skip_arg] + new_ty: Ty<'a>, + }, +} + +pub enum TyOrSig<'tcx> { + Ty(Highlighted<'tcx, Ty<'tcx>>), + ClosureSig(Highlighted<'tcx, Binder<'tcx, FnSig<'tcx>>>), +} + +impl IntoDiagnosticArg for TyOrSig<'_> { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + match self { + TyOrSig::Ty(ty) => ty.into_diagnostic_arg(), + TyOrSig::ClosureSig(sig) => sig.into_diagnostic_arg(), + } + } +} + +#[derive(Subdiagnostic)] +pub enum ActualImplExplNotes<'tcx> { + #[note(infer_actual_impl_expl_expected_signature_two)] + ExpectedSignatureTwo { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + lifetime_2: usize, + }, + #[note(infer_actual_impl_expl_expected_signature_any)] + ExpectedSignatureAny { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + }, + #[note(infer_actual_impl_expl_expected_signature_some)] + ExpectedSignatureSome { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + }, + #[note(infer_actual_impl_expl_expected_signature_nothing)] + ExpectedSignatureNothing { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + }, + #[note(infer_actual_impl_expl_expected_passive_two)] + ExpectedPassiveTwo { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + lifetime_2: usize, + }, + #[note(infer_actual_impl_expl_expected_passive_any)] + ExpectedPassiveAny { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + }, + #[note(infer_actual_impl_expl_expected_passive_some)] + ExpectedPassiveSome { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + }, + #[note(infer_actual_impl_expl_expected_passive_nothing)] + ExpectedPassiveNothing { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + }, + #[note(infer_actual_impl_expl_expected_other_two)] + ExpectedOtherTwo { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + lifetime_2: usize, + }, + #[note(infer_actual_impl_expl_expected_other_any)] + ExpectedOtherAny { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + }, + #[note(infer_actual_impl_expl_expected_other_some)] + ExpectedOtherSome { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + }, + #[note(infer_actual_impl_expl_expected_other_nothing)] + ExpectedOtherNothing { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + }, + #[note(infer_actual_impl_expl_but_actually_implements_trait)] + ButActuallyImplementsTrait { + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + has_lifetime: bool, + lifetime: usize, + }, + #[note(infer_actual_impl_expl_but_actually_implemented_for_ty)] + ButActuallyImplementedForTy { + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + has_lifetime: bool, + lifetime: usize, + ty: String, + }, + #[note(infer_actual_impl_expl_but_actually_ty_implements)] + ButActuallyTyImplements { + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + has_lifetime: bool, + lifetime: usize, + ty: String, + }, +} + +pub enum ActualImplExpectedKind { + Signature, + Passive, + Other, +} + +pub enum ActualImplExpectedLifetimeKind { + Two, + Any, + Some, + Nothing, +} + +impl<'tcx> ActualImplExplNotes<'tcx> { + pub fn new_expected( + kind: ActualImplExpectedKind, + lt_kind: ActualImplExpectedLifetimeKind, + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + lifetime_2: usize, + ) -> Self { + match (kind, lt_kind) { + (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Two) => { + Self::ExpectedSignatureTwo { + leading_ellipsis, + ty_or_sig, + trait_path, + lifetime_1, + lifetime_2, + } + } + (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Any) => { + Self::ExpectedSignatureAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } + } + (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Some) => { + Self::ExpectedSignatureSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } + } + (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Nothing) => { + Self::ExpectedSignatureNothing { leading_ellipsis, ty_or_sig, trait_path } + } + (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Two) => { + Self::ExpectedPassiveTwo { + leading_ellipsis, + ty_or_sig, + trait_path, + lifetime_1, + lifetime_2, + } + } + (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Any) => { + Self::ExpectedPassiveAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } + } + (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Some) => { + Self::ExpectedPassiveSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } + } + (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Nothing) => { + Self::ExpectedPassiveNothing { leading_ellipsis, ty_or_sig, trait_path } + } + (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Two) => { + Self::ExpectedOtherTwo { + leading_ellipsis, + ty_or_sig, + trait_path, + lifetime_1, + lifetime_2, + } + } + (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Any) => { + Self::ExpectedOtherAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } + } + (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Some) => { + Self::ExpectedOtherSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } + } + (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Nothing) => { + Self::ExpectedOtherNothing { leading_ellipsis, ty_or_sig, trait_path } + } + } + } +} + +#[derive(Diagnostic)] +#[diag(infer_trait_placeholder_mismatch)] +pub struct TraitPlaceholderMismatch<'tcx> { + #[primary_span] + pub span: Span, + #[label(label_satisfy)] + pub satisfy_span: Option<Span>, + #[label(label_where)] + pub where_span: Option<Span>, + #[label(label_dup)] + pub dup_span: Option<Span>, + pub def_id: String, + pub trait_def_id: String, + + #[subdiagnostic] + pub actual_impl_expl_notes: Vec<ActualImplExplNotes<'tcx>>, +} + +pub struct ConsiderBorrowingParamHelp { + pub spans: Vec<Span>, +} + +impl AddToDiagnostic for ConsiderBorrowingParamHelp { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, f: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + let mut type_param_span: MultiSpan = self.spans.clone().into(); + for &span in &self.spans { + // Seems like we can't call f() here as Into<DiagnosticMessage> is required + type_param_span.push_span_label(span, fluent::infer_tid_consider_borrowing); + } + let msg = f(diag, fluent::infer_tid_param_help.into()); + diag.span_help(type_param_span, msg); + } +} + +#[derive(Subdiagnostic)] +#[help(infer_tid_rel_help)] +pub struct RelationshipHelp; + +#[derive(Diagnostic)] +#[diag(infer_trait_impl_diff)] +pub struct TraitImplDiff { + #[primary_span] + #[label(found)] + pub sp: Span, + #[label(expected)] + pub trait_sp: Span, + #[note(expected_found)] + pub note: (), + #[subdiagnostic] + pub param_help: ConsiderBorrowingParamHelp, + #[subdiagnostic] + // Seems like subdiagnostics are always pushed to the end, so this one + // also has to be a subdiagnostic to maintain order. + pub rel_help: Option<RelationshipHelp>, + pub expected: String, + pub found: String, +} + +pub struct DynTraitConstraintSuggestion { + pub span: Span, + pub ident: Ident, +} + +impl AddToDiagnostic for DynTraitConstraintSuggestion { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, f: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + let mut multi_span: MultiSpan = vec![self.span].into(); + multi_span.push_span_label(self.span, fluent::infer_dtcs_has_lifetime_req_label); + multi_span.push_span_label(self.ident.span, fluent::infer_dtcs_introduces_requirement); + let msg = f(diag, fluent::infer_dtcs_has_req_note.into()); + diag.span_note(multi_span, msg); + let msg = f(diag, fluent::infer_dtcs_suggestion.into()); + diag.span_suggestion_verbose( + self.span.shrink_to_hi(), + msg, + " + '_", + Applicability::MaybeIncorrect, + ); + } +} + +#[derive(Diagnostic)] +#[diag(infer_but_calling_introduces, code = "E0772")] +pub struct ButCallingIntroduces { + #[label(label1)] + pub param_ty_span: Span, + #[primary_span] + #[label(label2)] + pub cause_span: Span, + + pub has_param_name: bool, + pub param_name: String, + pub has_lifetime: bool, + pub lifetime: String, + pub assoc_item: Symbol, + pub has_impl_path: bool, + pub impl_path: String, +} + +pub struct ReqIntroducedLocations { + pub span: MultiSpan, + pub spans: Vec<Span>, + pub fn_decl_span: Span, + pub cause_span: Span, + pub add_label: bool, +} + +impl AddToDiagnostic for ReqIntroducedLocations { + fn add_to_diagnostic_with<F>(mut self, diag: &mut Diagnostic, f: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + for sp in self.spans { + self.span.push_span_label(sp, fluent::infer_ril_introduced_here); + } + + if self.add_label { + self.span.push_span_label(self.fn_decl_span, fluent::infer_ril_introduced_by); + } + self.span.push_span_label(self.cause_span, fluent::infer_ril_because_of); + let msg = f(diag, fluent::infer_ril_static_introduced_by.into()); + diag.span_note(self.span, msg); + } +} + +pub struct MoreTargeted { + pub ident: Symbol, +} + +impl AddToDiagnostic for MoreTargeted { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _f: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + diag.code(rustc_errors::error_code!(E0772)); + diag.set_primary_message(fluent::infer_more_targeted); + diag.set_arg("ident", self.ident); + } +} + +#[derive(Diagnostic)] +#[diag(infer_but_needs_to_satisfy, code = "E0759")] +pub struct ButNeedsToSatisfy { + #[primary_span] + pub sp: Span, + #[label(influencer)] + pub influencer_point: Span, + #[label(used_here)] + pub spans: Vec<Span>, + #[label(require)] + pub require_span_as_label: Option<Span>, + #[note(require)] + pub require_span_as_note: Option<Span>, + #[note(introduced_by_bound)] + pub bound: Option<Span>, + + #[subdiagnostic] + pub req_introduces_loc: Option<ReqIntroducedLocations>, + + pub spans_empty: bool, + pub has_lifetime: bool, + pub lifetime: String, +} diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index e9186540a7b..d816a9ed3d7 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -427,3 +427,15 @@ impl<'tcx> ToTrace<'tcx> for ty::AliasTy<'tcx> { } } } + +impl<'tcx> ToTrace<'tcx> for ty::FnSig<'tcx> { + fn to_trace( + _: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + TypeTrace { cause: cause.clone(), values: Sigs(ExpectedFound::new(a_is_expected, a, b)) } + } +} diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index ec5221379d2..091635e6c73 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -6,8 +6,7 @@ //! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html use crate::infer::canonical::{ - Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Canonicalized, - OriginalQueryValues, + Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, OriginalQueryValues, }; use crate::infer::InferCtxt; use rustc_middle::ty::flags::FlagComputation; @@ -40,7 +39,7 @@ impl<'tcx> InferCtxt<'tcx> { &self, value: V, query_state: &mut OriginalQueryValues<'tcx>, - ) -> Canonicalized<'tcx, V> + ) -> Canonical<'tcx, V> where V: TypeFoldable<'tcx>, { @@ -59,7 +58,7 @@ impl<'tcx> InferCtxt<'tcx> { &self, value: V, query_state: &mut OriginalQueryValues<'tcx>, - ) -> Canonicalized<'tcx, V> + ) -> Canonical<'tcx, V> where V: TypeFoldable<'tcx>, { @@ -99,7 +98,7 @@ impl<'tcx> InferCtxt<'tcx> { /// out the [chapter in the rustc dev guide][c]. /// /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query-result - pub fn canonicalize_response<V>(&self, value: V) -> Canonicalized<'tcx, V> + pub fn canonicalize_response<V>(&self, value: V) -> Canonical<'tcx, V> where V: TypeFoldable<'tcx>, { @@ -113,7 +112,7 @@ impl<'tcx> InferCtxt<'tcx> { ) } - pub fn canonicalize_user_type_annotation<V>(&self, value: V) -> Canonicalized<'tcx, V> + pub fn canonicalize_user_type_annotation<V>(&self, value: V) -> Canonical<'tcx, V> where V: TypeFoldable<'tcx>, { @@ -135,7 +134,7 @@ impl<'tcx> InferCtxt<'tcx> { &self, value: V, query_state: &mut OriginalQueryValues<'tcx>, - ) -> Canonicalized<'tcx, V> + ) -> Canonical<'tcx, V> where V: TypeFoldable<'tcx>, { @@ -524,7 +523,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { tcx: TyCtxt<'tcx>, canonicalize_region_mode: &dyn CanonicalizeMode, query_state: &mut OriginalQueryValues<'tcx>, - ) -> Canonicalized<'tcx, V> + ) -> Canonical<'tcx, V> where V: TypeFoldable<'tcx>, { diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs index a722613e331..3d49182f0b8 100644 --- a/compiler/rustc_infer/src/infer/canonical/query_response.rs +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -9,7 +9,7 @@ use crate::infer::canonical::substitute::{substitute_value, CanonicalExt}; use crate::infer::canonical::{ - Canonical, CanonicalVarValues, CanonicalizedQueryResponse, Certainty, OriginalQueryValues, + Canonical, CanonicalQueryResponse, CanonicalVarValues, Certainty, OriginalQueryValues, QueryOutlivesConstraint, QueryRegionConstraints, QueryResponse, }; use crate::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; @@ -57,7 +57,7 @@ impl<'tcx> InferCtxt<'tcx> { inference_vars: CanonicalVarValues<'tcx>, answer: T, fulfill_cx: &mut dyn TraitEngine<'tcx>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, T>> + ) -> Fallible<CanonicalQueryResponse<'tcx, T>> where T: Debug + TypeFoldable<'tcx>, Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>, @@ -156,10 +156,7 @@ impl<'tcx> InferCtxt<'tcx> { /// As the new solver does canonicalization slightly differently, this is also used there /// for now. This should hopefully change fairly soon. pub fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> { - self.inner - .borrow_mut() - .opaque_type_storage - .take_opaque_types() + std::mem::take(&mut self.inner.borrow_mut().opaque_type_storage.opaque_types) .into_iter() .map(|(k, v)| (self.tcx.mk_opaque(k.def_id.to_def_id(), k.substs), v.hidden_type.ty)) .collect() diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 9a1c49c1aa6..77e38e47fcf 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -219,7 +219,7 @@ impl<'tcx> InferCtxt<'tcx> { /// /// As `3 + 4` contains `N` in its substs, this must not succeed. /// - /// See `src/test/ui/const-generics/occurs-check/` for more examples where this is relevant. + /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant. #[instrument(level = "debug", skip(self))] fn unify_const_variable( &self, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 7222eb77682..533a3c768eb 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1,4 +1,3 @@ -// ignore-tidy-filelength //! Error Reporting Code for the inference engine //! //! Because of the way inference, and in particular region inference, @@ -56,6 +55,7 @@ use crate::infer::ExpectedFound; use crate::traits::error_reporting::report_object_safety_error; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, + PredicateObligation, }; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -92,8 +92,12 @@ pub mod nice_region_error; pub struct TypeErrCtxt<'a, 'tcx> { pub infcx: &'a InferCtxt<'tcx>, pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>, - pub normalize_fn_sig: Box<dyn Fn(ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> + 'a>, pub fallback_has_occurred: bool, + + pub normalize_fn_sig: Box<dyn Fn(ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> + 'a>, + + pub autoderef_steps: + Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, Vec<PredicateObligation<'tcx>>)> + 'a>, } impl TypeErrCtxt<'_, '_> { @@ -303,6 +307,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( None, format!("captures `{}`", hidden_region), None, + Some(reg_info.def_id), ) } } @@ -310,7 +315,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( // Ugh. This is a painful case: the hidden region is not one // that we can easily summarize or explain. This can happen // in a case like - // `src/test/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: + // `tests/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: // // ``` // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> { @@ -1395,7 +1400,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { /// `swap_secondary_and_primary` is used to make projection errors in particular nicer by using /// the message in `secondary_span` as the primary label, and apply the message that would /// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on - /// E0271, like `src/test/ui/issues/issue-39970.stderr`. + /// E0271, like `tests/ui/issues/issue-39970.stderr`. #[instrument( level = "debug", skip(self, diag, secondary_span, swap_secondary_and_primary, prefer_label) @@ -1428,8 +1433,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { impl<'tcx> OpaqueTypesVisitor<'tcx> { fn visit_expected_found( tcx: TyCtxt<'tcx>, - expected: Ty<'tcx>, - found: Ty<'tcx>, + expected: impl TypeVisitable<'tcx>, + found: impl TypeVisitable<'tcx>, ignore_span: Span, ) -> Self { let mut types_visitor = OpaqueTypesVisitor { @@ -1569,6 +1574,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { _ => (false, Mismatch::Fixed("type")), } } + ValuePairs::Sigs(infer::ExpectedFound { expected, found }) => { + OpaqueTypesVisitor::visit_expected_found(self.tcx, expected, found, span) + .report(diag); + (false, Mismatch::Fixed("signature")) + } ValuePairs::TraitRefs(_) | ValuePairs::PolyTraitRefs(_) => { (false, Mismatch::Fixed("trait")) } @@ -1772,9 +1782,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // like when you have two references but one is `usize` and the other // is `f32`. In those cases we still want to show the `note`. If the // value from `ef` is `Infer(_)`, then we ignore it. - if !ef.expected.is_ty_infer() { + if !ef.expected.is_ty_or_numeric_infer() { ef.expected != values.expected - } else if !ef.found.is_ty_infer() { + } else if !ef.found.is_ty_or_numeric_infer() { ef.found != values.found } else { false @@ -2040,6 +2050,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ret => ret, } } + infer::Sigs(exp_found) => { + let exp_found = self.resolve_vars_if_possible(exp_found); + if exp_found.references_error() { + return None; + } + let (exp, fnd) = self.cmp_fn_sig( + &ty::Binder::dummy(exp_found.expected), + &ty::Binder::dummy(exp_found.found), + ); + Some((exp, fnd, None, None)) + } } } @@ -2128,18 +2149,21 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // suggest adding an explicit lifetime bound to it. let generics = self.tcx.generics_of(generic_param_scope); // type_param_span is (span, has_bounds) + let mut is_synthetic = false; + let mut ast_generics = None; let type_param_span = match bound_kind { GenericKind::Param(ref param) => { // Account for the case where `param` corresponds to `Self`, // which doesn't have the expected type argument. if !(generics.has_self && param.index == 0) { let type_param = generics.type_param(param, self.tcx); + is_synthetic = type_param.kind.is_synthetic(); type_param.def_id.as_local().map(|def_id| { // Get the `hir::Param` to verify whether it already has any bounds. // We do this to avoid suggesting code that ends up as `T: 'a'b`, // instead we suggest `T: 'a + 'b` in that case. let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); - let ast_generics = self.tcx.hir().get_generics(hir_id.owner.def_id); + ast_generics = self.tcx.hir().get_generics(hir_id.owner.def_id); let bounds = ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id)); // `sp` only covers `T`, change it so that it covers @@ -2171,11 +2195,64 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .unwrap_or("'lt".to_string()) }; - let add_lt_sugg = generics - .params - .first() - .and_then(|param| param.def_id.as_local()) - .map(|def_id| (self.tcx.def_span(def_id).shrink_to_lo(), format!("{}, ", new_lt))); + let mut add_lt_suggs: Vec<Option<_>> = vec![]; + if is_synthetic { + if let Some(ast_generics) = ast_generics { + let named_lifetime_param_exist = ast_generics.params.iter().any(|p| { + matches!( + p.kind, + hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit } + ) + }); + if named_lifetime_param_exist && let [param, ..] = ast_generics.params + { + add_lt_suggs.push(Some(( + self.tcx.def_span(param.def_id).shrink_to_lo(), + format!("{new_lt}, "), + ))); + } else { + add_lt_suggs + .push(Some((ast_generics.span.shrink_to_hi(), format!("<{new_lt}>")))); + } + } + } else { + if let [param, ..] = &generics.params[..] && let Some(def_id) = param.def_id.as_local() + { + add_lt_suggs + .push(Some((self.tcx.def_span(def_id).shrink_to_lo(), format!("{new_lt}, ")))); + } + } + + if let Some(ast_generics) = ast_generics { + for p in ast_generics.params { + if p.is_elided_lifetime() { + if self + .tcx + .sess + .source_map() + .span_to_prev_source(p.span.shrink_to_hi()) + .ok() + .map_or(false, |s| *s.as_bytes().last().unwrap() == b'&') + { + add_lt_suggs + .push(Some( + ( + p.span.shrink_to_hi(), + if let Ok(snip) = self.tcx.sess.source_map().span_to_next_source(p.span) + && snip.starts_with(' ') + { + format!("{new_lt}") + } else { + format!("{new_lt} ") + } + ) + )); + } else { + add_lt_suggs.push(Some((p.span.shrink_to_hi(), format!("<{new_lt}>")))); + } + } + } + } let labeled_user_string = match bound_kind { GenericKind::Param(ref p) => format!("the parameter type `{}`", p), @@ -2199,20 +2276,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); } - fn binding_suggestion<S: fmt::Display>( + fn binding_suggestion<'tcx, S: fmt::Display>( err: &mut Diagnostic, type_param_span: Option<(Span, bool)>, - bound_kind: GenericKind<'_>, + bound_kind: GenericKind<'tcx>, sub: S, - add_lt_sugg: Option<(Span, String)>, + add_lt_suggs: Vec<Option<(Span, String)>>, ) { let msg = "consider adding an explicit lifetime bound"; if let Some((sp, has_lifetimes)) = type_param_span { let suggestion = if has_lifetimes { format!(" + {}", sub) } else { format!(": {}", sub) }; let mut suggestions = vec![(sp, suggestion)]; - if let Some(add_lt_sugg) = add_lt_sugg { - suggestions.push(add_lt_sugg); + for add_lt_sugg in add_lt_suggs { + if let Some(add_lt_sugg) = add_lt_sugg { + suggestions.push(add_lt_sugg); + } } err.multipart_suggestion_verbose( format!("{msg}..."), @@ -2236,9 +2315,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; let mut sugg = vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {}", new_lt))]; - if let Some(lt) = add_lt_sugg.clone() { - sugg.push(lt); - sugg.rotate_right(1); + for add_lt_sugg in add_lt_suggs.clone() { + if let Some(lt) = add_lt_sugg { + sugg.push(lt); + sugg.rotate_right(1); + } } // `MaybeIncorrect` due to issue #41966. err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect); @@ -2342,7 +2423,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // for the bound is not suitable for suggestions when `-Zverbose` is set because it // uses `Debug` output, so we handle it specially here so that suggestions are // always correct. - binding_suggestion(&mut err, type_param_span, bound_kind, name, None); + binding_suggestion(&mut err, type_param_span, bound_kind, name, vec![]); err } @@ -2355,7 +2436,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { "{} may not live long enough", labeled_user_string ); - binding_suggestion(&mut err, type_param_span, bound_kind, "'static", None); + binding_suggestion(&mut err, type_param_span, bound_kind, "'static", vec![]); err } @@ -2394,7 +2475,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { type_param_span, bound_kind, new_lt, - add_lt_sugg, + add_lt_suggs, ); } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index a4c36b4c9cd..b8c843a8a5a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -78,7 +78,7 @@ impl InferenceDiagnosticsData { } fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str { - if in_type.is_ty_infer() { + if in_type.is_ty_or_numeric_infer() { "" } else if self.name == "_" { // FIXME: Consider specializing this message if there is a single `_` @@ -195,12 +195,12 @@ fn ty_to_string<'tcx>( // invalid pseudo-syntax, we want the `fn`-pointer output instead. (ty::FnDef(..), _) => ty.fn_sig(infcx.tcx).print(printer).unwrap().into_buffer(), (_, Some(def_id)) - if ty.is_ty_infer() + if ty.is_ty_or_numeric_infer() && infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn) == Some(def_id) => { "Vec<_>".to_string() } - _ if ty.is_ty_infer() => "/* Type */".to_string(), + _ if ty.is_ty_or_numeric_infer() => "/* Type */".to_string(), // FIXME: The same thing for closures, but this only works when the closure // does not capture anything. // @@ -680,7 +680,7 @@ impl<'tcx> InferSourceKind<'tcx> { | InferSourceKind::ClosureReturn { ty, .. } => { if ty.is_closure() { ("closure", closure_as_fn_str(infcx, ty)) - } else if !ty.is_ty_infer() { + } else if !ty.is_ty_or_numeric_infer() { ("normal", ty_to_string(infcx, ty, None)) } else { ("other", String::new()) @@ -813,7 +813,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { self.attempt += 1; if let Some(InferSource { kind: InferSourceKind::GenericArg { def_id: did, ..}, .. }) = self.infer_source && let InferSourceKind::LetBinding { ref ty, ref mut def_id, ..} = new_source.kind - && ty.is_ty_infer() + && ty.is_ty_or_numeric_infer() { // Customize the output so we talk about `let x: Vec<_> = iter.collect();` instead of // `let x: _ = iter.collect();`, as this is a very common case. diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs index 8a0e332f9c7..59fb74eb543 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -9,7 +9,7 @@ mod different_lifetimes; pub mod find_anon_type; mod mismatched_static_lifetime; mod named_anon_conflict; -mod placeholder_error; +pub(crate) mod placeholder_error; mod placeholder_relation; mod static_impl_trait; mod trait_impl_difference; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs index 3fe7c1598fc..4e13ec90228 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -1,8 +1,11 @@ //! Error Reporting for Anonymous Region Lifetime Errors //! where one region is named and the other is anonymous. -use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use crate::{ + errors::ExplicitLifetimeRequired, + infer::error_reporting::nice_region_error::find_anon_type::find_anon_type, +}; +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; use rustc_middle::ty; use rustc_span::symbol::kw; @@ -86,31 +89,17 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { { return None; } - - let (error_var, span_label_var) = match param.pat.simple_ident() { - Some(simple_ident) => ( - format!("the type of `{}`", simple_ident), - format!("the type of `{}`", simple_ident), - ), - None => ("parameter type".to_owned(), "type".to_owned()), + let named = named.to_string(); + let err = match param.pat.simple_ident() { + Some(simple_ident) => ExplicitLifetimeRequired::WithIdent { + span, + simple_ident, + named, + new_ty_span, + new_ty, + }, + None => ExplicitLifetimeRequired::WithParamType { span, named, new_ty_span, new_ty }, }; - - let mut diag = struct_span_err!( - self.tcx().sess, - span, - E0621, - "explicit lifetime required in {}", - error_var - ); - - diag.span_label(span, format!("lifetime `{}` required", named)); - diag.span_suggestion( - new_ty_span, - &format!("add explicit lifetime `{}` to {}", named, span_label_var), - new_ty, - Applicability::Unspecified, - ); - - Some(diag) + Some(self.tcx().sess.parse_sess.create_err(err)) } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs index fed9fda74bf..202f39521e9 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -1,10 +1,14 @@ +use crate::errors::{ + ActualImplExpectedKind, ActualImplExpectedLifetimeKind, ActualImplExplNotes, + TraitPlaceholderMismatch, TyOrSig, +}; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::ValuePairs; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCause, ObligationCauseCode}; use rustc_data_structures::intern::Interned; -use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, IntoDiagnosticArg}; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; use rustc_middle::ty::error::ExpectedFound; @@ -12,7 +16,43 @@ use rustc_middle::ty::print::{FmtPrinter, Print, RegionHighlightMode}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, RePlaceholder, ReVar, Region, TyCtxt}; -use std::fmt::{self, Write}; +use std::fmt; + +// HACK(eddyb) maybe move this in a more central location. +#[derive(Copy, Clone)] +pub struct Highlighted<'tcx, T> { + tcx: TyCtxt<'tcx>, + highlight: RegionHighlightMode<'tcx>, + value: T, +} + +impl<'tcx, T> IntoDiagnosticArg for Highlighted<'tcx, T> +where + T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>, Error = fmt::Error, Output = FmtPrinter<'a, 'tcx>>, +{ + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + rustc_errors::DiagnosticArgValue::Str(self.to_string().into()) + } +} + +impl<'tcx, T> Highlighted<'tcx, T> { + fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> { + Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) } + } +} + +impl<'tcx, T> fmt::Display for Highlighted<'tcx, T> +where + T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>, Error = fmt::Error, Output = FmtPrinter<'a, 'tcx>>, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS); + printer.region_highlight_mode = self.highlight; + + let s = self.value.print(printer)?.into_buffer(); + f.write_str(&s) + } +} impl<'tcx> NiceRegionError<'_, 'tcx> { /// When given a `ConcreteFailure` for a function with arguments containing a named region and @@ -205,26 +245,21 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { actual_substs: SubstsRef<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let span = cause.span(); - let msg = format!( - "implementation of `{}` is not general enough", - self.tcx().def_path_str(trait_def_id), - ); - let mut err = self.tcx().sess.struct_span_err(span, &msg); - - let leading_ellipsis = if let ObligationCauseCode::ItemObligation(def_id) - | ObligationCauseCode::ExprItemObligation(def_id, ..) = - *cause.code() - { - err.span_label(span, "doesn't satisfy where-clause"); - err.span_label( - self.tcx().def_span(def_id), - &format!("due to a where-clause on `{}`...", self.tcx().def_path_str(def_id)), - ); - true - } else { - err.span_label(span, &msg); - false - }; + + let (leading_ellipsis, satisfy_span, where_span, dup_span, def_id) = + if let ObligationCauseCode::ItemObligation(def_id) + | ObligationCauseCode::ExprItemObligation(def_id, ..) = *cause.code() + { + ( + true, + Some(span), + Some(self.tcx().def_span(def_id)), + None, + self.tcx().def_path_str(def_id), + ) + } else { + (false, None, None, Some(span), String::new()) + }; let expected_trait_ref = self .cx @@ -284,8 +319,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { ?expected_self_ty_has_vid, ); - self.explain_actual_impl_that_was_found( - &mut err, + let actual_impl_expl_notes = self.explain_actual_impl_that_was_found( sub_placeholder, sup_placeholder, has_sub, @@ -299,7 +333,15 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { leading_ellipsis, ); - err + self.tcx().sess.create_err(TraitPlaceholderMismatch { + span, + satisfy_span, + where_span, + dup_span, + def_id, + trait_def_id: self.tcx().def_path_str(trait_def_id), + actual_impl_expl_notes, + }) } /// Add notes with details about the expected and actual trait refs, with attention to cases @@ -309,7 +351,6 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { /// due to the number of combinations we have to deal with. fn explain_actual_impl_that_was_found( &self, - err: &mut Diagnostic, sub_placeholder: Option<Region<'tcx>>, sup_placeholder: Option<Region<'tcx>>, has_sub: Option<usize>, @@ -321,39 +362,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { actual_has_vid: Option<usize>, any_self_ty_has_vid: bool, leading_ellipsis: bool, - ) { - // HACK(eddyb) maybe move this in a more central location. - #[derive(Copy, Clone)] - struct Highlighted<'tcx, T> { - tcx: TyCtxt<'tcx>, - highlight: RegionHighlightMode<'tcx>, - value: T, - } - - impl<'tcx, T> Highlighted<'tcx, T> { - fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> { - Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) } - } - } - - impl<'tcx, T> fmt::Display for Highlighted<'tcx, T> - where - T: for<'a> Print< - 'tcx, - FmtPrinter<'a, 'tcx>, - Error = fmt::Error, - Output = FmtPrinter<'a, 'tcx>, - >, - { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS); - printer.region_highlight_mode = self.highlight; - - let s = self.value.print(printer)?.into_buffer(); - f.write_str(&s) - } - } - + ) -> Vec<ActualImplExplNotes<'tcx>> { // The weird thing here with the `maybe_highlighting_region` calls and the // the match inside is meant to be like this: // @@ -380,120 +389,110 @@ impl<'tcx> NiceRegionError<'_, 'tcx> { let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref); expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub); expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup); - err.note(&{ - let passive_voice = match (has_sub, has_sup) { - (Some(_), _) | (_, Some(_)) => any_self_ty_has_vid, - (None, None) => { - expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid); - match expected_has_vid { - Some(_) => true, - None => any_self_ty_has_vid, - } - } - }; - let mut note = if same_self_type { - let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty()); - self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid); - - if self_ty.value.is_closure() - && self.tcx().is_fn_trait(expected_trait_ref.value.def_id) - { - let closure_sig = self_ty.map(|closure| { - if let ty::Closure(_, substs) = closure.kind() { - self.tcx().signature_unclosure( - substs.as_closure().sig(), - rustc_hir::Unsafety::Normal, - ) - } else { - bug!("type is not longer closure"); - } - }); - - format!( - "{}closure with signature `{}` must implement `{}`", - if leading_ellipsis { "..." } else { "" }, - closure_sig, - expected_trait_ref.map(|tr| tr.print_only_trait_path()), - ) - } else { - format!( - "{}`{}` must implement `{}`", - if leading_ellipsis { "..." } else { "" }, - self_ty, - expected_trait_ref.map(|tr| tr.print_only_trait_path()), - ) + let passive_voice = match (has_sub, has_sup) { + (Some(_), _) | (_, Some(_)) => any_self_ty_has_vid, + (None, None) => { + expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid); + match expected_has_vid { + Some(_) => true, + None => any_self_ty_has_vid, } - } else if passive_voice { - format!( - "{}`{}` would have to be implemented for the type `{}`", - if leading_ellipsis { "..." } else { "" }, + } + }; + + let (kind, ty_or_sig, trait_path) = if same_self_type { + let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty()); + self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid); + + if self_ty.value.is_closure() && self.tcx().is_fn_trait(expected_trait_ref.value.def_id) + { + let closure_sig = self_ty.map(|closure| { + if let ty::Closure(_, substs) = closure.kind() { + self.tcx().signature_unclosure( + substs.as_closure().sig(), + rustc_hir::Unsafety::Normal, + ) + } else { + bug!("type is not longer closure"); + } + }); + ( + ActualImplExpectedKind::Signature, + TyOrSig::ClosureSig(closure_sig), expected_trait_ref.map(|tr| tr.print_only_trait_path()), - expected_trait_ref.map(|tr| tr.self_ty()), ) } else { - format!( - "{}`{}` must implement `{}`", - if leading_ellipsis { "..." } else { "" }, - expected_trait_ref.map(|tr| tr.self_ty()), + ( + ActualImplExpectedKind::Other, + TyOrSig::Ty(self_ty), expected_trait_ref.map(|tr| tr.print_only_trait_path()), ) - }; + } + } else if passive_voice { + ( + ActualImplExpectedKind::Passive, + TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())), + expected_trait_ref.map(|tr| tr.print_only_trait_path()), + ) + } else { + ( + ActualImplExpectedKind::Other, + TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())), + expected_trait_ref.map(|tr| tr.print_only_trait_path()), + ) + }; - match (has_sub, has_sup) { - (Some(n1), Some(n2)) => { - let _ = write!( - note, - ", for any two lifetimes `'{}` and `'{}`...", - std::cmp::min(n1, n2), - std::cmp::max(n1, n2), - ); - } - (Some(n), _) | (_, Some(n)) => { - let _ = write!(note, ", for any lifetime `'{}`...", n,); - } - (None, None) => { - if let Some(n) = expected_has_vid { - let _ = write!(note, ", for some specific lifetime `'{}`...", n,); - } + let (lt_kind, lifetime_1, lifetime_2) = match (has_sub, has_sup) { + (Some(n1), Some(n2)) => { + (ActualImplExpectedLifetimeKind::Two, std::cmp::min(n1, n2), std::cmp::max(n1, n2)) + } + (Some(n), _) | (_, Some(n)) => (ActualImplExpectedLifetimeKind::Any, n, 0), + (None, None) => { + if let Some(n) = expected_has_vid { + (ActualImplExpectedLifetimeKind::Some, n, 0) + } else { + (ActualImplExpectedLifetimeKind::Nothing, 0, 0) } } + }; - note - }); + let note_1 = ActualImplExplNotes::new_expected( + kind, + lt_kind, + leading_ellipsis, + ty_or_sig, + trait_path, + lifetime_1, + lifetime_2, + ); let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref); actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid); - err.note(&{ - let passive_voice = match actual_has_vid { - Some(_) => any_self_ty_has_vid, - None => true, - }; - let mut note = if same_self_type { - format!( - "...but it actually implements `{}`", - actual_trait_ref.map(|tr| tr.print_only_trait_path()), - ) - } else if passive_voice { - format!( - "...but `{}` is actually implemented for the type `{}`", - actual_trait_ref.map(|tr| tr.print_only_trait_path()), - actual_trait_ref.map(|tr| tr.self_ty()), - ) - } else { - format!( - "...but `{}` actually implements `{}`", - actual_trait_ref.map(|tr| tr.self_ty()), - actual_trait_ref.map(|tr| tr.print_only_trait_path()), - ) - }; + let passive_voice = match actual_has_vid { + Some(_) => any_self_ty_has_vid, + None => true, + }; - if let Some(n) = actual_has_vid { - let _ = write!(note, ", for some specific lifetime `'{}`", n); + let trait_path = actual_trait_ref.map(|tr| tr.print_only_trait_path()); + let ty = actual_trait_ref.map(|tr| tr.self_ty()).to_string(); + let has_lifetime = actual_has_vid.is_some(); + let lifetime = actual_has_vid.unwrap_or_default(); + + let note_2 = if same_self_type { + ActualImplExplNotes::ButActuallyImplementsTrait { trait_path, has_lifetime, lifetime } + } else if passive_voice { + ActualImplExplNotes::ButActuallyImplementedForTy { + trait_path, + ty, + has_lifetime, + lifetime, } + } else { + ActualImplExplNotes::ButActuallyTyImplements { trait_path, ty, has_lifetime, lifetime } + }; - note - }); + vec![note_1, note_2] } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index d9cdfa9dd4f..fb0f09198cc 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -1,20 +1,28 @@ //! Error Reporting for static impl Traits. +use crate::errors::{ + ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted, + ReqIntroducedLocations, +}; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_ty, Visitor}; -use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind}; +use rustc_hir::{ + self as hir, GenericBound, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, Node, + TyKind, +}; use rustc_middle::ty::{ self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, }; use rustc_span::symbol::Ident; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; use std::ops::ControlFlow; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { @@ -49,46 +57,32 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } let param = self.find_param_with_region(*sup_r, *sub_r)?; - let lifetime = if sup_r.has_name() { - format!("lifetime `{}`", sup_r) - } else { - "an anonymous lifetime `'_`".to_string() + let simple_ident = param.param.pat.simple_ident(); + + let (has_impl_path, impl_path) = match ctxt.assoc_item.container { + AssocItemContainer::TraitContainer => { + let id = ctxt.assoc_item.container_id(tcx); + (true, tcx.def_path_str(id)) + } + AssocItemContainer::ImplContainer => (false, String::new()), }; - let mut err = struct_span_err!( - tcx.sess, - cause.span, - E0772, - "{} has {} but calling `{}` introduces an implicit `'static` lifetime \ - requirement", - param - .param - .pat - .simple_ident() - .map(|s| format!("`{}`", s)) - .unwrap_or_else(|| "`fn` parameter".to_string()), - lifetime, - ctxt.assoc_item.name, - ); - err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime)); - err.span_label( - cause.span, - &format!( - "...is used and required to live as long as `'static` here \ - because of an implicit lifetime bound on the {}", - match ctxt.assoc_item.container { - AssocItemContainer::TraitContainer => { - let id = ctxt.assoc_item.container_id(tcx); - format!("`impl` of `{}`", tcx.def_path_str(id)) - } - AssocItemContainer::ImplContainer => "inherent `impl`".to_string(), - }, - ), - ); + + let mut err = self.tcx().sess.create_err(ButCallingIntroduces { + param_ty_span: param.param_ty_span, + cause_span: cause.span, + has_param_name: simple_ident.is_some(), + param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(), + has_lifetime: sup_r.has_name(), + lifetime: sup_r.to_string(), + assoc_item: ctxt.assoc_item.name, + has_impl_path, + impl_path, + }); if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) { let reported = err.emit(); return Some(reported); } else { - err.cancel(); + err.cancel() } } return None; @@ -104,25 +98,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let sp = var_origin.span(); let return_sp = sub_origin.span(); let param = self.find_param_with_region(*sup_r, *sub_r)?; - let (lifetime_name, lifetime) = if sup_r.has_name() { - (sup_r.to_string(), format!("lifetime `{}`", sup_r)) - } else { - ("'_".to_owned(), "an anonymous lifetime `'_`".to_string()) - }; - let param_name = param - .param - .pat - .simple_ident() - .map(|s| format!("`{}`", s)) - .unwrap_or_else(|| "`fn` parameter".to_string()); - let mut err = struct_span_err!( - tcx.sess, - sp, - E0759, - "{} has {} but it needs to satisfy a `'static` lifetime requirement", - param_name, - lifetime, - ); + let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() }; let (mention_influencer, influencer_point) = if sup_origin.span().overlaps(param.param_ty_span) { @@ -141,7 +117,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } else { (!sup_origin.span().overlaps(return_sp), param.param_ty_span) }; - err.span_label(influencer_point, &format!("this data with {}...", lifetime)); debug!("try_report_static_impl_trait: param_info={:?}", param); @@ -155,31 +130,19 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { spans.dedup_by_key(|span| (span.lo(), span.hi())); // We try to make the output have fewer overlapping spans if possible. - let require_msg = if spans.is_empty() { - "...is used and required to live as long as `'static` here" - } else { - "...and is required to live as long as `'static` here" - }; let require_span = if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp }; - for span in &spans { - err.span_label(*span, "...is used here..."); - } - - if spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp) { - // If any of the "captured here" labels appears on the same line or after - // `require_span`, we put it on a note to ensure the text flows by appearing - // always at the end. - err.span_note(require_span, require_msg); + let spans_empty = spans.is_empty(); + let require_as_note = spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp); + let bound = if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin { + Some(*bound) } else { - // We don't need a note, it's already at the end, it can be shown as a `span_label`. - err.span_label(require_span, require_msg); - } + None + }; + + let mut subdiag = None; - if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin { - err.span_note(*bound, "`'static` lifetime requirement introduced by this bound"); - } if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin { if let ObligationCauseCode::ReturnValue(hir_id) | ObligationCauseCode::BlockTailExpression(hir_id) = cause.code() @@ -187,33 +150,50 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let parent_id = tcx.hir().get_parent_item(*hir_id); if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) { let mut span: MultiSpan = fn_decl.output.span().into(); + let mut spans = Vec::new(); let mut add_label = true; if let hir::FnRetTy::Return(ty) = fn_decl.output { let mut v = StaticLifetimeVisitor(vec![], tcx.hir()); v.visit_ty(ty); if !v.0.is_empty() { span = v.0.clone().into(); - for sp in v.0 { - span.push_span_label(sp, "`'static` requirement introduced here"); - } + spans = v.0; add_label = false; } } - if add_label { - span.push_span_label( - fn_decl.output.span(), - "requirement introduced by this return type", - ); - } - span.push_span_label(cause.span, "because of this returned expression"); - err.span_note( + let fn_decl_span = fn_decl.output.span(); + + subdiag = Some(ReqIntroducedLocations { span, - "`'static` lifetime requirement introduced by the return type", - ); + spans, + fn_decl_span, + cause_span: cause.span, + add_label, + }); } } } + let diag = ButNeedsToSatisfy { + sp, + influencer_point, + spans: spans.clone(), + // If any of the "captured here" labels appears on the same line or after + // `require_span`, we put it on a note to ensure the text flows by appearing + // always at the end. + require_span_as_note: require_as_note.then_some(require_span), + // We don't need a note, it's already at the end, it can be shown as a `span_label`. + require_span_as_label: (!require_as_note).then_some(require_span), + req_introduces_loc: subdiag, + + has_lifetime: sup_r.has_name(), + lifetime: sup_r.to_string(), + spans_empty, + bound, + }; + + let mut err = self.tcx().sess.create_err(diag); + let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); let mut override_error_code = None; @@ -247,12 +227,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) { // Provide a more targeted error code and description. - err.code(rustc_errors::error_code!(E0772)); - err.set_primary_message(&format!( - "{} has {} but calling `{}` introduces an implicit `'static` lifetime \ - requirement", - param_name, lifetime, ident, - )); + let retarget_subdiag = MoreTargeted { ident }; + retarget_subdiag.add_to_diagnostic(&mut err); } let arg = match param.param.pat.simple_ident() { @@ -268,6 +244,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { Some(arg), captures, Some((param.param_ty_span, param.param_ty.to_string())), + Some(anon_reg_sup.def_id), ); let reported = err.emit(); @@ -283,6 +260,7 @@ pub fn suggest_new_region_bound( arg: Option<String>, captures: String, param: Option<(Span, String)>, + scope_def_id: Option<LocalDefId>, ) { debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns); // FIXME: account for the need of parens in `&(dyn Trait + '_)` @@ -340,12 +318,69 @@ pub fn suggest_new_region_bound( _ => false, }) { } else { - err.span_suggestion_verbose( - fn_return.span.shrink_to_hi(), - &format!("{declare} `{ty}` {captures}, {explicit}",), - &plus_lt, - Applicability::MaybeIncorrect, - ); + // get a lifetime name of existing named lifetimes if any + let existing_lt_name = if let Some(id) = scope_def_id + && let Some(generics) = tcx.hir().get_generics(id) + && let named_lifetimes = generics + .params + .iter() + .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit })) + .map(|p| { if let hir::ParamName::Plain(name) = p.name {Some(name.to_string())} else {None}}) + .filter(|n| ! matches!(n, None)) + .collect::<Vec<_>>() + && named_lifetimes.len() > 0 { + named_lifetimes[0].clone() + } else { + None + }; + let name = if let Some(name) = &existing_lt_name { + format!("{}", name) + } else { + format!("'a") + }; + // if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used. + // introducing a new lifetime `'a` or making use of one from existing named lifetimes if any + if let Some(id) = scope_def_id + && let Some(generics) = tcx.hir().get_generics(id) + && let mut spans_suggs = generics + .params + .iter() + .filter(|p| p.is_elided_lifetime()) + .map(|p| + if p.span.hi() - p.span.lo() == rustc_span::BytePos(1) { // Ampersand (elided without '_) + (p.span.shrink_to_hi(),format!("{name} ")) + } else { // Underscore (elided with '_) + (p.span, format!("{name}")) + } + ) + .collect::<Vec<_>>() + && spans_suggs.len() > 1 + { + let use_lt = + if existing_lt_name == None { + spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>"))); + format!("you can introduce a named lifetime parameter `{name}`") + } else { + // make use the existing named lifetime + format!("you can use the named lifetime parameter `{name}`") + }; + spans_suggs + .push((fn_return.span.shrink_to_hi(), format!(" + {name} "))); + err.multipart_suggestion_verbose( + &format!( + "{declare} `{ty}` {captures}, {use_lt}", + ), + spans_suggs, + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion_verbose( + fn_return.span.shrink_to_hi(), + &format!("{declare} `{ty}` {captures}, {explicit}",), + &plus_lt, + Applicability::MaybeIncorrect, + ); + } } } TyKind::TraitObject(_, lt, _) => { @@ -488,21 +523,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); hir_v.visit_ty(&self_ty); - for span in &traits { - let mut multi_span: MultiSpan = vec![*span].into(); - multi_span - .push_span_label(*span, "this has an implicit `'static` lifetime requirement"); - multi_span.push_span_label( - ident.span, - "calling this method introduces the `impl`'s 'static` requirement", - ); - err.span_note(multi_span, "the used `impl` has a `'static` requirement"); - err.span_suggestion_verbose( - span.shrink_to_hi(), - "consider relaxing the implicit `'static` requirement", - " + '_", - Applicability::MaybeIncorrect, - ); + for &span in &traits { + let subdiag = DynTraitConstraintSuggestion { span, ident }; + subdiag.add_to_diagnostic(err); suggested = true; } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs index dc1dc898922..40c0c806e1f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -1,15 +1,17 @@ //! Error Reporting for `impl` items that do not match the obligations from their `trait`. +use crate::errors::{ConsiderBorrowingParamHelp, RelationshipHelp, TraitImplDiff}; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; -use crate::infer::Subtype; +use crate::infer::{Subtype, ValuePairs}; use crate::traits::ObligationCauseCode::CompareImplItemObligation; -use rustc_errors::{ErrorGuaranteed, MultiSpan}; +use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; +use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::print::RegionHighlightMode; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; use rustc_span::Span; @@ -22,22 +24,27 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let error = self.error.as_ref()?; debug!("try_report_impl_not_conforming_to_trait {:?}", error); if let RegionResolutionError::SubSupConflict( - _, - var_origin, - sub_origin, - _sub, - sup_origin, - _sup, - _, - ) = error.clone() + _, + var_origin, + sub_origin, + _sub, + sup_origin, + _sup, + _, + ) = error.clone() && let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin) - && let sub_expected_found @ Some((sub_expected, sub_found)) = sub_trace.values.ty() - && let sup_expected_found @ Some(_) = sup_trace.values.ty() && let CompareImplItemObligation { trait_item_def_id, .. } = sub_trace.cause.code() - && sup_expected_found == sub_expected_found + && sub_trace.values == sup_trace.values + && let ValuePairs::Sigs(ExpectedFound { expected, found }) = sub_trace.values { - let guar = - self.emit_err(var_origin.span(), sub_expected, sub_found, *trait_item_def_id); + // FIXME(compiler-errors): Don't like that this needs `Ty`s, but + // all of the region highlighting machinery only deals with those. + let guar = self.emit_err( + var_origin.span(), + self.cx.tcx.mk_fn_ptr(ty::Binder::dummy(expected)), + self.cx.tcx.mk_fn_ptr(ty::Binder::dummy(found)), + *trait_item_def_id, + ); return Some(guar); } None @@ -51,10 +58,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { trait_def_id: DefId, ) -> ErrorGuaranteed { let trait_sp = self.tcx().def_span(trait_def_id); - let mut err = self - .tcx() - .sess - .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature"); // Mark all unnamed regions in the type with a number. // This diagnostic is called in response to lifetime errors, so be informative. @@ -91,9 +94,6 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let found = self.cx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name; - err.span_label(sp, &format!("found `{}`", found)); - err.span_label(trait_sp, &format!("expected `{}`", expected)); - // Get the span of all the used type parameters in the method. let assoc_item = self.tcx().associated_item(trait_def_id); let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] }; @@ -110,26 +110,18 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } _ => {} } - let mut type_param_span: MultiSpan = visitor.types.to_vec().into(); - for &span in &visitor.types { - type_param_span - .push_span_label(span, "consider borrowing this type parameter in the trait"); - } - err.note(&format!("expected `{}`\n found `{}`", expected, found)); - - err.span_help( - type_param_span, - "the lifetime requirements from the `impl` do not correspond to the requirements in \ - the `trait`", - ); - if visitor.types.is_empty() { - err.help( - "verify the lifetime relationships in the `trait` and `impl` between the `self` \ - argument, the other inputs and its output", - ); - } - err.emit() + let diag = TraitImplDiff { + sp, + trait_sp, + note: (), + param_help: ConsiderBorrowingParamHelp { spans: visitor.types.to_vec() }, + rel_help: visitor.types.is_empty().then_some(RelationshipHelp), + expected, + found, + }; + + self.tcx().sess.emit_err(diag) } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index d91ef882bc4..7504ed094a3 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -25,29 +25,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer::Reborrow(span) => { RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diagnostic(err) } - infer::ReborrowUpvar(span, ref upvar_id) => { - let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); - RegionOriginNote::WithName { - span, - msg: fluent::infer_reborrow, - name: &var_name.to_string(), - continues: false, - } - .add_to_diagnostic(err); - } infer::RelateObjectBound(span) => { RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } .add_to_diagnostic(err); } - infer::DataBorrowed(ty, span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_data_borrowed, - name: &self.ty_to_string(ty), - continues: false, - } - .add_to_diagnostic(err); - } infer::ReferenceOutlivesReferent(ty, span) => { RegionOriginNote::WithName { span, @@ -162,33 +143,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); err } - infer::ReborrowUpvar(span, ref upvar_id) => { - let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0313, - "lifetime of borrowed pointer outlives lifetime of captured variable `{}`...", - var_name - ); - note_and_explain_region( - self.tcx, - &mut err, - "...the borrowed pointer is valid for ", - sub, - "...", - None, - ); - note_and_explain_region( - self.tcx, - &mut err, - &format!("...but `{}` is only valid for ", var_name), - sup, - "", - None, - ); - err - } infer::RelateObjectBound(span) => { let mut err = struct_span_err!( self.tcx.sess, @@ -264,32 +218,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); err } - infer::DataBorrowed(ty, span) => { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0490, - "a value of type `{}` is borrowed for too long", - self.ty_to_string(ty) - ); - note_and_explain_region( - self.tcx, - &mut err, - "the type is valid for ", - sub, - "", - None, - ); - note_and_explain_region( - self.tcx, - &mut err, - "but the borrow lasts for ", - sup, - "", - None, - ); - err - } infer::ReferenceOutlivesReferent(ty, span) => { let mut err = struct_span_err!( self.tcx.sess, diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 30ca9f41d6e..5b02956a106 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -411,7 +411,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { span: Span, ) { let hir = self.tcx.hir(); - let fn_hir_id = hir.get_parent_node(cause.body_id); + let fn_hir_id = hir.parent_id(cause.body_id); if let Some(node) = self.tcx.hir().find(fn_hir_id) && let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_sig, _, body_id), .. @@ -585,45 +585,42 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let hir::StmtKind::Local(local) = &stmt.kind else { continue; }; local.pat.walk(&mut find_compatible_candidates); } - match hir.find(hir.get_parent_node(blk.hir_id)) { - Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => { - match hir.find(hir.get_parent_node(*hir_id)) { - Some(hir::Node::Arm(hir::Arm { pat, .. })) => { - pat.walk(&mut find_compatible_candidates); - } - Some( - hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. }) - | hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(_, body), - .. - }) - | hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)), - .. - }) - | hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { body, .. }), - .. - }), - ) => { - for param in hir.body(*body).params { - param.pat.walk(&mut find_compatible_candidates); - } - } - Some(hir::Node::Expr(hir::Expr { - kind: - hir::ExprKind::If( - hir::Expr { kind: hir::ExprKind::Let(let_), .. }, - then_block, - _, - ), + match hir.find_parent(blk.hir_id) { + Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => match hir.find_parent(*hir_id) { + Some(hir::Node::Arm(hir::Arm { pat, .. })) => { + pat.walk(&mut find_compatible_candidates); + } + Some( + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(_, body), .. + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)), + .. + }) + | hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { body, .. }), .. - })) if then_block.hir_id == *hir_id => { - let_.pat.walk(&mut find_compatible_candidates); + }), + ) => { + for param in hir.body(*body).params { + param.pat.walk(&mut find_compatible_candidates); } - _ => {} } - } + Some(hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::If( + hir::Expr { kind: hir::ExprKind::Let(let_), .. }, + then_block, + _, + ), + .. + })) if then_block.hir_id == *hir_id => { + let_.pat.walk(&mut find_compatible_candidates); + } + _ => {} + }, _ => {} } diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index da2c6fbc05f..897545046c3 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -702,26 +702,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // Obtain the spans for all the places that can // influence the constraints on this value for // richer diagnostics in `static_impl_trait`. - let influences: Vec<Span> = self - .data - .constraints - .iter() - .filter_map(|(constraint, origin)| match (constraint, origin) { - ( - Constraint::VarSubVar(_, sup), - SubregionOrigin::DataBorrowed(_, sp), - ) if sup == &node_vid => Some(*sp), - _ => None, - }) - .collect(); - - self.collect_error_for_expanding_node( - graph, - &mut dup_vec, - node_vid, - errors, - influences, - ); + + self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors); } } } @@ -775,7 +757,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { dup_vec: &mut IndexVec<RegionVid, Option<RegionVid>>, node_idx: RegionVid, errors: &mut Vec<RegionResolutionError<'tcx>>, - influences: Vec<Span>, ) { // Errors in expanding nodes result from a lower-bound that is // not contained by an upper-bound. @@ -830,7 +811,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { lower_bound.region, upper_bound.origin.clone(), upper_bound.region, - influences, + vec![], )); return; } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index b17a465eb38..4acd0d0edfe 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -361,6 +361,7 @@ pub enum ValuePairs<'tcx> { Terms(ExpectedFound<ty::Term<'tcx>>), TraitRefs(ExpectedFound<ty::TraitRef<'tcx>>), PolyTraitRefs(ExpectedFound<ty::PolyTraitRef<'tcx>>), + Sigs(ExpectedFound<ty::FnSig<'tcx>>), } impl<'tcx> ValuePairs<'tcx> { @@ -409,12 +410,6 @@ pub enum SubregionOrigin<'tcx> { /// Creating a pointer `b` to contents of another reference Reborrow(Span), - /// Creating a pointer `b` to contents of an upvar - ReborrowUpvar(Span, ty::UpvarId), - - /// Data with type `Ty<'tcx>` was borrowed - DataBorrowed(Ty<'tcx>, Span), - /// (&'a &'b T) where a >= b ReferenceOutlivesReferent(Ty<'tcx>, Span), @@ -690,6 +685,10 @@ impl<'tcx> InferCtxt<'tcx> { typeck_results: None, fallback_has_occurred: false, normalize_fn_sig: Box::new(|fn_sig| fn_sig), + autoderef_steps: Box::new(|ty| { + debug_assert!(false, "shouldn't be using autoderef_steps outside of typeck"); + vec![(ty, vec![])] + }), } } @@ -1340,6 +1339,12 @@ impl<'tcx> InferCtxt<'tcx> { var_infos } + #[instrument(level = "debug", skip(self), ret)] + pub fn take_opaque_types(&self) -> opaque_types::OpaqueTypeMap<'tcx> { + debug_assert_ne!(self.defining_use_anchor, DefiningAnchor::Error); + std::mem::take(&mut self.inner.borrow_mut().opaque_type_storage.opaque_types) + } + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { self.resolve_vars_if_possible(t).to_string() } @@ -1681,13 +1686,29 @@ impl<'tcx> InferCtxt<'tcx> { } impl<'tcx> TypeErrCtxt<'_, 'tcx> { + /// Processes registered region obliations and resolves regions, reporting + /// any errors if any were raised. Prefer using this function over manually + /// calling `resolve_regions_and_report_errors`. + pub fn check_region_obligations_and_report_errors( + &self, + generic_param_scope: LocalDefId, + outlives_env: &OutlivesEnvironment<'tcx>, + ) -> Result<(), ErrorGuaranteed> { + self.process_registered_region_obligations( + outlives_env.region_bound_pairs(), + outlives_env.param_env, + ); + + self.resolve_regions_and_report_errors(generic_param_scope, outlives_env) + } + /// Process the region constraints and report any errors that /// result. After this, no more unification operations should be /// done -- or the compiler will panic -- but it is legal to use /// `resolve_vars_if_possible` as well as `fully_resolve`. /// /// Make sure to call [`InferCtxt::process_registered_region_obligations`] - /// first, or preferably use [`InferCtxt::check_region_obligations_and_report_errors`] + /// first, or preferably use [`TypeErrCtxt::check_region_obligations_and_report_errors`] /// to do both of these operations together. pub fn resolve_regions_and_report_errors( &self, @@ -1954,8 +1975,6 @@ impl<'tcx> SubregionOrigin<'tcx> { RelateParamBound(a, ..) => a, RelateRegionParamBound(a) => a, Reborrow(a) => a, - ReborrowUpvar(a, _) => a, - DataBorrowed(_, a) => a, ReferenceOutlivesReferent(_, a) => a, CompareImplItemObligation { span, .. } => span, AscribeUserTypeProvePredicate(span) => span, diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index a130fde47ed..749e960bfd0 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -61,7 +61,7 @@ impl<'tcx> InferCtxt<'tcx> { .as_local() .map_or(false, |def_id| self.opaque_type_origin(def_id, span).is_some()) }; - let value = value.fold_with(&mut ty::fold::BottomUpFolder { + let value = value.fold_with(&mut BottomUpFolder { tcx: self.tcx, lt_op: |lt| lt, ct_op: |ct| ct, diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs index c146902d594..ae4b85c8799 100644 --- a/compiler/rustc_infer/src/infer/opaque_types/table.rs +++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs @@ -29,11 +29,6 @@ impl<'tcx> OpaqueTypeStorage<'tcx> { } } - #[instrument(level = "debug", ret)] - pub fn take_opaque_types(&mut self) -> OpaqueTypeMap<'tcx> { - std::mem::take(&mut self.opaque_types) - } - #[inline] pub(crate) fn with_log<'a>( &'a mut self, diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index f71c39dc0d2..a85e6a19b11 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -60,7 +60,6 @@ //! imply that `'b: 'a`. use crate::infer::outlives::components::{push_outlives_components, Component}; -use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::outlives::verify::VerifyBoundCx; use crate::infer::{ @@ -68,9 +67,7 @@ use crate::infer::{ }; use crate::traits::{ObligationCause, ObligationCauseCode}; use rustc_data_structures::undo_log::UndoLogs; -use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::DefId; -use rustc_hir::def_id::LocalDefId; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, Region, SubstsRef, Ty, TyCtxt, TypeVisitable}; @@ -116,7 +113,7 @@ impl<'tcx> InferCtxt<'tcx> { std::mem::take(&mut self.inner.borrow_mut().region_obligations) } - /// NOTE: Prefer using [`InferCtxt::check_region_obligations_and_report_errors`] + /// NOTE: Prefer using `TypeErrCtxt::check_region_obligations_and_report_errors` /// instead of calling this directly. /// /// Process the region obligations that must be proven (during @@ -170,22 +167,6 @@ impl<'tcx> InferCtxt<'tcx> { outlives.type_must_outlive(origin, sup_type, sub_region, category); } } - - /// Processes registered region obliations and resolves regions, reporting - /// any errors if any were raised. Prefer using this function over manually - /// calling `resolve_regions_and_report_errors`. - pub fn check_region_obligations_and_report_errors( - &self, - generic_param_scope: LocalDefId, - outlives_env: &OutlivesEnvironment<'tcx>, - ) -> Result<(), ErrorGuaranteed> { - self.process_registered_region_obligations( - outlives_env.region_bound_pairs(), - outlives_env.param_env, - ); - - self.err_ctxt().resolve_regions_and_report_errors(generic_param_scope, outlives_env) - } } /// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index e67dec31dce..f817c5bc1cd 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -45,6 +45,7 @@ rustc_plugin_impl = { path = "../rustc_plugin_impl" } rustc_privacy = { path = "../rustc_privacy" } rustc_query_impl = { path = "../rustc_query_impl" } rustc_resolve = { path = "../rustc_resolve" } +rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } rustc_ty_utils = { path = "../rustc_ty_utils" } diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index f5135c78dc8..15d7e977bbe 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -87,3 +87,7 @@ pub struct FailedWritingFile<'a> { pub path: &'a Path, pub error: io::Error, } + +#[derive(Diagnostic)] +#[diag(interface_proc_macro_crate_panic_abort)] +pub struct ProcMacroCratePanicAbort; diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 4c22ab68a56..7f761b005ed 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -90,8 +90,7 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String .into_iter() .map(|s| { let sess = ParseSess::with_silent_emitter(Some(format!( - "this error occurred on the command line: `--cfg={}`", - s + "this error occurred on the command line: `--cfg={s}`" ))); let filename = FileName::cfg_spec_source_code(&s); @@ -150,8 +149,7 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg { 'specs: for s in specs { let sess = ParseSess::with_silent_emitter(Some(format!( - "this error occurred on the command line: `--check-cfg={}`", - s + "this error occurred on the command line: `--check-cfg={s}`" ))); let filename = FileName::cfg_spec_source_code(&s); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 89d9450cf4e..50c40206d80 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1,7 +1,8 @@ use crate::errors::{ CantEmitMIR, EmojiIdentifier, ErrorWritingDependencies, FerrisIdentifier, GeneratedFileConflictsWithDirectory, InputFileWouldBeOverWritten, MixedBinCrate, - MixedProcMacroCrate, OutDirError, ProcMacroDocWithoutArg, TempsDirError, + MixedProcMacroCrate, OutDirError, ProcMacroCratePanicAbort, ProcMacroDocWithoutArg, + TempsDirError, }; use crate::interface::{Compiler, Result}; use crate::proc_macro_decls; @@ -36,6 +37,7 @@ use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::FileName; +use rustc_target::spec::PanicStrategy; use rustc_trait_selection::traits; use std::any::Any; @@ -380,6 +382,10 @@ pub fn configure_and_expand( } } + if is_proc_macro_crate && sess.panic_strategy() == PanicStrategy::Abort { + sess.emit_warning(ProcMacroCratePanicAbort); + } + // For backwards compatibility, we don't try to run proc macro injection // if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being // specified. This should only affect users who manually invoke 'rustdoc', as @@ -620,7 +626,7 @@ fn write_out_deps( // prevents `make` from spitting out an error if a file is later // deleted. For more info see #28735 for path in files { - writeln!(file, "{}:", path)?; + writeln!(file, "{path}:")?; } // Emit special comments with information about accessed environment variables. @@ -633,9 +639,9 @@ fn write_out_deps( envs.sort_unstable(); writeln!(file)?; for (k, v) in envs { - write!(file, "# env-dep:{}", k)?; + write!(file, "# env-dep:{k}")?; if let Some(v) = v { - write!(file, "={}", v)?; + write!(file, "={v}")?; } writeln!(file)?; } @@ -817,23 +823,26 @@ pub fn create_global_ctxt<'tcx>( lint_store, arena, hir_arena, - untracked_resolutions, untracked, - krate, dep_graph, queries.on_disk_cache.as_ref().map(OnDiskCache::as_dyn), queries.as_dyn(), rustc_query_impl::query_callbacks(arena), - crate_name, - outputs, ) }) }); let mut qcx = QueryContext { gcx }; qcx.enter(|tcx| { - tcx.feed_unit_query() - .resolver_for_lowering(tcx.arena.alloc(Steal::new(untracked_resolver_for_lowering))) + let feed = tcx.feed_unit_query(); + feed.resolver_for_lowering( + tcx.arena.alloc(Steal::new((untracked_resolver_for_lowering, krate))), + ); + feed.resolutions(tcx.arena.alloc(untracked_resolutions)); + feed.output_filenames(tcx.arena.alloc(std::sync::Arc::new(outputs))); + feed.features_query(sess.features_untracked()); + let feed = tcx.feed_local_crate(); + feed.crate_name(crate_name); }); qcx } diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 1d0c7f5b7a3..041bb9eb7a1 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -5,6 +5,7 @@ use crate::passes::{self, BoxedResolver, QueryContext}; use rustc_ast as ast; use rustc_codegen_ssa::traits::CodegenBackend; use rustc_codegen_ssa::CodegenResults; +use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; use rustc_hir::def_id::LOCAL_CRATE; @@ -19,43 +20,53 @@ use rustc_session::{output::find_crate_name, Session}; use rustc_span::symbol::sym; use rustc_span::Symbol; use std::any::Any; -use std::cell::{Ref, RefCell, RefMut}; +use std::cell::{RefCell, RefMut}; use std::rc::Rc; use std::sync::Arc; /// Represent the result of a query. /// -/// This result can be stolen with the [`take`] method and generated with the [`compute`] method. +/// This result can be stolen once with the [`steal`] method and generated with the [`compute`] method. /// -/// [`take`]: Self::take +/// [`steal`]: Steal::steal /// [`compute`]: Self::compute pub struct Query<T> { - result: RefCell<Option<Result<T>>>, + /// `None` means no value has been computed yet. + result: RefCell<Option<Result<Steal<T>>>>, } impl<T> Query<T> { - fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<&Query<T>> { - self.result.borrow_mut().get_or_insert_with(f).as_ref().map(|_| self).map_err(|&err| err) + fn compute<F: FnOnce() -> Result<T>>(&self, f: F) -> Result<QueryResult<'_, T>> { + RefMut::filter_map( + self.result.borrow_mut(), + |r: &mut Option<Result<Steal<T>>>| -> Option<&mut Steal<T>> { + r.get_or_insert_with(|| f().map(Steal::new)).as_mut().ok() + }, + ) + .map_err(|r| *r.as_ref().unwrap().as_ref().map(|_| ()).unwrap_err()) + .map(QueryResult) } +} + +pub struct QueryResult<'a, T>(RefMut<'a, Steal<T>>); + +impl<'a, T> std::ops::Deref for QueryResult<'a, T> { + type Target = RefMut<'a, Steal<T>>; - /// Takes ownership of the query result. Further attempts to take or peek the query - /// result will panic unless it is generated by calling the `compute` method. - pub fn take(&self) -> T { - self.result.borrow_mut().take().expect("missing query result").unwrap() + fn deref(&self) -> &Self::Target { + &self.0 } +} - /// Borrows the query result using the RefCell. Panics if the result is stolen. - pub fn peek(&self) -> Ref<'_, T> { - Ref::map(self.result.borrow(), |r| { - r.as_ref().unwrap().as_ref().expect("missing query result") - }) +impl<'a, T> std::ops::DerefMut for QueryResult<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } +} - /// Mutably borrows the query result using the RefCell. Panics if the result is stolen. - pub fn peek_mut(&self) -> RefMut<'_, T> { - RefMut::map(self.result.borrow_mut(), |r| { - r.as_mut().unwrap().as_mut().expect("missing query result") - }) +impl<'a, 'tcx> QueryResult<'a, QueryContext<'tcx>> { + pub fn enter<T>(mut self, f: impl FnOnce(TyCtxt<'tcx>) -> T) -> T { + (*self.0).get_mut().enter(f) } } @@ -111,24 +122,24 @@ impl<'tcx> Queries<'tcx> { self.compiler.codegen_backend() } - fn dep_graph_future(&self) -> Result<&Query<Option<DepGraphFuture>>> { + fn dep_graph_future(&self) -> Result<QueryResult<'_, Option<DepGraphFuture>>> { self.dep_graph_future.compute(|| { let sess = self.session(); Ok(sess.opts.build_dep_graph().then(|| rustc_incremental::load_dep_graph(sess))) }) } - pub fn parse(&self) -> Result<&Query<ast::Crate>> { + pub fn parse(&self) -> Result<QueryResult<'_, ast::Crate>> { self.parse.compute(|| { passes::parse(self.session(), &self.compiler.input) .map_err(|mut parse_error| parse_error.emit()) }) } - pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, Lrc<LintStore>)>> { + pub fn register_plugins(&self) -> Result<QueryResult<'_, (ast::Crate, Lrc<LintStore>)>> { self.register_plugins.compute(|| { - let crate_name = *self.crate_name()?.peek(); - let krate = self.parse()?.take(); + let crate_name = *self.crate_name()?.borrow(); + let krate = self.parse()?.steal(); let empty: &(dyn Fn(&Session, &mut LintStore) + Sync + Send) = &|_, _| {}; let (krate, lint_store) = passes::register_plugins( @@ -150,11 +161,11 @@ impl<'tcx> Queries<'tcx> { }) } - pub fn crate_name(&self) -> Result<&Query<Symbol>> { + pub fn crate_name(&self) -> Result<QueryResult<'_, Symbol>> { self.crate_name.compute(|| { Ok({ let parse_result = self.parse()?; - let krate = parse_result.peek(); + let krate = parse_result.borrow(); // parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches. find_crate_name(self.session(), &krate.attrs, &self.compiler.input) }) @@ -163,11 +174,12 @@ impl<'tcx> Queries<'tcx> { pub fn expansion( &self, - ) -> Result<&Query<(Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>> { + ) -> Result<QueryResult<'_, (Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>> + { trace!("expansion"); self.expansion.compute(|| { - let crate_name = *self.crate_name()?.peek(); - let (krate, lint_store) = self.register_plugins()?.take(); + let crate_name = *self.crate_name()?.borrow(); + let (krate, lint_store) = self.register_plugins()?.steal(); let _timer = self.session().timer("configure_and_expand"); let sess = self.session(); let mut resolver = passes::create_resolver( @@ -183,10 +195,10 @@ impl<'tcx> Queries<'tcx> { }) } - fn dep_graph(&self) -> Result<&Query<DepGraph>> { + fn dep_graph(&self) -> Result<QueryResult<'_, DepGraph>> { self.dep_graph.compute(|| { let sess = self.session(); - let future_opt = self.dep_graph_future()?.take(); + let future_opt = self.dep_graph_future()?.steal(); let dep_graph = future_opt .and_then(|future| { let (prev_graph, prev_work_products) = @@ -199,10 +211,11 @@ impl<'tcx> Queries<'tcx> { }) } - pub fn prepare_outputs(&self) -> Result<&Query<OutputFilenames>> { + pub fn prepare_outputs(&self) -> Result<QueryResult<'_, OutputFilenames>> { self.prepare_outputs.compute(|| { - let (krate, boxed_resolver, _) = &*self.expansion()?.peek(); - let crate_name = *self.crate_name()?.peek(); + let expansion = self.expansion()?; + let (krate, boxed_resolver, _) = &*expansion.borrow(); + let crate_name = *self.crate_name()?.borrow(); passes::prepare_outputs( self.session(), self.compiler, @@ -213,12 +226,12 @@ impl<'tcx> Queries<'tcx> { }) } - pub fn global_ctxt(&'tcx self) -> Result<&Query<QueryContext<'tcx>>> { + pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'_, QueryContext<'tcx>>> { self.global_ctxt.compute(|| { - let crate_name = *self.crate_name()?.peek(); - let outputs = self.prepare_outputs()?.take(); - let dep_graph = self.dep_graph()?.peek().clone(); - let (krate, resolver, lint_store) = self.expansion()?.take(); + let crate_name = *self.crate_name()?.borrow(); + let outputs = self.prepare_outputs()?.steal(); + let dep_graph = self.dep_graph()?.borrow().clone(); + let (krate, resolver, lint_store) = self.expansion()?.steal(); Ok(passes::create_global_ctxt( self.compiler, lint_store, @@ -235,9 +248,9 @@ impl<'tcx> Queries<'tcx> { }) } - pub fn ongoing_codegen(&'tcx self) -> Result<&Query<Box<dyn Any>>> { + pub fn ongoing_codegen(&'tcx self) -> Result<QueryResult<'_, Box<dyn Any>>> { self.ongoing_codegen.compute(|| { - self.global_ctxt()?.peek_mut().enter(|tcx| { + self.global_ctxt()?.enter(|tcx| { tcx.analysis(()).ok(); // Don't do code generation if there were any errors @@ -293,12 +306,10 @@ impl<'tcx> Queries<'tcx> { let sess = self.session().clone(); let codegen_backend = self.codegen_backend().clone(); - let dep_graph = self.dep_graph()?.peek().clone(); - let (crate_hash, prepare_outputs) = self - .global_ctxt()? - .peek_mut() - .enter(|tcx| (tcx.crate_hash(LOCAL_CRATE), tcx.output_filenames(()).clone())); - let ongoing_codegen = self.ongoing_codegen()?.take(); + let (crate_hash, prepare_outputs, dep_graph) = self.global_ctxt()?.enter(|tcx| { + (tcx.crate_hash(LOCAL_CRATE), tcx.output_filenames(()).clone(), tcx.dep_graph.clone()) + }); + let ongoing_codegen = self.ongoing_codegen()?.steal(); Ok(Linker { sess, @@ -382,6 +393,7 @@ impl Compiler { // NOTE: intentionally does not compute the global context if it hasn't been built yet, // since that likely means there was a parse error. if let Some(Ok(gcx)) = &mut *queries.global_ctxt.result.borrow_mut() { + let gcx = gcx.get_mut(); // We assume that no queries are run past here. If there are new queries // after this point, they'll show up as "<unknown>" in self-profiling data. { diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index eb3baba999b..07b28cc86ce 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -3,17 +3,17 @@ use crate::interface::parse_cfgspecs; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig}; -use rustc_session::config::InstrumentCoverage; -use rustc_session::config::Strip; +use rustc_session::config::rustc_optgroups; +use rustc_session::config::TraitSolver; use rustc_session::config::{build_configuration, build_session_options, to_crate_config}; use rustc_session::config::{ - rustc_optgroups, ErrorOutputType, ExternLocation, LocationDetail, Options, Passes, -}; -use rustc_session::config::{ BranchProtection, Externs, OomStrategy, OutputType, OutputTypes, PAuthKey, PacRet, ProcMacroExecutionStrategy, SymbolManglingVersion, WasiExecModel, }; use rustc_session::config::{CFGuard, ExternEntry, LinkerPluginLto, LtoCli, SwitchWithOptPath}; +use rustc_session::config::{DumpMonoStatsFormat, MirSpanview}; +use rustc_session::config::{ErrorOutputType, ExternLocation, LocationDetail, Options, Strip}; +use rustc_session::config::{InstrumentCoverage, Passes}; use rustc_session::lint::Level; use rustc_session::search_paths::SearchPath; use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind}; @@ -647,6 +647,9 @@ fn test_unstable_options_tracking_hash() { untracked!(dump_mir_dir, String::from("abc")); untracked!(dump_mir_exclude_pass_number, true); untracked!(dump_mir_graphviz, true); + untracked!(dump_mir_spanview, Some(MirSpanview::Statement)); + untracked!(dump_mono_stats, SwitchWithOptPath::Enabled(Some("mono-items-dir/".into()))); + untracked!(dump_mono_stats_format, DumpMonoStatsFormat::Json); untracked!(dylib_lto, true); untracked!(emit_stack_sizes, true); untracked!(future_incompat_test, true); @@ -712,7 +715,7 @@ fn test_unstable_options_tracking_hash() { tracked!(asm_comments, true); tracked!(assume_incomplete_release, true); tracked!(binary_dep_depinfo, true); - tracked!(box_noalias, Some(false)); + tracked!(box_noalias, false); tracked!( branch_protection, Some(BranchProtection { @@ -720,7 +723,6 @@ fn test_unstable_options_tracking_hash() { pac_ret: Some(PacRet { leaf: true, key: PAuthKey::B }) }) ); - tracked!(chalk, true); tracked!(codegen_backend, Some("abc".to_string())); tracked!(crate_attr, vec!["abc".to_string()]); tracked!(debug_info_for_profiling, true); @@ -746,13 +748,14 @@ fn test_unstable_options_tracking_hash() { tracked!(link_only, true); tracked!(llvm_plugins, vec![String::from("plugin_name")]); tracked!(location_detail, LocationDetail { file: true, line: false, column: false }); + tracked!(log_backtrace, Some("filter".to_string())); tracked!(maximal_hir_to_mir_coverage, true); tracked!(merge_functions, Some(MergeFunctions::Disabled)); tracked!(mir_emit_retag, true); tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]); tracked!(mir_opt_level, Some(4)); tracked!(move_size_limit, Some(4096)); - tracked!(mutable_noalias, Some(true)); + tracked!(mutable_noalias, false); tracked!(no_generate_arange_section, true); tracked!(no_jump_tables, true); tracked!(no_link, true); @@ -790,6 +793,7 @@ fn test_unstable_options_tracking_hash() { tracked!(thinlto, Some(true)); tracked!(thir_unsafeck, true); tracked!(tls_model, Some(TlsModel::GeneralDynamic)); + tracked!(trait_solver, TraitSolver::Chalk); tracked!(translate_remapped_path_to_local_path, false); tracked!(trap_unreachable, Some(false)); tracked!(treat_err_as_bug, NonZeroUsize::new(1)); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 4142964a0da..02a7756c8d4 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -205,13 +205,13 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>( fn load_backend_from_dylib(path: &Path) -> MakeBackendFn { let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| { - let err = format!("couldn't load codegen backend {:?}: {}", path, err); + let err = format!("couldn't load codegen backend {path:?}: {err}"); early_error(ErrorOutputType::default(), &err); }); let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") } .unwrap_or_else(|e| { - let err = format!("couldn't load codegen backend: {}", e); + let err = format!("couldn't load codegen backend: {e}"); early_error(ErrorOutputType::default(), &err); }); @@ -304,8 +304,7 @@ fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> M .join("\n* "); let err = format!( "failed to find a `codegen-backends` folder \ - in the sysroot candidates:\n* {}", - candidates + in the sysroot candidates:\n* {candidates}" ); early_error(ErrorOutputType::default(), &err); }); @@ -325,7 +324,7 @@ fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> M let expected_names = &[ format!("rustc_codegen_{}-{}", backend_name, env!("CFG_RELEASE")), - format!("rustc_codegen_{}", backend_name), + format!("rustc_codegen_{backend_name}"), ]; for entry in d.filter_map(|e| e.ok()) { let path = entry.path(); @@ -354,7 +353,7 @@ fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> M match file { Some(ref s) => load_backend_from_dylib(s), None => { - let err = format!("unsupported builtin codegen backend `{}`", backend_name); + let err = format!("unsupported builtin codegen backend `{backend_name}`"); early_error(ErrorOutputType::default(), &err); } } @@ -389,7 +388,7 @@ pub(crate) fn check_attr_crate_type( BuiltinLintDiagnostics::UnknownCrateTypes( span, "did you mean".to_string(), - format!("\"{}\"", candidate), + format!("\"{candidate}\""), ), ); } else { diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs index abebc533cc1..3593f141df6 100644 --- a/compiler/rustc_lint/src/array_into_iter.rs +++ b/compiler/rustc_lint/src/array_into_iter.rs @@ -1,5 +1,5 @@ +use crate::lints::{ArrayIntoIterDiag, ArrayIntoIterDiagSub}; use crate::{LateContext, LateLintPass, LintContext}; -use rustc_errors::{fluent, Applicability}; use rustc_hir as hir; use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; @@ -118,41 +118,23 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter { // to an array or to a slice. _ => bug!("array type coerced to something other than array or slice"), }; - cx.struct_span_lint( + let sub = if self.for_expr_span == expr.span { + Some(ArrayIntoIterDiagSub::RemoveIntoIter { + span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), + }) + } else if receiver_ty.is_array() { + Some(ArrayIntoIterDiagSub::UseExplicitIntoIter { + start_span: expr.span.shrink_to_lo(), + end_span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), + }) + } else { + None + }; + cx.emit_spanned_lint( ARRAY_INTO_ITER, call.ident.span, - fluent::lint_array_into_iter, - |diag| { - diag.set_arg("target", target); - diag.span_suggestion( - call.ident.span, - fluent::use_iter_suggestion, - "iter", - Applicability::MachineApplicable, - ); - if self.for_expr_span == expr.span { - diag.span_suggestion( - receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), - fluent::remove_into_iter_suggestion, - "", - Applicability::MaybeIncorrect, - ); - } else if receiver_ty.is_array() { - diag.multipart_suggestion( - fluent::use_explicit_into_iter_suggestion, - vec![ - (expr.span.shrink_to_lo(), "IntoIterator::into_iter(".into()), - ( - receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()), - ")".into(), - ), - ], - Applicability::MaybeIncorrect, - ); - } - diag - }, - ) + ArrayIntoIterDiag { target, suggestion: call.ident.span, sub }, + ); } } } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 10d8db5393d..6f445426df7 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -22,6 +22,23 @@ use crate::{ errors::BuiltinEllpisisInclusiveRangePatterns, + lints::{ + BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinClashingExtern, + BuiltinClashingExternSub, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink, + BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr, + BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives, + BuiltinExplicitOutlivesSuggestion, BuiltinIncompleteFeatures, + BuiltinIncompleteFeaturesHelp, BuiltinIncompleteFeaturesNote, BuiltinKeywordIdents, + BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, + BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, + BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds, + BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause, + BuiltinUnexpectedCliConfigName, BuiltinUnexpectedCliConfigValue, + BuiltinUngatedAsyncFnTrackCaller, BuiltinUnnameableTestItems, BuiltinUnpermittedTypeInit, + BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, + BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, + BuiltinWhileTrue, SuggestChangingAssocTypes, + }, types::{transparent_newtype_field, CItemKind}, EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext, }; @@ -33,10 +50,7 @@ use rustc_ast::{self as ast, *}; use rustc_ast_pretty::pprust::{self, expr_to_string}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{ - fluent, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, DiagnosticMessage, - DiagnosticStyledString, MultiSpan, -}; +use rustc_errors::{fluent, Applicability, DecorateLint, MultiSpan}; use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -110,25 +124,17 @@ impl EarlyLintPass for WhileTrue { && !cond.span.from_expansion() { let condition_span = e.span.with_hi(cond.span.hi()); - cx.struct_span_lint( - WHILE_TRUE, - condition_span, - fluent::lint_builtin_while_true, - |lint| { - lint.span_suggestion_short( - condition_span, - fluent::suggestion, - format!( + let replace = format!( "{}loop", label.map_or_else(String::new, |label| format!( "{}: ", label.ident, )) - ), - Applicability::MachineApplicable, - ) - }, - ) + ); + cx.emit_spanned_lint(WHILE_TRUE, condition_span, BuiltinWhileTrue { + suggestion: condition_span, + replace, + }); } } } @@ -164,12 +170,7 @@ impl BoxPointers { for leaf in ty.walk() { if let GenericArgKind::Type(leaf_ty) = leaf.unpack() { if leaf_ty.is_box() { - cx.struct_span_lint( - BOX_POINTERS, - span, - fluent::lint_builtin_box_pointers, - |lint| lint.set_arg("ty", ty), - ); + cx.emit_spanned_lint(BOX_POINTERS, span, BuiltinBoxPointers { ty }); } } } @@ -267,19 +268,13 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { if cx.tcx.find_field_index(ident, &variant) == Some(cx.typeck_results().field_index(fieldpat.hir_id)) { - cx.struct_span_lint( + cx.emit_spanned_lint( NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, - fluent::lint_builtin_non_shorthand_field_patterns, - |lint| { - let suggested_ident = - format!("{}{}", binding_annot.prefix_str(), ident); - lint.set_arg("ident", ident).span_suggestion( - fieldpat.span, - fluent::suggestion, - suggested_ident, - Applicability::MachineApplicable, - ) + BuiltinNonShorthandFieldPatterns { + ident, + suggestion: fieldpat.span, + prefix: binding_annot.prefix_str(), }, ); } @@ -321,48 +316,21 @@ impl UnsafeCode { &self, cx: &EarlyContext<'_>, span: Span, - msg: impl Into<DiagnosticMessage>, - decorate: impl for<'a, 'b> FnOnce( - &'b mut DiagnosticBuilder<'a, ()>, - ) -> &'b mut DiagnosticBuilder<'a, ()>, + decorate: impl for<'a> DecorateLint<'a, ()>, ) { // This comes from a macro that has `#[allow_internal_unsafe]`. if span.allows_unsafe() { return; } - cx.struct_span_lint(UNSAFE_CODE, span, msg, decorate); - } - - fn report_overridden_symbol_name( - &self, - cx: &EarlyContext<'_>, - span: Span, - msg: DiagnosticMessage, - ) { - self.report_unsafe(cx, span, msg, |lint| { - lint.note(fluent::lint_builtin_overridden_symbol_name) - }) - } - - fn report_overridden_symbol_section( - &self, - cx: &EarlyContext<'_>, - span: Span, - msg: DiagnosticMessage, - ) { - self.report_unsafe(cx, span, msg, |lint| { - lint.note(fluent::lint_builtin_overridden_symbol_section) - }) + cx.emit_spanned_lint(UNSAFE_CODE, span, decorate); } } impl EarlyLintPass for UnsafeCode { fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) { if attr.has_name(sym::allow_internal_unsafe) { - self.report_unsafe(cx, attr.span, fluent::lint_builtin_allow_internal_unsafe, |lint| { - lint - }); + self.report_unsafe(cx, attr.span, BuiltinUnsafe::AllowInternalUnsafe); } } @@ -371,7 +339,7 @@ impl EarlyLintPass for UnsafeCode { if let ast::ExprKind::Block(ref blk, _) = e.kind { // Don't warn about generated blocks; that'll just pollute the output. if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) { - self.report_unsafe(cx, blk.span, fluent::lint_builtin_unsafe_block, |lint| lint); + self.report_unsafe(cx, blk.span, BuiltinUnsafe::UnsafeBlock); } } } @@ -379,62 +347,38 @@ impl EarlyLintPass for UnsafeCode { fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) { match it.kind { ast::ItemKind::Trait(box ast::Trait { unsafety: ast::Unsafe::Yes(_), .. }) => { - self.report_unsafe(cx, it.span, fluent::lint_builtin_unsafe_trait, |lint| lint) + self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeTrait); } ast::ItemKind::Impl(box ast::Impl { unsafety: ast::Unsafe::Yes(_), .. }) => { - self.report_unsafe(cx, it.span, fluent::lint_builtin_unsafe_impl, |lint| lint) + self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeImpl); } ast::ItemKind::Fn(..) => { if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) { - self.report_overridden_symbol_name( - cx, - attr.span, - fluent::lint_builtin_no_mangle_fn, - ); + self.report_unsafe(cx, attr.span, BuiltinUnsafe::NoMangleFn); } if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) { - self.report_overridden_symbol_name( - cx, - attr.span, - fluent::lint_builtin_export_name_fn, - ); + self.report_unsafe(cx, attr.span, BuiltinUnsafe::ExportNameFn); } if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::link_section) { - self.report_overridden_symbol_section( - cx, - attr.span, - fluent::lint_builtin_link_section_fn, - ); + self.report_unsafe(cx, attr.span, BuiltinUnsafe::LinkSectionFn); } } ast::ItemKind::Static(..) => { if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) { - self.report_overridden_symbol_name( - cx, - attr.span, - fluent::lint_builtin_no_mangle_static, - ); + self.report_unsafe(cx, attr.span, BuiltinUnsafe::NoMangleStatic); } if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) { - self.report_overridden_symbol_name( - cx, - attr.span, - fluent::lint_builtin_export_name_static, - ); + self.report_unsafe(cx, attr.span, BuiltinUnsafe::ExportNameStatic); } if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::link_section) { - self.report_overridden_symbol_section( - cx, - attr.span, - fluent::lint_builtin_link_section_static, - ); + self.report_unsafe(cx, attr.span, BuiltinUnsafe::LinkSectionStatic); } } @@ -445,18 +389,10 @@ impl EarlyLintPass for UnsafeCode { fn check_impl_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) { if let ast::AssocItemKind::Fn(..) = it.kind { if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) { - self.report_overridden_symbol_name( - cx, - attr.span, - fluent::lint_builtin_no_mangle_method, - ); + self.report_unsafe(cx, attr.span, BuiltinUnsafe::NoMangleMethod); } if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) { - self.report_overridden_symbol_name( - cx, - attr.span, - fluent::lint_builtin_export_name_method, - ); + self.report_unsafe(cx, attr.span, BuiltinUnsafe::ExportNameMethod); } } } @@ -471,13 +407,13 @@ impl EarlyLintPass for UnsafeCode { body, ) = fk { - let msg = match ctxt { + let decorator = match ctxt { FnCtxt::Foreign => return, - FnCtxt::Free => fluent::lint_builtin_decl_unsafe_fn, - FnCtxt::Assoc(_) if body.is_none() => fluent::lint_builtin_decl_unsafe_method, - FnCtxt::Assoc(_) => fluent::lint_builtin_impl_unsafe_method, + FnCtxt::Free => BuiltinUnsafe::DeclUnsafeFn, + FnCtxt::Assoc(_) if body.is_none() => BuiltinUnsafe::DeclUnsafeMethod, + FnCtxt::Assoc(_) => BuiltinUnsafe::ImplUnsafeMethod, }; - self.report_unsafe(cx, span, msg, |lint| lint); + self.report_unsafe(cx, span, decorator); } } } @@ -578,11 +514,10 @@ impl MissingDoc { let attrs = cx.tcx.hir().attrs(cx.tcx.hir().local_def_id_to_hir_id(def_id)); let has_doc = attrs.iter().any(has_doc); if !has_doc { - cx.struct_span_lint( + cx.emit_spanned_lint( MISSING_DOCS, cx.tcx.def_span(def_id), - fluent::lint_builtin_missing_doc, - |lint| lint.set_arg("article", article).set_arg("desc", desc), + BuiltinMissingDoc { article, desc }, ); } } @@ -799,12 +734,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { ) .is_ok() { - cx.struct_span_lint( - MISSING_COPY_IMPLEMENTATIONS, - item.span, - fluent::lint_builtin_missing_copy_impl, - |lint| lint, - ) + cx.emit_spanned_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, BuiltinMissingCopyImpl); } } } @@ -878,11 +808,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { } if !self.impling_types.as_ref().unwrap().contains(&item.owner_id.def_id) { - cx.struct_span_lint( + cx.emit_spanned_lint( MISSING_DEBUG_IMPLEMENTATIONS, item.span, - fluent::lint_builtin_missing_debug_impl, - |lint| lint.set_arg("debug", cx.tcx.def_path_str(debug)), + BuiltinMissingDebugImpl { tcx: cx.tcx, def_id: debug }, ); } } @@ -958,19 +887,11 @@ impl EarlyLintPass for AnonymousParameters { } else { ("<type>", Applicability::HasPlaceholders) }; - cx.struct_span_lint( + cx.emit_spanned_lint( ANONYMOUS_PARAMETERS, arg.pat.span, - fluent::lint_builtin_anonymous_params, - |lint| { - lint.span_suggestion( - arg.pat.span, - fluent::suggestion, - format!("_: {}", ty_snip), - appl, - ) - }, - ) + BuiltinAnonymousParams { suggestion: (arg.pat.span, appl), ty_snip }, + ); } } } @@ -1005,42 +926,30 @@ impl EarlyLintPass for DeprecatedAttr { _, ) = gate { - // FIXME(davidtwco) translatable deprecated attr - cx.struct_span_lint( + let suggestion = match suggestion { + Some(msg) => { + BuiltinDeprecatedAttrLinkSuggestion::Msg { suggestion: attr.span, msg } + } + None => { + BuiltinDeprecatedAttrLinkSuggestion::Default { suggestion: attr.span } + } + }; + cx.emit_spanned_lint( DEPRECATED, attr.span, - fluent::lint_builtin_deprecated_attr_link, - |lint| { - lint.set_arg("name", name) - .set_arg("reason", reason) - .set_arg("link", link) - .span_suggestion_short( - attr.span, - suggestion.map(|s| s.into()).unwrap_or( - fluent::lint_builtin_deprecated_attr_default_suggestion, - ), - "", - Applicability::MachineApplicable, - ) - }, + BuiltinDeprecatedAttrLink { name, reason, link, suggestion }, ); } return; } } if attr.has_name(sym::no_start) || attr.has_name(sym::crate_id) { - cx.struct_span_lint( + cx.emit_spanned_lint( DEPRECATED, attr.span, - fluent::lint_builtin_deprecated_attr_used, - |lint| { - lint.set_arg("name", pprust::path_to_string(&attr.get_normal_item().path)) - .span_suggestion_short( - attr.span, - fluent::lint_builtin_deprecated_attr_default_suggestion, - "", - Applicability::MachineApplicable, - ) + BuiltinDeprecatedAttrUsed { + name: pprust::path_to_string(&attr.get_normal_item().path), + suggestion: attr.span, }, ); } @@ -1069,20 +978,18 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: & let span = sugared_span.take().unwrap_or(attr.span); if is_doc_comment || attr.has_name(sym::doc) { - cx.struct_span_lint( + let sub = match attr.kind { + AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => { + BuiltinUnusedDocCommentSub::PlainHelp + } + AttrKind::DocComment(CommentKind::Block, _) => { + BuiltinUnusedDocCommentSub::BlockHelp + } + }; + cx.emit_spanned_lint( UNUSED_DOC_COMMENTS, span, - fluent::lint_builtin_unused_doc_comment, - |lint| { - lint.set_arg("kind", node_kind).span_label(node_span, fluent::label).help( - match attr.kind { - AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => { - fluent::plain_help - } - AttrKind::DocComment(CommentKind::Block, _) => fluent::block_help, - }, - ) - }, + BuiltinUnusedDocComment { kind: node_kind, label: node_span, sub }, ); } } @@ -1197,20 +1104,10 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { match param.kind { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => { - cx.struct_span_lint( + cx.emit_spanned_lint( NO_MANGLE_GENERIC_ITEMS, span, - fluent::lint_builtin_no_mangle_generic, - |lint| { - lint.span_suggestion_short( - no_mangle_attr.span, - fluent::suggestion, - "", - // Use of `#[no_mangle]` suggests FFI intent; correct - // fix may be to monomorphize source by hand - Applicability::MaybeIncorrect, - ) - }, + BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span }, ); break; } @@ -1225,30 +1122,23 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { } hir::ItemKind::Const(..) => { if cx.sess().contains_name(attrs, sym::no_mangle) { + // account for "pub const" (#45562) + let start = cx + .tcx + .sess + .source_map() + .span_to_snippet(it.span) + .map(|snippet| snippet.find("const").unwrap_or(0)) + .unwrap_or(0) as u32; + // `const` is 5 chars + let suggestion = it.span.with_hi(BytePos(it.span.lo().0 + start + 5)); + // Const items do not refer to a particular location in memory, and therefore // don't have anything to attach a symbol to - cx.struct_span_lint( + cx.emit_spanned_lint( NO_MANGLE_CONST_ITEMS, it.span, - fluent::lint_builtin_const_no_mangle, - |lint| { - // account for "pub const" (#45562) - let start = cx - .tcx - .sess - .source_map() - .span_to_snippet(it.span) - .map(|snippet| snippet.find("const").unwrap_or(0)) - .unwrap_or(0) as u32; - // `const` is 5 chars - let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5)); - lint.span_suggestion( - const_span, - fluent::suggestion, - "pub static", - Applicability::MachineApplicable, - ) - }, + BuiltinConstNoMangle { suggestion }, ); } } @@ -1309,12 +1199,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind())) { if from_mutbl < to_mutbl { - cx.struct_span_lint( - MUTABLE_TRANSMUTES, - expr.span, - fluent::lint_builtin_mutable_transmutes, - |lint| lint, - ); + cx.emit_spanned_lint(MUTABLE_TRANSMUTES, expr.span, BuiltinMutablesTransmutes); } } @@ -1362,12 +1247,7 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { if attr.has_name(sym::feature) { if let Some(items) = attr.meta_item_list() { for item in items { - cx.struct_span_lint( - UNSTABLE_FEATURES, - item.span(), - fluent::lint_builtin_unstable_features, - |lint| lint, - ); + cx.emit_spanned_lint(UNSTABLE_FEATURES, item.span(), BuiltinUnstableFeatures); } } } @@ -1422,20 +1302,10 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { // Now, check if the function has the `#[track_caller]` attribute && let Some(attr) = attrs.iter().find(|attr| attr.has_name(sym::track_caller)) { - cx.struct_span_lint( - UNGATED_ASYNC_FN_TRACK_CALLER, - attr.span, - fluent::lint_ungated_async_fn_track_caller, - |lint| { - lint.span_label(span, fluent::label); - rustc_session::parse::add_feature_diagnostics( - lint, - &cx.tcx.sess.parse_sess, - sym::closure_track_caller, - ); - lint - }, - ); + cx.emit_spanned_lint(UNGATED_ASYNC_FN_TRACK_CALLER, attr.span, BuiltinUngatedAsyncFnTrackCaller { + label: span, + parse_sess: &cx.tcx.sess.parse_sess, + }); } } } @@ -1493,18 +1363,13 @@ impl UnreachablePub { applicability = Applicability::MaybeIncorrect; } let def_span = cx.tcx.def_span(def_id); - cx.struct_span_lint( + cx.emit_spanned_lint( UNREACHABLE_PUB, def_span, - fluent::lint_builtin_unreachable_pub, - |lint| { - lint.set_arg("what", what); - - lint.span_suggestion(vis_span, fluent::suggestion, "pub(crate)", applicability); - if exportable { - lint.help(fluent::help); - } - lint + BuiltinUnreachablePub { + what, + suggestion: (vis_span, applicability), + help: exportable.then_some(()), }, ); } @@ -1526,7 +1391,7 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub { fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { let map = cx.tcx.hir(); - if matches!(map.get(map.get_parent_node(field.hir_id)), Node::Variant(_)) { + if matches!(map.get_parent(field.hir_id), Node::Variant(_)) { return; } self.perform_lint(cx, "field", field.def_id, field.vis_span, false); @@ -1569,7 +1434,7 @@ declare_lint_pass!( ); impl TypeAliasBounds { - fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool { + pub(crate) fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool { match *qpath { hir::QPath::TypeRelative(ref ty, _) => { // If this is a type variable, we found a `T::Assoc`. @@ -1583,29 +1448,6 @@ impl TypeAliasBounds { hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false, } } - - fn suggest_changing_assoc_types(ty: &hir::Ty<'_>, err: &mut Diagnostic) { - // Access to associates types should use `<T as Bound>::Assoc`, which does not need a - // bound. Let's see if this type does that. - - // We use a HIR visitor to walk the type. - use rustc_hir::intravisit::{self, Visitor}; - struct WalkAssocTypes<'a> { - err: &'a mut Diagnostic, - } - impl Visitor<'_> for WalkAssocTypes<'_> { - fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) { - if TypeAliasBounds::is_type_variable_assoc(qpath) { - self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help); - } - intravisit::walk_qpath(self, qpath, id) - } - } - - // Let's go for a walk! - let mut visitor = WalkAssocTypes { err }; - visitor.visit_ty(ty); - } } impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { @@ -1639,35 +1481,31 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { let mut suggested_changing_assoc_types = false; if !where_spans.is_empty() { - cx.lint(TYPE_ALIAS_BOUNDS, fluent::lint_builtin_type_alias_where_clause, |lint| { - lint.set_span(where_spans); - lint.span_suggestion( - type_alias_generics.where_clause_span, - fluent::suggestion, - "", - Applicability::MachineApplicable, - ); - if !suggested_changing_assoc_types { - TypeAliasBounds::suggest_changing_assoc_types(ty, lint); - suggested_changing_assoc_types = true; - } - lint + let sub = (!suggested_changing_assoc_types).then(|| { + suggested_changing_assoc_types = true; + SuggestChangingAssocTypes { ty } }); + cx.emit_spanned_lint( + TYPE_ALIAS_BOUNDS, + where_spans, + BuiltinTypeAliasWhereClause { + suggestion: type_alias_generics.where_clause_span, + sub, + }, + ); } if !inline_spans.is_empty() { - cx.lint(TYPE_ALIAS_BOUNDS, fluent::lint_builtin_type_alias_generic_bounds, |lint| { - lint.set_span(inline_spans); - lint.multipart_suggestion( - fluent::suggestion, - inline_sugg, - Applicability::MachineApplicable, - ); - if !suggested_changing_assoc_types { - TypeAliasBounds::suggest_changing_assoc_types(ty, lint); - } - lint + let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg }; + let sub = (!suggested_changing_assoc_types).then(|| { + suggested_changing_assoc_types = true; + SuggestChangingAssocTypes { ty } }); + cx.emit_spanned_lint( + TYPE_ALIAS_BOUNDS, + inline_spans, + BuiltinTypeAliasGenericBounds { suggestion, sub }, + ); } } } @@ -1767,14 +1605,10 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { TypeWellFormedFromEnv(..) => continue, }; if predicate.is_global() { - cx.struct_span_lint( + cx.emit_spanned_lint( TRIVIAL_BOUNDS, span, - fluent::lint_builtin_trivial_bounds, - |lint| { - lint.set_arg("predicate_kind_name", predicate_kind_name) - .set_arg("predicate", predicate) - }, + BuiltinTrivialBounds { predicate_kind_name, predicate }, ); } } @@ -1875,8 +1709,6 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { }; if let Some((start, end, join)) = endpoints { - let msg = fluent::lint_builtin_ellipsis_inclusive_range_patterns; - let suggestion = fluent::suggestion; if parenthesise { self.node_id = Some(pat.id); let end = expr_to_string(&end); @@ -1891,14 +1723,14 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { replace, }); } else { - cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, msg, |lint| { - lint.span_suggestion( - pat.span, - suggestion, + cx.emit_spanned_lint( + ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, + pat.span, + BuiltinEllipsisInclusiveRangePatternsLint::Parenthesise { + suggestion: pat.span, replace, - Applicability::MachineApplicable, - ) - }); + }, + ); } } else { let replace = "..="; @@ -1909,14 +1741,13 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { replace: replace.to_string(), }); } else { - cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, msg, |lint| { - lint.span_suggestion_short( - join, - suggestion, - replace, - Applicability::MachineApplicable, - ) - }); + cx.emit_spanned_lint( + ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, + join, + BuiltinEllipsisInclusiveRangePatternsLint::NonParenthesise { + suggestion: join, + }, + ); } }; } @@ -1996,12 +1827,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems { let attrs = cx.tcx.hir().attrs(it.hir_id()); if let Some(attr) = cx.sess().find_by_name(attrs, sym::rustc_test_marker) { - cx.struct_span_lint( - UNNAMEABLE_TEST_ITEMS, - attr.span, - fluent::lint_builtin_unnameable_test_items, - |lint| lint, - ); + cx.emit_spanned_lint(UNNAMEABLE_TEST_ITEMS, attr.span, BuiltinUnnameableTestItems); } } @@ -2117,18 +1943,10 @@ impl KeywordIdents { return; } - cx.struct_span_lint( + cx.emit_spanned_lint( KEYWORD_IDENTS, ident.span, - fluent::lint_builtin_keyword_idents, - |lint| { - lint.set_arg("kw", ident).set_arg("next", next_edition).span_suggestion( - ident.span, - fluent::suggestion, - format!("r#{}", ident), - Applicability::MachineApplicable, - ) - }, + BuiltinKeywordIdents { kw: ident, next: next_edition, suggestion: ident.span }, ); } } @@ -2405,16 +2223,15 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { Applicability::MaybeIncorrect }; - cx.struct_span_lint( + cx.emit_spanned_lint( EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), - fluent::lint_builtin_explicit_outlives, - |lint| { - lint.set_arg("count", bound_count).multipart_suggestion( - fluent::suggestion, - lint_spans.into_iter().map(|span| (span, String::new())).collect(), + BuiltinExplicitOutlives { + count: bound_count, + suggestion: BuiltinExplicitOutlivesSuggestion { + spans: lint_spans, applicability, - ) + }, }, ); } @@ -2463,24 +2280,18 @@ impl EarlyLintPass for IncompleteFeatures { .chain(features.declared_lib_features.iter().map(|(name, span)| (name, span))) .filter(|(&name, _)| features.incomplete(name)) .for_each(|(&name, &span)| { - cx.struct_span_lint( + let note = rustc_feature::find_feature_issue(name, GateIssue::Language) + .map(|n| BuiltinIncompleteFeaturesNote { n }); + let help = if HAS_MIN_FEATURES.contains(&name) { + Some(BuiltinIncompleteFeaturesHelp) + } else { + None + }; + cx.emit_spanned_lint( INCOMPLETE_FEATURES, span, - fluent::lint_builtin_incomplete_features, - |lint| { - lint.set_arg("name", name); - if let Some(n) = - rustc_feature::find_feature_issue(name, GateIssue::Language) - { - lint.set_arg("n", n); - lint.note(fluent::note); - } - if HAS_MIN_FEATURES.contains(&name) { - lint.help(fluent::help); - } - lint - }, - ) + BuiltinIncompleteFeatures { name, note, help }, + ); }); } } @@ -2525,6 +2336,36 @@ declare_lint! { declare_lint_pass!(InvalidValue => [INVALID_VALUE]); +/// Information about why a type cannot be initialized this way. +pub struct InitError { + pub(crate) message: String, + /// Spans from struct fields and similar that can be obtained from just the type. + pub(crate) span: Option<Span>, + /// Used to report a trace through adts. + pub(crate) nested: Option<Box<InitError>>, +} +impl InitError { + fn spanned(self, span: Span) -> InitError { + Self { span: Some(span), ..self } + } + + fn nested(self, nested: impl Into<Option<InitError>>) -> InitError { + assert!(self.nested.is_none()); + Self { nested: nested.into().map(Box::new), ..self } + } +} + +impl<'a> From<&'a str> for InitError { + fn from(s: &'a str) -> Self { + s.to_owned().into() + } +} +impl From<String> for InitError { + fn from(message: String) -> Self { + Self { message, span: None, nested: None } + } +} + impl<'tcx> LateLintPass<'tcx> for InvalidValue { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) { #[derive(Debug, Copy, Clone, PartialEq)] @@ -2533,36 +2374,6 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { Uninit, } - /// Information about why a type cannot be initialized this way. - struct InitError { - message: String, - /// Spans from struct fields and similar that can be obtained from just the type. - span: Option<Span>, - /// Used to report a trace through adts. - nested: Option<Box<InitError>>, - } - impl InitError { - fn spanned(self, span: Span) -> InitError { - Self { span: Some(span), ..self } - } - - fn nested(self, nested: impl Into<Option<InitError>>) -> InitError { - assert!(self.nested.is_none()); - Self { nested: nested.into().map(Box::new), ..self } - } - } - - impl<'a> From<&'a str> for InitError { - fn from(s: &'a str) -> Self { - s.to_owned().into() - } - } - impl From<String> for InitError { - fn from(message: String) -> Self { - Self { message, span: None, nested: None } - } - } - /// Test if this constant is all-0. fn is_zero(expr: &hir::Expr<'_>) -> bool { use hir::ExprKind::*; @@ -2786,46 +2597,16 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { // using zeroed or uninitialized memory. // We are extremely conservative with what we warn about. let conjured_ty = cx.typeck_results().expr_ty(expr); - if let Some(mut err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) - { - // FIXME(davidtwco): make translatable - cx.struct_span_lint( + if let Some(err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) { + let msg = match init { + InitKind::Zeroed => fluent::lint_builtin_unpermitted_type_init_zeroed, + InitKind::Uninit => fluent::lint_builtin_unpermitted_type_init_unint, + }; + let sub = BuiltinUnpermittedTypeInitSub { err }; + cx.emit_spanned_lint( INVALID_VALUE, expr.span, - DelayDm(|| { - format!( - "the type `{}` does not permit {}", - conjured_ty, - match init { - InitKind::Zeroed => "zero-initialization", - InitKind::Uninit => "being left uninitialized", - }, - ) - }), - |lint| { - lint.span_label( - expr.span, - "this code causes undefined behavior when executed", - ); - lint.span_label( - expr.span, - "help: use `MaybeUninit<T>` instead, \ - and only call `assume_init` after initialization is done", - ); - loop { - if let Some(span) = err.span { - lint.span_note(span, &err.message); - } else { - lint.note(&err.message); - } - if let Some(e) = err.nested { - err = *e; - } else { - break; - } - } - lint - }, + BuiltinUnpermittedTypeInit { msg, ty: conjured_ty, label: expr.span, sub }, ); } } @@ -3171,31 +2952,39 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations { SymbolName::Normal(_) => fi.span, SymbolName::Link(_, annot_span) => fi.span.to(annot_span), }; - // Finally, emit the diagnostic. - let msg = if orig.get_name() == this_fi.ident.name { - fluent::lint_builtin_clashing_extern_same_name + // Finally, emit the diagnostic. + let this = this_fi.ident.name; + let orig = orig.get_name(); + let previous_decl_label = get_relevant_span(orig_fi); + let mismatch_label = get_relevant_span(this_fi); + let sub = BuiltinClashingExternSub { + tcx, + expected: existing_decl_ty, + found: this_decl_ty, + }; + let decorator = if orig == this { + BuiltinClashingExtern::SameName { + this, + orig, + previous_decl_label, + mismatch_label, + sub, + } } else { - fluent::lint_builtin_clashing_extern_diff_name + BuiltinClashingExtern::DiffName { + this, + orig, + previous_decl_label, + mismatch_label, + sub, + } }; - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( CLASHING_EXTERN_DECLARATIONS, this_fi.hir_id(), get_relevant_span(this_fi), - msg, - |lint| { - let mut expected_str = DiagnosticStyledString::new(); - expected_str.push(existing_decl_ty.fn_sig(tcx).to_string(), false); - let mut found_str = DiagnosticStyledString::new(); - found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true); - - lint.set_arg("this_fi", this_fi.ident.name) - .set_arg("orig", orig.get_name()) - .span_label(get_relevant_span(orig_fi), fluent::previous_decl_label) - .span_label(get_relevant_span(this_fi), fluent::mismatch_label) - // FIXME(davidtwco): translatable expected/found - .note_expected_found(&"", expected_str, &"", found_str) - }, + decorator, ); } } @@ -3275,11 +3064,10 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind { if is_null_ptr(cx, expr_deref) { - cx.struct_span_lint( + cx.emit_spanned_lint( DEREF_NULLPTR, expr.span, - fluent::lint_builtin_deref_nullptr, - |lint| lint.span_label(expr.span, fluent::label), + BuiltinDerefNullptr { label: expr.span }, ); } } @@ -3324,6 +3112,7 @@ declare_lint! { declare_lint_pass!(NamedAsmLabels => [NAMED_ASM_LABELS]); impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { + #[allow(rustc::diagnostic_outside_of_impl)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { if let hir::Expr { kind: hir::ExprKind::InlineAsm(hir::InlineAsm { template_strs, .. }), @@ -3464,16 +3253,17 @@ impl EarlyLintPass for SpecialModuleName { } match item.ident.name.as_str() { - "lib" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, "found module declaration for lib.rs", |lint| { - lint - .note("lib.rs is the root of this crate's library target") - .help("to refer to it from other targets, use the library's name as the path") - }), - "main" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, "found module declaration for main.rs", |lint| { - lint - .note("a binary crate cannot be used as library") - }), - _ => continue + "lib" => cx.emit_spanned_lint( + SPECIAL_MODULE_NAME, + item.span, + BuiltinSpecialModuleNameUsed::Lib, + ), + "main" => cx.emit_spanned_lint( + SPECIAL_MODULE_NAME, + item.span, + BuiltinSpecialModuleNameUsed::Main, + ), + _ => continue, } } } @@ -3489,31 +3279,16 @@ impl EarlyLintPass for UnexpectedCfgs { let cfg = &cx.sess().parse_sess.config; let check_cfg = &cx.sess().parse_sess.check_config; for &(name, value) in cfg { - if let Some(names_valid) = &check_cfg.names_valid { - if !names_valid.contains(&name) { - cx.lookup( - UNEXPECTED_CFGS, - None::<MultiSpan>, - fluent::lint_builtin_unexpected_cli_config_name, - |diag| diag.help(fluent::help).set_arg("name", name), - ); - } + if let Some(names_valid) = &check_cfg.names_valid && !names_valid.contains(&name){ + cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName { + name, + }); } - if let Some(value) = value { - if let Some(values) = &check_cfg.values_valid.get(&name) { - if !values.contains(&value) { - cx.lookup( - UNEXPECTED_CFGS, - None::<MultiSpan>, - fluent::lint_builtin_unexpected_cli_config_value, - |diag| { - diag.help(fluent::help) - .set_arg("name", name) - .set_arg("value", value) - }, - ); - } - } + if let Some(value) = value && let Some(values) = check_cfg.values_valid.get(&name) && !values.contains(&value) { + cx.emit_lint( + UNEXPECTED_CFGS, + BuiltinUnexpectedCliConfigValue { name, value }, + ); } } } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index a16bb7f1a5f..c9b9a622571 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -965,6 +965,7 @@ pub trait LintContext: Sized { /// Note that this function should only be called for [`LintExpectationId`]s /// retrieved from the current lint pass. Buffered or manually created ids can /// cause ICEs. + #[rustc_lint_diagnostics] fn fulfill_expectation(&self, expectation: LintExpectationId) { // We need to make sure that submitted expectation ids are correctly fulfilled suppressed // and stored between compilation sessions. To not manually do these steps, we simply create @@ -1011,6 +1012,7 @@ impl<'tcx> LintContext for LateContext<'tcx> { &*self.lint_store } + #[rustc_lint_diagnostics] fn lookup<S: Into<MultiSpan>>( &self, lint: &'static Lint, @@ -1045,6 +1047,7 @@ impl LintContext for EarlyContext<'_> { self.builder.lint_store() } + #[rustc_lint_diagnostics] fn lookup<S: Into<MultiSpan>>( &self, lint: &'static Lint, diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index 1d29a234a3c..dff5a645c17 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -1,6 +1,8 @@ -use crate::{LateContext, LateLintPass, LintContext}; +use crate::{ + lints::{SupertraitAsDerefTarget, SupertraitAsDerefTargetLabel}, + LateContext, LateLintPass, LintContext, +}; -use rustc_errors::DelayDm; use rustc_hir as hir; use rustc_middle::{traits::util::supertraits, ty}; use rustc_span::sym; @@ -71,22 +73,14 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { && supertraits(cx.tcx, t_principal.with_self_ty(cx.tcx, cx.tcx.types.trait_object_dummy_self)) .any(|sup| sup.map_bound(|x| ty::ExistentialTraitRef::erase_self_ty(cx.tcx, x)) == target_principal) { - cx.struct_span_lint( - DEREF_INTO_DYN_SUPERTRAIT, - cx.tcx.def_span(item.owner_id.def_id), - DelayDm(|| { - format!( - "`{t}` implements `Deref` with supertrait `{target_principal}` as target" - ) - }), - |lint| { - if let Some(target_span) = impl_.items.iter().find_map(|i| (i.ident.name == sym::Target).then_some(i.span)) { - lint.span_label(target_span, "target type is set here"); - } - - lint - }, - ) + let label = impl_.items.iter().find_map(|i| (i.ident.name == sym::Target).then_some(i.span)).map(|label| SupertraitAsDerefTargetLabel { + label, + }); + cx.emit_spanned_lint(DEREF_INTO_DYN_SUPERTRAIT, cx.tcx.def_span(item.owner_id.def_id), SupertraitAsDerefTarget { + t, + target_principal: target_principal.to_string(), + label, + }); } } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index c18abaef8e2..f9b2df49592 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -19,6 +19,7 @@ use crate::passes::{EarlyLintPass, EarlyLintPassObject}; use rustc_ast::ptr::P; use rustc_ast::visit::{self as ast_visit, Visitor}; use rustc_ast::{self as ast, walk_list, HasAttrs}; +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_middle::ty::RegisteredTools; use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; @@ -39,6 +40,7 @@ pub struct EarlyContextAndPass<'a, T: EarlyLintPass> { impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { // This always-inlined function is for the hot call site. #[inline(always)] + #[allow(rustc::diagnostic_outside_of_impl)] fn inlined_check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint; @@ -71,7 +73,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { self.inlined_check_id(id); debug!("early context: enter_attrs({:?})", attrs); lint_callback!(self, enter_lint_attrs, attrs); - f(self); + ensure_sufficient_stack(|| f(self)); debug!("early context: exit_attrs({:?})", attrs); lint_callback!(self, exit_lint_attrs, attrs); self.context.builder.pop(push); diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs index f9d7466228a..73bd4173270 100644 --- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs +++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs @@ -1,5 +1,8 @@ -use crate::{context::LintContext, LateContext, LateLintPass}; -use rustc_errors::fluent; +use crate::{ + context::LintContext, + lints::{EnumIntrinsicsMemDiscriminate, EnumIntrinsicsMemVariant}, + LateContext, LateLintPass, +}; use rustc_hir as hir; use rustc_middle::ty::{visit::TypeVisitable, Ty}; use rustc_span::{symbol::sym, Span}; @@ -50,11 +53,10 @@ fn enforce_mem_discriminant( ) { let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0); if is_non_enum(ty_param) { - cx.struct_span_lint( + cx.emit_spanned_lint( ENUM_INTRINSICS_NON_ENUMS, expr_span, - fluent::lint_enum_intrinsics_mem_discriminant, - |lint| lint.set_arg("ty_param", ty_param).span_note(args_span, fluent::note), + EnumIntrinsicsMemDiscriminate { ty_param, note: args_span }, ); } } @@ -62,11 +64,10 @@ fn enforce_mem_discriminant( fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, span: Span) { let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0); if is_non_enum(ty_param) { - cx.struct_span_lint( + cx.emit_spanned_lint( ENUM_INTRINSICS_NON_ENUMS, span, - fluent::lint_enum_intrinsics_mem_variant, - |lint| lint.set_arg("ty_param", ty_param).note(fluent::note), + EnumIntrinsicsMemVariant { ty_param }, ); } } diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index 1a769893f55..f3ae2609186 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -8,12 +8,12 @@ use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] #[diag(lint_overruled_attribute, code = "E0453")] -pub struct OverruledAttribute { +pub struct OverruledAttribute<'a> { #[primary_span] pub span: Span, #[label] pub overruled: Span, - pub lint_level: String, + pub lint_level: &'a str, pub lint_source: Symbol, #[subdiagnostic] pub sub: OverruledAttributeSub, @@ -38,6 +38,7 @@ impl AddToDiagnostic for OverruledAttributeSub { OverruledAttributeSub::NodeSource { span, reason } => { diag.span_label(span, fluent::lint_node_source); if let Some(rationale) = reason { + #[allow(rustc::untranslatable_diagnostic)] diag.note(rationale.as_str()); } } diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index cf8f31bcbd0..e9eb14ea188 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -1,8 +1,7 @@ -use crate::builtin; -use rustc_errors::fluent; -use rustc_hir::HirId; +use crate::lints::{Expectation, ExpectationNote}; use rustc_middle::ty::query::Providers; -use rustc_middle::{lint::LintExpectation, ty::TyCtxt}; +use rustc_middle::ty::TyCtxt; +use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS; use rustc_session::lint::LintExpectationId; use rustc_span::symbol::sym; use rustc_span::Symbol; @@ -12,7 +11,7 @@ pub(crate) fn provide(providers: &mut Providers) { } fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) { - if !tcx.sess.features_untracked().enabled(sym::lint_reasons) { + if !tcx.features().enabled(sym::lint_reasons) { return; } @@ -28,34 +27,17 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) { if !fulfilled_expectations.contains(&id) && tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter)) { - emit_unfulfilled_expectation_lint(tcx, *hir_id, expectation); + let rationale = expectation.reason.map(|rationale| ExpectationNote { rationale }); + let note = expectation.is_unfulfilled_lint_expectations.then_some(()); + tcx.emit_spanned_lint( + UNFULFILLED_LINT_EXPECTATIONS, + *hir_id, + expectation.emission_span, + Expectation { rationale, note }, + ); } } else { unreachable!("at this stage all `LintExpectationId`s are stable"); } } } - -fn emit_unfulfilled_expectation_lint( - tcx: TyCtxt<'_>, - hir_id: HirId, - expectation: &LintExpectation, -) { - tcx.struct_span_lint_hir( - builtin::UNFULFILLED_LINT_EXPECTATIONS, - hir_id, - expectation.emission_span, - fluent::lint_expectation, - |lint| { - if let Some(rationale) = expectation.reason { - lint.note(rationale.as_str()); - } - - if expectation.is_unfulfilled_lint_expectations { - lint.note(fluent::note); - } - - lint - }, - ); -} diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index 182734fa9fc..5219992ee94 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -1,7 +1,12 @@ -use crate::{LateContext, LateLintPass, LintContext}; +use crate::{ + lints::{ + ForLoopsOverFalliblesDiag, ForLoopsOverFalliblesLoopSub, ForLoopsOverFalliblesQuestionMark, + ForLoopsOverFalliblesSuggestion, + }, + LateContext, LateLintPass, LintContext, +}; use hir::{Expr, Pat}; -use rustc_errors::{Applicability, DelayDm}; use rustc_hir as hir; use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause}; use rustc_middle::ty::{self, List}; @@ -53,53 +58,29 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { _ => return, }; - let msg = DelayDm(|| { - format!( - "for loop over {article} `{ty}`. This is more readably written as an `if let` statement", - ) - }); - - cx.struct_span_lint(FOR_LOOPS_OVER_FALLIBLES, arg.span, msg, |lint| { - if let Some(recv) = extract_iterator_next_call(cx, arg) + let sub = if let Some(recv) = extract_iterator_next_call(cx, arg) && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span) { - lint.span_suggestion( - recv.span.between(arg.span.shrink_to_hi()), - format!("to iterate over `{recv_snip}` remove the call to `next`"), - ".by_ref()", - Applicability::MaybeIncorrect - ); + ForLoopsOverFalliblesLoopSub::RemoveNext { suggestion: recv.span.between(arg.span.shrink_to_hi()), recv_snip } } else { - lint.multipart_suggestion_verbose( - "to check pattern in a loop use `while let`", - vec![ - // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts - (expr.span.with_hi(pat.span.lo()), format!("while let {var}(")), - (pat.span.between(arg.span), ") = ".to_string()), - ], - Applicability::MaybeIncorrect - ); - } - - if suggest_question_mark(cx, adt, substs, expr.span) { - lint.span_suggestion( - arg.span.shrink_to_hi(), - "consider unwrapping the `Result` with `?` to iterate over its contents", - "?", - Applicability::MaybeIncorrect, - ); - } - - lint.multipart_suggestion_verbose( - "consider using `if let` to clear intent", - vec![ - // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts - (expr.span.with_hi(pat.span.lo()), format!("if let {var}(")), - (pat.span.between(arg.span), ") = ".to_string()), - ], - Applicability::MaybeIncorrect, - ) - }) + ForLoopsOverFalliblesLoopSub::UseWhileLet { start_span: expr.span.with_hi(pat.span.lo()), end_span: pat.span.between(arg.span), var } + } ; + let question_mark = if suggest_question_mark(cx, adt, substs, expr.span) { + Some(ForLoopsOverFalliblesQuestionMark { suggestion: arg.span.shrink_to_hi() }) + } else { + None + }; + let suggestion = ForLoopsOverFalliblesSuggestion { + var, + start_span: expr.span.with_hi(pat.span.lo()), + end_span: pat.span.between(arg.span), + }; + + cx.emit_spanned_lint( + FOR_LOOPS_OVER_FALLIBLES, + arg.span, + ForLoopsOverFalliblesDiag { article, ty, sub, question_mark, suggestion }, + ); } } diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs index dc2f5c0e296..7c1af6bee1d 100644 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs @@ -1,7 +1,12 @@ -use crate::{EarlyContext, EarlyLintPass, LintContext}; +use crate::{ + lints::{ + HiddenUnicodeCodepointsDiag, HiddenUnicodeCodepointsDiagLabels, + HiddenUnicodeCodepointsDiagSub, + }, + EarlyContext, EarlyLintPass, LintContext, +}; use ast::util::unicode::{contains_text_flow_control_chars, TEXT_FLOW_CONTROL_CHARS}; use rustc_ast as ast; -use rustc_errors::{fluent, Applicability, SuggestionStyle}; use rustc_span::{BytePos, Span, Symbol}; declare_lint! { @@ -60,55 +65,19 @@ impl HiddenUnicodeCodepoints { }) .collect(); - cx.struct_span_lint( + let count = spans.len(); + let labels = point_at_inner_spans + .then_some(HiddenUnicodeCodepointsDiagLabels { spans: spans.clone() }); + let sub = if point_at_inner_spans && !spans.is_empty() { + HiddenUnicodeCodepointsDiagSub::Escape { spans } + } else { + HiddenUnicodeCodepointsDiagSub::NoEscape { spans } + }; + + cx.emit_spanned_lint( TEXT_DIRECTION_CODEPOINT_IN_LITERAL, span, - fluent::lint_hidden_unicode_codepoints, - |lint| { - lint.set_arg("label", label); - lint.set_arg("count", spans.len()); - lint.span_label(span, fluent::label); - lint.note(fluent::note); - if point_at_inner_spans { - for (c, span) in &spans { - lint.span_label(*span, format!("{:?}", c)); - } - } - if point_at_inner_spans && !spans.is_empty() { - lint.multipart_suggestion_with_style( - fluent::suggestion_remove, - spans.iter().map(|(_, span)| (*span, "".to_string())).collect(), - Applicability::MachineApplicable, - SuggestionStyle::HideCodeAlways, - ); - lint.multipart_suggestion( - fluent::suggestion_escape, - spans - .into_iter() - .map(|(c, span)| { - let c = format!("{:?}", c); - (span, c[1..c.len() - 1].to_string()) - }) - .collect(), - Applicability::MachineApplicable, - ); - } else { - // FIXME: in other suggestions we've reversed the inner spans of doc comments. We - // should do the same here to provide the same good suggestions as we do for - // literals above. - lint.set_arg( - "escaped", - spans - .into_iter() - .map(|(c, _)| format!("{:?}", c)) - .collect::<Vec<String>>() - .join(", "), - ); - lint.note(fluent::suggestion_remove); - lint.note(fluent::no_suggestion_note_escape); - } - lint - }, + HiddenUnicodeCodepointsDiag { label, count, span_label: span, labels, sub }, ); } } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 4f92661dbd3..5eb54cc0034 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -1,9 +1,12 @@ //! Some lints that are only useful in the compiler or crates that use compiler internals, such as //! Clippy. +use crate::lints::{ + BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistantDocKeyword, + QueryInstability, TyQualified, TykindDiag, TykindKind, UntranslatableDiag, +}; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_ast as ast; -use rustc_errors::{fluent, Applicability}; use rustc_hir::def::Res; use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath}; use rustc_hir::{HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind}; @@ -29,20 +32,15 @@ impl LateLintPass<'_> for DefaultHashTypes { // don't lint imports, only actual usages return; } - let replace = match cx.tcx.get_diagnostic_name(def_id) { + let preferred = match cx.tcx.get_diagnostic_name(def_id) { Some(sym::HashMap) => "FxHashMap", Some(sym::HashSet) => "FxHashSet", _ => return, }; - cx.struct_span_lint( + cx.emit_spanned_lint( DEFAULT_HASH_TYPES, path.span, - fluent::lint_default_hash_types, - |lint| { - lint.set_arg("preferred", replace) - .set_arg("used", cx.tcx.item_name(def_id)) - .note(fluent::note) - }, + DefaultHashTypesDiag { preferred, used: cx.tcx.item_name(def_id) }, ); } } @@ -83,12 +81,11 @@ impl LateLintPass<'_> for QueryStability { if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) { let def_id = instance.def_id(); if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) { - cx.struct_span_lint( + cx.emit_spanned_lint( POTENTIAL_QUERY_INSTABILITY, span, - fluent::lint_query_instability, - |lint| lint.set_arg("query", cx.tcx.item_name(def_id)).note(fluent::note), - ) + QueryInstability { query: cx.tcx.item_name(def_id) }, + ); } } } @@ -126,14 +123,8 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { let span = path.span.with_hi( segment.args.map_or(segment.ident.span, |a| a.span_ext).hi() ); - cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, fluent::lint_tykind_kind, |lint| { - lint - .span_suggestion( - span, - fluent::suggestion, - "ty", - Applicability::MaybeIncorrect, // ty maybe needs an import - ) + cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindKind { + suggestion: span, }); } } @@ -143,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { TyKind::Path(QPath::Resolved(_, path)) => { if lint_ty_kind_usage(cx, &path.res) { let hir = cx.tcx.hir(); - let span = match hir.find(hir.get_parent_node(ty.hir_id)) { + let span = match hir.find_parent(ty.hir_id) { Some(Node::Pat(Pat { kind: PatKind::Path(qpath) @@ -190,39 +181,17 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { match span { Some(span) => { - cx.struct_span_lint( - USAGE_OF_TY_TYKIND, - path.span, - fluent::lint_tykind_kind, - |lint| lint.span_suggestion( - span, - fluent::suggestion, - "ty", - Applicability::MaybeIncorrect, // ty maybe needs an import - ) - ) + cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindKind { + suggestion: span, + }); }, - None => cx.struct_span_lint( - USAGE_OF_TY_TYKIND, - path.span, - fluent::lint_tykind, - |lint| lint.help(fluent::help) - ) - } - } else if !ty.span.from_expansion() && let Some(t) = is_ty_or_ty_ctxt(cx, &path) { - if path.segments.len() > 1 { - cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, fluent::lint_ty_qualified, |lint| { - lint - .set_arg("ty", t.clone()) - .span_suggestion( - path.span, - fluent::suggestion, - t, - // The import probably needs to be changed - Applicability::MaybeIncorrect, - ) - }) + None => cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindDiag), } + } else if !ty.span.from_expansion() && path.segments.len() > 1 && let Some(t) = is_ty_or_ty_ctxt(cx, &path) { + cx.emit_spanned_lint(USAGE_OF_QUALIFIED_TY, path.span, TyQualified { + ty: t.clone(), + suggestion: path.span, + }); } } _ => {} @@ -303,12 +272,11 @@ impl EarlyLintPass for LintPassImpl { && call_site.ctxt().outer_expn_data().kind != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass) { - cx.struct_span_lint( + cx.emit_spanned_lint( LINT_PASS_IMPL_WITHOUT_MACRO, lint_pass.path.span, - fluent::lint_lintpass_by_hand, - |lint| lint.help(fluent::help), - ) + LintPassByHand, + ); } } } @@ -338,17 +306,16 @@ impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword { if let Some(list) = attr.meta_item_list() { for nested in list { if nested.has_name(sym::keyword) { - let v = nested + let keyword = nested .value_str() .expect("#[doc(keyword = \"...\")] expected a value!"); - if is_doc_keyword(v) { + if is_doc_keyword(keyword) { return; } - cx.struct_span_lint( + cx.emit_spanned_lint( EXISTING_DOC_KEYWORD, attr.span, - fluent::lint_non_existant_doc_keyword, - |lint| lint.set_arg("keyword", v).help(fluent::help), + NonExistantDocKeyword { keyword }, ); } } @@ -407,12 +374,7 @@ impl LateLintPass<'_> for Diagnostics { } debug!(?found_impl); if !found_parent_with_attr && !found_impl { - cx.struct_span_lint( - DIAGNOSTIC_OUTSIDE_OF_IMPL, - span, - fluent::lint_diag_out_of_impl, - |lint| lint, - ) + cx.emit_spanned_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl); } let mut found_diagnostic_message = false; @@ -428,12 +390,7 @@ impl LateLintPass<'_> for Diagnostics { } debug!(?found_diagnostic_message); if !found_parent_with_attr && !found_diagnostic_message { - cx.struct_span_lint( - UNTRANSLATABLE_DIAGNOSTIC, - span, - fluent::lint_untranslatable_diag, - |lint| lint, - ) + cx.emit_spanned_lint(UNTRANSLATABLE_DIAGNOSTIC, span, UntranslatableDiag); } } } @@ -465,9 +422,9 @@ impl LateLintPass<'_> for BadOptAccess { let Some(lit) = item.lit() && let ast::LitKind::Str(val, _) = lit.kind { - cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, val.as_str(), |lint| - lint - ); + cx.emit_spanned_lint(BAD_OPT_ACCESS, expr.span, BadOptAccessDiag { + msg: val.as_str(), + }); } } } diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index 04d844d21dc..b83a9665fc0 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -1,5 +1,8 @@ -use crate::{LateContext, LateLintPass, LintContext}; -use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan}; +use crate::{ + lints::{NonBindingLet, NonBindingLetSub}, + LateContext, LateLintPass, LintContext, +}; +use rustc_errors::MultiSpan; use rustc_hir as hir; use rustc_middle::ty; use rustc_span::Symbol; @@ -119,6 +122,11 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { _ => false, }; + let sub = NonBindingLetSub { + suggestion: local.pat.span, + multi_suggestion_start: local.span.until(init.span), + multi_suggestion_end: init.span.shrink_to_hi(), + }; if is_sync_lock { let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]); span.push_span_label( @@ -129,41 +137,14 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { init.span, "this binding will immediately drop the value assigned to it".to_string(), ); - cx.struct_span_lint( - LET_UNDERSCORE_LOCK, - span, - "non-binding let on a synchronization lock", - |lint| build_lint(lint, local, init.span), - ) + cx.emit_spanned_lint(LET_UNDERSCORE_LOCK, span, NonBindingLet::SyncLock { sub }); } else { - cx.struct_span_lint( + cx.emit_spanned_lint( LET_UNDERSCORE_DROP, local.span, - "non-binding let on a type that implements `Drop`", - |lint| build_lint(lint, local, init.span), - ) + NonBindingLet::DropType { sub }, + ); } } } } - -fn build_lint<'a, 'b>( - lint: &'a mut DiagnosticBuilder<'b, ()>, - local: &hir::Local<'_>, - init_span: rustc_span::Span, -) -> &'a mut DiagnosticBuilder<'b, ()> { - lint.span_suggestion_verbose( - local.pat.span, - "consider binding to an unused variable to avoid immediately dropping the value", - "_unused", - Applicability::MachineApplicable, - ) - .multipart_suggestion( - "consider immediately dropping the value", - vec![ - (local.span.until(init_span), "drop(".to_string()), - (init_span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ) -} diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index e9d3d44a3f9..09dfb1022d8 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,9 +1,13 @@ use crate::context::{CheckLintNameResult, LintStore}; use crate::late::unerased_lint_store; +use crate::lints::{ + DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAtributeLint, RenamedOrRemovedLint, + RenamedOrRemovedLintSuggestion, UnknownLint, UnknownLintSuggestion, +}; use rustc_ast as ast; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, MultiSpan}; +use rustc_errors::{fluent, DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirId; @@ -15,6 +19,7 @@ use rustc_middle::lint::{ }; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; +use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES}; use rustc_session::lint::{ builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS}, Level, Lint, LintExpectationId, LintId, @@ -583,57 +588,32 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { old_src, id_name ); - - let decorate_diag = |diag: &mut Diagnostic| { - diag.span_label(src.span(), "overruled by previous forbid"); - match old_src { - LintLevelSource::Default => { - diag.note(&format!( - "`forbid` lint level is the default for {}", - id.to_string() - )); - } - LintLevelSource::Node { span, reason, .. } => { - diag.span_label(span, "`forbid` level set here"); - if let Some(rationale) = reason { - diag.note(rationale.as_str()); - } - } - LintLevelSource::CommandLine(_, _) => { - diag.note("`forbid` lint level was set on command line"); - } + let sub = match old_src { + LintLevelSource::Default => { + OverruledAttributeSub::DefaultSource { id: id.to_string() } } + LintLevelSource::Node { span, reason, .. } => { + OverruledAttributeSub::NodeSource { span, reason } + } + LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource, }; if !fcw_warning { self.sess.emit_err(OverruledAttribute { span: src.span(), overruled: src.span(), - lint_level: level.as_str().to_string(), + lint_level: level.as_str(), lint_source: src.name(), - sub: match old_src { - LintLevelSource::Default => { - OverruledAttributeSub::DefaultSource { id: id.to_string() } - } - LintLevelSource::Node { span, reason, .. } => { - OverruledAttributeSub::NodeSource { span, reason } - } - LintLevelSource::CommandLine(_, _) => { - OverruledAttributeSub::CommandLineSource - } - }, + sub, }); } else { - self.struct_lint( + self.emit_spanned_lint( FORBIDDEN_LINT_GROUPS, - Some(src.span().into()), - format!( - "{}({}) incompatible with previous forbid", - level.as_str(), - src.name(), - ), - |lint| { - decorate_diag(lint); - lint + src.span().into(), + OverruledAtributeLint { + overruled: src.span(), + lint_level: level.as_str(), + lint_source: src.name(), + sub, }, ); } @@ -858,25 +838,13 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } Err((Some(ids), ref new_lint_name)) => { let lint = builtin::RENAMED_AND_REMOVED_LINTS; - let (lvl, src) = self.provider.get_lint_level(lint, &sess); - struct_lint_level( - self.sess, + self.emit_spanned_lint( lint, - lvl, - src, - Some(sp.into()), - format!( - "lint name `{}` is deprecated \ - and may not have an effect in the future.", - name - ), - |lint| { - lint.span_suggestion( - sp, - "change it to", - new_lint_name, - Applicability::MachineApplicable, - ) + sp.into(), + DeprecatedLintName { + name, + suggestion: sp, + replace: &new_lint_name, }, ); @@ -917,54 +885,29 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { _ if !self.warn_about_weird_lints => {} CheckLintNameResult::Warning(msg, renamed) => { - let lint = builtin::RENAMED_AND_REMOVED_LINTS; - let (renamed_lint_level, src) = self.provider.get_lint_level(lint, &sess); - struct_lint_level( - self.sess, - lint, - renamed_lint_level, - src, - Some(sp.into()), - msg, - |lint| { - if let Some(new_name) = &renamed { - lint.span_suggestion( - sp, - "use the new name", - new_name, - Applicability::MachineApplicable, - ); - } - lint - }, + let suggestion = + renamed.as_ref().map(|replace| RenamedOrRemovedLintSuggestion { + suggestion: sp, + replace: replace.as_str(), + }); + self.emit_spanned_lint( + RENAMED_AND_REMOVED_LINTS, + sp.into(), + RenamedOrRemovedLint { msg, suggestion }, ); } CheckLintNameResult::NoLint(suggestion) => { - let lint = builtin::UNKNOWN_LINTS; - let (level, src) = self.provider.get_lint_level(lint, self.sess); let name = if let Some(tool_ident) = tool_ident { format!("{}::{}", tool_ident.name, name) } else { name.to_string() }; - struct_lint_level( - self.sess, - lint, - level, - src, - Some(sp.into()), - format!("unknown lint: `{}`", name), - |lint| { - if let Some(suggestion) = suggestion { - lint.span_suggestion( - sp, - "did you mean", - suggestion, - Applicability::MaybeIncorrect, - ); - } - lint - }, + let suggestion = suggestion + .map(|replace| UnknownLintSuggestion { suggestion: sp, replace }); + self.emit_spanned_lint( + UNKNOWN_LINTS, + sp.into(), + UnknownLint { name, suggestion }, ); } } @@ -1010,20 +953,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { continue }; - let lint = builtin::UNUSED_ATTRIBUTES; - let (lint_level, lint_src) = self.provider.get_lint_level(lint, &self.sess); - struct_lint_level( - self.sess, - lint, - lint_level, - lint_src, - Some(lint_attr_span.into()), - format!( - "{}({}) is ignored unless specified at crate level", - level.as_str(), - lint_attr_name - ), - |lint| lint, + self.emit_spanned_lint( + UNUSED_ATTRIBUTES, + lint_attr_span.into(), + IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name }, ); // don't set a separate error for every lint in the group break; @@ -1047,11 +980,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { level, src, Some(span.into()), - format!("unknown lint: `{}`", lint_id.lint.name_lower()), + fluent::lint_unknown_gated_lint, |lint| { - lint.note( - &format!("the `{}` lint is unstable", lint_id.lint.name_lower(),), - ); + lint.set_arg("name", lint_id.lint.name_lower()); + lint.note(fluent::note); add_feature_diagnostics(lint, &self.sess.parse_sess, feature); lint }, @@ -1086,6 +1018,25 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let (level, src) = self.lint_level(lint); struct_lint_level(self.sess, lint, level, src, span, msg, decorate) } + + pub fn emit_spanned_lint( + &self, + lint: &'static Lint, + span: MultiSpan, + decorate: impl for<'a> DecorateLint<'a, ()>, + ) { + let (level, src) = self.lint_level(lint); + struct_lint_level(self.sess, lint, level, src, Some(span), decorate.msg(), |lint| { + decorate.decorate_lint(lint) + }); + } + + pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> DecorateLint<'a, ()>) { + let (level, src) = self.lint_level(lint); + struct_lint_level(self.sess, lint, level, src, None, decorate.msg(), |lint| { + decorate.decorate_lint(lint) + }); + } } pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 1275d6f223c..3d818154cb9 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -38,6 +38,8 @@ #![feature(never_type)] #![feature(rustc_attrs)] #![recursion_limit = "256"] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate rustc_middle; @@ -60,6 +62,7 @@ mod internal; mod late; mod let_underscore; mod levels; +mod lints; mod methods; mod non_ascii_idents; mod non_fmt_panic; diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs new file mode 100644 index 00000000000..c3782a49689 --- /dev/null +++ b/compiler/rustc_lint/src/lints.rs @@ -0,0 +1,1479 @@ +#![allow(rustc::untranslatable_diagnostic)] +#![allow(rustc::diagnostic_outside_of_impl)] +use std::num::NonZeroU32; + +use rustc_errors::{ + fluent, AddToDiagnostic, Applicability, DecorateLint, DiagnosticMessage, + DiagnosticStyledString, SuggestionStyle, +}; +use rustc_hir::def_id::DefId; +use rustc_macros::{LintDiagnostic, Subdiagnostic}; +use rustc_middle::ty::{Predicate, Ty, TyCtxt}; +use rustc_session::parse::ParseSess; +use rustc_span::{edition::Edition, sym, symbol::Ident, Span, Symbol}; + +use crate::{ + builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext, +}; + +// array_into_iter.rs +#[derive(LintDiagnostic)] +#[diag(lint_array_into_iter)] +pub struct ArrayIntoIterDiag<'a> { + pub target: &'a str, + #[suggestion(use_iter_suggestion, code = "iter", applicability = "machine-applicable")] + pub suggestion: Span, + #[subdiagnostic] + pub sub: Option<ArrayIntoIterDiagSub>, +} + +#[derive(Subdiagnostic)] +pub enum ArrayIntoIterDiagSub { + #[suggestion(remove_into_iter_suggestion, code = "", applicability = "maybe-incorrect")] + RemoveIntoIter { + #[primary_span] + span: Span, + }, + #[multipart_suggestion(use_explicit_into_iter_suggestion, applicability = "maybe-incorrect")] + UseExplicitIntoIter { + #[suggestion_part(code = "IntoIterator::into_iter(")] + start_span: Span, + #[suggestion_part(code = ")")] + end_span: Span, + }, +} + +// builtin.rs +#[derive(LintDiagnostic)] +#[diag(lint_builtin_while_true)] +pub struct BuiltinWhileTrue { + #[suggestion(style = "short", code = "{replace}", applicability = "machine-applicable")] + pub suggestion: Span, + pub replace: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_box_pointers)] +pub struct BuiltinBoxPointers<'a> { + pub ty: Ty<'a>, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_non_shorthand_field_patterns)] +pub struct BuiltinNonShorthandFieldPatterns { + pub ident: Ident, + #[suggestion(code = "{prefix}{ident}", applicability = "machine-applicable")] + pub suggestion: Span, + pub prefix: &'static str, +} + +#[derive(LintDiagnostic)] +pub enum BuiltinUnsafe { + #[diag(lint_builtin_allow_internal_unsafe)] + AllowInternalUnsafe, + #[diag(lint_builtin_unsafe_block)] + UnsafeBlock, + #[diag(lint_builtin_unsafe_trait)] + UnsafeTrait, + #[diag(lint_builtin_unsafe_impl)] + UnsafeImpl, + #[diag(lint_builtin_no_mangle_fn)] + #[note(lint_builtin_overridden_symbol_name)] + NoMangleFn, + #[diag(lint_builtin_export_name_fn)] + #[note(lint_builtin_overridden_symbol_name)] + ExportNameFn, + #[diag(lint_builtin_link_section_fn)] + #[note(lint_builtin_overridden_symbol_section)] + LinkSectionFn, + #[diag(lint_builtin_no_mangle_static)] + #[note(lint_builtin_overridden_symbol_name)] + NoMangleStatic, + #[diag(lint_builtin_export_name_static)] + #[note(lint_builtin_overridden_symbol_name)] + ExportNameStatic, + #[diag(lint_builtin_link_section_static)] + #[note(lint_builtin_overridden_symbol_section)] + LinkSectionStatic, + #[diag(lint_builtin_no_mangle_method)] + #[note(lint_builtin_overridden_symbol_name)] + NoMangleMethod, + #[diag(lint_builtin_export_name_method)] + #[note(lint_builtin_overridden_symbol_name)] + ExportNameMethod, + #[diag(lint_builtin_decl_unsafe_fn)] + DeclUnsafeFn, + #[diag(lint_builtin_decl_unsafe_method)] + DeclUnsafeMethod, + #[diag(lint_builtin_impl_unsafe_method)] + ImplUnsafeMethod, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_missing_doc)] +pub struct BuiltinMissingDoc<'a> { + pub article: &'a str, + pub desc: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_missing_copy_impl)] +pub struct BuiltinMissingCopyImpl; + +pub struct BuiltinMissingDebugImpl<'a> { + pub tcx: TyCtxt<'a>, + pub def_id: DefId, +} + +// Needed for def_path_str +impl<'a> DecorateLint<'a, ()> for BuiltinMissingDebugImpl<'_> { + fn decorate_lint<'b>( + self, + diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, + ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + diag.set_arg("debug", self.tcx.def_path_str(self.def_id)); + diag + } + + fn msg(&self) -> DiagnosticMessage { + fluent::lint_builtin_missing_debug_impl + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_anonymous_params)] +pub struct BuiltinAnonymousParams<'a> { + #[suggestion(code = "_: {ty_snip}")] + pub suggestion: (Span, Applicability), + pub ty_snip: &'a str, +} + +// FIXME(davidtwco) translatable deprecated attr +#[derive(LintDiagnostic)] +#[diag(lint_builtin_deprecated_attr_link)] +pub struct BuiltinDeprecatedAttrLink<'a> { + pub name: Symbol, + pub reason: &'a str, + pub link: &'a str, + #[subdiagnostic] + pub suggestion: BuiltinDeprecatedAttrLinkSuggestion<'a>, +} + +#[derive(Subdiagnostic)] +pub enum BuiltinDeprecatedAttrLinkSuggestion<'a> { + #[suggestion(msg_suggestion, code = "", applicability = "machine-applicable")] + Msg { + #[primary_span] + suggestion: Span, + msg: &'a str, + }, + #[suggestion(default_suggestion, code = "", applicability = "machine-applicable")] + Default { + #[primary_span] + suggestion: Span, + }, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_deprecated_attr_used)] +pub struct BuiltinDeprecatedAttrUsed { + pub name: String, + #[suggestion( + lint_builtin_deprecated_attr_default_suggestion, + style = "short", + code = "", + applicability = "machine-applicable" + )] + pub suggestion: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_unused_doc_comment)] +pub struct BuiltinUnusedDocComment<'a> { + pub kind: &'a str, + #[label] + pub label: Span, + #[subdiagnostic] + pub sub: BuiltinUnusedDocCommentSub, +} + +#[derive(Subdiagnostic)] +pub enum BuiltinUnusedDocCommentSub { + #[help(plain_help)] + PlainHelp, + #[help(block_help)] + BlockHelp, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_no_mangle_generic)] +pub struct BuiltinNoMangleGeneric { + // Use of `#[no_mangle]` suggests FFI intent; correct + // fix may be to monomorphize source by hand + #[suggestion(style = "short", code = "", applicability = "maybe-incorrect")] + pub suggestion: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_const_no_mangle)] +pub struct BuiltinConstNoMangle { + #[suggestion(code = "pub static", applicability = "machine-applicable")] + pub suggestion: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_mutable_transmutes)] +pub struct BuiltinMutablesTransmutes; + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_unstable_features)] +pub struct BuiltinUnstableFeatures; + +// lint_ungated_async_fn_track_caller +pub struct BuiltinUngatedAsyncFnTrackCaller<'a> { + pub label: Span, + pub parse_sess: &'a ParseSess, +} + +impl<'a> DecorateLint<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> { + fn decorate_lint<'b>( + self, + diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, + ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + diag.span_label(self.label, fluent::label); + rustc_session::parse::add_feature_diagnostics( + diag, + &self.parse_sess, + sym::closure_track_caller, + ); + diag + } + + fn msg(&self) -> DiagnosticMessage { + fluent::lint_ungated_async_fn_track_caller + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_unreachable_pub)] +pub struct BuiltinUnreachablePub<'a> { + pub what: &'a str, + #[suggestion(code = "pub(crate)")] + pub suggestion: (Span, Applicability), + #[help] + pub help: Option<()>, +} + +pub struct SuggestChangingAssocTypes<'a, 'b> { + pub ty: &'a rustc_hir::Ty<'b>, +} + +impl AddToDiagnostic for SuggestChangingAssocTypes<'_, '_> { + fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F) + where + F: Fn( + &mut rustc_errors::Diagnostic, + rustc_errors::SubdiagnosticMessage, + ) -> rustc_errors::SubdiagnosticMessage, + { + // Access to associates types should use `<T as Bound>::Assoc`, which does not need a + // bound. Let's see if this type does that. + + // We use a HIR visitor to walk the type. + use rustc_hir::intravisit::{self, Visitor}; + struct WalkAssocTypes<'a> { + err: &'a mut rustc_errors::Diagnostic, + } + impl Visitor<'_> for WalkAssocTypes<'_> { + fn visit_qpath( + &mut self, + qpath: &rustc_hir::QPath<'_>, + id: rustc_hir::HirId, + span: Span, + ) { + if TypeAliasBounds::is_type_variable_assoc(qpath) { + self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help); + } + intravisit::walk_qpath(self, qpath, id) + } + } + + // Let's go for a walk! + let mut visitor = WalkAssocTypes { err: diag }; + visitor.visit_ty(self.ty); + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_type_alias_where_clause)] +pub struct BuiltinTypeAliasWhereClause<'a, 'b> { + #[suggestion(code = "", applicability = "machine-applicable")] + pub suggestion: Span, + #[subdiagnostic] + pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_type_alias_generic_bounds)] +pub struct BuiltinTypeAliasGenericBounds<'a, 'b> { + #[subdiagnostic] + pub suggestion: BuiltinTypeAliasGenericBoundsSuggestion, + #[subdiagnostic] + pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>, +} + +pub struct BuiltinTypeAliasGenericBoundsSuggestion { + pub suggestions: Vec<(Span, String)>, +} + +impl AddToDiagnostic for BuiltinTypeAliasGenericBoundsSuggestion { + fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F) + where + F: Fn( + &mut rustc_errors::Diagnostic, + rustc_errors::SubdiagnosticMessage, + ) -> rustc_errors::SubdiagnosticMessage, + { + diag.multipart_suggestion( + fluent::suggestion, + self.suggestions, + Applicability::MachineApplicable, + ); + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_trivial_bounds)] +pub struct BuiltinTrivialBounds<'a> { + pub predicate_kind_name: &'a str, + pub predicate: Predicate<'a>, +} + +#[derive(LintDiagnostic)] +pub enum BuiltinEllipsisInclusiveRangePatternsLint { + #[diag(lint_builtin_ellipsis_inclusive_range_patterns)] + Parenthesise { + #[suggestion(code = "{replace}", applicability = "machine-applicable")] + suggestion: Span, + replace: String, + }, + #[diag(lint_builtin_ellipsis_inclusive_range_patterns)] + NonParenthesise { + #[suggestion(style = "short", code = "..=", applicability = "machine-applicable")] + suggestion: Span, + }, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_unnameable_test_items)] +pub struct BuiltinUnnameableTestItems; + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_keyword_idents)] +pub struct BuiltinKeywordIdents { + pub kw: Ident, + pub next: Edition, + #[suggestion(code = "r#{kw}", applicability = "machine-applicable")] + pub suggestion: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_explicit_outlives)] +pub struct BuiltinExplicitOutlives { + pub count: usize, + #[subdiagnostic] + pub suggestion: BuiltinExplicitOutlivesSuggestion, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(suggestion)] +pub struct BuiltinExplicitOutlivesSuggestion { + #[suggestion_part(code = "")] + pub spans: Vec<Span>, + #[applicability] + pub applicability: Applicability, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_incomplete_features)] +pub struct BuiltinIncompleteFeatures { + pub name: Symbol, + #[subdiagnostic] + pub note: Option<BuiltinIncompleteFeaturesNote>, + #[subdiagnostic] + pub help: Option<BuiltinIncompleteFeaturesHelp>, +} + +#[derive(Subdiagnostic)] +#[help(help)] +pub struct BuiltinIncompleteFeaturesHelp; + +#[derive(Subdiagnostic)] +#[note(note)] +pub struct BuiltinIncompleteFeaturesNote { + pub n: NonZeroU32, +} + +pub struct BuiltinUnpermittedTypeInit<'a> { + pub msg: DiagnosticMessage, + pub ty: Ty<'a>, + pub label: Span, + pub sub: BuiltinUnpermittedTypeInitSub, +} + +impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> { + fn decorate_lint<'b>( + self, + diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, + ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + diag.set_arg("ty", self.ty); + diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label); + diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label_suggestion); + self.sub.add_to_diagnostic(diag); + diag + } + + fn msg(&self) -> rustc_errors::DiagnosticMessage { + self.msg.clone() + } +} + +// FIXME(davidtwco): make translatable +pub struct BuiltinUnpermittedTypeInitSub { + pub err: InitError, +} + +impl AddToDiagnostic for BuiltinUnpermittedTypeInitSub { + fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F) + where + F: Fn( + &mut rustc_errors::Diagnostic, + rustc_errors::SubdiagnosticMessage, + ) -> rustc_errors::SubdiagnosticMessage, + { + let mut err = self.err; + loop { + if let Some(span) = err.span { + diag.span_note(span, err.message); + } else { + diag.note(err.message); + } + if let Some(e) = err.nested { + err = *e; + } else { + break; + } + } + } +} + +#[derive(LintDiagnostic)] +pub enum BuiltinClashingExtern<'a> { + #[diag(lint_builtin_clashing_extern_same_name)] + SameName { + this: Symbol, + orig: Symbol, + #[label(previous_decl_label)] + previous_decl_label: Span, + #[label(mismatch_label)] + mismatch_label: Span, + #[subdiagnostic] + sub: BuiltinClashingExternSub<'a>, + }, + #[diag(lint_builtin_clashing_extern_diff_name)] + DiffName { + this: Symbol, + orig: Symbol, + #[label(previous_decl_label)] + previous_decl_label: Span, + #[label(mismatch_label)] + mismatch_label: Span, + #[subdiagnostic] + sub: BuiltinClashingExternSub<'a>, + }, +} + +// FIXME(davidtwco): translatable expected/found +pub struct BuiltinClashingExternSub<'a> { + pub tcx: TyCtxt<'a>, + pub expected: Ty<'a>, + pub found: Ty<'a>, +} + +impl AddToDiagnostic for BuiltinClashingExternSub<'_> { + fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F) + where + F: Fn( + &mut rustc_errors::Diagnostic, + rustc_errors::SubdiagnosticMessage, + ) -> rustc_errors::SubdiagnosticMessage, + { + let mut expected_str = DiagnosticStyledString::new(); + expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false); + let mut found_str = DiagnosticStyledString::new(); + found_str.push(self.found.fn_sig(self.tcx).to_string(), true); + diag.note_expected_found(&"", expected_str, &"", found_str); + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_deref_nullptr)] +pub struct BuiltinDerefNullptr { + #[label] + pub label: Span, +} + +// FIXME: migrate fluent::lint::builtin_asm_labels + +#[derive(LintDiagnostic)] +pub enum BuiltinSpecialModuleNameUsed { + #[diag(lint_builtin_special_module_name_used_lib)] + #[note] + #[help] + Lib, + #[diag(lint_builtin_special_module_name_used_main)] + #[note] + Main, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_unexpected_cli_config_name)] +#[help] +pub struct BuiltinUnexpectedCliConfigName { + pub name: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_builtin_unexpected_cli_config_value)] +#[help] +pub struct BuiltinUnexpectedCliConfigValue { + pub name: Symbol, + pub value: Symbol, +} + +// deref_into_dyn_supertrait.rs +#[derive(LintDiagnostic)] +#[diag(lint_supertrait_as_deref_target)] +pub struct SupertraitAsDerefTarget<'a> { + pub t: Ty<'a>, + pub target_principal: String, + // pub target_principal: Binder<'a, ExistentialTraitRef<'b>>, + #[subdiagnostic] + pub label: Option<SupertraitAsDerefTargetLabel>, +} + +#[derive(Subdiagnostic)] +#[label(label)] +pub struct SupertraitAsDerefTargetLabel { + #[primary_span] + pub label: Span, +} + +// enum_intrinsics_non_enums.rs +#[derive(LintDiagnostic)] +#[diag(lint_enum_intrinsics_mem_discriminant)] +pub struct EnumIntrinsicsMemDiscriminate<'a> { + pub ty_param: Ty<'a>, + #[note] + pub note: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_enum_intrinsics_mem_variant)] +#[note] +pub struct EnumIntrinsicsMemVariant<'a> { + pub ty_param: Ty<'a>, +} + +// expect.rs +#[derive(LintDiagnostic)] +#[diag(lint_expectation)] +pub struct Expectation { + #[subdiagnostic] + pub rationale: Option<ExpectationNote>, + #[note] + pub note: Option<()>, +} + +#[derive(Subdiagnostic)] +#[note(rationale)] +pub struct ExpectationNote { + pub rationale: Symbol, +} + +// for_loops_over_fallibles.rs +#[derive(LintDiagnostic)] +#[diag(lint_for_loops_over_fallibles)] +pub struct ForLoopsOverFalliblesDiag<'a> { + pub article: &'static str, + pub ty: &'static str, + #[subdiagnostic] + pub sub: ForLoopsOverFalliblesLoopSub<'a>, + #[subdiagnostic] + pub question_mark: Option<ForLoopsOverFalliblesQuestionMark>, + #[subdiagnostic] + pub suggestion: ForLoopsOverFalliblesSuggestion<'a>, +} + +#[derive(Subdiagnostic)] +pub enum ForLoopsOverFalliblesLoopSub<'a> { + #[suggestion(remove_next, code = ".by_ref()", applicability = "maybe-incorrect")] + RemoveNext { + #[primary_span] + suggestion: Span, + recv_snip: String, + }, + #[multipart_suggestion(use_while_let, applicability = "maybe-incorrect")] + UseWhileLet { + #[suggestion_part(code = "while let {var}(")] + start_span: Span, + #[suggestion_part(code = ") = ")] + end_span: Span, + var: &'a str, + }, +} + +#[derive(Subdiagnostic)] +#[suggestion(use_question_mark, code = "?", applicability = "maybe-incorrect")] +pub struct ForLoopsOverFalliblesQuestionMark { + #[primary_span] + pub suggestion: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")] +pub struct ForLoopsOverFalliblesSuggestion<'a> { + pub var: &'a str, + #[suggestion_part(code = "if let {var}(")] + pub start_span: Span, + #[suggestion_part(code = ") = ")] + pub end_span: Span, +} + +// hidden_unicode_codepoints.rs +#[derive(LintDiagnostic)] +#[diag(lint_hidden_unicode_codepoints)] +#[note] +pub struct HiddenUnicodeCodepointsDiag<'a> { + pub label: &'a str, + pub count: usize, + #[label] + pub span_label: Span, + #[subdiagnostic] + pub labels: Option<HiddenUnicodeCodepointsDiagLabels>, + #[subdiagnostic] + pub sub: HiddenUnicodeCodepointsDiagSub, +} + +pub struct HiddenUnicodeCodepointsDiagLabels { + pub spans: Vec<(char, Span)>, +} + +impl AddToDiagnostic for HiddenUnicodeCodepointsDiagLabels { + fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F) + where + F: Fn( + &mut rustc_errors::Diagnostic, + rustc_errors::SubdiagnosticMessage, + ) -> rustc_errors::SubdiagnosticMessage, + { + for (c, span) in self.spans { + diag.span_label(span, format!("{:?}", c)); + } + } +} + +pub enum HiddenUnicodeCodepointsDiagSub { + Escape { spans: Vec<(char, Span)> }, + NoEscape { spans: Vec<(char, Span)> }, +} + +// Used because of multiple multipart_suggestion and note +impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub { + fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F) + where + F: Fn( + &mut rustc_errors::Diagnostic, + rustc_errors::SubdiagnosticMessage, + ) -> rustc_errors::SubdiagnosticMessage, + { + match self { + HiddenUnicodeCodepointsDiagSub::Escape { spans } => { + diag.multipart_suggestion_with_style( + fluent::suggestion_remove, + spans.iter().map(|(_, span)| (*span, "".to_string())).collect(), + Applicability::MachineApplicable, + SuggestionStyle::HideCodeAlways, + ); + diag.multipart_suggestion( + fluent::suggestion_escape, + spans + .into_iter() + .map(|(c, span)| { + let c = format!("{:?}", c); + (span, c[1..c.len() - 1].to_string()) + }) + .collect(), + Applicability::MachineApplicable, + ); + } + HiddenUnicodeCodepointsDiagSub::NoEscape { spans } => { + // FIXME: in other suggestions we've reversed the inner spans of doc comments. We + // should do the same here to provide the same good suggestions as we do for + // literals above. + diag.set_arg( + "escaped", + spans + .into_iter() + .map(|(c, _)| format!("{:?}", c)) + .collect::<Vec<String>>() + .join(", "), + ); + diag.note(fluent::suggestion_remove); + diag.note(fluent::no_suggestion_note_escape); + } + } + } +} + +// internal.rs +#[derive(LintDiagnostic)] +#[diag(lint_default_hash_types)] +#[note] +pub struct DefaultHashTypesDiag<'a> { + pub preferred: &'a str, + pub used: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_query_instability)] +#[note] +pub struct QueryInstability { + pub query: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_tykind_kind)] +pub struct TykindKind { + #[suggestion(code = "ty", applicability = "maybe-incorrect")] + pub suggestion: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_tykind)] +#[help] +pub struct TykindDiag; + +#[derive(LintDiagnostic)] +#[diag(lint_ty_qualified)] +pub struct TyQualified { + pub ty: String, + #[suggestion(code = "{ty}", applicability = "maybe-incorrect")] + pub suggestion: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_lintpass_by_hand)] +#[help] +pub struct LintPassByHand; + +#[derive(LintDiagnostic)] +#[diag(lint_non_existant_doc_keyword)] +#[help] +pub struct NonExistantDocKeyword { + pub keyword: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_diag_out_of_impl)] +pub struct DiagOutOfImpl; + +#[derive(LintDiagnostic)] +#[diag(lint_untranslatable_diag)] +pub struct UntranslatableDiag; + +#[derive(LintDiagnostic)] +#[diag(lint_bad_opt_access)] +pub struct BadOptAccessDiag<'a> { + pub msg: &'a str, +} + +// let_underscore.rs +#[derive(LintDiagnostic)] +pub enum NonBindingLet { + #[diag(lint_non_binding_let_on_sync_lock)] + SyncLock { + #[subdiagnostic] + sub: NonBindingLetSub, + }, + #[diag(lint_non_binding_let_on_drop_type)] + DropType { + #[subdiagnostic] + sub: NonBindingLetSub, + }, +} + +pub struct NonBindingLetSub { + pub suggestion: Span, + pub multi_suggestion_start: Span, + pub multi_suggestion_end: Span, +} + +impl AddToDiagnostic for NonBindingLetSub { + fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F) + where + F: Fn( + &mut rustc_errors::Diagnostic, + rustc_errors::SubdiagnosticMessage, + ) -> rustc_errors::SubdiagnosticMessage, + { + diag.span_suggestion_verbose( + self.suggestion, + fluent::lint_non_binding_let_suggestion, + "_unused", + Applicability::MachineApplicable, + ); + diag.multipart_suggestion( + fluent::lint_non_binding_let_multi_suggestion, + vec![ + (self.multi_suggestion_start, "drop(".to_string()), + (self.multi_suggestion_end, ")".to_string()), + ], + Applicability::MachineApplicable, + ); + } +} + +// levels.rs +#[derive(LintDiagnostic)] +#[diag(lint_overruled_attribute)] +pub struct OverruledAtributeLint<'a> { + #[label] + pub overruled: Span, + pub lint_level: &'a str, + pub lint_source: Symbol, + #[subdiagnostic] + pub sub: OverruledAttributeSub, +} + +#[derive(LintDiagnostic)] +#[diag(lint_deprecated_lint_name)] +pub struct DeprecatedLintName<'a> { + pub name: String, + #[suggestion(code = "{replace}", applicability = "machine-applicable")] + pub suggestion: Span, + pub replace: &'a str, +} + +// FIXME: Non-translatable msg +#[derive(LintDiagnostic)] +#[diag(lint_renamed_or_removed_lint)] +pub struct RenamedOrRemovedLint<'a> { + pub msg: &'a str, + #[subdiagnostic] + pub suggestion: Option<RenamedOrRemovedLintSuggestion<'a>>, +} + +#[derive(Subdiagnostic)] +#[suggestion(suggestion, code = "{replace}", applicability = "machine-applicable")] +pub struct RenamedOrRemovedLintSuggestion<'a> { + #[primary_span] + pub suggestion: Span, + pub replace: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unknown_lint)] +pub struct UnknownLint { + pub name: String, + #[subdiagnostic] + pub suggestion: Option<UnknownLintSuggestion>, +} + +#[derive(Subdiagnostic)] +#[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")] +pub struct UnknownLintSuggestion { + #[primary_span] + pub suggestion: Span, + pub replace: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_ignored_unless_crate_specified)] +pub struct IgnoredUnlessCrateSpecified<'a> { + pub level: &'a str, + pub name: Symbol, +} + +// methods.rs +#[derive(LintDiagnostic)] +#[diag(lint_cstring_ptr)] +#[note] +#[help] +pub struct CStringPtr { + #[label(as_ptr_label)] + pub as_ptr: Span, + #[label(unwrap_label)] + pub unwrap: Span, +} + +// non_ascii_idents.rs +#[derive(LintDiagnostic)] +#[diag(lint_identifier_non_ascii_char)] +pub struct IdentifierNonAsciiChar; + +#[derive(LintDiagnostic)] +#[diag(lint_identifier_uncommon_codepoints)] +pub struct IdentifierUncommonCodepoints; + +#[derive(LintDiagnostic)] +#[diag(lint_confusable_identifier_pair)] +pub struct ConfusableIdentifierPair { + pub existing_sym: Symbol, + pub sym: Symbol, + #[label] + pub label: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_mixed_script_confusables)] +#[note(includes_note)] +#[note] +pub struct MixedScriptConfusables { + pub set: String, + pub includes: String, +} + +// non_fmt_panic.rs +pub struct NonFmtPanicUnused { + pub count: usize, + pub suggestion: Option<Span>, +} + +// Used because of two suggestions based on one Option<Span> +impl<'a> DecorateLint<'a, ()> for NonFmtPanicUnused { + fn decorate_lint<'b>( + self, + diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, + ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + diag.set_arg("count", self.count); + diag.note(fluent::note); + if let Some(span) = self.suggestion { + diag.span_suggestion( + span.shrink_to_hi(), + fluent::add_args_suggestion, + ", ...", + Applicability::HasPlaceholders, + ); + diag.span_suggestion( + span.shrink_to_lo(), + fluent::add_fmt_suggestion, + "\"{}\", ", + Applicability::MachineApplicable, + ); + } + diag + } + + fn msg(&self) -> rustc_errors::DiagnosticMessage { + fluent::lint_non_fmt_panic_unused + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_non_fmt_panic_braces)] +#[note] +pub struct NonFmtPanicBraces { + pub count: usize, + #[suggestion(code = "\"{{}}\", ", applicability = "machine-applicable")] + pub suggestion: Option<Span>, +} + +// nonstandard_style.rs +#[derive(LintDiagnostic)] +#[diag(lint_non_camel_case_type)] +pub struct NonCamelCaseType<'a> { + pub sort: &'a str, + pub name: &'a str, + #[subdiagnostic] + pub sub: NonCamelCaseTypeSub, +} + +#[derive(Subdiagnostic)] +pub enum NonCamelCaseTypeSub { + #[label(label)] + Label { + #[primary_span] + span: Span, + }, + #[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")] + Suggestion { + #[primary_span] + span: Span, + replace: String, + }, +} + +#[derive(LintDiagnostic)] +#[diag(lint_non_snake_case)] +pub struct NonSnakeCaseDiag<'a> { + pub sort: &'a str, + pub name: &'a str, + pub sc: String, + #[subdiagnostic] + pub sub: NonSnakeCaseDiagSub, +} + +pub enum NonSnakeCaseDiagSub { + Label { span: Span }, + Help, + RenameOrConvertSuggestion { span: Span, suggestion: Ident }, + ConvertSuggestion { span: Span, suggestion: String }, + SuggestionAndNote { span: Span }, +} + +impl AddToDiagnostic for NonSnakeCaseDiagSub { + fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F) + where + F: Fn( + &mut rustc_errors::Diagnostic, + rustc_errors::SubdiagnosticMessage, + ) -> rustc_errors::SubdiagnosticMessage, + { + match self { + NonSnakeCaseDiagSub::Label { span } => { + diag.span_label(span, fluent::label); + } + NonSnakeCaseDiagSub::Help => { + diag.help(fluent::help); + } + NonSnakeCaseDiagSub::ConvertSuggestion { span, suggestion } => { + diag.span_suggestion( + span, + fluent::convert_suggestion, + suggestion, + Applicability::MaybeIncorrect, + ); + } + NonSnakeCaseDiagSub::RenameOrConvertSuggestion { span, suggestion } => { + diag.span_suggestion( + span, + fluent::rename_or_convert_suggestion, + suggestion, + Applicability::MaybeIncorrect, + ); + } + NonSnakeCaseDiagSub::SuggestionAndNote { span } => { + diag.note(fluent::cannot_convert_note); + diag.span_suggestion( + span, + fluent::rename_suggestion, + "", + Applicability::MaybeIncorrect, + ); + } + } + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_non_upper_case_global)] +pub struct NonUpperCaseGlobal<'a> { + pub sort: &'a str, + pub name: &'a str, + #[subdiagnostic] + pub sub: NonUpperCaseGlobalSub, +} + +#[derive(Subdiagnostic)] +pub enum NonUpperCaseGlobalSub { + #[label(label)] + Label { + #[primary_span] + span: Span, + }, + #[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")] + Suggestion { + #[primary_span] + span: Span, + replace: String, + }, +} + +// noop_method_call.rs +#[derive(LintDiagnostic)] +#[diag(lint_noop_method_call)] +#[note] +pub struct NoopMethodCallDiag<'a> { + pub method: Symbol, + pub receiver_ty: Ty<'a>, + #[label] + pub label: Span, +} + +// pass_by_value.rs +#[derive(LintDiagnostic)] +#[diag(lint_pass_by_value)] +pub struct PassByValueDiag { + pub ty: String, + #[suggestion(code = "{ty}", applicability = "maybe-incorrect")] + pub suggestion: Span, +} + +// redundant_semicolon.rs +#[derive(LintDiagnostic)] +#[diag(lint_redundant_semicolons)] +pub struct RedundantSemicolonsDiag { + pub multiple: bool, + #[suggestion(code = "", applicability = "maybe-incorrect")] + pub suggestion: Span, +} + +// traits.rs +pub struct DropTraitConstraintsDiag<'a> { + pub predicate: Predicate<'a>, + pub tcx: TyCtxt<'a>, + pub def_id: DefId, +} + +// Needed for def_path_str +impl<'a> DecorateLint<'a, ()> for DropTraitConstraintsDiag<'_> { + fn decorate_lint<'b>( + self, + diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, + ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + diag.set_arg("predicate", self.predicate); + diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id)) + } + + fn msg(&self) -> rustc_errors::DiagnosticMessage { + fluent::lint_drop_trait_constraints + } +} + +pub struct DropGlue<'a> { + pub tcx: TyCtxt<'a>, + pub def_id: DefId, +} + +// Needed for def_path_str +impl<'a> DecorateLint<'a, ()> for DropGlue<'_> { + fn decorate_lint<'b>( + self, + diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, + ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id)) + } + + fn msg(&self) -> rustc_errors::DiagnosticMessage { + fluent::lint_drop_glue + } +} + +// types.rs +#[derive(LintDiagnostic)] +#[diag(lint_range_endpoint_out_of_range)] +pub struct RangeEndpointOutOfRange<'a> { + pub ty: &'a str, + #[suggestion(code = "{start}..={literal}{suffix}", applicability = "machine-applicable")] + pub suggestion: Span, + pub start: String, + pub literal: u128, + pub suffix: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag(lint_overflowing_bin_hex)] +pub struct OverflowingBinHex<'a> { + pub ty: &'a str, + pub lit: String, + pub dec: u128, + pub actually: String, + #[subdiagnostic] + pub sign: OverflowingBinHexSign, + #[subdiagnostic] + pub sub: Option<OverflowingBinHexSub<'a>>, +} + +pub enum OverflowingBinHexSign { + Positive, + Negative, +} + +impl AddToDiagnostic for OverflowingBinHexSign { + fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F) + where + F: Fn( + &mut rustc_errors::Diagnostic, + rustc_errors::SubdiagnosticMessage, + ) -> rustc_errors::SubdiagnosticMessage, + { + match self { + OverflowingBinHexSign::Positive => { + diag.note(fluent::positive_note); + } + OverflowingBinHexSign::Negative => { + diag.note(fluent::negative_note); + diag.note(fluent::negative_becomes_note); + } + } + } +} + +#[derive(Subdiagnostic)] +pub enum OverflowingBinHexSub<'a> { + #[suggestion( + suggestion, + code = "{sans_suffix}{suggestion_ty}", + applicability = "machine-applicable" + )] + Suggestion { + #[primary_span] + span: Span, + suggestion_ty: &'a str, + sans_suffix: &'a str, + }, + #[help(help)] + Help { suggestion_ty: &'a str }, +} + +#[derive(LintDiagnostic)] +#[diag(lint_overflowing_int)] +#[note] +pub struct OverflowingInt<'a> { + pub ty: &'a str, + pub lit: String, + pub min: i128, + pub max: u128, + #[subdiagnostic] + pub help: Option<OverflowingIntHelp<'a>>, +} + +#[derive(Subdiagnostic)] +#[help(help)] +pub struct OverflowingIntHelp<'a> { + pub suggestion_ty: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag(lint_only_cast_u8_to_char)] +pub struct OnlyCastu8ToChar { + #[suggestion(code = "'\\u{{{literal:X}}}'", applicability = "machine-applicable")] + pub span: Span, + pub literal: u128, +} + +#[derive(LintDiagnostic)] +#[diag(lint_overflowing_uint)] +#[note] +pub struct OverflowingUInt<'a> { + pub ty: &'a str, + pub lit: String, + pub min: u128, + pub max: u128, +} + +#[derive(LintDiagnostic)] +#[diag(lint_overflowing_literal)] +#[note] +pub struct OverflowingLiteral<'a> { + pub ty: &'a str, + pub lit: String, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_comparisons)] +pub struct UnusedComparisons; + +pub struct ImproperCTypes<'a> { + pub ty: Ty<'a>, + pub desc: &'a str, + pub label: Span, + pub help: Option<DiagnosticMessage>, + pub note: DiagnosticMessage, + pub span_note: Option<Span>, +} + +// Used because of the complexity of Option<DiagnosticMessage>, DiagnosticMessage, and Option<Span> +impl<'a> DecorateLint<'a, ()> for ImproperCTypes<'_> { + fn decorate_lint<'b>( + self, + diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, + ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + diag.set_arg("ty", self.ty); + diag.set_arg("desc", self.desc); + diag.span_label(self.label, fluent::label); + if let Some(help) = self.help { + diag.help(help); + } + diag.note(self.note); + if let Some(note) = self.span_note { + diag.span_note(note, fluent::note); + } + diag + } + + fn msg(&self) -> rustc_errors::DiagnosticMessage { + fluent::lint_improper_ctypes + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_variant_size_differences)] +pub struct VariantSizeDifferencesDiag { + pub largest: u64, +} + +#[derive(LintDiagnostic)] +#[diag(lint_atomic_ordering_load)] +#[help] +pub struct AtomicOrderingLoad; + +#[derive(LintDiagnostic)] +#[diag(lint_atomic_ordering_store)] +#[help] +pub struct AtomicOrderingStore; + +#[derive(LintDiagnostic)] +#[diag(lint_atomic_ordering_fence)] +#[help] +pub struct AtomicOrderingFence; + +#[derive(LintDiagnostic)] +#[diag(lint_atomic_ordering_invalid)] +#[help] +pub struct InvalidAtomicOrderingDiag { + pub method: Symbol, + #[label] + pub fail_order_arg_span: Span, +} + +// unused.rs +#[derive(LintDiagnostic)] +#[diag(lint_unused_op)] +pub struct UnusedOp<'a> { + pub op: &'a str, + #[label] + pub label: Span, + #[suggestion(style = "verbose", code = "let _ = ", applicability = "machine-applicable")] + pub suggestion: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_result)] +pub struct UnusedResult<'a> { + pub ty: Ty<'a>, +} + +// FIXME(davidtwco): this isn't properly translatable becauses of the +// pre/post strings +#[derive(LintDiagnostic)] +#[diag(lint_unused_closure)] +#[note] +pub struct UnusedClosure<'a> { + pub count: usize, + pub pre: &'a str, + pub post: &'a str, +} + +// FIXME(davidtwco): this isn't properly translatable becauses of the +// pre/post strings +#[derive(LintDiagnostic)] +#[diag(lint_unused_generator)] +#[note] +pub struct UnusedGenerator<'a> { + pub count: usize, + pub pre: &'a str, + pub post: &'a str, +} + +// FIXME(davidtwco): this isn't properly translatable becauses of the pre/post +// strings +pub struct UnusedDef<'a, 'b> { + pub pre: &'a str, + pub post: &'a str, + pub cx: &'a LateContext<'b>, + pub def_id: DefId, + pub note: Option<Symbol>, +} + +// Needed because of def_path_str +impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> { + fn decorate_lint<'b>( + self, + diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, + ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + diag.set_arg("pre", self.pre); + diag.set_arg("post", self.post); + diag.set_arg("def", self.cx.tcx.def_path_str(self.def_id)); + // check for #[must_use = "..."] + if let Some(note) = self.note { + diag.note(note.as_str()); + } + diag + } + + fn msg(&self) -> rustc_errors::DiagnosticMessage { + fluent::lint_unused_def + } +} + +#[derive(LintDiagnostic)] +#[diag(lint_path_statement_drop)] +pub struct PathStatementDrop { + #[subdiagnostic] + pub sub: PathStatementDropSub, +} + +#[derive(Subdiagnostic)] +pub enum PathStatementDropSub { + #[suggestion(suggestion, code = "drop({snippet});", applicability = "machine-applicable")] + Suggestion { + #[primary_span] + span: Span, + snippet: String, + }, + #[help(help)] + Help { + #[primary_span] + span: Span, + }, +} + +#[derive(LintDiagnostic)] +#[diag(lint_path_statement_no_effect)] +pub struct PathStatementNoEffect; + +#[derive(LintDiagnostic)] +#[diag(lint_unused_delim)] +pub struct UnusedDelim<'a> { + pub delim: &'static str, + pub item: &'a str, + #[subdiagnostic] + pub suggestion: Option<UnusedDelimSuggestion>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(suggestion, applicability = "machine-applicable")] +pub struct UnusedDelimSuggestion { + #[suggestion_part(code = "{start_replace}")] + pub start_span: Span, + pub start_replace: &'static str, + #[suggestion_part(code = "{end_replace}")] + pub end_span: Span, + pub end_replace: &'static str, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_import_braces)] +pub struct UnusedImportBracesDiag { + pub node: Symbol, +} + +#[derive(LintDiagnostic)] +#[diag(lint_unused_allocation)] +pub struct UnusedAllocationDiag; + +#[derive(LintDiagnostic)] +#[diag(lint_unused_allocation_mut)] +pub struct UnusedAllocationMutDiag; diff --git a/compiler/rustc_lint/src/methods.rs b/compiler/rustc_lint/src/methods.rs index e2d7d5b49f6..3045fc1a476 100644 --- a/compiler/rustc_lint/src/methods.rs +++ b/compiler/rustc_lint/src/methods.rs @@ -1,7 +1,7 @@ +use crate::lints::CStringPtr; use crate::LateContext; use crate::LateLintPass; use crate::LintContext; -use rustc_errors::fluent; use rustc_hir::{Expr, ExprKind, PathSegment}; use rustc_middle::ty; use rustc_span::{symbol::sym, ExpnKind, Span}; @@ -90,16 +90,10 @@ fn lint_cstring_as_ptr( if cx.tcx.is_diagnostic_item(sym::Result, def.did()) { if let ty::Adt(adt, _) = substs.type_at(0).kind() { if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did()) { - cx.struct_span_lint( + cx.emit_spanned_lint( TEMPORARY_CSTRING_AS_PTR, as_ptr_span, - fluent::lint_cstring_ptr, - |diag| { - diag.span_label(as_ptr_span, fluent::as_ptr_label) - .span_label(unwrap.span, fluent::unwrap_label) - .note(fluent::note) - .help(fluent::help) - }, + CStringPtr { as_ptr: as_ptr_span, unwrap: unwrap.span }, ); } } diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index dea9506acb2..f130a98185d 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -1,7 +1,10 @@ +use crate::lints::{ + ConfusableIdentifierPair, IdentifierNonAsciiChar, IdentifierUncommonCodepoints, + MixedScriptConfusables, +}; use crate::{EarlyContext, EarlyLintPass, LintContext}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::fluent; use rustc_span::symbol::Symbol; declare_lint! { @@ -180,21 +183,11 @@ impl EarlyLintPass for NonAsciiIdents { continue; } has_non_ascii_idents = true; - cx.struct_span_lint( - NON_ASCII_IDENTS, - sp, - fluent::lint_identifier_non_ascii_char, - |lint| lint, - ); + cx.emit_spanned_lint(NON_ASCII_IDENTS, sp, IdentifierNonAsciiChar); if check_uncommon_codepoints && !symbol_str.chars().all(GeneralSecurityProfile::identifier_allowed) { - cx.struct_span_lint( - UNCOMMON_CODEPOINTS, - sp, - fluent::lint_identifier_uncommon_codepoints, - |lint| lint, - ) + cx.emit_spanned_lint(UNCOMMON_CODEPOINTS, sp, IdentifierUncommonCodepoints); } } @@ -222,14 +215,13 @@ impl EarlyLintPass for NonAsciiIdents { .entry(skeleton_sym) .and_modify(|(existing_symbol, existing_span, existing_is_ascii)| { if !*existing_is_ascii || !is_ascii { - cx.struct_span_lint( + cx.emit_spanned_lint( CONFUSABLE_IDENTS, sp, - fluent::lint_confusable_identifier_pair, - |lint| { - lint.set_arg("existing_sym", *existing_symbol) - .set_arg("sym", symbol) - .span_label(*existing_span, fluent::label) + ConfusableIdentifierPair { + existing_sym: *existing_symbol, + sym: symbol, + label: *existing_span, }, ); } @@ -331,24 +323,18 @@ impl EarlyLintPass for NonAsciiIdents { } for ((sp, ch_list), script_set) in lint_reports { - cx.struct_span_lint( + let mut includes = String::new(); + for (idx, ch) in ch_list.into_iter().enumerate() { + if idx != 0 { + includes += ", "; + } + let char_info = format!("'{}' (U+{:04X})", ch, ch as u32); + includes += &char_info; + } + cx.emit_spanned_lint( MIXED_SCRIPT_CONFUSABLES, sp, - fluent::lint_mixed_script_confusables, - |lint| { - let mut includes = String::new(); - for (idx, ch) in ch_list.into_iter().enumerate() { - if idx != 0 { - includes += ", "; - } - let char_info = format!("'{}' (U+{:04X})", ch, ch as u32); - includes += &char_info; - } - lint.set_arg("set", script_set.to_string()) - .set_arg("includes", includes) - .note(fluent::includes_note) - .note(fluent::note) - }, + MixedScriptConfusables { set: script_set.to_string(), includes }, ); } } diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 8dccfe0046c..4a02c6cce47 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -1,3 +1,4 @@ +use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused}; use crate::{LateContext, LateLintPass, LintContext}; use rustc_ast as ast; use rustc_errors::{fluent, Applicability}; @@ -118,6 +119,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc arg_span = expn.call_site; } + #[allow(rustc::diagnostic_outside_of_impl)] cx.struct_span_lint(NON_FMT_PANICS, arg_span, fluent::lint_non_fmt_panic, |lint| { lint.set_arg("name", symbol); lint.note(fluent::note); @@ -253,25 +255,14 @@ fn check_panic_str<'tcx>( .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end))) .collect(), }; - cx.struct_span_lint(NON_FMT_PANICS, arg_spans, fluent::lint_non_fmt_panic_unused, |lint| { - lint.set_arg("count", n_arguments); - lint.note(fluent::note); - if is_arg_inside_call(arg.span, span) { - lint.span_suggestion( - arg.span.shrink_to_hi(), - fluent::add_args_suggestion, - ", ...", - Applicability::HasPlaceholders, - ); - lint.span_suggestion( - arg.span.shrink_to_lo(), - fluent::add_fmt_suggestion, - "\"{}\", ", - Applicability::MachineApplicable, - ); - } - lint - }); + cx.emit_spanned_lint( + NON_FMT_PANICS, + arg_spans, + NonFmtPanicUnused { + count: n_arguments, + suggestion: is_arg_inside_call(arg.span, span).then_some(arg.span), + }, + ); } else { let brace_spans: Option<Vec<_>> = snippet.filter(|s| s.starts_with('"') || s.starts_with("r#")).map(|s| { @@ -281,22 +272,12 @@ fn check_panic_str<'tcx>( .collect() }); let count = brace_spans.as_ref().map(|v| v.len()).unwrap_or(/* any number >1 */ 2); - cx.struct_span_lint( + cx.emit_spanned_lint( NON_FMT_PANICS, brace_spans.unwrap_or_else(|| vec![span]), - fluent::lint_non_fmt_panic_braces, - |lint| { - lint.set_arg("count", count); - lint.note(fluent::note); - if is_arg_inside_call(arg.span, span) { - lint.span_suggestion( - arg.span.shrink_to_lo(), - fluent::suggestion, - "\"{}\", ", - Applicability::MachineApplicable, - ); - } - lint + NonFmtPanicBraces { + count, + suggestion: is_arg_inside_call(arg.span, span).then_some(arg.span.shrink_to_lo()), }, ); } diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 91fcd6d690e..74d234fabea 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,7 +1,10 @@ +use crate::lints::{ + NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub, + NonUpperCaseGlobal, NonUpperCaseGlobalSub, +}; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_ast as ast; use rustc_attr as attr; -use rustc_errors::{fluent, Applicability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; @@ -136,30 +139,17 @@ impl NonCamelCaseTypes { let name = ident.name.as_str(); if !is_camel_case(name) { - cx.struct_span_lint( + let cc = to_camel_case(name); + let sub = if *name != cc { + NonCamelCaseTypeSub::Suggestion { span: ident.span, replace: cc } + } else { + NonCamelCaseTypeSub::Label { span: ident.span } + }; + cx.emit_spanned_lint( NON_CAMEL_CASE_TYPES, ident.span, - fluent::lint_non_camel_case_type, - |lint| { - let cc = to_camel_case(name); - // We cannot provide meaningful suggestions - // if the characters are in the category of "Lowercase Letter". - if *name != cc { - lint.span_suggestion( - ident.span, - fluent::suggestion, - to_camel_case(name), - Applicability::MaybeIncorrect, - ); - } else { - lint.span_label(ident.span, fluent::label); - } - - lint.set_arg("sort", sort); - lint.set_arg("name", name); - lint - }, - ) + NonCamelCaseType { sort, name, sub }, + ); } } } @@ -294,47 +284,37 @@ impl NonSnakeCase { let name = ident.name.as_str(); if !is_snake_case(name) { - cx.struct_span_lint(NON_SNAKE_CASE, ident.span, fluent::lint_non_snake_case, |lint| { - let sc = NonSnakeCase::to_snake_case(name); - // We cannot provide meaningful suggestions - // if the characters are in the category of "Uppercase Letter". - if name != sc { - // We have a valid span in almost all cases, but we don't have one when linting a crate - // name provided via the command line. - if !ident.span.is_dummy() { - let sc_ident = Ident::from_str_and_span(&sc, ident.span); - let (message, suggestion) = if sc_ident.is_reserved() { - // We shouldn't suggest a reserved identifier to fix non-snake-case identifiers. - // Instead, recommend renaming the identifier entirely or, if permitted, - // escaping it to create a raw identifier. - if sc_ident.name.can_be_raw() { - (fluent::rename_or_convert_suggestion, sc_ident.to_string()) - } else { - lint.note(fluent::cannot_convert_note); - (fluent::rename_suggestion, String::new()) + let span = ident.span; + let sc = NonSnakeCase::to_snake_case(name); + // We cannot provide meaningful suggestions + // if the characters are in the category of "Uppercase Letter". + let sub = if name != sc { + // We have a valid span in almost all cases, but we don't have one when linting a crate + // name provided via the command line. + if !span.is_dummy() { + let sc_ident = Ident::from_str_and_span(&sc, span); + if sc_ident.is_reserved() { + // We shouldn't suggest a reserved identifier to fix non-snake-case identifiers. + // Instead, recommend renaming the identifier entirely or, if permitted, + // escaping it to create a raw identifier. + if sc_ident.name.can_be_raw() { + NonSnakeCaseDiagSub::RenameOrConvertSuggestion { + span, + suggestion: sc_ident, } } else { - (fluent::convert_suggestion, sc.clone()) - }; - - lint.span_suggestion( - ident.span, - message, - suggestion, - Applicability::MaybeIncorrect, - ); + NonSnakeCaseDiagSub::SuggestionAndNote { span } + } } else { - lint.help(fluent::help); + NonSnakeCaseDiagSub::ConvertSuggestion { span, suggestion: sc.clone() } } } else { - lint.span_label(ident.span, fluent::label); + NonSnakeCaseDiagSub::Help } - - lint.set_arg("sort", sort); - lint.set_arg("name", name); - lint.set_arg("sc", sc); - lint - }); + } else { + NonSnakeCaseDiagSub::Label { span } + }; + cx.emit_spanned_lint(NON_SNAKE_CASE, span, NonSnakeCaseDiag { sort, name, sc, sub }); } } } @@ -444,8 +424,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) { if let PatKind::Binding(_, hid, ident, _) = p.kind { - if let hir::Node::PatField(field) = cx.tcx.hir().get(cx.tcx.hir().get_parent_node(hid)) - { + if let hir::Node::PatField(field) = cx.tcx.hir().get_parent(hid) { if !field.is_shorthand { // Only check if a new name has been introduced, to avoid warning // on both the struct definition and this pattern. @@ -491,30 +470,19 @@ impl NonUpperCaseGlobals { fn check_upper_case(cx: &LateContext<'_>, sort: &str, ident: &Ident) { let name = ident.name.as_str(); if name.chars().any(|c| c.is_lowercase()) { - cx.struct_span_lint( + let uc = NonSnakeCase::to_snake_case(&name).to_uppercase(); + // We cannot provide meaningful suggestions + // if the characters are in the category of "Lowercase Letter". + let sub = if *name != uc { + NonUpperCaseGlobalSub::Suggestion { span: ident.span, replace: uc } + } else { + NonUpperCaseGlobalSub::Label { span: ident.span } + }; + cx.emit_spanned_lint( NON_UPPER_CASE_GLOBALS, ident.span, - fluent::lint_non_upper_case_global, - |lint| { - let uc = NonSnakeCase::to_snake_case(&name).to_uppercase(); - // We cannot provide meaningful suggestions - // if the characters are in the category of "Lowercase Letter". - if *name != uc { - lint.span_suggestion( - ident.span, - fluent::suggestion, - uc, - Applicability::MaybeIncorrect, - ); - } else { - lint.span_label(ident.span, fluent::label); - } - - lint.set_arg("sort", sort); - lint.set_arg("name", name); - lint - }, - ) + NonUpperCaseGlobal { sort, name, sub }, + ); } } } diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 2ef425a1093..d67a00619dd 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -1,7 +1,7 @@ use crate::context::LintContext; +use crate::lints::NoopMethodCallDiag; use crate::LateContext; use crate::LateLintPass; -use rustc_errors::fluent; use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind}; use rustc_middle::ty; @@ -85,11 +85,10 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { } let expr_span = expr.span; let span = expr_span.with_lo(receiver.span.hi()); - cx.struct_span_lint(NOOP_METHOD_CALL, span, fluent::lint_noop_method_call, |lint| { - lint.set_arg("method", call.ident.name) - .set_arg("receiver_ty", receiver_ty) - .span_label(span, fluent::label) - .note(fluent::note) - }); + cx.emit_spanned_lint( + NOOP_METHOD_CALL, + span, + NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span }, + ); } } diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index 22caadfab17..57482a9edba 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -1,5 +1,5 @@ +use crate::lints::PassByValueDiag; use crate::{LateContext, LateLintPass, LintContext}; -use rustc_errors::{fluent, Applicability}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::{GenericArg, PathSegment, QPath, TyKind}; @@ -29,20 +29,11 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue { } } if let Some(t) = path_for_pass_by_value(cx, &inner_ty) { - cx.struct_span_lint( + cx.emit_spanned_lint( PASS_BY_VALUE, ty.span, - fluent::lint_pass_by_value, - |lint| { - lint.set_arg("ty", t.clone()).span_suggestion( - ty.span, - fluent::suggestion, - t, - // Changing type of function argument - Applicability::MaybeIncorrect, - ) - }, - ) + PassByValueDiag { ty: t.clone(), suggestion: ty.span }, + ); } } _ => {} diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs index 3521de7fc08..9a8b14b4907 100644 --- a/compiler/rustc_lint/src/redundant_semicolon.rs +++ b/compiler/rustc_lint/src/redundant_semicolon.rs @@ -1,6 +1,5 @@ -use crate::{EarlyContext, EarlyLintPass, LintContext}; +use crate::{lints::RedundantSemicolonsDiag, EarlyContext, EarlyLintPass, LintContext}; use rustc_ast::{Block, StmtKind}; -use rustc_errors::{fluent, Applicability}; use rustc_span::Span; declare_lint! { @@ -48,18 +47,10 @@ fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, boo return; } - cx.struct_span_lint( + cx.emit_spanned_lint( REDUNDANT_SEMICOLONS, span, - fluent::lint_redundant_semicolons, - |lint| { - lint.set_arg("multiple", multiple).span_suggestion( - span, - fluent::suggestion, - "", - Applicability::MaybeIncorrect, - ) - }, + RedundantSemicolonsDiag { multiple, suggestion: span }, ); } } diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index 1b21c2dac37..7ea1a138b7e 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -1,7 +1,7 @@ +use crate::lints::{DropGlue, DropTraitConstraintsDiag}; use crate::LateContext; use crate::LateLintPass; use crate::LintContext; -use rustc_errors::fluent; use rustc_hir as hir; use rustc_span::symbol::sym; @@ -101,17 +101,13 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { if trait_predicate.trait_ref.self_ty().is_impl_trait() { continue; } - let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { - continue; + let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { + return }; - cx.struct_span_lint( + cx.emit_spanned_lint( DROP_BOUNDS, span, - fluent::lint_drop_trait_constraints, - |lint| { - lint.set_arg("predicate", predicate) - .set_arg("needs_drop", cx.tcx.def_path_str(needs_drop)) - }, + DropTraitConstraintsDiag { predicate, tcx: cx.tcx, def_id }, ); } } @@ -123,12 +119,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { }; for bound in &bounds[..] { let def_id = bound.trait_ref.trait_def_id(); - if cx.tcx.lang_items().drop_trait() == def_id - && let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) - { - cx.struct_span_lint(DYN_DROP, bound.span, fluent::lint_drop_glue, |lint| { - lint.set_arg("needs_drop", cx.tcx.def_path_str(needs_drop)) - }); + if cx.tcx.lang_items().drop_trait() == def_id { + let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { + return + }; + cx.emit_spanned_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id }); } } } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 8e27bc03c48..f2ab44ac97c 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,11 +1,16 @@ +use crate::lints::{ + AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, + InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, + OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt, + RangeEndpointOutOfRange, UnusedComparisons, VariantSizeDifferencesDiag, +}; use crate::{LateContext, LateLintPass, LintContext}; use rustc_ast as ast; use rustc_attr as attr; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{fluent, Applicability, DiagnosticMessage}; +use rustc_errors::{fluent, DiagnosticMessage}; use rustc_hir as hir; use rustc_hir::{is_range_literal, Expr, ExprKind, Node}; -use rustc_macros::LintDiagnostic; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton}; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; @@ -127,10 +132,9 @@ fn lint_overflowing_range_endpoint<'tcx>( ) -> bool { // We only want to handle exclusive (`..`) ranges, // which are represented as `ExprKind::Struct`. - let par_id = cx.tcx.hir().get_parent_node(expr.hir_id); + let par_id = cx.tcx.hir().parent_id(expr.hir_id); let Node::ExprField(field) = cx.tcx.hir().get(par_id) else { return false }; - let field_par_id = cx.tcx.hir().get_parent_node(field.hir_id); - let Node::Expr(struct_expr) = cx.tcx.hir().get(field_par_id) else { return false }; + let Node::Expr(struct_expr) = cx.tcx.hir().get_parent(field.hir_id) else { return false }; if !is_range_literal(struct_expr) { return false; }; @@ -147,32 +151,22 @@ fn lint_overflowing_range_endpoint<'tcx>( }; let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false }; - cx.struct_span_lint( + use rustc_ast::{LitIntType, LitKind}; + let suffix = match lit.node { + LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(), + LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(), + LitKind::Int(_, LitIntType::Unsuffixed) => "", + _ => bug!(), + }; + cx.emit_spanned_lint( OVERFLOWING_LITERALS, struct_expr.span, - fluent::lint_range_endpoint_out_of_range, - |lint| { - use ast::{LitIntType, LitKind}; - - lint.set_arg("ty", ty); - - // We need to preserve the literal's suffix, - // as it may determine typing information. - let suffix = match lit.node { - LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(), - LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(), - LitKind::Int(_, LitIntType::Unsuffixed) => "", - _ => bug!(), - }; - let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix); - lint.span_suggestion( - struct_expr.span, - fluent::suggestion, - suggestion, - Applicability::MachineApplicable, - ); - - lint + RangeEndpointOutOfRange { + ty, + suggestion: struct_expr.span, + start, + literal: lit_val - 1, + suffix, }, ); @@ -229,58 +223,37 @@ fn report_bin_hex_error( val: u128, negative: bool, ) { - cx.struct_span_lint( - OVERFLOWING_LITERALS, - expr.span, - fluent::lint_overflowing_bin_hex, - |lint| { - let (t, actually) = match ty { - attr::IntType::SignedInt(t) => { - let actually = if negative { - -(size.sign_extend(val) as i128) - } else { - size.sign_extend(val) as i128 - }; - (t.name_str(), actually.to_string()) - } - attr::IntType::UnsignedInt(t) => { - let actually = size.truncate(val); - (t.name_str(), actually.to_string()) - } + let (t, actually) = match ty { + attr::IntType::SignedInt(t) => { + let actually = if negative { + -(size.sign_extend(val) as i128) + } else { + size.sign_extend(val) as i128 }; - - if negative { - // If the value is negative, - // emits a note about the value itself, apart from the literal. - lint.note(fluent::negative_note); - lint.note(fluent::negative_becomes_note); + (t.name_str(), actually.to_string()) + } + attr::IntType::UnsignedInt(t) => { + let actually = size.truncate(val); + (t.name_str(), actually.to_string()) + } + }; + let sign = + if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive }; + let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map( + |suggestion_ty| { + if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { + let (sans_suffix, _) = repr_str.split_at(pos); + OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix } } else { - lint.note(fluent::positive_note); - } - if let Some(sugg_ty) = - get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative) - { - lint.set_arg("suggestion_ty", sugg_ty); - if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { - let (sans_suffix, _) = repr_str.split_at(pos); - lint.span_suggestion( - expr.span, - fluent::suggestion, - format!("{}{}", sans_suffix, sugg_ty), - Applicability::MachineApplicable, - ); - } else { - lint.help(fluent::help); - } + OverflowingBinHexSub::Help { suggestion_ty } } - lint.set_arg("ty", t) - .set_arg("lit", repr_str) - .set_arg("dec", val) - .set_arg("actually", actually); - - lint }, ); + cx.emit_spanned_lint( + OVERFLOWING_LITERALS, + expr.span, + OverflowingBinHex { ty: t, lit: repr_str.clone(), dec: val, actually, sign, sub }, + ) } // This function finds the next fitting type and generates a suggestion string. @@ -364,28 +337,19 @@ fn lint_int_literal<'tcx>( return; } - cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, fluent::lint_overflowing_int, |lint| { - lint.set_arg("ty", t.name_str()) - .set_arg( - "lit", - cx.sess() - .source_map() - .span_to_snippet(lit.span) - .expect("must get snippet from literal"), - ) - .set_arg("min", min) - .set_arg("max", max) - .note(fluent::note); - - if let Some(sugg_ty) = - get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) - { - lint.set_arg("suggestion_ty", sugg_ty); - lint.help(fluent::help); - } - - lint - }); + let lit = cx + .sess() + .source_map() + .span_to_snippet(lit.span) + .expect("must get snippet from literal"); + let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative) + .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty }); + + cx.emit_spanned_lint( + OVERFLOWING_LITERALS, + e.span, + OverflowingInt { ty: t.name_str(), lit, min, max, help }, + ); } } @@ -404,23 +368,15 @@ fn lint_uint_literal<'tcx>( _ => bug!(), }; if lit_val < min || lit_val > max { - let parent_id = cx.tcx.hir().get_parent_node(e.hir_id); + let parent_id = cx.tcx.hir().parent_id(e.hir_id); if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) { match par_e.kind { hir::ExprKind::Cast(..) => { if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { - cx.struct_span_lint( + cx.emit_spanned_lint( OVERFLOWING_LITERALS, par_e.span, - fluent::lint_only_cast_u8_to_char, - |lint| { - lint.span_suggestion( - par_e.span, - fluent::suggestion, - format!("'\\u{{{:X}}}'", lit_val), - Applicability::MachineApplicable, - ) - }, + OnlyCastu8ToChar { span: par_e.span, literal: lit_val }, ); return; } @@ -444,19 +400,20 @@ fn lint_uint_literal<'tcx>( ); return; } - cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, fluent::lint_overflowing_uint, |lint| { - lint.set_arg("ty", t.name_str()) - .set_arg( - "lit", - cx.sess() - .source_map() - .span_to_snippet(lit.span) - .expect("must get snippet from literal"), - ) - .set_arg("min", min) - .set_arg("max", max) - .note(fluent::note) - }); + cx.emit_spanned_lint( + OVERFLOWING_LITERALS, + e.span, + OverflowingUInt { + ty: t.name_str(), + lit: cx + .sess() + .source_map() + .span_to_snippet(lit.span) + .expect("must get snippet from literal"), + min, + max, + }, + ); } } @@ -485,20 +442,16 @@ fn lint_literal<'tcx>( _ => bug!(), }; if is_infinite == Ok(true) { - cx.struct_span_lint( + cx.emit_spanned_lint( OVERFLOWING_LITERALS, e.span, - fluent::lint_overflowing_literal, - |lint| { - lint.set_arg("ty", t.name_str()) - .set_arg( - "lit", - cx.sess() - .source_map() - .span_to_snippet(lit.span) - .expect("must get snippet from literal"), - ) - .note(fluent::note) + OverflowingLiteral { + ty: t.name_str(), + lit: cx + .sess() + .source_map() + .span_to_snippet(lit.span) + .expect("must get snippet from literal"), }, ); } @@ -518,12 +471,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } hir::ExprKind::Binary(binop, ref l, ref r) => { if is_comparison(binop) && !check_limits(cx, binop, &l, &r) { - cx.struct_span_lint( - UNUSED_COMPARISONS, - e.span, - fluent::lint_unused_comparisons, - |lint| lint, - ); + cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); } } hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit), @@ -879,39 +827,39 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { ) -> FfiResult<'tcx> { use FfiResult::*; - if def.repr().transparent() { + let transparent_safety = def.repr().transparent().then(|| { // Can assume that at most one field is not a ZST, so only check // that field's type for FFI-safety. if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) { - self.check_field_type_for_ffi(cache, field, substs) + return self.check_field_type_for_ffi(cache, field, substs); } else { // All fields are ZSTs; this means that the type should behave - // like (), which is FFI-unsafe + // like (), which is FFI-unsafe... except if all fields are PhantomData, + // which is tested for below FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None } } - } else { - // We can't completely trust repr(C) markings; make sure the fields are - // actually safe. - let mut all_phantom = !variant.fields.is_empty(); - for field in &variant.fields { - match self.check_field_type_for_ffi(cache, &field, substs) { - FfiSafe => { - all_phantom = false; - } - FfiPhantom(..) if def.is_enum() => { - return FfiUnsafe { - ty, - reason: fluent::lint_improper_ctypes_enum_phantomdata, - help: None, - }; - } - FfiPhantom(..) => {} - r => return r, + }); + // We can't completely trust repr(C) markings; make sure the fields are + // actually safe. + let mut all_phantom = !variant.fields.is_empty(); + for field in &variant.fields { + match self.check_field_type_for_ffi(cache, &field, substs) { + FfiSafe => { + all_phantom = false; + } + FfiPhantom(..) if !def.repr().transparent() && def.is_enum() => { + return FfiUnsafe { + ty, + reason: fluent::lint_improper_ctypes_enum_phantomdata, + help: None, + }; } + FfiPhantom(..) => {} + r => return transparent_safety.unwrap_or(r), } - - if all_phantom { FfiPhantom(ty) } else { FfiSafe } } + + if all_phantom { FfiPhantom(ty) } else { transparent_safety.unwrap_or(FfiSafe) } } /// Checks if the given type is "ffi-safe" (has a stable, well-defined @@ -1175,26 +1123,21 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { CItemKind::Declaration => IMPROPER_CTYPES, CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS, }; - - self.cx.struct_span_lint(lint, sp, fluent::lint_improper_ctypes, |lint| { - let item_description = match self.mode { - CItemKind::Declaration => "block", - CItemKind::Definition => "fn", + let desc = match self.mode { + CItemKind::Declaration => "block", + CItemKind::Definition => "fn", + }; + let span_note = if let ty::Adt(def, _) = ty.kind() + && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) { + Some(sp) + } else { + None }; - lint.set_arg("ty", ty); - lint.set_arg("desc", item_description); - lint.span_label(sp, fluent::label); - if let Some(help) = help { - lint.help(help); - } - lint.note(note); - if let ty::Adt(def, _) = ty.kind() { - if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) { - lint.span_note(sp, fluent::note); - } - } - lint - }); + self.cx.emit_spanned_lint( + lint, + sp, + ImproperCTypes { ty, desc, label: sp, help, note, span_note }, + ); } fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { @@ -1398,11 +1341,10 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences { // We only warn if the largest variant is at least thrice as large as // the second-largest. if largest > slargest * 3 && slargest > 0 { - cx.struct_span_lint( + cx.emit_spanned_lint( VARIANT_SIZE_DIFFERENCES, enum_definition.variants[largest_index].span, - fluent::lint_variant_size_differences, - |lint| lint.set_arg("largest", largest), + VariantSizeDifferencesDiag { largest }, ); } } @@ -1510,17 +1452,19 @@ impl InvalidAtomicOrdering { fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) { if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store]) - && let Some((ordering_arg, invalid_ordering, msg)) = match method { - sym::load => Some((&args[0], sym::Release, fluent::lint_atomic_ordering_load)), - sym::store => Some((&args[1], sym::Acquire, fluent::lint_atomic_ordering_store)), + && let Some((ordering_arg, invalid_ordering)) = match method { + sym::load => Some((&args[0], sym::Release)), + sym::store => Some((&args[1], sym::Acquire)), _ => None, } && let Some(ordering) = Self::match_ordering(cx, ordering_arg) && (ordering == invalid_ordering || ordering == sym::AcqRel) { - cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, msg, |lint| { - lint.help(fluent::help) - }); + if method == sym::load { + cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad); + } else { + cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore); + }; } } @@ -1531,10 +1475,7 @@ impl InvalidAtomicOrdering { && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence)) && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed) { - cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, fluent::lint_atomic_ordering_fence, |lint| { - lint - .help(fluent::help) - }); + cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence); } } @@ -1551,15 +1492,6 @@ impl InvalidAtomicOrdering { let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return }; if matches!(fail_ordering, sym::Release | sym::AcqRel) { - #[derive(LintDiagnostic)] - #[diag(lint_atomic_ordering_invalid)] - #[help] - struct InvalidAtomicOrderingDiag { - method: Symbol, - #[label] - fail_order_arg_span: Span, - } - cx.emit_spanned_lint( INVALID_ATOMIC_ORDERING, fail_order_arg.span, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 525079681ca..ac2b32b44e6 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1,9 +1,14 @@ +use crate::lints::{ + PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag, + UnusedAllocationMutDiag, UnusedClosure, UnusedDef, UnusedDelim, UnusedDelimSuggestion, + UnusedGenerator, UnusedImportBracesDiag, UnusedOp, UnusedResult, +}; use crate::Lint; use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_ast as ast; use rustc_ast::util::{classify, parser}; use rustc_ast::{ExprKind, StmtKind}; -use rustc_errors::{fluent, pluralize, Applicability, MultiSpan}; +use rustc_errors::{pluralize, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -163,23 +168,20 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let mut op_warned = false; if let Some(must_use_op) = must_use_op { - cx.struct_span_lint(UNUSED_MUST_USE, expr.span, fluent::lint_unused_op, |lint| { - lint.set_arg("op", must_use_op) - .span_label(expr.span, fluent::label) - .span_suggestion_verbose( - expr.span.shrink_to_lo(), - fluent::suggestion, - "let _ = ", - Applicability::MachineApplicable, - ) - }); + cx.emit_spanned_lint( + UNUSED_MUST_USE, + expr.span, + UnusedOp { + op: must_use_op, + label: expr.span, + suggestion: expr.span.shrink_to_lo(), + }, + ); op_warned = true; } if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) { - cx.struct_span_lint(UNUSED_RESULTS, s.span, fluent::lint_unused_result, |lint| { - lint.set_arg("ty", ty) - }); + cx.emit_spanned_lint(UNUSED_RESULTS, s.span, UnusedResult { ty }); } fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { @@ -402,47 +404,31 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ); } MustUsePath::Closure(span) => { - cx.struct_span_lint( + cx.emit_spanned_lint( UNUSED_MUST_USE, *span, - fluent::lint_unused_closure, - |lint| { - // FIXME(davidtwco): this isn't properly translatable because of the - // pre/post strings - lint.set_arg("count", plural_len) - .set_arg("pre", descr_pre) - .set_arg("post", descr_post) - .note(fluent::note) - }, + UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post }, ); } MustUsePath::Generator(span) => { - cx.struct_span_lint( + cx.emit_spanned_lint( UNUSED_MUST_USE, *span, - fluent::lint_unused_generator, - |lint| { - // FIXME(davidtwco): this isn't properly translatable because of the - // pre/post strings - lint.set_arg("count", plural_len) - .set_arg("pre", descr_pre) - .set_arg("post", descr_post) - .note(fluent::note) - }, + UnusedGenerator { count: plural_len, pre: descr_pre, post: descr_post }, ); } MustUsePath::Def(span, def_id, reason) => { - cx.struct_span_lint(UNUSED_MUST_USE, *span, fluent::lint_unused_def, |lint| { - // FIXME(davidtwco): this isn't properly translatable because of the pre/post - // strings - lint.set_arg("pre", descr_pre); - lint.set_arg("post", descr_post); - lint.set_arg("def", cx.tcx.def_path_str(*def_id)); - if let Some(note) = reason { - lint.note(note.as_str()); - } - lint - }); + cx.emit_spanned_lint( + UNUSED_MUST_USE, + *span, + UnusedDef { + pre: descr_pre, + post: descr_post, + cx, + def_id: *def_id, + note: *reason, + }, + ); } } } @@ -478,31 +464,15 @@ impl<'tcx> LateLintPass<'tcx> for PathStatements { if let hir::ExprKind::Path(_) = expr.kind { let ty = cx.typeck_results().expr_ty(expr); if ty.needs_drop(cx.tcx, cx.param_env) { - cx.struct_span_lint( - PATH_STATEMENTS, - s.span, - fluent::lint_path_statement_drop, - |lint| { - if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) { - lint.span_suggestion( - s.span, - fluent::suggestion, - format!("drop({});", snippet), - Applicability::MachineApplicable, - ); - } else { - lint.span_help(s.span, fluent::suggestion); - } - lint - }, - ); + let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) + { + PathStatementDropSub::Suggestion { span: s.span, snippet } + } else { + PathStatementDropSub::Help { span: s.span } + }; + cx.emit_spanned_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub }) } else { - cx.struct_span_lint( - PATH_STATEMENTS, - s.span, - fluent::lint_path_statement_no_effect, - |lint| lint, - ); + cx.emit_spanned_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect); } } } @@ -695,36 +665,35 @@ trait UnusedDelimLint { } else { MultiSpan::from(value_span) }; - cx.struct_span_lint(self.lint(), primary_span, fluent::lint_unused_delim, |lint| { - lint.set_arg("delim", Self::DELIM_STR); - lint.set_arg("item", msg); - if let Some((lo, hi)) = spans { - let sm = cx.sess().source_map(); - let lo_replace = + let suggestion = spans.map(|(lo, hi)| { + let sm = cx.sess().source_map(); + let lo_replace = if keep_space.0 && let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(' ') { - " ".to_string() + " " } else { - "".to_string() + "" }; - let hi_replace = + let hi_replace = if keep_space.1 && let Ok(snip) = sm.span_to_next_source(hi) && !snip.starts_with(' ') { - " ".to_string() + " " } else { - "".to_string() + "" }; - - let replacement = vec![(lo, lo_replace), (hi, hi_replace)]; - lint.multipart_suggestion( - fluent::suggestion, - replacement, - Applicability::MachineApplicable, - ); + UnusedDelimSuggestion { + start_span: lo, + start_replace: lo_replace, + end_span: hi, + end_replace: hi_replace, } - lint }); + cx.emit_spanned_lint( + self.lint(), + primary_span, + UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion }, + ); } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { @@ -1297,11 +1266,10 @@ impl UnusedImportBraces { ast::UseTreeKind::Nested(_) => return, }; - cx.struct_span_lint( + cx.emit_spanned_lint( UNUSED_IMPORT_BRACES, item.span, - fluent::lint_unused_import_braces, - |lint| lint.set_arg("node", node_name), + UnusedImportBracesDiag { node: node_name }, ); } } @@ -1351,17 +1319,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAllocation { for adj in cx.typeck_results().expr_adjustments(e) { if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind { - cx.struct_span_lint( - UNUSED_ALLOCATION, - e.span, - match m { - adjustment::AutoBorrowMutability::Not => fluent::lint_unused_allocation, - adjustment::AutoBorrowMutability::Mut { .. } => { - fluent::lint_unused_allocation_mut - } - }, - |lint| lint, - ); + match m { + adjustment::AutoBorrowMutability::Not => { + cx.emit_spanned_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag); + } + adjustment::AutoBorrowMutability::Mut { .. } => { + cx.emit_spanned_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag); + } + }; } } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 28317d6cea0..6cdf5097083 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -4033,10 +4033,10 @@ declare_lint! { /// /// This can be used to implement an unsound API if used incorrectly. pub IMPLIED_BOUNDS_ENTAILMENT, - Warn, + Deny, "impl method assumes more implied bounds than its corresponding trait method", @future_incompatible = FutureIncompatibleInfo { reference: "issue #105572 <https://github.com/rust-lang/rust/issues/105572>", - reason: FutureIncompatibilityReason::FutureReleaseError, + reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow, }; } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index aa54b3d8ac2..f4b4c5168bf 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -253,6 +253,19 @@ impl Level { } } + pub fn to_cmd_flag(self) -> &'static str { + match self { + Level::Warn => "-W", + Level::Deny => "-D", + Level::Forbid => "-F", + Level::Allow => "-A", + Level::ForceWarn(_) => "--force-warn", + Level::Expect(_) => { + unreachable!("the expect level does not have a commandline flag") + } + } + } + pub fn is_error(self) -> bool { match self { Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn(_) => false, diff --git a/compiler/rustc_llvm/build.rs b/compiler/rustc_llvm/build.rs index 0b3c057345a..9fe59a1d826 100644 --- a/compiler/rustc_llvm/build.rs +++ b/compiler/rustc_llvm/build.rs @@ -58,7 +58,7 @@ fn restore_library_path() { /// Supposed to be used for all variables except those set for build scripts by cargo /// <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts> fn tracked_env_var_os<K: AsRef<OsStr> + Display>(key: K) -> Option<OsString> { - println!("cargo:rerun-if-env-changed={}", key); + println!("cargo:rerun-if-env-changed={key}"); env::var_os(key) } @@ -84,7 +84,7 @@ fn output(cmd: &mut Command) -> String { let output = match cmd.stderr(Stdio::inherit()).output() { Ok(status) => status, Err(e) => { - println!("\n\nfailed to execute command: {:?}\nerror: {}\n\n", cmd, e); + println!("\n\nfailed to execute command: {cmd:?}\nerror: {e}\n\n"); std::process::exit(1); } }; @@ -100,7 +100,7 @@ fn output(cmd: &mut Command) -> String { fn main() { for component in REQUIRED_COMPONENTS.iter().chain(OPTIONAL_COMPONENTS.iter()) { - println!("cargo:rustc-check-cfg=values(llvm_component,\"{}\")", component); + println!("cargo:rustc-check-cfg=values(llvm_component,\"{component}\")"); } if tracked_env_var_os("RUST_CHECK").is_some() { @@ -164,12 +164,12 @@ fn main() { for component in REQUIRED_COMPONENTS { if !components.contains(component) { - panic!("require llvm component {} but wasn't found", component); + panic!("require llvm component {component} but wasn't found"); } } for component in components.iter() { - println!("cargo:rustc-cfg=llvm_component=\"{}\"", component); + println!("cargo:rustc-cfg=llvm_component=\"{component}\""); } // Link in our own LLVM shims, compiled with the same flags as LLVM @@ -283,7 +283,7 @@ fn main() { } let kind = if name.starts_with("LLVM") { llvm_kind } else { "dylib" }; - println!("cargo:rustc-link-lib={}={}", kind, name); + println!("cargo:rustc-link-lib={kind}={name}"); } // LLVM ldflags @@ -302,11 +302,11 @@ fn main() { println!("cargo:rustc-link-search=native={}", stripped.replace(&host, &target)); } } else if let Some(stripped) = lib.strip_prefix("-LIBPATH:") { - println!("cargo:rustc-link-search=native={}", stripped); + println!("cargo:rustc-link-search=native={stripped}"); } else if let Some(stripped) = lib.strip_prefix("-l") { - println!("cargo:rustc-link-lib={}", stripped); + println!("cargo:rustc-link-lib={stripped}"); } else if let Some(stripped) = lib.strip_prefix("-L") { - println!("cargo:rustc-link-search=native={}", stripped); + println!("cargo:rustc-link-search=native={stripped}"); } } @@ -318,9 +318,9 @@ fn main() { if let Some(s) = llvm_linker_flags { for lib in s.into_string().unwrap().split_whitespace() { if let Some(stripped) = lib.strip_prefix("-l") { - println!("cargo:rustc-link-lib={}", stripped); + println!("cargo:rustc-link-lib={stripped}"); } else if let Some(stripped) = lib.strip_prefix("-L") { - println!("cargo:rustc-link-search=native={}", stripped); + println!("cargo:rustc-link-search=native={stripped}"); } } } @@ -359,14 +359,14 @@ fn main() { let path = PathBuf::from(s); println!("cargo:rustc-link-search=native={}", path.parent().unwrap().display()); if target.contains("windows") { - println!("cargo:rustc-link-lib=static:-bundle={}", stdcppname); + println!("cargo:rustc-link-lib=static:-bundle={stdcppname}"); } else { - println!("cargo:rustc-link-lib=static={}", stdcppname); + println!("cargo:rustc-link-lib=static={stdcppname}"); } } else if cxxflags.contains("stdlib=libc++") { println!("cargo:rustc-link-lib=c++"); } else { - println!("cargo:rustc-link-lib={}", stdcppname); + println!("cargo:rustc-link-lib={stdcppname}"); } } diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index 7da6ab71309..03e6d2149e9 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -28,8 +28,8 @@ extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( for (size_t i = 0; i < FilenamesLen; i++) { FilenameRefs.push_back(std::string(Filenames[i])); } - auto FilenamesWriter = coverage::CoverageFilenamesSectionWriter( - makeArrayRef(FilenameRefs)); + auto FilenamesWriter = + coverage::CoverageFilenamesSectionWriter(ArrayRef<std::string>(FilenameRefs)); RawRustStringOstream OS(BufferOut); FilenamesWriter.write(OS); } @@ -45,15 +45,16 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer( // Convert from FFI representation to LLVM representation. SmallVector<coverage::CounterMappingRegion, 0> MappingRegions; MappingRegions.reserve(NumMappingRegions); - for (const auto &Region : makeArrayRef(RustMappingRegions, NumMappingRegions)) { + for (const auto &Region : ArrayRef<LLVMRustCounterMappingRegion>( + RustMappingRegions, NumMappingRegions)) { MappingRegions.emplace_back( Region.Count, Region.FalseCount, Region.FileID, Region.ExpandedFileID, Region.LineStart, Region.ColumnStart, Region.LineEnd, Region.ColumnEnd, Region.Kind); } auto CoverageMappingWriter = coverage::CoverageMappingWriter( - makeArrayRef(VirtualFileMappingIDs, NumVirtualFileMappingIDs), - makeArrayRef(Expressions, NumExpressions), + ArrayRef<unsigned>(VirtualFileMappingIDs, NumVirtualFileMappingIDs), + ArrayRef<coverage::CounterExpression>(Expressions, NumExpressions), MappingRegions); RawRustStringOstream OS(BufferOut); CoverageMappingWriter.write(OS); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 279b6991854..8f94e8a4ab2 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -257,7 +257,7 @@ template<typename T> static inline void AddAttributes(T *t, unsigned Index, PALNew = PAL.addAttributes(t->getContext(), Index, B); #else AttrBuilder B(t->getContext()); - for (LLVMAttributeRef Attr : makeArrayRef(Attrs, AttrsLen)) + for (LLVMAttributeRef Attr : ArrayRef<LLVMAttributeRef>(Attrs, AttrsLen)) B.addAttribute(unwrap(Attr)); PALNew = PAL.addAttributesAtIndex(t->getContext(), Index, B); #endif @@ -1064,7 +1064,7 @@ extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerator( LLVMRustDIBuilderRef Builder, const char *Name, size_t NameLen, const uint64_t Value[2], unsigned SizeInBits, bool IsUnsigned) { return wrap(Builder->createEnumerator(StringRef(Name, NameLen), - APSInt(APInt(SizeInBits, makeArrayRef(Value, 2)), IsUnsigned))); + APSInt(APInt(SizeInBits, ArrayRef<uint64_t>(Value, 2)), IsUnsigned))); } extern "C" LLVMMetadataRef LLVMRustDIBuilderCreateEnumerationType( @@ -1477,7 +1477,7 @@ extern "C" void LLVMRustAddHandler(LLVMValueRef CatchSwitchRef, extern "C" OperandBundleDef *LLVMRustBuildOperandBundleDef(const char *Name, LLVMValueRef *Inputs, unsigned NumInputs) { - return new OperandBundleDef(Name, makeArrayRef(unwrap(Inputs), NumInputs)); + return new OperandBundleDef(Name, ArrayRef<Value*>(unwrap(Inputs), NumInputs)); } extern "C" void LLVMRustFreeOperandBundleDef(OperandBundleDef *Bundle) { @@ -1491,8 +1491,8 @@ extern "C" LLVMValueRef LLVMRustBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVM Value *Callee = unwrap(Fn); FunctionType *FTy = unwrap<FunctionType>(Ty); return wrap(unwrap(B)->CreateCall( - FTy, Callee, makeArrayRef(unwrap(Args), NumArgs), - makeArrayRef(*OpBundles, NumOpBundles))); + FTy, Callee, ArrayRef<Value*>(unwrap(Args), NumArgs), + ArrayRef<OperandBundleDef>(*OpBundles, NumOpBundles))); } extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M) { @@ -1537,8 +1537,8 @@ LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, Value *Callee = unwrap(Fn); FunctionType *FTy = unwrap<FunctionType>(Ty); return wrap(unwrap(B)->CreateInvoke(FTy, Callee, unwrap(Then), unwrap(Catch), - makeArrayRef(unwrap(Args), NumArgs), - makeArrayRef(*OpBundles, NumOpBundles), + ArrayRef<Value*>(unwrap(Args), NumArgs), + ArrayRef<OperandBundleDef>(*OpBundles, NumOpBundles), Name)); } diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml index 3c50827c1ab..7f955b0a750 100644 --- a/compiler/rustc_log/Cargo.toml +++ b/compiler/rustc_log/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" tracing = "0.1.28" tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] } tracing-tree = "0.2.0" +tracing-core = "0.1.28" [dev-dependencies] rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs index ddf29c488c9..fc1cabd2de9 100644 --- a/compiler/rustc_log/src/lib.rs +++ b/compiler/rustc_log/src/lib.rs @@ -45,16 +45,34 @@ use std::env::{self, VarError}; use std::fmt::{self, Display}; use std::io::{self, IsTerminal}; +use tracing_core::{Event, Subscriber}; use tracing_subscriber::filter::{Directive, EnvFilter, LevelFilter}; +use tracing_subscriber::fmt::{ + format::{self, FormatEvent, FormatFields}, + FmtContext, +}; use tracing_subscriber::layer::SubscriberExt; pub fn init_rustc_env_logger() -> Result<(), Error> { - init_env_logger("RUSTC_LOG") + init_rustc_env_logger_with_backtrace_option(&None) +} + +pub fn init_rustc_env_logger_with_backtrace_option( + backtrace_target: &Option<String>, +) -> Result<(), Error> { + init_env_logger_with_backtrace_option("RUSTC_LOG", backtrace_target) } /// In contrast to `init_rustc_env_logger` this allows you to choose an env var /// other than `RUSTC_LOG`. pub fn init_env_logger(env: &str) -> Result<(), Error> { + init_env_logger_with_backtrace_option(env, &None) +} + +pub fn init_env_logger_with_backtrace_option( + env: &str, + backtrace_target: &Option<String>, +) -> Result<(), Error> { let filter = match env::var(env) { Ok(env) => EnvFilter::new(env), _ => EnvFilter::default().add_directive(Directive::from(LevelFilter::WARN)), @@ -88,11 +106,47 @@ pub fn init_env_logger(env: &str) -> Result<(), Error> { let layer = layer.with_thread_ids(true).with_thread_names(true); let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer); - tracing::subscriber::set_global_default(subscriber).unwrap(); + match backtrace_target { + Some(str) => { + let fmt_layer = tracing_subscriber::fmt::layer() + .with_writer(io::stderr) + .without_time() + .event_format(BacktraceFormatter { backtrace_target: str.to_string() }); + let subscriber = subscriber.with(fmt_layer); + tracing::subscriber::set_global_default(subscriber).unwrap(); + } + None => { + tracing::subscriber::set_global_default(subscriber).unwrap(); + } + }; Ok(()) } +struct BacktraceFormatter { + backtrace_target: String, +} + +impl<S, N> FormatEvent<S, N> for BacktraceFormatter +where + S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>, + N: for<'a> FormatFields<'a> + 'static, +{ + fn format_event( + &self, + _ctx: &FmtContext<'_, S, N>, + mut writer: format::Writer<'_>, + event: &Event<'_>, + ) -> fmt::Result { + let target = event.metadata().target(); + if !target.contains(&self.backtrace_target) { + return Ok(()); + } + let backtrace = std::backtrace::Backtrace::capture(); + writeln!(writer, "stack backtrace: \n{:?}", backtrace) + } +} + pub fn stdout_isatty() -> bool { io::stdout().is_terminal() } @@ -114,8 +168,7 @@ impl Display for Error { match self { Error::InvalidColorValue(value) => write!( formatter, - "invalid log color value '{}': expected one of always, never, or auto", - value, + "invalid log color value '{value}': expected one of always, never, or auto", ), Error::NonUnicodeColorValue => write!( formatter, diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 82f6812026a..e405462bbb8 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -382,10 +382,26 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { return Ok(quote! { #diag.subdiagnostic(#binding); }); } } - (Meta::List(_), "subdiagnostic") => { - throw_invalid_attr!(attr, &meta, |diag| { - diag.help("`subdiagnostic` does not support nested attributes") - }) + (Meta::List(MetaList { ref nested, .. }), "subdiagnostic") => { + if nested.len() == 1 + && let Some(NestedMeta::Meta(Meta::Path(path))) = nested.first() + && path.is_ident("eager") { + let handler = match &self.parent.kind { + DiagnosticDeriveKind::Diagnostic { handler } => handler, + DiagnosticDeriveKind::LintDiagnostic => { + throw_invalid_attr!(attr, &meta, |diag| { + diag.help("eager subdiagnostics are not supported on lints") + }) + } + }; + return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); }); + } else { + throw_invalid_attr!(attr, &meta, |diag| { + diag.help( + "`eager` is the only supported nested attribute for `subdiagnostic`", + ) + }) + } } _ => (), } diff --git a/compiler/rustc_macros/src/diagnostics/error.rs b/compiler/rustc_macros/src/diagnostics/error.rs index 4612f54e4b1..2d62d593163 100644 --- a/compiler/rustc_macros/src/diagnostics/error.rs +++ b/compiler/rustc_macros/src/diagnostics/error.rs @@ -76,11 +76,11 @@ pub(crate) fn invalid_attr(attr: &Attribute, meta: &Meta) -> Diagnostic { let span = attr.span().unwrap(); let path = path_to_string(&attr.path); match meta { - Meta::Path(_) => span_err(span, &format!("`#[{}]` is not a valid attribute", path)), + Meta::Path(_) => span_err(span, &format!("`#[{path}]` is not a valid attribute")), Meta::NameValue(_) => { - span_err(span, &format!("`#[{} = ...]` is not a valid attribute", path)) + span_err(span, &format!("`#[{path} = ...]` is not a valid attribute")) } - Meta::List(_) => span_err(span, &format!("`#[{}(...)]` is not a valid attribute", path)), + Meta::List(_) => span_err(span, &format!("`#[{path}(...)]` is not a valid attribute")), } } @@ -107,7 +107,7 @@ pub(crate) fn invalid_nested_attr(attr: &Attribute, nested: &NestedMeta) -> Diag let meta = match nested { syn::NestedMeta::Meta(meta) => meta, syn::NestedMeta::Lit(_) => { - return span_err(span, &format!("`#[{}(\"...\")]` is not a valid attribute", name)); + return span_err(span, &format!("`#[{name}(\"...\")]` is not a valid attribute")); } }; @@ -115,13 +115,11 @@ pub(crate) fn invalid_nested_attr(attr: &Attribute, nested: &NestedMeta) -> Diag let path = path_to_string(meta.path()); match meta { Meta::NameValue(..) => { - span_err(span, &format!("`#[{}({} = ...)]` is not a valid attribute", name, path)) - } - Meta::Path(..) => { - span_err(span, &format!("`#[{}({})]` is not a valid attribute", name, path)) + span_err(span, &format!("`#[{name}({path} = ...)]` is not a valid attribute")) } + Meta::Path(..) => span_err(span, &format!("`#[{name}({path})]` is not a valid attribute")), Meta::List(..) => { - span_err(span, &format!("`#[{}({}(...))]` is not a valid attribute", name, path)) + span_err(span, &format!("`#[{name}({path}(...))]` is not a valid attribute")) } } } diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs index 3e447c94ef1..32338f9dfc5 100644 --- a/compiler/rustc_macros/src/diagnostics/fluent.rs +++ b/compiler/rustc_macros/src/diagnostics/fluent.rs @@ -178,7 +178,7 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok opt: Default::default(), }; let dl = DisplayList::from(snippet); - eprintln!("{}\n", dl); + eprintln!("{dl}\n"); } continue; } @@ -265,7 +265,7 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok Diagnostic::spanned( path_span, Level::Error, - format!("overrides existing {}: `{}`", kind, id), + format!("overrides existing {kind}: `{id}`"), ) .span_help(previous_defns[&id], "previously defined in this resource") .emit(); diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 767db367322..baffd3cec9c 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -198,8 +198,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { throw_span_err!( attr.span().unwrap(), &format!( - "diagnostic slug must be first argument of a `#[{}(...)]` attribute", - name + "diagnostic slug must be first argument of a `#[{name}(...)]` attribute" ) ); }; diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 4ff9c777ad8..6f52a3de1b1 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -322,7 +322,7 @@ pub(crate) trait HasFieldMap { None => { span_err( span.unwrap(), - &format!("`{}` doesn't refer to a field on this type", field), + &format!("`{field}` doesn't refer to a field on this type"), ) .emit(); quote! { @@ -603,8 +603,7 @@ impl SubdiagnosticKind { if suggestion_kind != SuggestionKind::Normal { invalid_attr(attr, &meta) .help(format!( - r#"Use `#[suggestion(..., style = "{}")]` instead"#, - suggestion_kind + r#"Use `#[suggestion(..., style = "{suggestion_kind}")]` instead"# )) .emit(); } @@ -621,8 +620,7 @@ impl SubdiagnosticKind { if suggestion_kind != SuggestionKind::Normal { invalid_attr(attr, &meta) .help(format!( - r#"Use `#[multipart_suggestion(..., style = "{}")]` instead"#, - suggestion_kind + r#"Use `#[multipart_suggestion(..., style = "{suggestion_kind}")]` instead"# )) .emit(); } diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index ac916bb6068..bb3722fe156 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -1,5 +1,6 @@ #![feature(allow_internal_unstable)] #![feature(if_let_guard)] +#![feature(let_chains)] #![feature(never_type)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_span)] diff --git a/compiler/rustc_macros/src/newtype.rs b/compiler/rustc_macros/src/newtype.rs index 153473de624..89ea89cf502 100644 --- a/compiler/rustc_macros/src/newtype.rs +++ b/compiler/rustc_macros/src/newtype.rs @@ -41,7 +41,7 @@ impl Parse for Newtype { }; if let Some(old) = max.replace(literal.lit) { - panic!("Specified multiple max: {:?}", old); + panic!("Specified multiple max: {old:?}"); } false @@ -52,7 +52,7 @@ impl Parse for Newtype { }; if let Some(old) = debug_format.replace(literal.lit) { - panic!("Specified multiple debug format options: {:?}", old); + panic!("Specified multiple debug format options: {old:?}"); } false diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 789d83a0dd0..08e42a8a08f 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -239,7 +239,7 @@ fn doc_comment_from_desc(list: &Punctuated<Expr, token::Comma>) -> Result<Attrib .unwrap(); }, ); - let doc_string = format!("[query description - consider adding a doc-comment!] {}", doc_string); + let doc_string = format!("[query description - consider adding a doc-comment!] {doc_string}"); Ok(parse_quote! { #[doc = #doc_string] }) } diff --git a/compiler/rustc_macros/src/symbols.rs b/compiler/rustc_macros/src/symbols.rs index 92590c33b9d..04facbf657d 100644 --- a/compiler/rustc_macros/src/symbols.rs +++ b/compiler/rustc_macros/src/symbols.rs @@ -134,7 +134,7 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) { let mut check_dup = |span: Span, str: &str, errors: &mut Errors| { if let Some(prev_span) = keys.get(str) { - errors.error(span, format!("Symbol `{}` is duplicated", str)); + errors.error(span, format!("Symbol `{str}` is duplicated")); errors.error(*prev_span, "location of previous definition".to_string()); } else { keys.insert(str.to_string(), span); @@ -144,8 +144,8 @@ fn symbols_with_errors(input: TokenStream) -> (TokenStream, Vec<syn::Error>) { let mut check_order = |span: Span, str: &str, errors: &mut Errors| { if let Some((prev_span, ref prev_str)) = prev_key { if str < prev_str { - errors.error(span, format!("Symbol `{}` must precede `{}`", str, prev_str)); - errors.error(prev_span, format!("location of previous symbol `{}`", prev_str)); + errors.error(span, format!("Symbol `{str}` must precede `{prev_str}`")); + errors.error(prev_span, format!("location of previous symbol `{prev_str}`")); } } prev_key = Some((span, str.to_string())); diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index b34dc0df1e2..653f2b39d3e 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -112,7 +112,7 @@ impl<'a> std::fmt::Debug for CrateDump<'a> { writeln!(fmt, "resolved crates:")?; for (cnum, data) in self.0.iter_crate_data() { writeln!(fmt, " name: {}", data.name())?; - writeln!(fmt, " cnum: {}", cnum)?; + writeln!(fmt, " cnum: {cnum}")?; writeln!(fmt, " hash: {}", data.hash())?; writeln!(fmt, " reqd: {:?}", data.dep_kind())?; let CrateSource { dylib, rlib, rmeta } = data.source(); @@ -150,7 +150,7 @@ impl CStore { pub(crate) fn get_crate_data(&self, cnum: CrateNum) -> CrateMetadataRef<'_> { let cdata = self.metas[cnum] .as_ref() - .unwrap_or_else(|| panic!("Failed to get crate data for {:?}", cnum)); + .unwrap_or_else(|| panic!("Failed to get crate data for {cnum:?}")); CrateMetadataRef { cdata, cstore: self } } diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index 1fd35adf1bd..59869ee4173 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -45,7 +45,7 @@ pub fn find_native_static_library( for path in search_paths { for (prefix, suffix) in &formats { - let test = path.join(format!("{}{}{}", prefix, name, suffix)); + let test = path.join(format!("{prefix}{name}{suffix}")); if test.exists() { return test; } diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 99d8225a4c3..143d8f2f1e1 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -462,7 +462,7 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SyntaxContext { .root .syntax_contexts .get(cdata, id) - .unwrap_or_else(|| panic!("Missing SyntaxContext {:?} for crate {:?}", id, cname)) + .unwrap_or_else(|| panic!("Missing SyntaxContext {id:?} for crate {cname:?}")) .decode((cdata, sess)) }) } @@ -806,7 +806,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .tables .def_span .get(self, index) - .unwrap_or_else(|| panic!("Missing span for {:?}", index)) + .unwrap_or_else(|| panic!("Missing span for {index:?}")) .decode((self, sess)) } @@ -1249,7 +1249,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { .tables .proc_macro_quoted_spans .get(self, index) - .unwrap_or_else(|| panic!("Missing proc macro quoted span: {:?}", index)) + .unwrap_or_else(|| panic!("Missing proc macro quoted span: {index:?}")) .decode((self, sess)) } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index e167bbf57e6..cb451931dfe 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -230,7 +230,7 @@ provide! { tcx, def_id, other, cdata, .trait_impl_trait_tys .get(cdata, def_id.index) .map(|lazy| lazy.decode((cdata, tcx))) - .process_decoded(tcx, || panic!("{:?} does not have trait_impl_trait_tys", def_id))) + .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys"))) } visibility => { cdata.get_visibility(def_id.index) } diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index 40c94b372bb..a6133f1b417 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -58,7 +58,7 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for DefPathHashMapRef<'static> let _ = d.read_raw_bytes(len); let inner = odht::HashTable::from_raw_bytes(o).unwrap_or_else(|e| { - panic!("decode error: {}", e); + panic!("decode error: {e}"); }); DefPathHashMapRef::OwnedFromMetadata(inner) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 214a5842233..030328d1e26 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -145,7 +145,7 @@ impl<'a, 'tcx, I, T> Encodable<EncodeContext<'a, 'tcx>> for LazyTable<I, T> { impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for CrateNum { fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { if *self != LOCAL_CRATE && s.is_proc_macro { - panic!("Attempted to encode non-local CrateNum {:?} for proc-macro crate", self); + panic!("Attempted to encode non-local CrateNum {self:?} for proc-macro crate"); } s.emit_u32(self.as_u32()); } @@ -276,7 +276,7 @@ impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for Span { // Introduce a new scope so that we drop the 'lock()' temporary match &*source_file.external_src.lock() { ExternalSource::Foreign { metadata_index, .. } => *metadata_index, - src => panic!("Unexpected external source {:?}", src), + src => panic!("Unexpected external source {src:?}"), } }; @@ -733,12 +733,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let prefix = "meta-stats"; let perc = |bytes| (bytes * 100) as f64 / total_bytes as f64; - eprintln!("{} METADATA STATS", prefix); + eprintln!("{prefix} METADATA STATS"); eprintln!("{} {:<23}{:>10}", prefix, "Section", "Size"); - eprintln!( - "{} ----------------------------------------------------------------", - prefix - ); + eprintln!("{prefix} ----------------------------------------------------------------"); for (label, size) in stats { eprintln!( "{} {:<23}{:>10} ({:4.1}%)", @@ -748,10 +745,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { perc(size) ); } - eprintln!( - "{} ----------------------------------------------------------------", - prefix - ); + eprintln!("{prefix} ----------------------------------------------------------------"); eprintln!( "{} {:<23}{:>10} (of which {:.1}% are zero bytes)", prefix, @@ -759,7 +753,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { to_readable_str(total_bytes), perc(zero_bytes) ); - eprintln!("{}", prefix); + eprintln!("{prefix}"); } root @@ -1435,7 +1429,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let instance = ty::InstanceDef::Item(ty::WithOptConstParam::unknown(def_id.to_def_id())); let unused = tcx.unused_generic_params(instance); - if !unused.is_empty() { + if !unused.all_used() { record!(self.tables.unused_generic_params[def_id.to_def_id()] <- unused); } } @@ -1692,6 +1686,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } ty::Closure(_, substs) => { + let constness = self.tcx.constness(def_id.to_def_id()); + self.tables.constness.set(def_id.to_def_id().index, constness); record!(self.tables.fn_sig[def_id.to_def_id()] <- substs.as_closure().sig()); } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 26a41f633ff..bf9be714daf 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -13,7 +13,7 @@ use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, DefIndex, DefPathHash, StableCrateId}; use rustc_hir::definitions::DefKey; use rustc_hir::lang_items::LangItem; -use rustc_index::bit_set::{BitSet, FiniteBitSet}; +use rustc_index::bit_set::BitSet; use rustc_index::vec::IndexVec; use rustc_middle::metadata::ModChild; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; @@ -22,7 +22,7 @@ use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault; use rustc_middle::mir; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, ReprOptions, Ty}; +use rustc_middle::ty::{self, ReprOptions, Ty, UnusedGenericParams}; use rustc_middle::ty::{DeducedParamAttrs, GeneratorDiagnosticData, ParameterizedOverTcx, TyCtxt}; use rustc_serialize::opaque::FileEncoder; use rustc_session::config::SymbolManglingVersion; @@ -384,7 +384,7 @@ define_tables! { trait_item_def_id: Table<DefIndex, RawDefId>, inherent_impls: Table<DefIndex, LazyArray<DefIndex>>, expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>, - unused_generic_params: Table<DefIndex, LazyValue<FiniteBitSet<u32>>>, + unused_generic_params: Table<DefIndex, LazyValue<UnusedGenericParams>>, params_in_repr: Table<DefIndex, LazyValue<BitSet<u32>>>, repr_options: Table<DefIndex, LazyValue<ReprOptions>>, // `def_keys` and `def_path_hashes` represent a lazy version of a diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 75282f958b5..f816d614500 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -30,7 +30,12 @@ macro_rules! arena_types { [decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>, [decode] borrowck_result: rustc_middle::mir::BorrowCheckResult<'tcx>, - [] resolver: rustc_data_structures::steal::Steal<rustc_middle::ty::ResolverAstLowering>, + [] resolver: rustc_data_structures::steal::Steal<( + rustc_middle::ty::ResolverAstLowering, + rustc_data_structures::sync::Lrc<rustc_ast::Crate>, + )>, + [] output_filenames: std::sync::Arc<rustc_session::config::OutputFilenames>, + [] resolutions: rustc_middle::ty::ResolverGlobalCtxt, [decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult, [decode] code_region: rustc_middle::mir::coverage::CodeRegion, [] const_allocs: rustc_middle::mir::interpret::Allocation, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index d799d3a5ad7..48bae7a2d4e 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -69,7 +69,7 @@ impl<'hir> Iterator for ParentHirIterator<'hir> { } loop { // There are nodes that do not have entries, so we need to skip them. - let parent_id = self.map.get_parent_node(self.current_id); + let parent_id = self.map.parent_id(self.current_id); if parent_id == self.current_id { self.current_id = CRATE_HIR_ID; @@ -246,7 +246,7 @@ impl<'hir> Map<'hir> { }, Node::Variant(_) => DefKind::Variant, Node::Ctor(variant_data) => { - let ctor_of = match self.find(self.get_parent_node(hir_id)) { + let ctor_of = match self.find_parent(hir_id) { Some(Node::Item(..)) => def::CtorOf::Struct, Some(Node::Variant(..)) => def::CtorOf::Variant, _ => unreachable!(), @@ -257,7 +257,7 @@ impl<'hir> Map<'hir> { } } Node::AnonConst(_) => { - let inline = match self.find(self.get_parent_node(hir_id)) { + let inline = match self.find_parent(hir_id) { Some(Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. })) if anon_const.hir_id == hir_id => true, @@ -298,7 +298,7 @@ impl<'hir> Map<'hir> { /// Finds the id of the parent node to this one. /// /// If calling repeatedly and iterating over parents, prefer [`Map::parent_iter`]. - pub fn find_parent_node(self, id: HirId) -> Option<HirId> { + pub fn opt_parent_id(self, id: HirId) -> Option<HirId> { if id.local_id == ItemLocalId::from_u32(0) { Some(self.tcx.hir_owner_parent(id.owner)) } else { @@ -312,11 +312,19 @@ impl<'hir> Map<'hir> { } #[track_caller] - pub fn get_parent_node(self, hir_id: HirId) -> HirId { - self.find_parent_node(hir_id) + pub fn parent_id(self, hir_id: HirId) -> HirId { + self.opt_parent_id(hir_id) .unwrap_or_else(|| bug!("No parent for node {:?}", self.node_to_string(hir_id))) } + pub fn get_parent(self, hir_id: HirId) -> Node<'hir> { + self.get(self.parent_id(hir_id)) + } + + pub fn find_parent(self, hir_id: HirId) -> Option<Node<'hir>> { + self.find(self.opt_parent_id(hir_id)?) + } + /// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found. pub fn find(self, id: HirId) -> Option<Node<'hir>> { if id.local_id == ItemLocalId::from_u32(0) { @@ -414,7 +422,7 @@ impl<'hir> Map<'hir> { /// which this is the body of, i.e., a `fn`, `const` or `static` /// item (possibly associated), a closure, or a `hir::AnonConst`. pub fn body_owner(self, BodyId { hir_id }: BodyId) -> HirId { - let parent = self.get_parent_node(hir_id); + let parent = self.parent_id(hir_id); assert!(self.find(parent).map_or(false, |n| is_body_owner(n, hir_id)), "{hir_id:?}"); parent } @@ -477,7 +485,9 @@ impl<'hir> Map<'hir> { BodyOwnerKind::Static(mt) => ConstContext::Static(mt), BodyOwnerKind::Fn if self.tcx.is_constructor(def_id.to_def_id()) => return None, - BodyOwnerKind::Fn if self.tcx.is_const_fn_raw(def_id.to_def_id()) => { + BodyOwnerKind::Fn | BodyOwnerKind::Closure + if self.tcx.is_const_fn_raw(def_id.to_def_id()) => + { ConstContext::ConstFn } BodyOwnerKind::Fn if self.tcx.is_const_default_method(def_id.to_def_id()) => { @@ -642,21 +652,21 @@ impl<'hir> Map<'hir> { } /// Returns an iterator for the nodes in the ancestor tree of the `current_id` - /// until the crate root is reached. Prefer this over your own loop using `get_parent_node`. + /// until the crate root is reached. Prefer this over your own loop using `parent_id`. #[inline] pub fn parent_id_iter(self, current_id: HirId) -> impl Iterator<Item = HirId> + 'hir { ParentHirIterator { current_id, map: self } } /// Returns an iterator for the nodes in the ancestor tree of the `current_id` - /// until the crate root is reached. Prefer this over your own loop using `get_parent_node`. + /// until the crate root is reached. Prefer this over your own loop using `parent_id`. #[inline] pub fn parent_iter(self, current_id: HirId) -> impl Iterator<Item = (HirId, Node<'hir>)> { self.parent_id_iter(current_id).filter_map(move |id| Some((id, self.find(id)?))) } /// Returns an iterator for the nodes in the ancestor tree of the `current_id` - /// until the crate root is reached. Prefer this over your own loop using `get_parent_node`. + /// until the crate root is reached. Prefer this over your own loop using `parent_id`. #[inline] pub fn parent_owner_iter(self, current_id: HirId) -> ParentOwnerIterator<'hir> { ParentOwnerIterator { current_id, map: self } @@ -664,7 +674,7 @@ impl<'hir> Map<'hir> { /// Checks if the node is left-hand side of an assignment. pub fn is_lhs(self, id: HirId) -> bool { - match self.find(self.get_parent_node(id)) { + match self.find_parent(id) { Some(Node::Expr(expr)) => match expr.kind { ExprKind::Assign(lhs, _rhs, _span) => lhs.hir_id == id, _ => false, @@ -892,7 +902,7 @@ impl<'hir> Map<'hir> { Node::Pat(&Pat { kind: PatKind::Binding(_, _, ident, _), .. }) => Some(ident), // A `Ctor` doesn't have an identifier itself, but its parent // struct/variant does. Compare with `hir::Map::opt_span`. - Node::Ctor(..) => match self.find(self.get_parent_node(id))? { + Node::Ctor(..) => match self.find_parent(id)? { Node::Item(item) => Some(item.ident), Node::Variant(variant) => Some(variant.ident), _ => unreachable!(), @@ -1021,7 +1031,7 @@ impl<'hir> Map<'hir> { ForeignItemKind::Fn(decl, _, _) => until_within(item.span, decl.output.span()), _ => named_span(item.span, item.ident, None), }, - Node::Ctor(_) => return self.opt_span(self.get_parent_node(hir_id)), + Node::Ctor(_) => return self.opt_span(self.parent_id(hir_id)), Node::Expr(Expr { kind: ExprKind::Closure(Closure { fn_decl_span, .. }), span, @@ -1063,7 +1073,7 @@ impl<'hir> Map<'hir> { Node::PatField(field) => field.span, Node::Arm(arm) => arm.span, Node::Block(block) => block.span, - Node::Ctor(..) => self.span_with_body(self.get_parent_node(hir_id)), + Node::Ctor(..) => self.span_with_body(self.parent_id(hir_id)), Node::Lifetime(lifetime) => lifetime.ident.span, Node::GenericParam(param) => param.span, Node::Infer(i) => i.span, @@ -1093,7 +1103,7 @@ impl<'hir> Map<'hir> { /// Returns the HirId of `N` in `struct Foo<const N: usize = { ... }>` when /// called with the HirId for the `{ ... }` anon const pub fn opt_const_param_default_param_def_id(self, anon_const: HirId) -> Option<LocalDefId> { - match self.get(self.get_parent_node(anon_const)) { + match self.get_parent(anon_const) { Node::GenericParam(GenericParam { def_id: param_id, kind: GenericParamKind::Const { .. }, diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs index 0b32f67a81e..614cf1a0051 100644 --- a/compiler/rustc_middle/src/infer/canonical.rs +++ b/compiler/rustc_middle/src/infer/canonical.rs @@ -213,9 +213,7 @@ impl QueryRegionConstraints<'_> { } } -pub type Canonicalized<'tcx, V> = Canonical<'tcx, V>; - -pub type CanonicalizedQueryResponse<'tcx, T> = &'tcx Canonical<'tcx, QueryResponse<'tcx, T>>; +pub type CanonicalQueryResponse<'tcx, T> = &'tcx Canonical<'tcx, QueryResponse<'tcx, T>>; /// Indicates whether or not we were able to prove the query to be /// true. diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 51df42f6d14..c61de97d532 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -182,7 +182,7 @@ impl TyCtxt<'_> { if hir.attrs(id).iter().any(|attr| Level::from_attr(attr).is_some()) { return id; } - let next = hir.get_parent_node(id); + let next = hir.parent_id(id); if next == id { bug!("lint traversal reached the root of the crate"); } @@ -234,16 +234,7 @@ pub fn explain_lint_level_source( err.note_once(&format!("`#[{}({})]` on by default", level.as_str(), name)); } LintLevelSource::CommandLine(lint_flag_val, orig_level) => { - let flag = match orig_level { - Level::Warn => "-W", - Level::Deny => "-D", - Level::Forbid => "-F", - Level::Allow => "-A", - Level::ForceWarn(_) => "--force-warn", - Level::Expect(_) => { - unreachable!("the expect level does not have a commandline flag") - } - }; + let flag = orig_level.to_cmd_flag(); let hyphen_case_lint_name = name.replace('_', "-"); if lint_flag_val.as_str() == name { err.note_once(&format!( diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs index 01fe72de612..5ca4d260179 100644 --- a/compiler/rustc_middle/src/macros.rs +++ b/compiler/rustc_middle/src/macros.rs @@ -1,3 +1,13 @@ +/// A macro for triggering an ICE. +/// Calling `bug` instead of panicking will result in a nicer error message and should +/// therefore be prefered over `panic`/`unreachable` or others. +/// +/// If you have a span available, you should use [`span_bug`] instead. +/// +/// If the bug should only be emitted when compilation didn't fail, [`Session::delay_span_bug`] may be useful. +/// +/// [`Session::delay_span_bug`]: rustc_session::Session::delay_span_bug +/// [`span_bug`]: crate::span_bug #[macro_export] macro_rules! bug { () => ( $crate::bug!("impossible case reached") ); @@ -8,6 +18,14 @@ macro_rules! bug { }); } +/// A macro for triggering an ICE with a span. +/// Calling `span_bug!` instead of panicking will result in a nicer error message and point +/// at the code the compiler was compiling when it ICEd. This is the preferred way to trigger +/// ICEs. +/// +/// If the bug should only be emitted when compilation didn't fail, [`Session::delay_span_bug`] may be useful. +/// +/// [`Session::delay_span_bug`]: rustc_session::Session::delay_span_bug #[macro_export] macro_rules! span_bug { ($span:expr, $msg:expr) => ({ $crate::util::bug::span_bug_fmt($span, ::std::format_args!($msg)) }); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index a89e6566d56..14bdff4568f 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2506,7 +2506,7 @@ impl<'tcx> ConstantKind<'tcx> { } let hir_id = tcx.hir().local_def_id_to_hir_id(def.did); - let parent_substs = if let Some(parent_hir_id) = tcx.hir().find_parent_node(hir_id) { + let parent_substs = if let Some(parent_hir_id) = tcx.hir().opt_parent_id(hir_id) { if let Some(parent_did) = tcx.hir().opt_local_def_id(parent_hir_id) { InternalSubsts::identity_for_item(tcx, parent_did.to_def_id()) } else { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 37db2274f67..86655915736 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -27,12 +27,12 @@ rustc_queries! { } query resolutions(_: ()) -> &'tcx ty::ResolverGlobalCtxt { - eval_always + feedable no_hash desc { "getting the resolver outputs" } } - query resolver_for_lowering(_: ()) -> &'tcx Steal<ty::ResolverAstLowering> { + query resolver_for_lowering(_: ()) -> &'tcx Steal<(ty::ResolverAstLowering, Lrc<ast::Crate>)> { feedable no_hash desc { "getting the resolver for lowering" } @@ -1673,7 +1673,7 @@ rustc_queries! { /// Gets the name of the crate. query crate_name(_: CrateNum) -> Symbol { - eval_always + feedable desc { "fetching what a crate is named" } separate_provide_extern } @@ -1839,7 +1839,7 @@ rustc_queries! { desc { "getting codegen unit `{sym}`" } } - query unused_generic_params(key: ty::InstanceDef<'tcx>) -> FiniteBitSet<u32> { + query unused_generic_params(key: ty::InstanceDef<'tcx>) -> UnusedGenericParams { cache_on_disk_if { key.def_id().is_local() } desc { |tcx| "determining which generic parameters are unused by `{}`", @@ -1857,7 +1857,7 @@ rustc_queries! { /// This query returns an `&Arc` because codegen backends need the value even after the `TyCtxt` /// has been destroyed. query output_filenames(_: ()) -> &'tcx Arc<OutputFilenames> { - eval_always + feedable desc { "getting output filenames" } } @@ -2041,7 +2041,7 @@ rustc_queries! { } query features_query(_: ()) -> &'tcx rustc_feature::Features { - eval_always + feedable desc { "looking up enabled feature gates" } } diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index ac903010c8d..5f320708c84 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -9,6 +9,7 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/thir.html use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::RangeEnd; @@ -575,6 +576,12 @@ impl<'tcx> Pat<'tcx> { } } +impl<'tcx> IntoDiagnosticArg for Pat<'tcx> { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + format!("{}", self).into_diagnostic_arg() + } +} + #[derive(Clone, Debug, HashStable)] pub struct Ascription<'tcx> { pub annotation: CanonicalUserTypeAnnotation<'tcx>, diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index 6a149be3137..543f5b87e00 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -15,22 +15,19 @@ use rustc_span::source_map::Span; pub mod type_op { use crate::ty::fold::TypeFoldable; - use crate::ty::subst::UserSubsts; - use crate::ty::{Predicate, Ty}; - use rustc_hir::def_id::DefId; + use crate::ty::{Predicate, Ty, UserType}; use std::fmt; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)] #[derive(TypeFoldable, TypeVisitable)] pub struct AscribeUserType<'tcx> { pub mir_ty: Ty<'tcx>, - pub def_id: DefId, - pub user_substs: UserSubsts<'tcx>, + pub user_ty: UserType<'tcx>, } impl<'tcx> AscribeUserType<'tcx> { - pub fn new(mir_ty: Ty<'tcx>, def_id: DefId, user_substs: UserSubsts<'tcx>) -> Self { - Self { mir_ty, def_id, user_substs } + pub fn new(mir_ty: Ty<'tcx>, user_ty: UserType<'tcx>) -> Self { + Self { mir_ty, user_ty } } } diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index ec69864c951..1cc9fd526b4 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -131,7 +131,9 @@ pub enum SelectionCandidate<'tcx> { /// Implementation of a `Fn`-family trait by one of the anonymous types /// generated for an `||` expression. - ClosureCandidate, + ClosureCandidate { + is_const: bool, + }, /// Implementation of a `Generator` trait by one of the anonymous types /// generated for a generator. diff --git a/compiler/rustc_middle/src/ty/_match.rs b/compiler/rustc_middle/src/ty/_match.rs index cd147d7e558..b9c5a4e0d0d 100644 --- a/compiler/rustc_middle/src/ty/_match.rs +++ b/compiler/rustc_middle/src/ty/_match.rs @@ -89,7 +89,9 @@ impl<'tcx> TypeRelation<'tcx> for Match<'tcx> { Err(TypeError::Sorts(relate::expected_found(self, a, b))) } - (&ty::Error(_), _) | (_, &ty::Error(_)) => Ok(self.tcx().ty_error()), + (&ty::Error(guar), _) | (_, &ty::Error(guar)) => { + Ok(self.tcx().ty_error_with_guaranteed(guar)) + } _ => relate::super_relate_tys(self, a, b), } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5de414077a2..63f31e5a11f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -51,7 +51,7 @@ use rustc_macros::HashStable; use rustc_query_system::dep_graph::DepNodeIndex; use rustc_query_system::ich::StableHashingContext; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; -use rustc_session::config::{CrateType, OutputFilenames}; +use rustc_session::config::CrateType; use rustc_session::cstore::{CrateStoreDyn, Untracked}; use rustc_session::lint::Lint; use rustc_session::Limit; @@ -74,7 +74,6 @@ use std::hash::{Hash, Hasher}; use std::iter; use std::mem; use std::ops::{Bound, Deref}; -use std::sync::Arc; pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync { /// Creates a new `OnDiskCache` instance from the serialized data in `data`. @@ -363,6 +362,9 @@ impl<'tcx> TyCtxt<'tcx> { pub fn feed_unit_query(self) -> TyCtxtFeed<'tcx, ()> { TyCtxtFeed { tcx: self, key: () } } + pub fn feed_local_crate(self) -> TyCtxtFeed<'tcx, CrateNum> { + TyCtxtFeed { tcx: self, key: LOCAL_CRATE } + } } impl<'tcx, KEY: Copy> TyCtxtFeed<'tcx, KEY> { @@ -428,11 +430,6 @@ pub struct GlobalCtxt<'tcx> { pub consts: CommonConsts<'tcx>, untracked: Untracked, - /// Output of the resolver. - pub(crate) untracked_resolutions: ty::ResolverGlobalCtxt, - /// The entire crate as AST. This field serves as the input for the hir_crate query, - /// which lowers it from AST to HIR. It must not be read or used by anything else. - pub untracked_crate: Steal<Lrc<ast::Crate>>, /// This provides access to the incremental compilation on-disk cache for query results. /// Do not access this directly. It is only meant to be used by @@ -457,17 +454,11 @@ pub struct GlobalCtxt<'tcx> { /// Merge this with `selection_cache`? pub evaluation_cache: traits::EvaluationCache<'tcx>, - /// The definite name of the current crate after taking into account - /// attributes, commandline parameters, etc. - crate_name: Symbol, - /// Data layout specification for the current target. pub data_layout: TargetDataLayout, /// Stores memory for globals (statics/consts). pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>, - - output_filenames: Arc<OutputFilenames>, } impl<'tcx> TyCtxt<'tcx> { @@ -592,15 +583,11 @@ impl<'tcx> TyCtxt<'tcx> { lint_store: Lrc<dyn Any + sync::Send + sync::Sync>, arena: &'tcx WorkerLocal<Arena<'tcx>>, hir_arena: &'tcx WorkerLocal<hir::Arena<'tcx>>, - untracked_resolutions: ty::ResolverGlobalCtxt, untracked: Untracked, - krate: Lrc<ast::Crate>, dep_graph: DepGraph, on_disk_cache: Option<&'tcx dyn OnDiskCache<'tcx>>, queries: &'tcx dyn query::QueryEngine<'tcx>, query_kinds: &'tcx [DepKindStruct<'tcx>], - crate_name: Symbol, - output_filenames: OutputFilenames, ) -> GlobalCtxt<'tcx> { let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| { s.emit_fatal(err); @@ -622,8 +609,6 @@ impl<'tcx> TyCtxt<'tcx> { lifetimes: common_lifetimes, consts: common_consts, untracked, - untracked_resolutions, - untracked_crate: Steal::new(krate), on_disk_cache, queries, query_caches: query::QueryCaches::default(), @@ -632,10 +617,8 @@ impl<'tcx> TyCtxt<'tcx> { pred_rcache: Default::default(), selection_cache: Default::default(), evaluation_cache: Default::default(), - crate_name, data_layout, alloc_map: Lock::new(interpret::AllocMap::new()), - output_filenames: Arc::new(output_filenames), } } @@ -810,7 +793,7 @@ impl<'tcx> TyCtxt<'tcx> { // statements within the query system and we'd run into endless // recursion otherwise. let (crate_name, stable_crate_id) = if def_id.is_local() { - (self.crate_name, self.sess.local_stable_crate_id()) + (self.crate_name(LOCAL_CRATE), self.sess.local_stable_crate_id()) } else { let cstore = &*self.untracked.cstore; (cstore.crate_name(def_id.krate), cstore.stable_crate_id(def_id.krate)) @@ -2407,13 +2390,8 @@ fn ptr_eq<T, U>(t: *const T, u: *const U) -> bool { } pub fn provide(providers: &mut ty::query::Providers) { - providers.resolutions = |tcx, ()| &tcx.untracked_resolutions; providers.module_reexports = |tcx, id| tcx.resolutions(()).reexport_map.get(&id).map(|v| &v[..]); - providers.crate_name = |tcx, id| { - assert_eq!(id, LOCAL_CRATE); - tcx.crate_name - }; providers.maybe_unused_trait_imports = |tcx, ()| &tcx.resolutions(()).maybe_unused_trait_imports; providers.maybe_unused_extern_crates = @@ -2424,8 +2402,6 @@ pub fn provide(providers: &mut ty::query::Providers) { providers.extern_mod_stmt_cnum = |tcx, id| tcx.resolutions(()).extern_crate_map.get(&id).cloned(); - providers.output_filenames = |tcx, ()| &tcx.output_filenames; - providers.features_query = |tcx, ()| tcx.sess.features_untracked(); providers.is_panic_runtime = |tcx, cnum| { assert_eq!(cnum, LOCAL_CRATE); tcx.sess.contains_name(tcx.hir().krate_attrs(), sym::panic_runtime) diff --git a/compiler/rustc_middle/src/ty/erase_regions.rs b/compiler/rustc_middle/src/ty/erase_regions.rs index ffdac93bcd0..9e4f90caab0 100644 --- a/compiler/rustc_middle/src/ty/erase_regions.rs +++ b/compiler/rustc_middle/src/ty/erase_regions.rs @@ -21,7 +21,7 @@ impl<'tcx> TyCtxt<'tcx> { T: TypeFoldable<'tcx>, { // If there's nothing to erase avoid performing the query at all - if !value.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND | TypeFlags::HAS_FREE_REGIONS) { + if !value.has_type_flags(TypeFlags::HAS_LATE_BOUND | TypeFlags::HAS_FREE_REGIONS) { return value; } debug!("erase_regions({:?})", value); diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 14d07608a78..5d394f71f0d 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -2,10 +2,10 @@ use crate::traits::{ObligationCause, ObligationCauseCode}; use crate::ty::diagnostics::suggest_constraining_type_param; use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, Printer}; use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt}; -use hir::def::DefKind; use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; use rustc_errors::{pluralize, Diagnostic, MultiSpan}; use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind}; use rustc_hir::def_id::DefId; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span}; @@ -319,7 +319,11 @@ impl<'tcx> Ty<'tcx> { .into() } } - ty::FnDef(..) => "fn item".into(), + ty::FnDef(def_id, ..) => match tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => "struct constructor".into(), + DefKind::Ctor(CtorOf::Variant, _) => "enum constructor".into(), + _ => "fn item".into(), + }, ty::FnPtr(_) => "fn pointer".into(), ty::Dynamic(ref inner, ..) if let Some(principal) = inner.principal() => { format!("trait object `dyn {}`", tcx.def_path_str(principal.def_id())).into() @@ -366,7 +370,11 @@ impl<'tcx> Ty<'tcx> { _ => "reference", } .into(), - ty::FnDef(..) => "fn item".into(), + ty::FnDef(def_id, ..) => match tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => "struct constructor".into(), + DefKind::Ctor(CtorOf::Variant, _) => "enum constructor".into(), + _ => "fn item".into(), + }, ty::FnPtr(_) => "fn pointer".into(), ty::Dynamic(..) => "trait object".into(), ty::Closure(..) => "closure".into(), @@ -457,7 +465,7 @@ impl<'tcx> TyCtxt<'tcx> { .def_id .as_local() .map(|id| hir.local_def_id_to_hir_id(id)) - .and_then(|id| self.hir().find(self.hir().get_parent_node(id))) + .and_then(|id| self.hir().find_parent(id)) .as_ref() .and_then(|node| node.generics()) { diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs index 8306d670a65..b7eafc4b437 100644 --- a/compiler/rustc_middle/src/ty/flags.rs +++ b/compiler/rustc_middle/src/ty/flags.rs @@ -59,8 +59,18 @@ impl FlagComputation { { let mut computation = FlagComputation::new(); - if !value.bound_vars().is_empty() { - computation.flags = computation.flags | TypeFlags::HAS_RE_LATE_BOUND; + for bv in value.bound_vars() { + match bv { + ty::BoundVariableKind::Ty(_) => { + computation.flags |= TypeFlags::HAS_TY_LATE_BOUND; + } + ty::BoundVariableKind::Region(_) => { + computation.flags |= TypeFlags::HAS_RE_LATE_BOUND; + } + ty::BoundVariableKind::Const => { + computation.flags |= TypeFlags::HAS_CT_LATE_BOUND; + } + } } f(&mut computation, value.skip_binder()); @@ -131,6 +141,7 @@ impl FlagComputation { &ty::Bound(debruijn, _) => { self.add_bound_var(debruijn); + self.add_flags(TypeFlags::HAS_TY_LATE_BOUND); } &ty::Placeholder(..) => { @@ -303,6 +314,7 @@ impl FlagComputation { } ty::ConstKind::Bound(debruijn, _) => { self.add_bound_var(debruijn); + self.add_flags(TypeFlags::HAS_CT_LATE_BOUND); } ty::ConstKind::Param(_) => { self.add_flags(TypeFlags::HAS_CT_PARAM); diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 35d369ffc89..4ee4d7caec1 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -6,6 +6,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def::Namespace; use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::lang_items::LangItem; +use rustc_index::bit_set::FiniteBitSet; use rustc_macros::HashStable; use rustc_middle::ty::normalize_erasing_regions::NormalizationError; use rustc_span::Symbol; @@ -711,7 +712,7 @@ fn polymorphize<'tcx>( } InternalSubsts::for_item(tcx, def_id, |param, _| { - let is_unused = unused.contains(param.index).unwrap_or(false); + let is_unused = unused.is_unused(param.index); debug!("polymorphize: param={:?} is_unused={:?}", param, is_unused); match param.kind { // Upvar case: If parameter is a type parameter.. @@ -733,7 +734,7 @@ fn polymorphize<'tcx>( // Simple case: If parameter is a const or type parameter.. ty::GenericParamDefKind::Const { .. } | ty::GenericParamDefKind::Type { .. } if // ..and is within range and unused.. - unused.contains(param.index).unwrap_or(false) => + unused.is_unused(param.index) => // ..then use the identity for this parameter. tcx.mk_param_from_def(param), @@ -774,3 +775,36 @@ fn needs_fn_once_adapter_shim( (ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce, _) => Err(()), } } + +// Set bits represent unused generic parameters. +// An empty set indicates that all parameters are used. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Decodable, Encodable, HashStable)] +pub struct UnusedGenericParams(FiniteBitSet<u32>); + +impl UnusedGenericParams { + pub fn new_all_unused(amount: u32) -> Self { + let mut bitset = FiniteBitSet::new_empty(); + bitset.set_range(0..amount); + Self(bitset) + } + + pub fn new_all_used() -> Self { + Self(FiniteBitSet::new_empty()) + } + + pub fn mark_used(&mut self, idx: u32) { + self.0.clear(idx); + } + + pub fn is_unused(&self, idx: u32) -> bool { + self.0.contains(idx).unwrap_or(false) + } + + pub fn is_used(&self, idx: u32) -> bool { + !self.is_unused(idx) + } + + pub fn all_used(&self) -> bool { + self.0.is_empty() + } +} diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 9e0ca44d098..00f53afd663 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -670,29 +670,50 @@ where }); } - match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() { - ty::Slice(_) | ty::Str => TyMaybeWithLayout::Ty(tcx.types.usize), - ty::Dynamic(_, _, ty::Dyn) => { - TyMaybeWithLayout::Ty(tcx.mk_imm_ref( - tcx.lifetimes.re_static, - tcx.mk_array(tcx.types.usize, 3), - )) - /* FIXME: use actual fn pointers - Warning: naively computing the number of entries in the - vtable by counting the methods on the trait + methods on - all parent traits does not work, because some methods can - be not object safe and thus excluded from the vtable. - Increase this counter if you tried to implement this but - failed to do it without duplicating a lot of code from - other places in the compiler: 2 - tcx.mk_tup(&[ - tcx.mk_array(tcx.types.usize, 3), - tcx.mk_array(Option<fn()>), - ]) - */ + let mk_dyn_vtable = || { + tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.usize, 3)) + /* FIXME: use actual fn pointers + Warning: naively computing the number of entries in the + vtable by counting the methods on the trait + methods on + all parent traits does not work, because some methods can + be not object safe and thus excluded from the vtable. + Increase this counter if you tried to implement this but + failed to do it without duplicating a lot of code from + other places in the compiler: 2 + tcx.mk_tup(&[ + tcx.mk_array(tcx.types.usize, 3), + tcx.mk_array(Option<fn()>), + ]) + */ + }; + + let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() { + let metadata = tcx.normalize_erasing_regions( + cx.param_env(), + tcx.mk_projection(metadata_def_id, [pointee]), + ); + + // Map `Metadata = DynMetadata<dyn Trait>` back to a vtable, since it + // offers better information than `std::ptr::metadata::VTable`, + // and we rely on this layout information to trigger a panic in + // `std::mem::uninitialized::<&dyn Trait>()`, for example. + if let ty::Adt(def, substs) = metadata.kind() + && Some(def.did()) == tcx.lang_items().dyn_metadata() + && substs.type_at(0).is_trait() + { + mk_dyn_vtable() + } else { + metadata } - _ => bug!("TyAndLayout::field({:?}): not applicable", this), - } + } else { + match tcx.struct_tail_erasing_lifetimes(pointee, cx.param_env()).kind() { + ty::Slice(_) | ty::Str => tcx.types.usize, + ty::Dynamic(_, _, ty::Dyn) => mk_dyn_vtable(), + _ => bug!("TyAndLayout::field({:?}): not applicable", this), + } + }; + + TyMaybeWithLayout::Ty(metadata) } // Arrays and slices. diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index f01d74539a1..993e95b3514 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -86,7 +86,7 @@ pub use self::context::{ tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, OnDiskCache, TyCtxt, TyCtxtFeed, }; -pub use self::instance::{Instance, InstanceDef, ShortInstance}; +pub use self::instance::{Instance, InstanceDef, ShortInstance, UnusedGenericParams}; pub use self::list::List; pub use self::parameterized::ParameterizedOverTcx; pub use self::rvalue_scopes::RvalueScopes; @@ -2465,8 +2465,10 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn is_const_fn_raw(self, def_id: DefId) -> bool { - matches!(self.def_kind(def_id), DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..)) - && self.constness(def_id) == hir::Constness::Const + matches!( + self.def_kind(def_id), + DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::Closure + ) && self.constness(def_id) == hir::Constness::Const } #[inline] diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index a21e3961cb6..72f45198579 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -60,6 +60,7 @@ trivially_parameterized_over_tcx! { ty::ImplPolarity, ty::ReprOptions, ty::TraitDef, + ty::UnusedGenericParams, ty::Visibility<DefIndex>, ty::adjustment::CoerceUnsizedInfo, ty::fast_reject::SimplifiedType, diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index c49e75d68ad..a91e8de5f21 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -283,6 +283,8 @@ pub trait PrettyPrinter<'tcx>: /// This is typically the case for all non-`'_` regions. fn should_print_region(&self, region: ty::Region<'tcx>) -> bool; + fn reset_type_limit(&mut self) {} + // Defaults (should not be overridden): /// If possible, this returns a global path resolving to `def_id` that is visible @@ -1981,6 +1983,10 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> { self.0.ty_infer_name_resolver.as_ref().and_then(|func| func(id)) } + fn reset_type_limit(&mut self) { + self.printed_type_count = 0; + } + fn const_infer_name(&self, id: ty::ConstVid<'tcx>) -> Option<Symbol> { self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id)) } @@ -2722,11 +2728,15 @@ define_print_and_forward_display! { } ty::SubtypePredicate<'tcx> { - p!(print(self.a), " <: ", print(self.b)) + p!(print(self.a), " <: "); + cx.reset_type_limit(); + p!(print(self.b)) } ty::CoercePredicate<'tcx> { - p!(print(self.a), " -> ", print(self.b)) + p!(print(self.a), " -> "); + cx.reset_type_limit(); + p!(print(self.b)) } ty::TraitPredicate<'tcx> { @@ -2738,7 +2748,9 @@ define_print_and_forward_display! { } ty::ProjectionPredicate<'tcx> { - p!(print(self.projection_ty), " == ", print(self.term)) + p!(print(self.projection_ty), " == "); + cx.reset_type_limit(); + p!(print(self.term)) } ty::Term<'tcx> { diff --git a/compiler/rustc_middle/src/ty/query.rs b/compiler/rustc_middle/src/ty/query.rs index 642900d3ab4..9d4ee22a727 100644 --- a/compiler/rustc_middle/src/ty/query.rs +++ b/compiler/rustc_middle/src/ty/query.rs @@ -34,7 +34,7 @@ use crate::ty::layout::TyAndLayout; use crate::ty::subst::{GenericArg, SubstsRef}; use crate::ty::util::AlwaysRequiresDrop; use crate::ty::GeneratorDiagnosticData; -use crate::ty::{self, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt}; +use crate::ty::{self, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt, UnusedGenericParams}; use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; use rustc_attr as attr; @@ -50,7 +50,7 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId}; use rustc_hir::hir_id::OwnerId; use rustc_hir::lang_items::{LangItem, LanguageItems}; use rustc_hir::{Crate, ItemLocalId, TraitCandidate}; -use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec}; +use rustc_index::vec::IndexVec; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; use rustc_session::cstore::{CrateDepKind, CrateSource}; use rustc_session::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLib}; diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 4d34ca3d66b..65fd8d9753d 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -414,7 +414,7 @@ pub fn super_relate_tys<'tcx, R: TypeRelation<'tcx>>( bug!("bound types encountered in super_relate_tys") } - (&ty::Error(_), _) | (_, &ty::Error(_)) => Ok(tcx.ty_error()), + (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(tcx.ty_error_with_guaranteed(guar)), (&ty::Never, _) | (&ty::Char, _) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index f7e4c821569..bd5b04d5b2b 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1686,7 +1686,7 @@ impl<'tcx> Ty<'tcx> { } #[inline] - pub fn is_ty_infer(self) -> bool { + pub fn is_ty_or_numeric_infer(self) -> bool { matches!(self.kind(), Infer(_)) } diff --git a/compiler/rustc_middle/src/ty/subst.rs b/compiler/rustc_middle/src/ty/subst.rs index 0c33e5bda1a..2dec58ea82a 100644 --- a/compiler/rustc_middle/src/ty/subst.rs +++ b/compiler/rustc_middle/src/ty/subst.rs @@ -202,7 +202,7 @@ impl<'tcx> GenericArg<'tcx> { pub fn is_non_region_infer(self) -> bool { match self.unpack() { GenericArgKind::Lifetime(_) => false, - GenericArgKind::Type(ty) => ty.is_ty_infer(), + GenericArgKind::Type(ty) => ty.is_ty_or_numeric_infer(), GenericArgKind::Const(ct) => ct.is_ct_infer(), } } diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 136a4906c58..028a03c0b2b 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -626,7 +626,7 @@ pub struct CanonicalUserTypeAnnotation<'tcx> { pub inferred_ty: Ty<'tcx>, } -/// Canonicalized user type annotation. +/// Canonical user type annotation. pub type CanonicalUserType<'tcx> = Canonical<'tcx, UserType<'tcx>>; impl<'tcx> CanonicalUserType<'tcx> { @@ -679,7 +679,7 @@ impl<'tcx> CanonicalUserType<'tcx> { /// from constants that are named via paths, like `Foo::<A>::new` and /// so forth. #[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] +#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable, Lift)] pub enum UserType<'tcx> { Ty(Ty<'tcx>), diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index d5553f84f75..ca445558131 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -165,6 +165,14 @@ pub trait TypeVisitable<'tcx>: fmt::Debug + Clone { fn has_late_bound_regions(&self) -> bool { self.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND) } + /// True if there are any late-bound non-region variables + fn has_non_region_late_bound(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_LATE_BOUND - TypeFlags::HAS_RE_LATE_BOUND) + } + /// True if there are any late-bound variables + fn has_late_bound_vars(&self) -> bool { + self.has_type_flags(TypeFlags::HAS_LATE_BOUND) + } /// Indicates whether this value still has parameters/placeholders/inference variables /// which could be replaced later, in a way that would change the results of `impl` diff --git a/compiler/rustc_middle/src/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs index fd7045d6a03..b73ae593905 100644 --- a/compiler/rustc_middle/src/util/bug.rs +++ b/compiler/rustc_middle/src/util/bug.rs @@ -35,8 +35,7 @@ fn opt_span_bug_fmt<S: Into<MultiSpan>>( (Some(tcx), None) => tcx.sess.diagnostic().bug(&msg), (None, _) => panic_any(msg), } - }); - unreachable!(); + }) } /// A query to trigger a `delay_span_bug`. Clearly, if one has a `tcx` one can already trigger a diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 68179001b91..06523b0a1de 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1,9 +1,13 @@ +use crate::thir::pattern::deconstruct_pat::DeconstructedPat; use crate::thir::pattern::MatchCheckCtxt; use rustc_errors::Handler; use rustc_errors::{ - error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan, + error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + IntoDiagnostic, MultiSpan, SubdiagnosticMessage, }; +use rustc_hir::def::Res; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_middle::thir::Pat; use rustc_middle::ty::{self, Ty}; use rustc_span::{symbol::Ident, Span}; @@ -494,6 +498,16 @@ pub struct LowerRangeBoundMustBeLessThanOrEqualToUpper { } #[derive(Diagnostic)] +#[diag(mir_build_literal_in_range_out_of_bounds)] +pub struct LiteralOutOfRange<'tcx> { + #[primary_span] + #[label] + pub span: Span, + pub ty: Ty<'tcx>, + pub max: u128, +} + +#[derive(Diagnostic)] #[diag(mir_build_lower_range_bound_must_be_less_than_upper, code = "E0579")] pub struct LowerRangeBoundMustBeLessThanUpper { #[primary_span] @@ -614,3 +628,223 @@ pub enum MultipleMutBorrowOccurence { name_moved: Ident, }, } + +#[derive(Diagnostic)] +#[diag(mir_build_union_pattern)] +pub struct UnionPattern { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_type_not_structural)] +pub struct TypeNotStructural<'tcx> { + #[primary_span] + pub span: Span, + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag(mir_build_invalid_pattern)] +pub struct InvalidPattern<'tcx> { + #[primary_span] + pub span: Span, + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag(mir_build_unsized_pattern)] +pub struct UnsizedPattern<'tcx> { + #[primary_span] + pub span: Span, + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_float_pattern)] +pub struct FloatPattern; + +#[derive(LintDiagnostic)] +#[diag(mir_build_pointer_pattern)] +pub struct PointerPattern; + +#[derive(LintDiagnostic)] +#[diag(mir_build_indirect_structural_match)] +pub struct IndirectStructuralMatch<'tcx> { + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_nontrivial_structural_match)] +pub struct NontrivialStructuralMatch<'tcx> { + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_overlapping_range_endpoints)] +#[note] +pub struct OverlappingRangeEndpoints<'tcx> { + #[label(range)] + pub range: Span, + #[subdiagnostic] + pub overlap: Vec<Overlap<'tcx>>, +} + +pub struct Overlap<'tcx> { + pub span: Span, + pub range: Pat<'tcx>, +} + +impl<'tcx> AddToDiagnostic for Overlap<'tcx> { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + let Overlap { span, range } = self; + + // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]` + // does not support `#[subdiagnostic(eager)]`... + let message = format!("this range overlaps on `{range}`..."); + diag.span_label(span, message); + } +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_non_exhaustive_omitted_pattern)] +#[help] +#[note] +pub(crate) struct NonExhaustiveOmittedPattern<'tcx> { + pub scrut_ty: Ty<'tcx>, + #[subdiagnostic] + pub uncovered: Uncovered<'tcx>, +} + +#[derive(Subdiagnostic)] +#[label(mir_build_uncovered)] +pub(crate) struct Uncovered<'tcx> { + #[primary_span] + span: Span, + count: usize, + witness_1: Pat<'tcx>, + witness_2: Pat<'tcx>, + witness_3: Pat<'tcx>, + remainder: usize, +} + +impl<'tcx> Uncovered<'tcx> { + pub fn new<'p>( + span: Span, + cx: &MatchCheckCtxt<'p, 'tcx>, + witnesses: Vec<DeconstructedPat<'p, 'tcx>>, + ) -> Self { + let witness_1 = witnesses.get(0).unwrap().to_pat(cx); + Self { + span, + count: witnesses.len(), + // Substitute dummy values if witnesses is smaller than 3. These will never be read. + witness_2: witnesses.get(1).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()), + witness_3: witnesses.get(2).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()), + witness_1, + remainder: witnesses.len().saturating_sub(3), + } + } +} + +#[derive(Diagnostic)] +#[diag(mir_build_pattern_not_covered, code = "E0005")] +pub(crate) struct PatternNotCovered<'s, 'tcx> { + #[primary_span] + pub span: Span, + pub origin: &'s str, + #[subdiagnostic] + pub uncovered: Uncovered<'tcx>, + #[subdiagnostic] + pub inform: Option<Inform>, + #[subdiagnostic] + pub interpreted_as_const: Option<InterpretedAsConst>, + #[subdiagnostic] + pub adt_defined_here: Option<AdtDefinedHere<'tcx>>, + #[note(pattern_ty)] + pub _p: (), + pub pattern_ty: Ty<'tcx>, + #[subdiagnostic] + pub let_suggestion: Option<SuggestLet>, + #[subdiagnostic] + pub res_defined_here: Option<ResDefinedHere>, +} + +#[derive(Subdiagnostic)] +#[note(mir_build_inform_irrefutable)] +#[note(mir_build_more_information)] +pub struct Inform; + +pub struct AdtDefinedHere<'tcx> { + pub adt_def_span: Span, + pub ty: Ty<'tcx>, + pub variants: Vec<Variant>, +} + +pub struct Variant { + pub span: Span, +} + +impl<'tcx> AddToDiagnostic for AdtDefinedHere<'tcx> { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + diag.set_arg("ty", self.ty); + let mut spans = MultiSpan::from(self.adt_def_span); + + for Variant { span } in self.variants { + spans.push_span_label(span, rustc_errors::fluent::mir_build_variant_defined_here); + } + + diag.span_note(spans, rustc_errors::fluent::mir_build_adt_defined_here); + } +} + +#[derive(Subdiagnostic)] +#[label(mir_build_res_defined_here)] +pub struct ResDefinedHere { + #[primary_span] + pub def_span: Span, + pub res: Res, +} + +#[derive(Subdiagnostic)] +#[suggestion( + mir_build_interpreted_as_const, + code = "{variable}_var", + applicability = "maybe-incorrect" +)] +#[label(mir_build_confused)] +pub struct InterpretedAsConst { + #[primary_span] + pub span: Span, + pub article: &'static str, + pub variable: String, + pub res: Res, +} + +#[derive(Subdiagnostic)] +pub enum SuggestLet { + #[multipart_suggestion(mir_build_suggest_if_let, applicability = "has-placeholders")] + If { + #[suggestion_part(code = "if ")] + start_span: Span, + #[suggestion_part(code = " {{ todo!() }}")] + semi_span: Span, + count: usize, + }, + #[suggestion( + mir_build_suggest_let_else, + code = " else {{ todo!() }}", + applicability = "has-placeholders" + )] + Else { + #[primary_span] + end_span: Span, + count: usize, + }, +} diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 2b05e92fdcf..fb7ae6f1d24 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -10,6 +10,7 @@ #![feature(let_chains)] #![feature(min_specialization)] #![feature(once_cell)] +#![feature(try_blocks)] #![recursion_limit = "256"] #[macro_use] 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 a94d8d6c643..e13c0662ef8 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -9,8 +9,7 @@ use crate::errors::*; use rustc_arena::TypedArena; use rustc_ast::Mutability; use rustc_errors::{ - pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, - MultiSpan, + struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::*; @@ -247,14 +246,14 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { fn check_let_chain(&mut self, cx: &mut MatchCheckCtxt<'p, 'tcx>, pat_id: HirId) -> bool { let hir = self.tcx.hir(); - let parent = hir.get_parent_node(pat_id); + let parent = hir.parent_id(pat_id); // First, figure out if the given pattern is part of a let chain, // and if so, obtain the top node of the chain. let mut top = parent; let mut part_of_chain = false; loop { - let new_top = hir.get_parent_node(top); + let new_top = hir.parent_id(top); if let hir::Node::Expr( hir::Expr { kind: hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, lhs, rhs), @@ -378,8 +377,8 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let pattern = self.lower_pattern(&mut cx, pat, &mut false); let pattern_ty = pattern.ty(); - let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }]; - let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty); + let arm = MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }; + let report = compute_match_usefulness(&cx, &[arm], pat.hir_id, pattern_ty); // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We // only care about exhaustiveness here. @@ -390,145 +389,73 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { return; } - let joined_patterns = joined_uncovered_patterns(&cx, &witnesses); - - let mut bindings = vec![]; - - let mut err = struct_span_err!( - self.tcx.sess, - pat.span, - E0005, - "refutable pattern in {}: {} not covered", - origin, - joined_patterns - ); - let suggest_if_let = match &pat.kind { - hir::PatKind::Path(hir::QPath::Resolved(None, path)) - if path.segments.len() == 1 && path.segments[0].args.is_none() => + let (inform, interpreted_as_const, res_defined_here,let_suggestion) = + if let hir::PatKind::Path(hir::QPath::Resolved( + None, + hir::Path { + segments: &[hir::PathSegment { args: None, res, ident, .. }], + .. + }, + )) = &pat.kind { - const_not_var(&mut err, cx.tcx, pat, path); - false - } - _ => { - pat.walk(&mut |pat: &hir::Pat<'_>| { - match pat.kind { - hir::PatKind::Binding(_, _, ident, _) => { - bindings.push(ident); + ( + None, + Some(InterpretedAsConst { + span: pat.span, + article: res.article(), + variable: ident.to_string().to_lowercase(), + res, + }), + try { + ResDefinedHere { + def_span: cx.tcx.hir().res_span(res)?, + res, } - _ => {} + }, + None, + ) + } else if let Some(span) = sp && self.tcx.sess.source_map().is_span_accessible(span) { + let mut bindings = vec![]; + pat.walk_always(&mut |pat: &hir::Pat<'_>| { + if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { + bindings.push(ident); } - true }); - - err.span_label(pat.span, pattern_not_covered_label(&witnesses, &joined_patterns)); - true - } - }; - - if let (Some(span), true) = (sp, suggest_if_let) { - err.note( - "`let` bindings require an \"irrefutable pattern\", like a `struct` or \ - an `enum` with only one variant", - ); - if self.tcx.sess.source_map().is_span_accessible(span) { let semi_span = span.shrink_to_hi().with_lo(span.hi() - BytePos(1)); let start_span = span.shrink_to_lo(); let end_span = semi_span.shrink_to_lo(); - err.multipart_suggestion( - &format!( - "you might want to use `if let` to ignore the variant{} that {} matched", - pluralize!(witnesses.len()), - match witnesses.len() { - 1 => "isn't", - _ => "aren't", - }, - ), - vec![ - match &bindings[..] { - [] => (start_span, "if ".to_string()), - [binding] => (start_span, format!("let {} = if ", binding)), - bindings => ( - start_span, - format!( - "let ({}) = if ", - bindings - .iter() - .map(|ident| ident.to_string()) - .collect::<Vec<_>>() - .join(", ") - ), - ), - }, - match &bindings[..] { - [] => (semi_span, " { todo!() }".to_string()), - [binding] => { - (end_span, format!(" {{ {} }} else {{ todo!() }}", binding)) - } - bindings => ( - end_span, - format!( - " {{ ({}) }} else {{ todo!() }}", - bindings - .iter() - .map(|ident| ident.to_string()) - .collect::<Vec<_>>() - .join(", ") - ), - ), - }, - ], - Applicability::HasPlaceholders, - ); - if !bindings.is_empty() { - err.span_suggestion_verbose( - semi_span.shrink_to_lo(), - &format!( - "alternatively, you might want to use \ - let else to handle the variant{} that {} matched", - pluralize!(witnesses.len()), - match witnesses.len() { - 1 => "isn't", - _ => "aren't", - }, - ), - " else { todo!() }", - Applicability::HasPlaceholders, - ); - } - } - err.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch18-02-refutability.html", - ); - } + let count = witnesses.len(); - adt_defined_here(&cx, &mut err, pattern_ty, &witnesses); - err.note(&format!("the matched value is of type `{}`", pattern_ty)); - err.emit(); - } -} + let let_suggestion = if bindings.is_empty() {SuggestLet::If{start_span, semi_span, count}} else{ SuggestLet::Else{end_span, count }}; + (sp.map(|_|Inform), None, None, Some(let_suggestion)) + } else{ + (sp.map(|_|Inform), None, None, None) + }; -/// A path pattern was interpreted as a constant, not a new variable. -/// This caused an irrefutable match failure in e.g. `let`. -fn const_not_var(err: &mut Diagnostic, tcx: TyCtxt<'_>, pat: &Pat<'_>, path: &hir::Path<'_>) { - let descr = path.res.descr(); - err.span_label( - pat.span, - format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,), - ); + let adt_defined_here = try { + let ty = pattern_ty.peel_refs(); + let ty::Adt(def, _) = ty.kind() else { None? }; + let adt_def_span = cx.tcx.hir().get_if_local(def.did())?.ident()?.span; + let mut variants = vec![]; - err.span_suggestion( - pat.span, - "introduce a variable instead", - format!("{}_var", path.segments[0].ident).to_lowercase(), - // Cannot use `MachineApplicable` as it's not really *always* correct - // because there may be such an identifier in scope or the user maybe - // really wanted to match against the constant. This is quite unlikely however. - Applicability::MaybeIncorrect, - ); + for span in maybe_point_at_variant(&cx, *def, witnesses.iter().take(5)) { + variants.push(Variant { span }); + } + AdtDefinedHere { adt_def_span, ty, variants } + }; - if let Some(span) = tcx.hir().res_span(path.res) { - err.span_label(span, format!("{} defined here", descr)); + self.tcx.sess.emit_err(PatternNotCovered { + span: pat.span, + origin, + uncovered: Uncovered::new(pat.span, &cx, witnesses), + inform, + interpreted_as_const, + _p: (), + pattern_ty, + let_suggestion, + res_defined_here, + adt_defined_here, + }); } } @@ -1054,7 +981,7 @@ pub enum LetSource { fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource { let hir = tcx.hir(); - let parent = hir.get_parent_node(pat_id); + let parent = hir.parent_id(pat_id); let_source_parent(tcx, parent, Some(pat_id)) } @@ -1073,7 +1000,7 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L _ => {} } - let parent_parent = hir.get_parent_node(parent); + let parent_parent = hir.parent_id(parent); let parent_parent_node = hir.get(parent_parent); match parent_parent_node { hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_), .. }) => { @@ -1085,8 +1012,8 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L _ => {} } - let parent_parent_parent = hir.get_parent_node(parent_parent); - let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent); + let parent_parent_parent = hir.parent_id(parent_parent); + let parent_parent_parent_parent = hir.parent_id(parent_parent_parent); let parent_parent_parent_parent_node = hir.get(parent_parent_parent_parent); if let hir::Node::Expr(hir::Expr { diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 6470efab2e9..7f3519945c3 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,11 +1,9 @@ -use rustc_errors::DelayDm; use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::{self, Field}; use rustc_middle::thir::{FieldPat, Pat, PatKind}; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::Span; use rustc_trait_selection::traits::predicate_for_trait_def; @@ -15,6 +13,10 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation}; use std::cell::Cell; use super::PatCtxt; +use crate::errors::{ + FloatPattern, IndirectStructuralMatch, InvalidPattern, NontrivialStructuralMatch, + PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern, +}; impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Converts an evaluated constant to a pattern (if possible). @@ -105,47 +107,6 @@ impl<'tcx> ConstToPat<'tcx> { self.infcx.tcx } - fn adt_derive_msg(&self, adt_def: AdtDef<'tcx>) -> String { - let path = self.tcx().def_path_str(adt_def.did()); - format!( - "to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - path, path, - ) - } - - fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> { - traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| { - with_no_trimmed_paths!(match non_sm_ty.kind() { - ty::Adt(adt, _) => self.adt_derive_msg(*adt), - ty::Dynamic(..) => { - "trait objects cannot be used in patterns".to_string() - } - ty::Alias(ty::Opaque, ..) => { - "opaque types cannot be used in patterns".to_string() - } - ty::Closure(..) => { - "closures cannot be used in patterns".to_string() - } - ty::Generator(..) | ty::GeneratorWitness(..) => { - "generators cannot be used in patterns".to_string() - } - ty::Float(..) => { - "floating-point numbers cannot be used in patterns".to_string() - } - ty::FnPtr(..) => { - "function pointers cannot be used in patterns".to_string() - } - ty::RawPtr(..) => { - "raw pointers cannot be used in patterns".to_string() - } - _ => { - bug!("use of a value of `{non_sm_ty}` inside a pattern") - } - }) - }) - } - fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { ty.is_structural_eq_shallow(self.infcx.tcx) } @@ -176,7 +137,8 @@ impl<'tcx> ConstToPat<'tcx> { // If we were able to successfully convert the const to some pat, // double-check that all types in the const implement `Structural`. - let structural = self.search_for_structural_match_violation(cv.ty()); + let structural = + traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty()); debug!( "search_for_structural_match_violation cv.ty: {:?} returned: {:?}", cv.ty(), @@ -194,17 +156,18 @@ impl<'tcx> ConstToPat<'tcx> { return inlined_const_as_pat; } - if let Some(msg) = structural { + if let Some(non_sm_ty) = structural { if !self.type_may_have_partial_eq_impl(cv.ty()) { - // span_fatal avoids ICE from resolution of non-existent method (rare case). - self.tcx().sess.span_fatal(self.span, &msg); + // fatal avoids ICE from resolution of non-existent method (rare case). + self.tcx() + .sess + .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty: non_sm_ty }); } else if mir_structural_match_violation && !self.saw_const_match_lint.get() { - self.tcx().struct_span_lint_hir( + self.tcx().emit_spanned_lint( lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, self.span, - msg, - |lint| lint, + IndirectStructuralMatch { non_sm_ty }, ); } else { debug!( @@ -278,12 +241,11 @@ impl<'tcx> ConstToPat<'tcx> { let kind = match cv.ty().kind() { ty::Float(_) => { if self.include_lint_checks { - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, id, span, - "floating-point types cannot be used in patterns", - |lint| lint, + FloatPattern, ); } PatKind::Constant { value: cv } @@ -291,29 +253,22 @@ impl<'tcx> ConstToPat<'tcx> { ty::Adt(adt_def, _) if adt_def.is_union() => { // Matching on union fields is unsafe, we can't hide it in constants self.saw_const_match_error.set(true); - let msg = "cannot use unions in constant patterns"; - if self.include_lint_checks { - tcx.sess.span_err(span, msg); - } else { - tcx.sess.delay_span_bug(span, msg); - } + let err = UnionPattern { span }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } ty::Adt(..) if !self.type_may_have_partial_eq_impl(cv.ty()) // FIXME(#73448): Find a way to bring const qualification into parity with // `search_for_structural_match_violation` and then remove this condition. - && self.search_for_structural_match_violation(cv.ty()).is_some() => + + // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we + // could get `Option<NonStructEq>`, even though `Option` is annotated with derive. + && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) => { - // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we - // could get `Option<NonStructEq>`, even though `Option` is annotated with derive. - let msg = self.search_for_structural_match_violation(cv.ty()).unwrap(); self.saw_const_match_error.set(true); - if self.include_lint_checks { - tcx.sess.span_err(self.span, &msg); - } else { - tcx.sess.delay_span_bug(self.span, &msg); - } + let err = TypeNotStructural { span, non_sm_ty }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } // If the type is not structurally comparable, just emit the constant directly, @@ -331,19 +286,11 @@ impl<'tcx> ConstToPat<'tcx> { && !self.saw_const_match_lint.get() { self.saw_const_match_lint.set(true); - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( lint::builtin::INDIRECT_STRUCTURAL_MATCH, id, span, - DelayDm(|| { - format!( - "to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - cv.ty(), - cv.ty(), - ) - }), - |lint| lint, + IndirectStructuralMatch { non_sm_ty: cv.ty() }, ); } // Since we are behind a reference, we can just bubble the error up so we get a @@ -357,18 +304,9 @@ impl<'tcx> ConstToPat<'tcx> { adt_def, cv.ty() ); - let path = tcx.def_path_str(adt_def.did()); - let msg = format!( - "to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - path, path, - ); self.saw_const_match_error.set(true); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + let err = TypeNotStructural { span, non_sm_ty: cv.ty() }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } ty::Adt(adt_def, substs) if adt_def.is_enum() => { @@ -401,12 +339,8 @@ impl<'tcx> ConstToPat<'tcx> { // These are not allowed and will error elsewhere anyway. ty::Dynamic(..) => { self.saw_const_match_error.set(true); - let msg = format!("`{}` cannot be used in patterns", cv.ty()); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + let err = InvalidPattern { span, non_sm_ty: cv.ty() }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } // `&str` is represented as `ConstValue::Slice`, let's keep using this @@ -471,32 +405,26 @@ impl<'tcx> ConstToPat<'tcx> { // this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a // reference. This makes the rest of the matching logic simpler as it doesn't have // to figure out how to get a reference again. - ty::Adt(adt_def, _) if !self.type_marked_structural(*pointee_ty) => { + ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => { if self.behind_reference.get() { if self.include_lint_checks && !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() { - self.saw_const_match_lint.set(true); - let msg = self.adt_derive_msg(adt_def); - self.tcx().struct_span_lint_hir( + self.saw_const_match_lint.set(true); + tcx.emit_spanned_lint( lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, - self.span, - msg, - |lint| lint, + span, + IndirectStructuralMatch { non_sm_ty: *pointee_ty }, ); } PatKind::Constant { value: cv } } else { if !self.saw_const_match_error.get() { self.saw_const_match_error.set(true); - let msg = self.adt_derive_msg(adt_def); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + let err = TypeNotStructural { span, non_sm_ty: *pointee_ty }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); } PatKind::Wild } @@ -508,12 +436,10 @@ impl<'tcx> ConstToPat<'tcx> { if !pointee_ty.is_sized(tcx, param_env) { // `tcx.deref_mir_constant()` below will ICE with an unsized type // (except slices, which are handled in a separate arm above). - let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + + let err = UnsizedPattern { span, non_sm_ty: *pointee_ty }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); + PatKind::Wild } else { let old = self.behind_reference.replace(true); @@ -545,27 +471,19 @@ impl<'tcx> ConstToPat<'tcx> { && !self.saw_const_match_lint.get() { self.saw_const_match_lint.set(true); - let msg = "function pointers and unsized pointers in patterns behave \ - unpredictably and should not be relied upon. \ - See https://github.com/rust-lang/rust/issues/70861 for details."; - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( lint::builtin::POINTER_STRUCTURAL_MATCH, id, span, - msg, - |lint| lint, + PointerPattern ); } PatKind::Constant { value: cv } } _ => { self.saw_const_match_error.set(true); - let msg = format!("`{}` cannot be used in patterns", cv.ty()); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + let err = InvalidPattern { span, non_sm_ty: cv.ty() }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } }; @@ -576,21 +494,17 @@ impl<'tcx> ConstToPat<'tcx> { && mir_structural_match_violation // FIXME(#73448): Find a way to bring const qualification into parity with // `search_for_structural_match_violation` and then remove this condition. - && self.search_for_structural_match_violation(cv.ty()).is_some() - { - self.saw_const_match_lint.set(true); + // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we // could get `Option<NonStructEq>`, even though `Option` is annotated with derive. - let msg = self.search_for_structural_match_violation(cv.ty()).unwrap().replace( - "in a pattern,", - "in a pattern, the constant's initializer must be trivial or", - ); - tcx.struct_span_lint_hir( + && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) + { + self.saw_const_match_lint.set(true); + tcx.emit_spanned_lint( lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH, id, span, - msg, - |lint| lint, + NontrivialStructuralMatch {non_sm_ty} ); } diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index a95349d7670..17b3c475f83 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -67,6 +67,7 @@ use self::SliceKind::*; use super::compare_const_vals; use super::usefulness::{MatchCheckCtxt, PatCtxt}; +use crate::errors::{Overlap, OverlappingRangeEndpoints}; /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { @@ -96,7 +97,7 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { /// `IntRange` is never used to encode an empty range or a "range" that wraps /// around the (offset) space: i.e., `range.lo <= range.hi`. #[derive(Clone, PartialEq, Eq)] -pub(super) struct IntRange { +pub(crate) struct IntRange { range: RangeInclusive<u128>, /// Keeps the bias used for encoding the range. It depends on the type of the range and /// possibly the pointer size of the current architecture. The algorithm ensures we never @@ -284,32 +285,21 @@ impl IntRange { return; } - let overlaps: Vec<_> = pats + let overlap: Vec<_> = pats .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span()))) .filter(|(range, _)| self.suspicious_intersection(range)) - .map(|(range, span)| (self.intersection(&range).unwrap(), span)) + .map(|(range, span)| Overlap { + range: self.intersection(&range).unwrap().to_pat(pcx.cx.tcx, pcx.ty), + span, + }) .collect(); - if !overlaps.is_empty() { - pcx.cx.tcx.struct_span_lint_hir( + if !overlap.is_empty() { + pcx.cx.tcx.emit_spanned_lint( lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, hir_id, pcx.span, - "multiple patterns overlap on their endpoints", - |lint| { - for (int_range, span) in overlaps { - lint.span_label( - span, - &format!( - "this range overlaps on `{}`...", - int_range.to_pat(pcx.cx.tcx, pcx.ty) - ), - ); - } - lint.span_label(pcx.span, "... with this range"); - lint.note("you likely meant to write mutually exclusive ranges"); - lint - }, + OverlappingRangeEndpoints { overlap, range: pcx.span }, ); } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 2c775b39718..3a6ef87c9c6 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -2,7 +2,7 @@ mod check_match; mod const_to_pat; -mod deconstruct_pat; +pub(crate) mod deconstruct_pat; mod usefulness; pub(crate) use self::check_match::check_match; @@ -129,10 +129,20 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { hi: mir::ConstantKind<'tcx>, end: RangeEnd, span: Span, + lo_expr: Option<&hir::Expr<'tcx>>, + hi_expr: Option<&hir::Expr<'tcx>>, ) -> PatKind<'tcx> { assert_eq!(lo.ty(), ty); assert_eq!(hi.ty(), ty); let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env); + let max = || { + self.tcx + .layout_of(self.param_env.with_reveal_all_normalized(self.tcx).and(ty)) + .ok() + .unwrap() + .size + .unsigned_int_max() + }; match (end, cmp) { // `x..y` where `x < y`. // Non-empty because the range includes at least `x`. @@ -141,7 +151,27 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } // `x..y` where `x >= y`. The range is empty => error. (RangeEnd::Excluded, _) => { - self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span }); + let mut lower_overflow = false; + let mut higher_overflow = false; + if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr + && let rustc_ast::ast::LitKind::Int(val, _) = lit.node + { + if lo.eval_bits(self.tcx, self.param_env, ty) != val { + lower_overflow = true; + self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }); + } + } + if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr + && let rustc_ast::ast::LitKind::Int(val, _) = lit.node + { + if hi.eval_bits(self.tcx, self.param_env, ty) != val { + higher_overflow = true; + self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }); + } + } + if !lower_overflow && !higher_overflow { + self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span }); + } PatKind::Wild } // `x..=y` where `x == y`. @@ -152,10 +182,34 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } // `x..=y` where `x > y` hence the range is empty => error. (RangeEnd::Included, _) => { - self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper { - span, - teach: if self.tcx.sess.teach(&error_code!(E0030)) { Some(()) } else { None }, - }); + let mut lower_overflow = false; + let mut higher_overflow = false; + if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = lo_expr + && let rustc_ast::ast::LitKind::Int(val, _) = lit.node + { + if lo.eval_bits(self.tcx, self.param_env, ty) != val { + lower_overflow = true; + self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }); + } + } + if let Some(hir::Expr { kind: hir::ExprKind::Lit(lit), .. }) = hi_expr + && let rustc_ast::ast::LitKind::Int(val, _) = lit.node + { + if hi.eval_bits(self.tcx, self.param_env, ty) != val { + higher_overflow = true; + self.tcx.sess.emit_err(LiteralOutOfRange { span: lit.span, ty, max: max() }); + } + } + if !lower_overflow && !higher_overflow { + self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper { + span, + teach: if self.tcx.sess.teach(&error_code!(E0030)) { + Some(()) + } else { + None + }, + }); + } PatKind::Wild } } @@ -201,7 +255,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let (lp, hp) = (lo.as_ref().map(|(x, _)| x), hi.as_ref().map(|(x, _)| x)); let mut kind = match self.normalize_range_pattern_ends(ty, lp, hp) { - Some((lc, hc)) => self.lower_pattern_range(ty, lc, hc, end, lo_span), + Some((lc, hc)) => { + self.lower_pattern_range(ty, lc, hc, end, lo_span, lo_expr, hi_expr) + } None => { let msg = &format!( "found bad range pattern `{:?}` outside of error recovery", diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 8f80cb95e58..be66d0d4765 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -291,9 +291,8 @@ use self::ArmType::*; use self::Usefulness::*; - -use super::check_match::{joined_uncovered_patterns, pattern_not_covered_label}; use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard}; +use crate::errors::{NonExhaustiveOmittedPattern, Uncovered}; use rustc_data_structures::captures::Captures; @@ -743,31 +742,6 @@ impl<'p, 'tcx> Witness<'p, 'tcx> { } } -/// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` -/// is not exhaustive enough. -/// -/// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. -fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - scrut_ty: Ty<'tcx>, - sp: Span, - hir_id: HirId, - witnesses: Vec<DeconstructedPat<'p, 'tcx>>, -) { - cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, "some variants are not matched explicitly", |lint| { - let joined_patterns = joined_uncovered_patterns(cx, &witnesses); - lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); - lint.help( - "ensure that all variants are matched explicitly by adding the suggested match arms", - ); - lint.note(&format!( - "the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", - scrut_ty, - )); - lint - }); -} - /// Algorithm from <http://moscova.inria.fr/~maranget/papers/warn/index.html>. /// The algorithm from the paper has been modified to correctly handle empty /// types. The changes are: @@ -913,7 +887,19 @@ fn is_useful<'p, 'tcx>( .collect::<Vec<_>>() }; - lint_non_exhaustive_omitted_patterns(pcx.cx, pcx.ty, pcx.span, hir_id, patterns); + // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` + // is not exhaustive enough. + // + // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. + cx.tcx.emit_spanned_lint( + NON_EXHAUSTIVE_OMITTED_PATTERNS, + hir_id, + pcx.span, + NonExhaustiveOmittedPattern { + scrut_ty: pcx.ty, + uncovered: Uncovered::new(pcx.span, pcx.cx, patterns), + }, + ); } ret.extend(usefulness); diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 5ff6b9e7e69..077a21fc8af 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -287,7 +287,7 @@ impl Direction for Backward { | mir::TerminatorKind::InlineAsm { cleanup: Some(unwind), .. } if unwind == bb => { - if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) { + if dead_unwinds.map_or(true, |dead| !dead.contains(pred)) { propagate(pred, exit_state); } } diff --git a/compiler/rustc_mir_dataflow/src/framework/fmt.rs b/compiler/rustc_mir_dataflow/src/framework/fmt.rs index 209e6f7ac9f..490be166a91 100644 --- a/compiler/rustc_mir_dataflow/src/framework/fmt.rs +++ b/compiler/rustc_mir_dataflow/src/framework/fmt.rs @@ -143,7 +143,7 @@ where ", " }; - write!(f, "{}", delim)?; + write!(f, "{delim}")?; idx.fmt_with(ctxt, f)?; first = false; } @@ -164,7 +164,7 @@ where ", " }; - write!(f, "{}", delim)?; + write!(f, "{delim}")?; idx.fmt_with(ctxt, f)?; first = false; } diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index c9d5601f207..96c42894b69 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -71,7 +71,7 @@ where fn graph_id(&self) -> dot::Id<'_> { let name = graphviz_safe_def_name(self.body.source.def_id()); - dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap() + dot::Id::new(format!("graph_for_def_id_{name}")).unwrap() } fn node_id(&self, n: &Self::Node) -> dot::Id<'_> { @@ -190,7 +190,7 @@ where " cellpadding=\"3\"", " sides=\"rb\"", ); - write!(w, r#"<table{fmt}>"#, fmt = table_fmt)?; + write!(w, r#"<table{table_fmt}>"#)?; // A + B: Block header match self.style { @@ -372,7 +372,7 @@ where write!(w, concat!("<tr>", r#"<td colspan="2" {fmt}>MIR</td>"#,), fmt = fmt,)?; for name in state_column_names { - write!(w, "<td {fmt}>{name}</td>", fmt = fmt, name = name)?; + write!(w, "<td {fmt}>{name}</td>")?; } write!(w, "</tr>") @@ -394,18 +394,18 @@ where }; for (i, statement) in body[block].statements.iter().enumerate() { - let statement_str = format!("{:?}", statement); - let index_str = format!("{}", i); + let statement_str = format!("{statement:?}"); + let index_str = format!("{i}"); let after = next_in_dataflow_order(&mut afters); let before = befores.as_mut().map(next_in_dataflow_order); self.write_row(w, &index_str, &statement_str, |_this, w, fmt| { if let Some(before) = before { - write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = before)?; + write!(w, r#"<td {fmt} align="left">{before}</td>"#)?; } - write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = after) + write!(w, r#"<td {fmt} align="left">{after}</td>"#) })?; } @@ -421,10 +421,10 @@ where self.write_row(w, "T", &terminator_str, |_this, w, fmt| { if let Some(before) = before { - write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = before)?; + write!(w, r#"<td {fmt} align="left">{before}</td>"#)?; } - write!(w, r#"<td {fmt} align="left">{diff}</td>"#, fmt = fmt, diff = after) + write!(w, r#"<td {fmt} align="left">{after}</td>"#) }) } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 9b053985bed..5f22a418de8 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -129,13 +129,13 @@ impl<'tcx> fmt::Debug for MovePath<'tcx> { fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result { write!(w, "MovePath {{")?; if let Some(parent) = self.parent { - write!(w, " parent: {:?},", parent)?; + write!(w, " parent: {parent:?},")?; } if let Some(first_child) = self.first_child { - write!(w, " first_child: {:?},", first_child)?; + write!(w, " first_child: {first_child:?},")?; } if let Some(next_sibling) = self.next_sibling { - write!(w, " next_sibling: {:?}", next_sibling)?; + write!(w, " next_sibling: {next_sibling:?}")?; } write!(w, " place: {:?} }}", self.place) } diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index fe5ee4011ab..0522c657939 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -920,7 +920,7 @@ fn debug_with_context<V: Debug + Eq>( ) -> std::fmt::Result { for (local, place) in map.locals.iter_enumerated() { if let Some(place) = place { - debug_with_context_rec(*place, &format!("{:?}", local), new, old, map, f)?; + debug_with_context_rec(*place, &format!("{local:?}"), new, old, map, f)?; } } Ok(()) diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index e384cfe1659..5c45abc5a17 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -655,11 +655,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } - if self.tcx.sess.mir_opt_level() >= 4 { - self.eval_rvalue_with_identities(rvalue, place) - } else { - self.use_ecx(|this| this.ecx.eval_rvalue_into_place(rvalue, place)) - } + self.eval_rvalue_with_identities(rvalue, place) } // Attempt to use algebraic identities to eliminate constant expressions diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 42c580c63f0..09546330cec 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -71,7 +71,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS bbs[block].statements[statement_index].make_nop(); } - crate::simplify::SimplifyLocals.run_pass(tcx, body) + crate::simplify::simplify_locals(body, tcx) } pub struct DeadStoreElimination; diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index ae79c2290f6..16b8a901f36 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -524,11 +524,8 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { /// Returns the sequence of passes that do the initial cleanup of runtime MIR. fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let passes: &[&dyn MirPass<'tcx>] = &[ - &elaborate_box_derefs::ElaborateBoxDerefs, - &lower_intrinsics::LowerIntrinsics, - &simplify::SimplifyCfg::new("elaborate-drops"), - ]; + let passes: &[&dyn MirPass<'tcx>] = + &[&lower_intrinsics::LowerIntrinsics, &simplify::SimplifyCfg::new("elaborate-drops")]; pm::run_passes(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::PostCleanup))); } @@ -560,6 +557,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &multiple_return_terminators::MultipleReturnTerminators, &instcombine::InstCombine, &separate_const_switch::SeparateConstSwitch, + &simplify::SimplifyLocals::new("before-const-prop"), // // FIXME(#70073): This pass is responsible for both optimization as well as some lints. &const_prop::ConstProp, @@ -578,7 +576,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &o1(remove_noop_landing_pads::RemoveNoopLandingPads), &o1(simplify::SimplifyCfg::new("final")), &nrvo::RenameReturnPlace, - &simplify::SimplifyLocals, + &simplify::SimplifyLocals::new("final"), &multiple_return_terminators::MultipleReturnTerminators, &deduplicate_blocks::DeduplicateBlocks, // Some cleanup necessary at least for LLVM and potentially other codegen backends. diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 8212a7b523b..8f6abe7a912 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -379,9 +379,21 @@ fn save_unreachable_coverage( )); } -pub struct SimplifyLocals; +pub struct SimplifyLocals { + label: String, +} + +impl SimplifyLocals { + pub fn new(label: &str) -> SimplifyLocals { + SimplifyLocals { label: format!("SimplifyLocals-{}", label) } + } +} impl<'tcx> MirPass<'tcx> for SimplifyLocals { + fn name(&self) -> &str { + &self.label + } + fn is_enabled(&self, sess: &rustc_session::Session) -> bool { sess.mir_opt_level() > 0 } @@ -557,6 +569,7 @@ fn remove_unused_definitions(used_locals: &mut UsedLocals, body: &mut Body<'_>) StatementKind::SetDiscriminant { ref place, .. } | StatementKind::Deinit(ref place) => used_locals.is_used(place.local), + StatementKind::Nop => false, _ => true, }; diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index 06deca2fffb..d4b1cfe4337 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -87,7 +87,7 @@ where // unless otherwise is unreachable, in which case deleting a normal branch causes it to be merged with // the otherwise, keeping its unreachable. // This looses information about reachability causing worse codegen. - // For example (see src/test/codegen/match-optimizes-away.rs) + // For example (see tests/codegen/match-optimizes-away.rs) // // pub enum Two { A, B } // pub fn identity(x: Two) -> Two { diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 6ee5330b63f..c8af10576b4 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" [lib] [dependencies] +serde = "1" +serde_json = "1" smallvec = { version = "1.8.1", features = [ "union", "may_dangle" ] } tracing = "0.1" rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 59cc500a99d..b573df43250 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -526,7 +526,7 @@ fn collect_items_rec<'tcx>( let formatted_item = with_no_trimmed_paths!(starting_point.node.to_string()); tcx.sess.span_note_without_error( starting_point.span, - &format!("the above error was encountered while instantiating `{}`", formatted_item), + &format!("the above error was encountered while instantiating `{formatted_item}`"), ); } inlining_map.lock_mut().record_accesses(starting_point.node, &neighbors.items); diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index f15cf54718e..5233cfb2120 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -32,13 +32,13 @@ pub struct TypeLengthLimit { pub type_length: usize, } -pub struct UnusedGenericParams { +pub struct UnusedGenericParamsHint { pub span: Span, pub param_spans: Vec<Span>, pub param_names: Vec<String>, } -impl IntoDiagnostic<'_> for UnusedGenericParams { +impl IntoDiagnostic<'_> for UnusedGenericParamsHint { #[track_caller] fn into_diagnostic( self, @@ -50,7 +50,7 @@ impl IntoDiagnostic<'_> for UnusedGenericParams { // FIXME: I can figure out how to do a label with a fluent string with a fixed message, // or a label with a dynamic value in a hard-coded string, but I haven't figured out // how to combine the two. 😢 - diag.span_label(span, format!("generic parameter `{}` is unused", name)); + diag.span_label(span, format!("generic parameter `{name}` is unused")); } diag } diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs index 4f25fc71314..fd6bcad1898 100644 --- a/compiler/rustc_monomorphize/src/partitioning/mod.rs +++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs @@ -102,14 +102,14 @@ use std::path::{Path, PathBuf}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync; -use rustc_hir::def_id::DefIdSet; +use rustc_hir::def_id::{DefIdSet, LOCAL_CRATE}; use rustc_middle::mir; use rustc_middle::mir::mono::MonoItem; use rustc_middle::mir::mono::{CodegenUnit, Linkage}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::query::Providers; use rustc_middle::ty::TyCtxt; -use rustc_session::config::SwitchWithOptPath; +use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath}; use rustc_span::symbol::Symbol; use crate::collector::InliningMap; @@ -285,7 +285,7 @@ where use std::fmt::Write; let s = &mut String::new(); - let _ = writeln!(s, "{}", label); + let _ = writeln!(s, "{label}"); for cgu in cgus { let _ = writeln!(s, "CodegenUnit {} estimated size {} :", cgu.name(), cgu.size_estimate()); @@ -355,9 +355,8 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co } else { if mode_string != "lazy" { let message = format!( - "Unknown codegen-item collection mode '{}'. \ - Falling back to 'lazy' mode.", - mode_string + "Unknown codegen-item collection mode '{mode_string}'. \ + Falling back to 'lazy' mode." ); tcx.sess.warn(&message); } @@ -417,7 +416,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co // Output monomorphization stats per def_id if let SwitchWithOptPath::Enabled(ref path) = tcx.sess.opts.unstable_opts.dump_mono_stats { if let Err(err) = - dump_mono_items_stats(tcx, &codegen_units, path, tcx.sess.opts.crate_name.as_deref()) + dump_mono_items_stats(tcx, &codegen_units, path, tcx.crate_name(LOCAL_CRATE)) { tcx.sess.emit_fatal(CouldntDumpMonoStats { error: err.to_string() }); } @@ -470,7 +469,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co item_keys.sort(); for item in item_keys { - println!("MONO_ITEM {}", item); + println!("MONO_ITEM {item}"); } } @@ -483,7 +482,7 @@ fn dump_mono_items_stats<'tcx>( tcx: TyCtxt<'tcx>, codegen_units: &[CodegenUnit<'tcx>], output_directory: &Option<PathBuf>, - crate_name: Option<&str>, + crate_name: Symbol, ) -> Result<(), Box<dyn std::error::Error>> { let output_directory = if let Some(ref directory) = output_directory { fs::create_dir_all(directory)?; @@ -492,9 +491,11 @@ fn dump_mono_items_stats<'tcx>( Path::new(".") }; - let filename = format!("{}.mono_items.md", crate_name.unwrap_or("unknown-crate")); + let format = tcx.sess.opts.unstable_opts.dump_mono_stats_format; + let ext = format.extension(); + let filename = format!("{crate_name}.mono_items.{ext}"); let output_path = output_directory.join(&filename); - let file = File::create(output_path)?; + let file = File::create(&output_path)?; let mut file = BufWriter::new(file); // Gather instantiated mono items grouped by def_id @@ -508,30 +509,44 @@ fn dump_mono_items_stats<'tcx>( } } + #[derive(serde::Serialize)] + struct MonoItem { + name: String, + instantiation_count: usize, + size_estimate: usize, + total_estimate: usize, + } + // Output stats sorted by total instantiated size, from heaviest to lightest let mut stats: Vec<_> = items_per_def_id .into_iter() .map(|(def_id, items)| { + let name = with_no_trimmed_paths!(tcx.def_path_str(def_id)); let instantiation_count = items.len(); let size_estimate = items[0].size_estimate(tcx); let total_estimate = instantiation_count * size_estimate; - (def_id, instantiation_count, size_estimate, total_estimate) + MonoItem { name, instantiation_count, size_estimate, total_estimate } }) .collect(); - stats.sort_unstable_by_key(|(_, _, _, total_estimate)| cmp::Reverse(*total_estimate)); + stats.sort_unstable_by_key(|item| cmp::Reverse(item.total_estimate)); if !stats.is_empty() { - writeln!( - file, - "| Item | Instantiation count | Estimated Cost Per Instantiation | Total Estimated Cost |" - )?; - writeln!(file, "| --- | ---: | ---: | ---: |")?; - for (def_id, instantiation_count, size_estimate, total_estimate) in stats { - let item = with_no_trimmed_paths!(tcx.def_path_str(def_id)); - writeln!( - file, - "| {item} | {instantiation_count} | {size_estimate} | {total_estimate} |" - )?; + match format { + DumpMonoStatsFormat::Json => serde_json::to_writer(file, &stats)?, + DumpMonoStatsFormat::Markdown => { + writeln!( + file, + "| Item | Instantiation count | Estimated Cost Per Instantiation | Total Estimated Cost |" + )?; + writeln!(file, "| --- | ---: | ---: | ---: |")?; + + for MonoItem { name, instantiation_count, size_estimate, total_estimate } in stats { + writeln!( + file, + "| `{name}` | {instantiation_count} | {size_estimate} | {total_estimate} |" + )?; + } + } } } @@ -580,6 +595,6 @@ pub fn provide(providers: &mut Providers) { let (_, all) = tcx.collect_and_partition_mono_items(()); all.iter() .find(|cgu| cgu.name() == name) - .unwrap_or_else(|| panic!("failed to find cgu with name {:?}", name)) + .unwrap_or_else(|| panic!("failed to find cgu with name {name:?}")) }; } diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs index 703ed09a254..c8fc69eb856 100644 --- a/compiler/rustc_monomorphize/src/polymorphize.rs +++ b/compiler/rustc_monomorphize/src/polymorphize.rs @@ -6,7 +6,6 @@ //! for their size, offset of a field, etc.). use rustc_hir::{def::DefKind, def_id::DefId, ConstContext}; -use rustc_index::bit_set::FiniteBitSet; use rustc_middle::mir::{ self, visit::{TyContext, Visitor}, @@ -17,12 +16,12 @@ use rustc_middle::ty::{ query::Providers, subst::SubstsRef, visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, - Const, Ty, TyCtxt, + Const, Ty, TyCtxt, UnusedGenericParams, }; use rustc_span::symbol::sym; use std::ops::ControlFlow; -use crate::errors::UnusedGenericParams; +use crate::errors::UnusedGenericParamsHint; /// Provide implementations of queries relating to polymorphization analysis. pub fn provide(providers: &mut Providers) { @@ -36,16 +35,16 @@ pub fn provide(providers: &mut Providers) { fn unused_generic_params<'tcx>( tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>, -) -> FiniteBitSet<u32> { +) -> UnusedGenericParams { if !tcx.sess.opts.unstable_opts.polymorphize { // If polymorphization disabled, then all parameters are used. - return FiniteBitSet::new_empty(); + return UnusedGenericParams::new_all_used(); } let def_id = instance.def_id(); // Exit early if this instance should not be polymorphized. if !should_polymorphize(tcx, def_id, instance) { - return FiniteBitSet::new_empty(); + return UnusedGenericParams::new_all_used(); } let generics = tcx.generics_of(def_id); @@ -53,14 +52,13 @@ fn unused_generic_params<'tcx>( // Exit early when there are no parameters to be unused. if generics.count() == 0 { - return FiniteBitSet::new_empty(); + return UnusedGenericParams::new_all_used(); } // Create a bitset with N rightmost ones for each parameter. let generics_count: u32 = generics.count().try_into().expect("more generic parameters than can fit into a `u32`"); - let mut unused_parameters = FiniteBitSet::<u32>::new_empty(); - unused_parameters.set_range(0..generics_count); + let mut unused_parameters = UnusedGenericParams::new_all_unused(generics_count); debug!(?unused_parameters, "(start)"); mark_used_by_default_parameters(tcx, def_id, generics, &mut unused_parameters); @@ -78,7 +76,7 @@ fn unused_generic_params<'tcx>( debug!(?unused_parameters, "(end)"); // Emit errors for debugging and testing if enabled. - if !unused_parameters.is_empty() { + if !unused_parameters.all_used() { emit_unused_generic_params_error(tcx, def_id, generics, &unused_parameters); } @@ -136,13 +134,13 @@ fn mark_used_by_default_parameters<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, generics: &'tcx ty::Generics, - unused_parameters: &mut FiniteBitSet<u32>, + unused_parameters: &mut UnusedGenericParams, ) { match tcx.def_kind(def_id) { DefKind::Closure | DefKind::Generator => { for param in &generics.params { debug!(?param, "(closure/gen)"); - unused_parameters.clear(param.index); + unused_parameters.mark_used(param.index); } } DefKind::Mod @@ -178,7 +176,7 @@ fn mark_used_by_default_parameters<'tcx>( for param in &generics.params { debug!(?param, "(other)"); if let ty::GenericParamDefKind::Lifetime = param.kind { - unused_parameters.clear(param.index); + unused_parameters.mark_used(param.index); } } } @@ -196,7 +194,7 @@ fn emit_unused_generic_params_error<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, generics: &'tcx ty::Generics, - unused_parameters: &FiniteBitSet<u32>, + unused_parameters: &UnusedGenericParams, ) { let base_def_id = tcx.typeck_root_def_id(def_id); if !tcx.has_attr(base_def_id, sym::rustc_polymorphize_error) { @@ -213,7 +211,7 @@ fn emit_unused_generic_params_error<'tcx>( let mut next_generics = Some(generics); while let Some(generics) = next_generics { for param in &generics.params { - if unused_parameters.contains(param.index).unwrap_or(false) { + if unused_parameters.is_unused(param.index) { debug!(?param); let def_span = tcx.def_span(param.def_id); param_spans.push(def_span); @@ -224,14 +222,14 @@ fn emit_unused_generic_params_error<'tcx>( next_generics = generics.parent.map(|did| tcx.generics_of(did)); } - tcx.sess.emit_err(UnusedGenericParams { span: fn_span, param_spans, param_names }); + tcx.sess.emit_err(UnusedGenericParamsHint { span: fn_span, param_spans, param_names }); } /// Visitor used to aggregate generic parameter uses. struct MarkUsedGenericParams<'a, 'tcx> { tcx: TyCtxt<'tcx>, def_id: DefId, - unused_parameters: &'a mut FiniteBitSet<u32>, + unused_parameters: &'a mut UnusedGenericParams, } impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> { @@ -244,7 +242,7 @@ impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> { debug!(?self.unused_parameters, ?unused); for (i, arg) in substs.iter().enumerate() { let i = i.try_into().unwrap(); - if !unused.contains(i).unwrap_or(false) { + if unused.is_used(i) { arg.visit_with(self); } } @@ -308,7 +306,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { match c.kind() { ty::ConstKind::Param(param) => { debug!(?param); - self.unused_parameters.clear(param.index); + self.unused_parameters.mark_used(param.index); ControlFlow::CONTINUE } ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs }) @@ -342,55 +340,10 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { } ty::Param(param) => { debug!(?param); - self.unused_parameters.clear(param.index); + self.unused_parameters.mark_used(param.index); ControlFlow::CONTINUE } _ => ty.super_visit_with(self), } } } - -/// Visitor used to check if a generic parameter is used. -struct HasUsedGenericParams<'a> { - unused_parameters: &'a FiniteBitSet<u32>, -} - -impl<'a, 'tcx> TypeVisitor<'tcx> for HasUsedGenericParams<'a> { - type BreakTy = (); - - #[instrument(level = "debug", skip(self))] - fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> { - if !c.has_non_region_param() { - return ControlFlow::CONTINUE; - } - - match c.kind() { - ty::ConstKind::Param(param) => { - if self.unused_parameters.contains(param.index).unwrap_or(false) { - ControlFlow::CONTINUE - } else { - ControlFlow::BREAK - } - } - _ => c.super_visit_with(self), - } - } - - #[instrument(level = "debug", skip(self))] - fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - if !ty.has_non_region_param() { - return ControlFlow::CONTINUE; - } - - match ty.kind() { - ty::Param(param) => { - if self.unused_parameters.contains(param.index).unwrap_or(false) { - ControlFlow::CONTINUE - } else { - ControlFlow::BREAK - } - } - _ => ty.super_visit_with(self), - } - } -} diff --git a/compiler/rustc_monomorphize/src/util.rs b/compiler/rustc_monomorphize/src/util.rs index 6a4d2df1ead..33e1f6ce342 100644 --- a/compiler/rustc_monomorphize/src/util.rs +++ b/compiler/rustc_monomorphize/src/util.rs @@ -40,12 +40,12 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In let new_size = tcx .layout_of(param_env.and(after_feature_tys)) .map(|l| format!("{:?}", l.size.bytes())) - .unwrap_or_else(|e| format!("Failed {:?}", e)); + .unwrap_or_else(|e| format!("Failed {e:?}")); let old_size = tcx .layout_of(param_env.and(before_feature_tys)) .map(|l| format!("{:?}", l.size.bytes())) - .unwrap_or_else(|e| format!("Failed {:?}", e)); + .unwrap_or_else(|e| format!("Failed {e:?}")); let closure_span = tcx.def_span(closure_def_id); let src_file = tcx.sess.source_map().span_to_filename(closure_span); @@ -54,7 +54,7 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In .source_map() .span_to_lines(closure_span) .map(|l| format!("{:?} {:?}", l.lines.first(), l.lines.last())) - .unwrap_or_else(|e| format!("{:?}", e)); + .unwrap_or_else(|e| format!("{e:?}")); if let Err(e) = writeln!( file, @@ -64,7 +64,7 @@ pub(crate) fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: In src_file.prefer_local(), line_nos ) { - eprintln!("Error writing to file {}", e) + eprintln!("Error writing to file {e}") } } } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 574591529f3..06b970ad979 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -971,6 +971,24 @@ pub(crate) struct StructLiteralBodyWithoutPathSugg { } #[derive(Diagnostic)] +#[diag(parse_struct_literal_needing_parens)] +pub(crate) struct StructLiteralNeedingParens { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: StructLiteralNeedingParensSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(suggestion, applicability = "machine-applicable")] +pub(crate) struct StructLiteralNeedingParensSugg { + #[suggestion_part(code = "(")] + pub before: Span, + #[suggestion_part(code = ")")] + pub after: Span, +} + +#[derive(Diagnostic)] #[diag(parse_unmatched_angle_brackets)] pub(crate) struct UnmatchedAngleBrackets { #[primary_span] @@ -1237,3 +1255,27 @@ pub(crate) struct ExpectedFnPathFoundFnKeyword { #[suggestion(applicability = "machine-applicable", code = "Fn", style = "verbose")] pub fn_token_span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_where_clause_before_tuple_struct_body)] +pub(crate) struct WhereClauseBeforeTupleStructBody { + #[primary_span] + #[label] + pub span: Span, + #[label(name_label)] + pub name: Span, + #[label(body_label)] + pub body: Span, + #[subdiagnostic] + pub sugg: Option<WhereClauseBeforeTupleStructBodySugg>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(suggestion, applicability = "machine-applicable")] +pub(crate) struct WhereClauseBeforeTupleStructBodySugg { + #[suggestion_part(code = "{snippet}")] + pub left: Span, + pub snippet: String, + #[suggestion_part(code = "")] + pub right: Span, +} diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index b3231f55bc6..4c918c6702e 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -12,9 +12,10 @@ use crate::errors::{ IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, ParenthesesInForHead, ParenthesesInForHeadSugg, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath, - StructLiteralBodyWithoutPathSugg, SuggEscapeToUseAsIdentifier, SuggRemoveComma, - UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration, - UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, + StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg, + SuggEscapeToUseAsIdentifier, SuggRemoveComma, UnexpectedConstInGenericParam, + UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, + UseEqInstead, }; use crate::lexer::UnmatchedBrace; @@ -623,12 +624,15 @@ impl<'a> Parser<'a> { &mut self, lo: Span, s: BlockCheckMode, + maybe_struct_name: token::Token, + can_be_struct_literal: bool, ) -> Option<PResult<'a, P<Block>>> { if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) { // We might be having a struct literal where people forgot to include the path: // fn foo() -> Foo { // field: value, // } + info!(?maybe_struct_name, ?self.token); let mut snapshot = self.create_snapshot_for_diagnostic(); let path = Path { segments: ThinVec::new(), @@ -648,13 +652,6 @@ impl<'a> Parser<'a> { // field: value, // } } err.delay_as_bug(); - self.sess.emit_err(StructLiteralBodyWithoutPath { - span: expr.span, - sugg: StructLiteralBodyWithoutPathSugg { - before: expr.span.shrink_to_lo(), - after: expr.span.shrink_to_hi(), - }, - }); self.restore_snapshot(snapshot); let mut tail = self.mk_block( vec![self.mk_stmt_err(expr.span)], @@ -662,7 +659,25 @@ impl<'a> Parser<'a> { lo.to(self.prev_token.span), ); tail.could_be_bare_literal = true; - Ok(tail) + if maybe_struct_name.is_ident() && can_be_struct_literal { + // Account for `if Example { a: one(), }.is_pos() {}`. + Err(self.sess.create_err(StructLiteralNeedingParens { + span: maybe_struct_name.span.to(expr.span), + sugg: StructLiteralNeedingParensSugg { + before: maybe_struct_name.span.shrink_to_lo(), + after: expr.span.shrink_to_hi(), + }, + })) + } else { + self.sess.emit_err(StructLiteralBodyWithoutPath { + span: expr.span, + sugg: StructLiteralBodyWithoutPathSugg { + before: expr.span.shrink_to_lo(), + after: expr.span.shrink_to_hi(), + }, + }); + Ok(tail) + } } (Err(err), Ok(tail)) => { // We have a block tail that contains a somehow valid type ascription expr. @@ -1104,7 +1119,11 @@ impl<'a> Parser<'a> { return if token::ModSep == self.token.kind { // We have some certainty that this was a bad turbofish at this point. // `foo< bar >::` - err.suggest_turbofish = Some(op.span.shrink_to_lo()); + if let ExprKind::Binary(o, ..) = inner_op.kind && o.node == BinOpKind::Lt { + err.suggest_turbofish = Some(op.span.shrink_to_lo()); + } else { + err.help_turbofish = Some(()); + } let snapshot = self.create_snapshot_for_diagnostic(); self.bump(); // `::` @@ -1130,7 +1149,11 @@ impl<'a> Parser<'a> { } else if token::OpenDelim(Delimiter::Parenthesis) == self.token.kind { // We have high certainty that this was a bad turbofish at this point. // `foo< bar >(` - err.suggest_turbofish = Some(op.span.shrink_to_lo()); + if let ExprKind::Binary(o, ..) = inner_op.kind && o.node == BinOpKind::Lt { + err.suggest_turbofish = Some(op.span.shrink_to_lo()); + } else { + err.help_turbofish = Some(()); + } // Consume the fn call arguments. match self.consume_fn_args() { Err(()) => Err(err.into_diagnostic(&self.sess.span_diagnostic)), diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 1fc1ffd6cb6..dd2b03988c3 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1325,7 +1325,10 @@ impl<'a> Parser<'a> { self.parse_array_or_repeat_expr(Delimiter::Bracket) } else if self.check_path() { self.parse_path_start_expr() - } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) { + } else if self.check_keyword(kw::Move) + || self.check_keyword(kw::Static) + || self.check_const_closure() + { self.parse_closure_expr() } else if self.eat_keyword(kw::If) { self.parse_if_expr() @@ -1503,12 +1506,13 @@ impl<'a> Parser<'a> { prior_type_ascription: self.last_type_ascription, }); (lo.to(self.prev_token.span), ExprKind::MacCall(mac)) - } else if self.check(&token::OpenDelim(Delimiter::Brace)) && - let Some(expr) = self.maybe_parse_struct_expr(&qself, &path) { - if qself.is_some() { - self.sess.gated_spans.gate(sym::more_qualified_paths, path.span); - } - return expr; + } else if self.check(&token::OpenDelim(Delimiter::Brace)) + && let Some(expr) = self.maybe_parse_struct_expr(&qself, &path) + { + if qself.is_some() { + self.sess.gated_spans.gate(sym::more_qualified_paths, path.span); + } + return expr; } else { (path.span, ExprKind::Path(qself, path)) }; @@ -2038,7 +2042,7 @@ impl<'a> Parser<'a> { }); } - let (attrs, blk) = self.parse_block_common(lo, blk_mode)?; + let (attrs, blk) = self.parse_block_common(lo, blk_mode, true)?; Ok(self.mk_expr_with_attrs(blk.span, ExprKind::Block(blk, opt_label), attrs)) } @@ -2064,6 +2068,8 @@ impl<'a> Parser<'a> { ClosureBinder::NotPresent }; + let constness = self.parse_constness(Case::Sensitive); + let movability = if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; @@ -2110,6 +2116,7 @@ impl<'a> Parser<'a> { ExprKind::Closure(Box::new(ast::Closure { binder, capture_clause, + constness, asyncness, movability, fn_decl, diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index fa75670b2ed..8ba811715d8 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -1,11 +1,20 @@ +use crate::errors::{WhereClauseBeforeTupleStructBody, WhereClauseBeforeTupleStructBodySugg}; + use super::{ForceCollect, Parser, TrailingToken}; +use ast::token::Delimiter; use rustc_ast::token; use rustc_ast::{ self as ast, AttrVec, GenericBounds, GenericParam, GenericParamKind, TyKind, WhereClause, }; use rustc_errors::{Applicability, PResult}; -use rustc_span::symbol::kw; +use rustc_span::symbol::{kw, Ident}; +use rustc_span::Span; + +enum PredicateOrStructBody { + Predicate(ast::WherePredicate), + StructBody(Vec<ast::FieldDef>), +} impl<'a> Parser<'a> { /// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`. @@ -240,23 +249,39 @@ impl<'a> Parser<'a> { }) } - /// Parses an optional where-clause and places it in `generics`. + /// Parses an optional where-clause. /// /// ```ignore (only-for-syntax-highlight) /// where T : Trait<U, V> + 'b, 'a : 'b /// ``` pub(super) fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> { + self.parse_where_clause_common(None).map(|(clause, _)| clause) + } + + pub(super) fn parse_struct_where_clause( + &mut self, + struct_name: Ident, + body_insertion_point: Span, + ) -> PResult<'a, (WhereClause, Option<Vec<ast::FieldDef>>)> { + self.parse_where_clause_common(Some((struct_name, body_insertion_point))) + } + + fn parse_where_clause_common( + &mut self, + struct_: Option<(Ident, Span)>, + ) -> PResult<'a, (WhereClause, Option<Vec<ast::FieldDef>>)> { let mut where_clause = WhereClause { has_where_token: false, predicates: Vec::new(), span: self.prev_token.span.shrink_to_hi(), }; + let mut tuple_struct_body = None; if !self.eat_keyword(kw::Where) { - return Ok(where_clause); + return Ok((where_clause, None)); } where_clause.has_where_token = true; - let lo = self.prev_token.span; + let where_lo = self.prev_token.span; // We are considering adding generics to the `where` keyword as an alternative higher-rank // parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking @@ -272,7 +297,8 @@ impl<'a> Parser<'a> { } loop { - let lo = self.token.span; + let where_sp = where_lo.to(self.prev_token.span); + let pred_lo = self.token.span; if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { let lifetime = self.expect_lifetime(); // Bounds starting with a colon are mandatory, but possibly empty. @@ -280,13 +306,21 @@ impl<'a> Parser<'a> { let bounds = self.parse_lt_param_bounds(); where_clause.predicates.push(ast::WherePredicate::RegionPredicate( ast::WhereRegionPredicate { - span: lo.to(self.prev_token.span), + span: pred_lo.to(self.prev_token.span), lifetime, bounds, }, )); } else if self.check_type() { - where_clause.predicates.push(self.parse_ty_where_predicate()?); + match self.parse_ty_where_predicate_or_recover_tuple_struct_body( + struct_, pred_lo, where_sp, + )? { + PredicateOrStructBody::Predicate(pred) => where_clause.predicates.push(pred), + PredicateOrStructBody::StructBody(body) => { + tuple_struct_body = Some(body); + break; + } + } } else { break; } @@ -297,7 +331,7 @@ impl<'a> Parser<'a> { if self.eat_keyword_noexpect(kw::Where) { let msg = "cannot define duplicate `where` clauses on an item"; let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(lo, "previous `where` clause starts here"); + err.span_label(pred_lo, "previous `where` clause starts here"); err.span_suggestion_verbose( prev_token.shrink_to_hi().to(self.prev_token.span), "consider joining the two `where` clauses into one", @@ -310,8 +344,72 @@ impl<'a> Parser<'a> { } } - where_clause.span = lo.to(self.prev_token.span); - Ok(where_clause) + where_clause.span = where_lo.to(self.prev_token.span); + Ok((where_clause, tuple_struct_body)) + } + + fn parse_ty_where_predicate_or_recover_tuple_struct_body( + &mut self, + struct_: Option<(Ident, Span)>, + pred_lo: Span, + where_sp: Span, + ) -> PResult<'a, PredicateOrStructBody> { + let mut snapshot = None; + + if let Some(struct_) = struct_ + && self.may_recover() + && self.token.kind == token::OpenDelim(Delimiter::Parenthesis) + { + snapshot = Some((struct_, self.create_snapshot_for_diagnostic())); + }; + + match self.parse_ty_where_predicate() { + Ok(pred) => Ok(PredicateOrStructBody::Predicate(pred)), + Err(type_err) => { + let Some(((struct_name, body_insertion_point), mut snapshot)) = snapshot else { + return Err(type_err); + }; + + // Check if we might have encountered an out of place tuple struct body. + match snapshot.parse_tuple_struct_body() { + // Since we don't know the exact reason why we failed to parse the + // predicate (we might have stumbled upon something bogus like `(T): ?`), + // employ a simple heuristic to weed out some pathological cases: + // Look for a semicolon (strong indicator) or anything that might mark + // the end of the item (weak indicator) following the body. + Ok(body) + if matches!(snapshot.token.kind, token::Semi | token::Eof) + || snapshot.token.can_begin_item() => + { + type_err.cancel(); + + let body_sp = pred_lo.to(snapshot.prev_token.span); + let map = self.sess.source_map(); + + self.sess.emit_err(WhereClauseBeforeTupleStructBody { + span: where_sp, + name: struct_name.span, + body: body_sp, + sugg: map.span_to_snippet(body_sp).ok().map(|body| { + WhereClauseBeforeTupleStructBodySugg { + left: body_insertion_point.shrink_to_hi(), + snippet: body, + right: map.end_point(where_sp).to(body_sp), + } + }), + }); + + self.restore_snapshot(snapshot); + Ok(PredicateOrStructBody::StructBody(body)) + } + Ok(_) => Err(type_err), + Err(body_err) => { + body_err.cancel(); + Err(type_err) + } + } + } + } } fn parse_ty_where_predicate(&mut self) -> PResult<'a, ast::WherePredicate> { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index a958c294930..53680a82bdc 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1454,8 +1454,16 @@ impl<'a> Parser<'a> { // struct. let vdata = if self.token.is_keyword(kw::Where) { - generics.where_clause = self.parse_where_clause()?; - if self.eat(&token::Semi) { + let tuple_struct_body; + (generics.where_clause, tuple_struct_body) = + self.parse_struct_where_clause(class_name, generics.span)?; + + if let Some(body) = tuple_struct_body { + // If we see a misplaced tuple struct body: `struct Foo<T> where T: Copy, (T);` + let body = VariantData::Tuple(body, DUMMY_NODE_ID); + self.expect_semi()?; + body + } else if self.eat(&token::Semi) { // If we see a: `struct Foo<T> where T: Copy;` style decl. VariantData::Unit(DUMMY_NODE_ID) } else { @@ -1575,7 +1583,7 @@ impl<'a> Parser<'a> { Ok((fields, recovered)) } - fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<FieldDef>> { + pub(super) fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<FieldDef>> { // This is the case where we find `struct Foo<T>(T) where T: Copy;` // Unit like structs are handled in parse_item_struct function self.parse_paren_comma_seq(|p| { @@ -2214,7 +2222,8 @@ impl<'a> Parser<'a> { *sig_hi = self.prev_token.span; (AttrVec::new(), None) } else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() { - self.parse_inner_attrs_and_block().map(|(attrs, body)| (attrs, Some(body)))? + self.parse_block_common(self.token.span, BlockCheckMode::Default, false) + .map(|(attrs, body)| (attrs, Some(body)))? } else if self.token.kind == token::Eq { // Recover `fn foo() = $expr;`. self.bump(); // `=` diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 49d31981539..2fd2a4e5154 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -736,6 +736,16 @@ impl<'a> Parser<'a> { self.check_or_expected(self.token.can_begin_const_arg(), TokenType::Const) } + fn check_const_closure(&self) -> bool { + self.is_keyword_ahead(0, &[kw::Const]) + && self.look_ahead(1, |t| match &t.kind { + token::Ident(kw::Move | kw::Static | kw::Async, _) + | token::OrOr + | token::BinOp(token::Or) => true, + _ => false, + }) + } + fn check_inline_const(&self, dist: usize) -> bool { self.is_keyword_ahead(dist, &[kw::Const]) && self.look_ahead(dist + 1, |t| match &t.kind { diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 0daae457d30..1e5c2834960 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -498,7 +498,7 @@ impl<'a> Parser<'a> { /// Parses a block. Inner attributes are allowed. pub(super) fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (AttrVec, P<Block>)> { - self.parse_block_common(self.token.span, BlockCheckMode::Default) + self.parse_block_common(self.token.span, BlockCheckMode::Default, true) } /// Parses a block. Inner attributes are allowed. @@ -506,16 +506,23 @@ impl<'a> Parser<'a> { &mut self, lo: Span, blk_mode: BlockCheckMode, + can_be_struct_literal: bool, ) -> PResult<'a, (AttrVec, P<Block>)> { maybe_whole!(self, NtBlock, |x| (AttrVec::new(), x)); + let maybe_ident = self.prev_token.clone(); self.maybe_recover_unexpected_block_label(); if !self.eat(&token::OpenDelim(Delimiter::Brace)) { return self.error_block_no_opening_brace(); } let attrs = self.parse_inner_attributes()?; - let tail = match self.maybe_suggest_struct_literal(lo, blk_mode) { + let tail = match self.maybe_suggest_struct_literal( + lo, + blk_mode, + maybe_ident, + can_be_struct_literal, + ) { Some(tail) => tail?, None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?, }; diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 9f2aaca0acf..1eb227503f2 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -447,7 +447,7 @@ impl<'a> Parser<'a> { Some(pos) } else { let pos = self.to_span_index(pos); - let description = format!("expected `'}}'`, found `{:?}`", maybe); + let description = format!("expected `'}}'`, found `{maybe:?}`"); let label = "expected `}`".to_owned(); let (note, secondary_label) = if c == '}' { ( @@ -471,12 +471,12 @@ impl<'a> Parser<'a> { None } } else { - let description = format!("expected `{:?}` but string was terminated", c); + let description = format!("expected `{c:?}` but string was terminated"); // point at closing `"` let pos = self.input.len() - if self.append_newline { 1 } else { 0 }; let pos = self.to_span_index(pos); if c == '}' { - let label = format!("expected `{:?}`", c); + let label = format!("expected `{c:?}`"); let (note, secondary_label) = if c == '}' { ( Some( @@ -497,7 +497,7 @@ impl<'a> Parser<'a> { should_be_replaced_with_positional_argument: false, }); } else { - self.err(description, format!("expected `{:?}`", c), pos.to(pos)); + self.err(description, format!("expected `{c:?}`"), pos.to(pos)); } None } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index c59c06ac31e..f9f9799d3e4 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -82,6 +82,7 @@ impl CheckAttrVisitor<'_> { let attrs = self.tcx.hir().attrs(hir_id); for attr in attrs { let attr_is_valid = match attr.name_or_empty() { + sym::do_not_recommend => self.check_do_not_recommend(attr.span, target), sym::inline => self.check_inline(hir_id, attr, span, target), sym::no_coverage => self.check_no_coverage(hir_id, attr, span, target), sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target), @@ -241,6 +242,16 @@ impl CheckAttrVisitor<'_> { ); } + /// Checks if `#[do_not_recommend]` is applied on a trait impl. + fn check_do_not_recommend(&self, attr_span: Span, target: Target) -> bool { + if let Target::Impl = target { + true + } else { + self.tcx.sess.emit_err(errors::IncorrectDoNotRecommendLocation { span: attr_span }); + false + } + } + /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { match target { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index c6cd69add28..9c6519ea4bb 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -14,6 +14,13 @@ use rustc_span::{Span, Symbol, DUMMY_SP}; use crate::lang_items::Duplicate; +#[derive(Diagnostic)] +#[diag(passes_incorrect_do_not_recommend_location)] +pub struct IncorrectDoNotRecommendLocation { + #[primary_span] + pub span: Span, +} + #[derive(LintDiagnostic)] #[diag(passes_outer_crate_level_attr)] pub struct OuterCrateLevelAttr; diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 72d38aeac7a..564cb1baa69 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -915,7 +915,7 @@ impl<'tcx, 'a> TestReachabilityVisitor<'tcx, 'a> { if level != Level::Direct { error_msg.push_str(", "); } - error_msg.push_str(&format!("{:?}: {}", level, vis_str)); + error_msg.push_str(&format!("{level:?}: {vis_str}")); } } else { error_msg.push_str("not in the table"); @@ -1223,19 +1223,22 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { self.tcx.types.never, ); - for (trait_predicate, _, _) in bounds.trait_bounds { - if self.visit_trait(trait_predicate.skip_binder()).is_break() { - return; - } - } - - for (poly_predicate, _) in bounds.projection_bounds { - let pred = poly_predicate.skip_binder(); - let poly_pred_term = self.visit(pred.term); - if poly_pred_term.is_break() - || self.visit_projection_ty(pred.projection_ty).is_break() - { - return; + for (pred, _) in bounds.predicates() { + match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { + if self.visit_trait(trait_predicate.trait_ref).is_break() { + return; + } + } + ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) => { + let term = self.visit(proj_predicate.term); + if term.is_break() + || self.visit_projection_ty(proj_predicate.projection_ty).is_break() + { + return; + } + } + _ => {} } } } @@ -1752,7 +1755,7 @@ impl SearchInterfaceForPrivateItemsVisitor<'_> { // clauses that the compiler inferred. We only want to // consider the ones that the user wrote. This is important // for the inferred outlives rules; see - // `src/test/ui/rfc-2093-infer-outlives/privacy.rs`. + // `tests/ui/rfc-2093-infer-outlives/privacy.rs`. self.visit_predicates(self.tcx.explicit_predicates_of(self.item_def_id)); self } @@ -2141,7 +2144,7 @@ fn check_private_in_public(tcx: TyCtxt<'_>, (): ()) { if !old_error_set_ancestry.insert(id) { break; } - let parent = tcx.hir().get_parent_node(id); + let parent = tcx.hir().parent_id(id); if parent == id { break; } diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index b2111a1262a..46e77626479 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" edition = "2021" [lib] -doctest = false + [dependencies] measureme = "10.0.0" diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index 2bcfdab03c8..70c481fb0ee 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -787,7 +787,7 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for DefId { // which means that the definition with this hash is guaranteed to // still exist in the current compilation session. d.tcx.def_path_hash_to_def_id(def_path_hash, &mut || { - panic!("Failed to convert DefPathHash {:?}", def_path_hash) + panic!("Failed to convert DefPathHash {def_path_hash:?}") }) } } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 535445e70bc..6125ad4eff1 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -321,7 +321,7 @@ pub(crate) fn create_query_frame< ty::print::with_forced_impl_filename_line!(do_describe(tcx.tcx, key)) ); let description = - if tcx.sess.verbose() { format!("{} [{:?}]", description, name) } else { description }; + if tcx.sess.verbose() { format!("{description} [{name:?}]") } else { description }; let span = if kind == dep_graph::DepKind::def_span { // The `def_span` query is used to calculate `default_span`, // so exit to avoid infinite recursion. diff --git a/compiler/rustc_query_impl/src/profiling_support.rs b/compiler/rustc_query_impl/src/profiling_support.rs index 5f54bab9c31..4743170e9bf 100644 --- a/compiler/rustc_query_impl/src/profiling_support.rs +++ b/compiler/rustc_query_impl/src/profiling_support.rs @@ -111,7 +111,7 @@ impl<T: Debug> IntoSelfProfilingString for T { &self, builder: &mut QueryKeyStringBuilder<'_, '_>, ) -> StringId { - let s = format!("{:?}", self); + let s = format!("{self:?}"); builder.profiler.alloc_string(&s[..]) } } diff --git a/compiler/rustc_query_system/src/dep_graph/debug.rs b/compiler/rustc_query_system/src/dep_graph/debug.rs index f9f3169af69..c2c9600f555 100644 --- a/compiler/rustc_query_system/src/dep_graph/debug.rs +++ b/compiler/rustc_query_system/src/dep_graph/debug.rs @@ -29,7 +29,7 @@ impl DepNodeFilter { /// Tests whether `node` meets the filter, returning true if so. pub fn test<K: DepKind>(&self, node: &DepNode<K>) -> bool { - let debug_str = format!("{:?}", node); + let debug_str = format!("{node:?}"); self.text.split('&').map(|s| s.trim()).all(|f| debug_str.contains(f)) } } @@ -46,7 +46,7 @@ impl<K: DepKind> EdgeFilter<K> { pub fn new(test: &str) -> Result<EdgeFilter<K>, Box<dyn Error>> { let parts: Vec<_> = test.split("->").collect(); if parts.len() != 2 { - Err(format!("expected a filter like `a&b -> c&d`, not `{}`", test).into()) + Err(format!("expected a filter like `a&b -> c&d`, not `{test}`").into()) } else { Ok(EdgeFilter { source: DepNodeFilter::new(parts[0]), diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index d79c5816a9c..9e1ca6ab515 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -120,7 +120,7 @@ pub trait DepNodeParams<Tcx: DepContext>: fmt::Debug + Sized { } fn to_debug_str(&self, _: Tcx) -> String { - format!("{:?}", self) + format!("{self:?}") } /// This method tries to recover the query key from the given `DepNode`, diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 0b1ff5d709f..53c9da15737 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -316,10 +316,8 @@ impl<K: DepKind> DepGraph<K> { assert!( !self.dep_node_exists(&key), "forcing query with already existing `DepNode`\n\ - - query-key: {:?}\n\ - - dep-node: {:?}", - arg, - key + - query-key: {arg:?}\n\ + - dep-node: {key:?}" ); let task_deps = if cx.dep_context().is_eval_always(key.kind) { @@ -365,8 +363,7 @@ impl<K: DepKind> DepGraph<K> { debug_assert!( data.colors.get(prev_index).is_none(), "DepGraph::with_task() - Duplicate DepNodeColor \ - insertion for {:?}", - key + insertion for {key:?}" ); data.colors.insert(prev_index, color); @@ -447,7 +444,7 @@ impl<K: DepKind> DepGraph<K> { TaskDepsRef::Allow(deps) => deps.lock(), TaskDepsRef::Ignore => return, TaskDepsRef::Forbid => { - panic!("Illegal read of: {:?}", dep_node_index) + panic!("Illegal read of: {dep_node_index:?}") } }; let task_deps = &mut *task_deps; @@ -824,8 +821,7 @@ impl<K: DepKind> DepGraph<K> { debug_assert!( data.colors.get(prev_dep_node_index).is_none(), "DepGraph::try_mark_previous_green() - Duplicate DepNodeColor \ - insertion for {:?}", - dep_node + insertion for {dep_node:?}" ); if !side_effects.is_empty() { @@ -1164,7 +1160,7 @@ impl<K: DepKind> CurrentDepGraph<K> { if let Some(fingerprint) = fingerprint { if fingerprint == prev_graph.fingerprint_by_index(prev_index) { if print_status { - eprintln!("[task::green] {:?}", key); + eprintln!("[task::green] {key:?}"); } // This is a green node: it existed in the previous compilation, @@ -1186,7 +1182,7 @@ impl<K: DepKind> CurrentDepGraph<K> { (dep_node_index, Some((prev_index, DepNodeColor::Green(dep_node_index)))) } else { if print_status { - eprintln!("[task::red] {:?}", key); + eprintln!("[task::red] {key:?}"); } // This is a red node: it existed in the previous compilation, its query @@ -1209,7 +1205,7 @@ impl<K: DepKind> CurrentDepGraph<K> { } } else { if print_status { - eprintln!("[task::unknown] {:?}", key); + eprintln!("[task::unknown] {key:?}"); } // This is a red node, effectively: it existed in the previous compilation @@ -1234,7 +1230,7 @@ impl<K: DepKind> CurrentDepGraph<K> { } } else { if print_status { - eprintln!("[task::new] {:?}", key); + eprintln!("[task::new] {key:?}"); } let fingerprint = fingerprint.unwrap_or(Fingerprint::ZERO); diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index a918328d413..dfc1344f85c 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -270,17 +270,14 @@ impl<K: DepKind + Encodable<FileEncoder>> GraphEncoder<K> { eprintln!("[incremental]"); eprintln!("[incremental] DepGraph Statistics"); - eprintln!("{}", SEPARATOR); + eprintln!("{SEPARATOR}"); eprintln!("[incremental]"); eprintln!("[incremental] Total Node Count: {}", status.total_node_count); eprintln!("[incremental] Total Edge Count: {}", status.total_edge_count); if cfg!(debug_assertions) { - eprintln!("[incremental] Total Edge Reads: {}", total_read_count); - eprintln!( - "[incremental] Total Duplicate Edge Reads: {}", - total_duplicate_read_count - ); + eprintln!("[incremental] Total Edge Reads: {total_read_count}"); + eprintln!("[incremental] Total Duplicate Edge Reads: {total_duplicate_read_count}"); } eprintln!("[incremental]"); @@ -288,7 +285,7 @@ impl<K: DepKind + Encodable<FileEncoder>> GraphEncoder<K> { "[incremental] {:<36}| {:<17}| {:<12}| {:<17}|", "Node Kind", "Node Frequency", "Node Count", "Avg. Edge Count" ); - eprintln!("{}", SEPARATOR); + eprintln!("{SEPARATOR}"); for stat in stats { let node_kind_ratio = @@ -304,7 +301,7 @@ impl<K: DepKind + Encodable<FileEncoder>> GraphEncoder<K> { ); } - eprintln!("{}", SEPARATOR); + eprintln!("{SEPARATOR}"); eprintln!("[incremental]"); } } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index da1ac6a5fb2..b3b939eae88 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -549,8 +549,7 @@ where // can be forced from `DepNode`. debug_assert!( !qcx.dep_context().fingerprint_style(dep_node.kind).reconstructible(), - "missing on-disk cache entry for {:?}", - dep_node + "missing on-disk cache entry for {dep_node:?}" ); } @@ -589,8 +588,7 @@ where { assert!( tcx.dep_graph().is_green(dep_node), - "fingerprint for green query instance not loaded from cache: {:?}", - dep_node, + "fingerprint for green query instance not loaded from cache: {dep_node:?}", ); let new_hash = hash_result.map_or(Fingerprint::ZERO, |f| { @@ -669,16 +667,16 @@ fn incremental_verify_ich_failed(sess: &Session, dep_node: DebugArg<'_>, result: sess.emit_err(crate::error::Reentrant); } else { let run_cmd = if let Some(crate_name) = &sess.opts.crate_name { - format!("`cargo clean -p {}` or `cargo clean`", crate_name) + format!("`cargo clean -p {crate_name}` or `cargo clean`") } else { "`cargo clean`".to_string() }; sess.emit_err(crate::error::IncrementCompilation { run_cmd, - dep_node: format!("{:?}", dep_node), + dep_node: format!("{dep_node:?}"), }); - panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result); + panic!("Found unstable fingerprints for {dep_node:?}: {result:?}"); } INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.set(old_in_panic)); diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index cf635996268..b1b04c92a75 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -334,6 +334,15 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { self.r.field_names.insert(def_id, field_names); } + fn insert_field_visibilities_local(&mut self, def_id: DefId, vdata: &ast::VariantData) { + let field_vis = vdata + .fields() + .iter() + .map(|field| field.vis.span.until(field.ident.map_or(field.ty.span, |i| i.span))) + .collect(); + self.r.field_visibility_spans.insert(def_id, field_vis); + } + fn insert_field_names_extern(&mut self, def_id: DefId) { let field_names = self.r.cstore().struct_field_names_untracked(def_id, self.r.session).collect(); @@ -514,7 +523,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { ModuleKind::Block => unreachable!(), }; // HACK(eddyb) unclear how good this is, but keeping `$crate` - // in `source` breaks `src/test/ui/imports/import-crate-var.rs`, + // in `source` breaks `tests/ui/imports/import-crate-var.rs`, // while the current crate doesn't have a valid `crate_name`. if crate_name != kw::Empty { // `crate_name` should not be interpreted as relative. @@ -737,6 +746,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // Record field names for error reporting. self.insert_field_names_local(def_id, vdata); + self.insert_field_visibilities_local(def_id, vdata); // If this is a tuple or unit struct, define a name // in the value namespace as well. @@ -770,6 +780,8 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id.to_def_id()); self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion)); self.r.visibilities.insert(ctor_def_id, ctor_vis); + // We need the field visibility spans also for the constructor for E0603. + self.insert_field_visibilities_local(ctor_def_id.to_def_id(), vdata); self.r .struct_constructors @@ -783,6 +795,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // Record field names for error reporting. self.insert_field_names_local(def_id, vdata); + self.insert_field_visibilities_local(def_id, vdata); } ItemKind::Trait(..) => { @@ -1510,6 +1523,7 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> { // Record field names for error reporting. self.insert_field_names_local(def_id.to_def_id(), &variant.data); + self.insert_field_visibilities_local(def_id.to_def_id(), &variant.data); visit::walk_variant(self, variant); } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 37771693417..fb2aebbd18a 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -6,7 +6,9 @@ use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{ + pluralize, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, +}; use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS}; @@ -161,10 +163,11 @@ impl<'a> Resolver<'a> { found_use, DiagnosticMode::Normal, path, + "", ); err.emit(); } else if let Some((span, msg, sugg, appl)) = suggestion { - err.span_suggestion(span, msg, sugg, appl); + err.span_suggestion_verbose(span, msg, sugg, appl); err.emit(); } else if let [segment] = path.as_slice() && is_call { err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod); @@ -690,6 +693,7 @@ impl<'a> Resolver<'a> { FoundUse::Yes, DiagnosticMode::Pattern, vec![], + "", ); } err @@ -1344,6 +1348,7 @@ impl<'a> Resolver<'a> { FoundUse::Yes, DiagnosticMode::Normal, vec![], + "", ); if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { @@ -1601,6 +1606,16 @@ impl<'a> Resolver<'a> { err.span_label(ident.span, &format!("private {}", descr)); if let Some(span) = ctor_fields_span { err.span_label(span, "a constructor is private if any of the fields is private"); + if let Res::Def(_, d) = res && let Some(fields) = self.field_visibility_spans.get(&d) { + err.multipart_suggestion_verbose( + &format!( + "consider making the field{} publicly accessible", + pluralize!(fields.len()) + ), + fields.iter().map(|span| (*span, "pub ".to_string())).collect(), + Applicability::MaybeIncorrect, + ); + } } // Print the whole import chain to make it easier to see what happens. @@ -2309,7 +2324,7 @@ enum FoundUse { } /// Whether a binding is part of a pattern or a use statement. Used for diagnostics. -enum DiagnosticMode { +pub(crate) enum DiagnosticMode { Normal, /// The binding is part of a pattern Pattern, @@ -2324,6 +2339,8 @@ pub(crate) fn import_candidates( // This is `None` if all placement locations are inside expansions use_placement_span: Option<Span>, candidates: &[ImportSuggestion], + mode: DiagnosticMode, + append: &str, ) { show_candidates( session, @@ -2333,8 +2350,9 @@ pub(crate) fn import_candidates( candidates, Instead::Yes, FoundUse::Yes, - DiagnosticMode::Import, + mode, vec![], + append, ); } @@ -2352,6 +2370,7 @@ fn show_candidates( found_use: FoundUse, mode: DiagnosticMode, path: Vec<Segment>, + append: &str, ) { if candidates.is_empty() { return; @@ -2416,7 +2435,7 @@ fn show_candidates( // produce an additional newline to separate the new use statement // from the directly following item. let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" }; - candidate.0 = format!("{}{};\n{}", add_use, &candidate.0, additional_newline); + candidate.0 = format!("{add_use}{}{append};\n{additional_newline}", &candidate.0); } err.span_suggestions( diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 4d896b05526..00f65ac37b6 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1,6 +1,6 @@ //! A bunch of methods and structures more or less related to resolving imports. -use crate::diagnostics::{import_candidates, Suggestion}; +use crate::diagnostics::{import_candidates, DiagnosticMode, Suggestion}; use crate::Determinacy::{self, *}; use crate::Namespace::*; use crate::{module_to_string, names_to_string, ImportSuggestion}; @@ -402,7 +402,7 @@ struct UnresolvedImportError { label: Option<String>, note: Option<String>, suggestion: Option<Suggestion>, - candidate: Option<Vec<ImportSuggestion>>, + candidates: Option<Vec<ImportSuggestion>>, } pub struct ImportResolver<'a, 'b> { @@ -475,12 +475,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { errors = vec![]; } if seen_spans.insert(err.span) { - let path = import_path_to_string( - &import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(), - &import.kind, - err.span, - ); - errors.push((path, err)); + errors.push((import, err)); prev_root_id = import.root_id; } } else if is_indeterminate { @@ -494,10 +489,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> { label: None, note: None, suggestion: None, - candidate: None, + candidates: None, }; + // FIXME: there should be a better way of doing this than + // formatting this as a string then checking for `::` if path.contains("::") { - errors.push((path, err)) + errors.push((import, err)) } } } @@ -507,7 +504,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { } } - fn throw_unresolved_import_error(&self, errors: Vec<(String, UnresolvedImportError)>) { + fn throw_unresolved_import_error(&self, errors: Vec<(&Import<'_>, UnresolvedImportError)>) { if errors.is_empty() { return; } @@ -516,7 +513,17 @@ impl<'a, 'b> ImportResolver<'a, 'b> { const MAX_LABEL_COUNT: usize = 10; let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect()); - let paths = errors.iter().map(|(path, _)| format!("`{}`", path)).collect::<Vec<_>>(); + let paths = errors + .iter() + .map(|(import, err)| { + let path = import_path_to_string( + &import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(), + &import.kind, + err.span, + ); + format!("`{path}`") + }) + .collect::<Vec<_>>(); let msg = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); let mut diag = struct_span_err!(self.r.session, span, E0432, "{}", &msg); @@ -525,7 +532,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { diag.note(note); } - for (_, err) in errors.into_iter().take(MAX_LABEL_COUNT) { + for (import, err) in errors.into_iter().take(MAX_LABEL_COUNT) { if let Some(label) = err.label { diag.span_label(err.span, label); } @@ -538,14 +545,36 @@ impl<'a, 'b> ImportResolver<'a, 'b> { diag.multipart_suggestion(&msg, suggestions, applicability); } - if let Some(candidate) = &err.candidate { - import_candidates( - self.r.session, - &self.r.untracked.source_span, - &mut diag, - Some(err.span), - &candidate, - ) + if let Some(candidates) = &err.candidates { + match &import.kind { + ImportKind::Single { nested: false, source, target, .. } => import_candidates( + self.r.session, + &self.r.untracked.source_span, + &mut diag, + Some(err.span), + &candidates, + DiagnosticMode::Import, + (source != target) + .then(|| format!(" as {target}")) + .as_deref() + .unwrap_or(""), + ), + ImportKind::Single { nested: true, source, target, .. } => { + import_candidates( + self.r.session, + &self.r.untracked.source_span, + &mut diag, + None, + &candidates, + DiagnosticMode::Normal, + (source != target) + .then(|| format!(" as {target}")) + .as_deref() + .unwrap_or(""), + ); + } + _ => {} + } } } @@ -707,14 +736,14 @@ impl<'a, 'b> ImportResolver<'a, 'b> { String::from("a similar path exists"), Applicability::MaybeIncorrect, )), - candidate: None, + candidates: None, }, None => UnresolvedImportError { span, label: Some(label), note: None, suggestion, - candidate: None, + candidates: None, }, }; return Some(err); @@ -761,7 +790,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { )), note: None, suggestion: None, - candidate: None, + candidates: None, }); } } @@ -873,7 +902,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter()); let names = resolutions .filter_map(|(BindingKey { ident: i, .. }, resolution)| { - if *i == ident { + if i.name == ident.name { return None; } // Never suggest the same name match *resolution.borrow() { @@ -943,7 +972,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> { label: Some(label), note, suggestion, - candidate: if !parent_suggestion.is_empty() { + candidates: if !parent_suggestion.is_empty() { Some(parent_suggestion) } else { None diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index c44635b85f8..ca43762aa21 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1515,7 +1515,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { count: 1, }; let elision_candidate = LifetimeElisionCandidate::Missing(missing_lifetime); - for rib in self.lifetime_ribs.iter().rev() { + for (i, rib) in self.lifetime_ribs.iter().enumerate().rev() { debug!(?rib.kind); match rib.kind { LifetimeRibKind::AnonymousCreateParameter { binder, .. } => { @@ -1532,16 +1532,31 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> { } else { ("`'_` cannot be used here", "`'_` is a reserved lifetime name") }; - rustc_errors::struct_span_err!( + let mut diag = rustc_errors::struct_span_err!( self.r.session, lifetime.ident.span, E0637, "{}", msg, - ) - .span_label(lifetime.ident.span, note) - .emit(); - + ); + diag.span_label(lifetime.ident.span, note); + if elided { + for rib in self.lifetime_ribs[i..].iter().rev() { + if let LifetimeRibKind::Generics { + span, + kind: LifetimeBinderKind::PolyTrait | LifetimeBinderKind::WhereBound, + .. + } = &rib.kind + { + diag.span_help( + *span, + "consider introducing a higher-ranked lifetime here with `for<'a>`", + ); + break; + } + } + } + diag.emit(); self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate); return; } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 74522f18542..d92b046d0b9 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1451,6 +1451,17 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { .collect(); if non_visible_spans.len() > 0 { + if let Some(fields) = self.r.field_visibility_spans.get(&def_id) { + err.multipart_suggestion_verbose( + &format!( + "consider making the field{} publicly accessible", + pluralize!(fields.len()) + ), + fields.iter().map(|span| (*span, "pub ".to_string())).collect(), + Applicability::MaybeIncorrect, + ); + } + let mut m: MultiSpan = non_visible_spans.clone().into(); non_visible_spans .into_iter() @@ -2054,7 +2065,11 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { path: &[Segment], ) -> Option<(Span, &'static str, String, Applicability)> { let (ident, span) = match path { - [segment] if !segment.has_generic_args && segment.ident.name != kw::SelfUpper => { + [segment] + if !segment.has_generic_args + && segment.ident.name != kw::SelfUpper + && segment.ident.name != kw::Dyn => + { (segment.ident.to_string(), segment.ident.span) } _ => return None, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 2182b736937..f950e4a9bee 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -881,6 +881,10 @@ pub struct Resolver<'a> { /// Used for hints during error reporting. field_names: FxHashMap<DefId, Vec<Spanned<Symbol>>>, + /// Span of the privacy modifier in fields of an item `DefId` accessible with dot syntax. + /// Used for hints during error reporting. + field_visibility_spans: FxHashMap<DefId, Vec<Span>>, + /// All imports known to succeed or fail. determined_imports: Vec<&'a Import<'a>>, @@ -1133,7 +1137,7 @@ impl<'a, 'b> DefIdTree for &'a Resolver<'b> { } } -impl Resolver<'_> { +impl<'a> Resolver<'a> { fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> { self.node_id_to_def_id.get(&node).copied() } @@ -1190,6 +1194,10 @@ impl Resolver<'_> { self.cstore().item_generics_num_lifetimes(def_id, self.session) } } + + pub fn sess(&self) -> &'a Session { + self.session + } } impl<'a> Resolver<'a> { @@ -1268,6 +1276,7 @@ impl<'a> Resolver<'a> { has_self: FxHashSet::default(), field_names: FxHashMap::default(), + field_visibility_spans: FxHashMap::default(), determined_imports: Vec::new(), indeterminate_imports: Vec::new(), diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs index 6afd5fe5a7f..a8d82de02b7 100644 --- a/compiler/rustc_save_analysis/src/lib.rs +++ b/compiler/rustc_save_analysis/src/lib.rs @@ -600,7 +600,7 @@ impl<'tcx> SaveContext<'tcx> { if seg.res != Res::Err { seg.res } else { - let parent_node = self.tcx.hir().get_parent_node(hir_id); + let parent_node = self.tcx.hir().parent_id(hir_id); self.get_path_res(parent_node) } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 02e3992a6a9..1ccfc59f7a9 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -35,6 +35,7 @@ use std::hash::Hash; use std::iter; use std::path::{Path, PathBuf}; use std::str::{self, FromStr}; +use std::sync::LazyLock; pub mod sigpipe; @@ -554,6 +555,16 @@ pub enum PrintRequest { SplitDebuginfo, } +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum TraitSolver { + /// Classic trait solver in `rustc_trait_selection::traits::select` + Classic, + /// Chalk trait solver + Chalk, + /// Experimental trait solver in `rustc_trait_selection::solve` + Next, +} + pub enum Input { /// Load source code from a file. File(PathBuf), @@ -1312,7 +1323,12 @@ mod opt { unstable(longer(a, b), move |opts| opts.optmulti(a, b, c, d)) } } - +static EDITION_STRING: LazyLock<String> = LazyLock::new(|| { + format!( + "Specify which edition of the compiler to use when compiling code. \ +The default is {DEFAULT_EDITION} and the latest stable edition is {LATEST_STABLE_EDITION}." + ) +}); /// Returns the "short" subset of the rustc command line options, /// including metadata for each option, such as whether the option is /// part of the stable long-term interface for rustc. @@ -1345,7 +1361,7 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> { opt::opt_s( "", "edition", - "Specify which edition of the compiler to use when compiling code.", + &*EDITION_STRING, EDITION_NAME_LIST, ), opt::multi_s( @@ -2761,7 +2777,7 @@ pub(crate) mod dep_tracking { BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType, InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OomStrategy, OptLevel, OutputType, OutputTypes, Passes, SourceFileHashAlgorithm, SplitDwarfKind, - SwitchWithOptPath, SymbolManglingVersion, TrimmedDefPaths, + SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths, }; use crate::lint; use crate::options::WasiExecModel; @@ -2861,6 +2877,7 @@ pub(crate) mod dep_tracking { BranchProtection, OomStrategy, LanguageIdentifier, + TraitSolver, ); impl<T1, T2> DepTrackingHash for (T1, T2) @@ -2981,3 +2998,21 @@ pub enum ProcMacroExecutionStrategy { /// Run the proc-macro code on a different thread. CrossThread, } + +/// Which format to use for `-Z dump-mono-stats` +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub enum DumpMonoStatsFormat { + /// Pretty-print a markdown table + Markdown, + /// Emit structured JSON + Json, +} + +impl DumpMonoStatsFormat { + pub fn extension(self) -> &'static str { + match self { + Self::Markdown => "md", + Self::Json => "json", + } + } +} diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index e72b76cfee9..f5a72573d58 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -260,9 +260,11 @@ pub(crate) struct InvalidFloatLiteralSuffix { #[derive(Diagnostic)] #[diag(session_int_literal_too_large)] +#[note] pub(crate) struct IntLiteralTooLarge { #[primary_span] pub span: Span, + pub limit: String, } #[derive(Diagnostic)] @@ -361,8 +363,15 @@ pub fn report_lit_error(sess: &ParseSess, err: LitError, lit: token::Lit, span: _ => unreachable!(), }; } - LitError::IntTooLarge => { - sess.emit_err(IntLiteralTooLarge { span }); + LitError::IntTooLarge(base) => { + let max = u128::MAX; + let limit = match base { + 2 => format!("{max:#b}"), + 8 => format!("{max:#o}"), + 16 => format!("{max:#x}"), + _ => format!("{max}"), + }; + sess.emit_err(IntLiteralTooLarge { span, limit }); } } } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 9bf581ff73d..7b5fd6cc2a8 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -377,10 +377,13 @@ mod desc { pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of(); pub const parse_optimization_fuel: &str = "crate=integer"; pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`"; + pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`"; pub const parse_instrument_coverage: &str = "`all` (default), `except-unused-generics`, `except-unused-functions`, or `off`"; pub const parse_unpretty: &str = "`string` or `string=string`"; pub const parse_treat_err_as_bug: &str = "either no value or a number bigger than 0"; + pub const parse_trait_solver: &str = + "one of the supported solver modes (`classic`, `chalk`, or `next`)"; pub const parse_lto: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), `thin`, `fat`, or omitted"; pub const parse_linker_plugin_lto: &str = @@ -820,6 +823,21 @@ mod parse { true } + pub(crate) fn parse_dump_mono_stats(slot: &mut DumpMonoStatsFormat, v: Option<&str>) -> bool { + match v { + None => true, + Some("json") => { + *slot = DumpMonoStatsFormat::Json; + true + } + Some("markdown") => { + *slot = DumpMonoStatsFormat::Markdown; + true + } + Some(_) => false, + } + } + pub(crate) fn parse_instrument_coverage( slot: &mut Option<InstrumentCoverage>, v: Option<&str>, @@ -864,6 +882,18 @@ mod parse { } } + pub(crate) fn parse_trait_solver(slot: &mut TraitSolver, v: Option<&str>) -> bool { + match v { + Some("classic") => *slot = TraitSolver::Classic, + Some("chalk") => *slot = TraitSolver::Chalk, + Some("next") => *slot = TraitSolver::Next, + // default trait solver is subject to change.. + Some("default") => *slot = TraitSolver::Classic, + _ => return false, + } + true + } + pub(crate) fn parse_lto(slot: &mut LtoCli, v: Option<&str>) -> bool { if v.is_some() { let mut bool_arg = None; @@ -1211,7 +1241,7 @@ options! { // tidy-alphabetical-start allow_features: Option<Vec<String>> = (None, parse_opt_comma_list, [TRACKED], - "only allow the listed language features to be enabled in code (space separated)"), + "only allow the listed language features to be enabled in code (comma separated)"), always_encode_mir: bool = (false, parse_bool, [TRACKED], "encode MIR of all functions into the crate metadata (default: no)"), asm_comments: bool = (false, parse_bool, [TRACKED], @@ -1225,7 +1255,7 @@ options! { binary_dep_depinfo: bool = (false, parse_bool, [TRACKED], "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \ (default: no)"), - box_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED], + box_noalias: bool = (true, parse_bool, [TRACKED], "emit noalias metadata for box (default: yes)"), branch_protection: Option<BranchProtection> = (None, parse_branch_protection, [TRACKED], "set options for branch target identification and pointer authentication on AArch64"), @@ -1233,8 +1263,6 @@ options! { "instrument control-flow architecture protection"), cgu_partitioning_strategy: Option<String> = (None, parse_opt_string, [TRACKED], "the codegen unit partitioning strategy to use"), - chalk: bool = (false, parse_bool, [TRACKED], - "enable the experimental Chalk-based trait solving engine"), codegen_backend: Option<String> = (None, parse_opt_string, [TRACKED], "the backend to use"), combine_cgu: bool = (false, parse_bool, [TRACKED], @@ -1295,7 +1323,9 @@ options! { an additional `.html` file showing the computed coverage spans."), dump_mono_stats: SwitchWithOptPath = (SwitchWithOptPath::Disabled, parse_switch_with_opt_path, [UNTRACKED], - "output statistics about monomorphization collection (format: markdown)"), + "output statistics about monomorphization collection"), + dump_mono_stats_format: DumpMonoStatsFormat = (DumpMonoStatsFormat::Markdown, parse_dump_mono_stats, [UNTRACKED], + "the format to use for -Z dump-mono-stats (`markdown` (default) or `json`)"), dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED], "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), dylib_lto: bool = (false, parse_bool, [UNTRACKED], @@ -1381,6 +1411,8 @@ options! { "what location details should be tracked when using caller_location, either \ `none`, or a comma separated list of location details, for which \ valid options are `file`, `line`, and `column` (default: `file,line,column`)"), + log_backtrace: Option<String> = (None, parse_opt_string, [TRACKED], + "add a backtrace along with logging"), ls: bool = (false, parse_bool, [UNTRACKED], "list the symbols defined by a library crate (default: no)"), macro_backtrace: bool = (false, parse_bool, [UNTRACKED], @@ -1407,7 +1439,7 @@ options! { "use line numbers relative to the function in mir pretty printing"), move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED], "the size at which the `large_assignments` lint starts to be emitted"), - mutable_noalias: Option<bool> = (None, parse_opt_bool, [TRACKED], + mutable_noalias: bool = (true, parse_bool, [TRACKED], "emit noalias metadata for mutable references (default: yes)"), nll_facts: bool = (false, parse_bool, [UNTRACKED], "dump facts from NLL analysis into side files (default: no)"), @@ -1540,7 +1572,7 @@ options! { /// o/w tests have closure@path span_free_formats: bool = (false, parse_bool, [UNTRACKED], "exclude spans when debug-printing compiler state (default: no)"), - split_dwarf_inlining: bool = (true, parse_bool, [TRACKED], + split_dwarf_inlining: bool = (false, parse_bool, [TRACKED], "provide minimal debug info in the object/executable to facilitate online \ symbolication/stack traces in the absence of .dwo/.dwp files when using Split DWARF"), split_dwarf_kind: SplitDwarfKind = (SplitDwarfKind::Split, parse_split_dwarf_kind, [TRACKED], @@ -1591,6 +1623,8 @@ options! { "for every macro invocation, print its name and arguments (default: no)"), track_diagnostics: bool = (false, parse_bool, [UNTRACKED], "tracks where in rustc a diagnostic was emitted"), + trait_solver: TraitSolver = (TraitSolver::Classic, parse_trait_solver, [TRACKED], + "specify the trait solver mode used by rustc (default: classic)"), // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved // alongside query results and changes to translation options can affect diagnostics - so // translation options should be tracked. diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 4a3d29414d6..1b2e8d9dc70 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -590,7 +590,19 @@ impl Session { pub fn warn(&self, msg: impl Into<DiagnosticMessage>) { self.diagnostic().warn(msg) } - /// Delay a span_bug() call until abort_if_errors() + + /// Ensures that compilation cannot succeed. + /// + /// If this function has been called but no errors have been emitted and + /// compilation succeeds, it will cause an internal compiler error (ICE). + /// + /// This can be used in code paths that should never run on successful compilations. + /// For example, it can be used to create an [`ErrorGuaranteed`] + /// (but you should prefer threading through the [`ErrorGuaranteed`] from an error emission directly). + /// + /// If no span is available, use [`DUMMY_SP`]. + /// + /// [`DUMMY_SP`]: rustc_span::DUMMY_SP #[track_caller] pub fn delay_span_bug<S: Into<MultiSpan>>( &self, diff --git a/compiler/rustc_span/Cargo.toml b/compiler/rustc_span/Cargo.toml index 5ce2577b63c..ae81d95e279 100644 --- a/compiler/rustc_span/Cargo.toml +++ b/compiler/rustc_span/Cargo.toml @@ -15,6 +15,6 @@ scoped-tls = "1.0" unicode-width = "0.1.4" cfg-if = "1.0" tracing = "0.1" -sha1 = { package = "sha-1", version = "0.10.0" } +sha1 = "0.10.0" sha2 = "0.10.1" md5 = { package = "md-5", version = "0.10.0" } diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 221f65b66e6..7c5e1427d1e 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -303,7 +303,7 @@ impl DefId { // i.e. don't use closures. match self.as_local() { Some(local_def_id) => local_def_id, - None => panic!("DefId::expect_local: `{:?}` isn't local", self), + None => panic!("DefId::expect_local: `{self:?}` isn't local"), } } diff --git a/compiler/rustc_span/src/edition.rs b/compiler/rustc_span/src/edition.rs index 065d3660e50..b43183916bc 100644 --- a/compiler/rustc_span/src/edition.rs +++ b/compiler/rustc_span/src/edition.rs @@ -44,7 +44,7 @@ impl fmt::Display for Edition { Edition::Edition2021 => "2021", Edition::Edition2024 => "2024", }; - write!(f, "{}", s) + write!(f, "{s}") } } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 4e70dfb6147..a9a9a3fbf9d 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -110,7 +110,7 @@ fn assert_default_hashing_controls<CTX: HashStableContext>(ctx: &CTX, msg: &str) // Such configuration must not be used for metadata. HashingControls { hash_spans } if hash_spans == !ctx.unstable_opts_incremental_ignore_spans() => {} - other => panic!("Attempted hashing of {msg} with non-default HashingControls: {:?}", other), + other => panic!("Attempted hashing of {msg} with non-default HashingControls: {other:?}"), } } @@ -629,7 +629,7 @@ pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symb pub fn debug_hygiene_data(verbose: bool) -> String { HygieneData::with(|data| { if verbose { - format!("{:#?}", data) + format!("{data:#?}") } else { let mut s = String::from("Expansions:"); let mut debug_expn_data = |(id, expn_data): (&ExpnId, &ExpnData)| { @@ -1067,9 +1067,9 @@ impl ExpnKind { match *self { ExpnKind::Root => kw::PathRoot.to_string(), ExpnKind::Macro(macro_kind, name) => match macro_kind { - MacroKind::Bang => format!("{}!", name), - MacroKind::Attr => format!("#[{}]", name), - MacroKind::Derive => format!("#[derive({})]", name), + MacroKind::Bang => format!("{name}!"), + MacroKind::Attr => format!("#[{name}]"), + MacroKind::Derive => format!("#[derive({name})]"), }, ExpnKind::AstPass(kind) => kind.descr().to_string(), ExpnKind::Desugaring(kind) => format!("desugaring of {}", kind.descr()), @@ -1466,11 +1466,7 @@ impl<D: Decoder> Decodable<D> for SyntaxContext { /// collisions are only possible between `ExpnId`s within the same crate. fn update_disambiguator(expn_data: &mut ExpnData, mut ctx: impl HashStableContext) -> ExpnHash { // This disambiguator should not have been set yet. - assert_eq!( - expn_data.disambiguator, 0, - "Already set disambiguator for ExpnData: {:?}", - expn_data - ); + assert_eq!(expn_data.disambiguator, 0, "Already set disambiguator for ExpnData: {expn_data:?}"); assert_default_hashing_controls(&ctx, "ExpnData (disambiguator)"); let mut expn_hash = expn_data.hash_expn(&mut ctx); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index bee4b0a2332..7e61f2f9f73 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -329,7 +329,7 @@ impl fmt::Display for FileNameDisplay<'_> { ProcMacroSourceCode(_) => write!(fmt, "<proc-macro source code>"), CfgSpec(_) => write!(fmt, "<cfgspec>"), CliCrateAttr(_) => write!(fmt, "<crate attribute>"), - Custom(ref s) => write!(fmt, "<{}>", s), + Custom(ref s) => write!(fmt, "<{s}>"), DocTest(ref path, _) => write!(fmt, "{}", path.display()), InlineAsm(_) => write!(fmt, "<inline asm>"), } @@ -1074,7 +1074,7 @@ impl NonNarrowChar { 0 => NonNarrowChar::ZeroWidth(pos), 2 => NonNarrowChar::Wide(pos), 4 => NonNarrowChar::Tab(pos), - _ => panic!("width {} given for non-narrow character", width), + _ => panic!("width {width} given for non-narrow character"), } } diff --git a/compiler/rustc_span/src/profiling.rs b/compiler/rustc_span/src/profiling.rs index f169007fab4..0ab890b9f01 100644 --- a/compiler/rustc_span/src/profiling.rs +++ b/compiler/rustc_span/src/profiling.rs @@ -27,7 +27,7 @@ impl SpannedEventArgRecorder for EventArgRecorder<'_> { if let Some(source_map) = &*session_globals.source_map.borrow() { source_map.span_to_embeddable_string(span) } else { - format!("{:?}", span) + format!("{span:?}") } }); self.record_arg(span_arg); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 85510fa2c66..706002f79b1 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -193,6 +193,7 @@ symbols! { FromIterator, FromResidual, Future, + FutureOutput, FxHashMap, FxHashSet, GlobalAlloc, @@ -497,6 +498,7 @@ symbols! { console, const_allocate, const_async_blocks, + const_closures, const_compare_raw_pointers, const_constructor, const_deallocate, @@ -612,6 +614,7 @@ symbols! { dispatch_from_dyn, div, div_assign, + do_not_recommend, doc, doc_alias, doc_auto_cfg, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index a59c9011ab2..23ff6b333f0 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -175,7 +175,7 @@ impl SymbolPath { fn finish(mut self, hash: u64) -> String { self.finalize_pending_component(); // E = end name-sequence - let _ = write!(self.result, "17h{:016x}E", hash); + let _ = write!(self.result, "17h{hash:016x}E"); self.result } } @@ -227,7 +227,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> { self = self.print_type(ty)?; self.write_str("; ")?; if let Some(size) = size.kind().try_to_bits(self.tcx().data_layout.pointer_size) { - write!(self, "{}", size)? + write!(self, "{size}")? } else if let ty::ConstKind::Param(param) = size.kind() { self = param.print(self)? } else { diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 62f44a48032..547a5907660 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -269,8 +269,7 @@ fn compute_symbol_name<'tcx>( debug_assert!( rustc_demangle::try_demangle(&symbol).is_ok(), - "compute_symbol_name: `{}` cannot be demangled", - symbol + "compute_symbol_name: `{symbol}` cannot be demangled" ); symbol diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs index 150459ce0f5..c6899f8f244 100644 --- a/compiler/rustc_symbol_mangling/src/test.rs +++ b/compiler/rustc_symbol_mangling/src/test.rs @@ -74,7 +74,7 @@ impl SymbolNamesTest<'_> { tcx.sess.emit_err(TestOutput { span: attr.span, kind: Kind::DemanglingAlt, - content: format!("{:#}", demangling), + content: format!("{demangling:#}"), }); } } diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index e9b85705086..0759b95bd94 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -126,11 +126,11 @@ fn encode_const<'tcx>( if value < zero { s.push('n') }; - let _ = write!(s, "{}", value); + let _ = write!(s, "{value}"); } fn push_unsigned_value<T: Display>(s: &mut String, value: T) { - let _ = write!(s, "{}", value); + let _ = write!(s, "{value}"); } if let Some(scalar_int) = c.kind().try_to_scalar_int() { diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index 4285aa62cb9..0d446d654dc 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -206,6 +206,7 @@ impl<'tcx> SymbolMangler<'tcx> { where T: TypeVisitable<'tcx>, { + // FIXME(non-lifetime-binders): What to do here? let regions = if value.has_late_bound_regions() { self.tcx.collect_referenced_late_bound_regions(value) } else { @@ -609,7 +610,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { bits = val.unsigned_abs(); } - let _ = write!(self.out, "{:x}_", bits); + let _ = write!(self.out, "{bits:x}_"); } // FIXME(valtrees): Remove the special case for `str` @@ -637,7 +638,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> { // FIXME(eddyb) use a specialized hex-encoding loop. for byte in s.bytes() { - let _ = write!(self.out, "{:02x}", byte); + let _ = write!(self.out, "{byte:02x}"); } self.push("_"); diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index a5ffaebea0b..3b8c867d35b 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -71,12 +71,7 @@ mod attr_impl { const NonNull = 1 << 3; const ReadOnly = 1 << 4; const InReg = 1 << 5; - // Due to past miscompiles in LLVM, we use a separate attribute for - // &mut arguments, so that the codegen backend can decide whether - // or not to actually emit the attribute. It can also be controlled - // with the `-Zmutable-noalias` debugging option. - const NoAliasMutRef = 1 << 6; - const NoUndef = 1 << 7; + const NoUndef = 1 << 6; } } } @@ -177,12 +172,12 @@ impl Reg { 17..=32 => dl.i32_align.abi, 33..=64 => dl.i64_align.abi, 65..=128 => dl.i128_align.abi, - _ => panic!("unsupported integer: {:?}", self), + _ => panic!("unsupported integer: {self:?}"), }, RegKind::Float => match self.size.bits() { 32 => dl.f32_align.abi, 64 => dl.f64_align.abi, - _ => panic!("unsupported float: {:?}", self), + _ => panic!("unsupported float: {self:?}"), }, RegKind::Vector => dl.vector_align(self.size).abi, } @@ -642,7 +637,7 @@ impl fmt::Display for AdjustForForeignAbiError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Unsupported { arch, abi } => { - write!(f, "target architecture {:?} does not support `extern {}` ABI", arch, abi) + write!(f, "target architecture {arch:?} does not support `extern {abi}` ABI") } } } @@ -760,7 +755,7 @@ impl FromStr for Conv { "AmdGpuKernel" => Ok(Conv::AmdGpuKernel), "AvrInterrupt" => Ok(Conv::AvrInterrupt), "AvrNonBlockingInterrupt" => Ok(Conv::AvrNonBlockingInterrupt), - _ => Err(format!("'{}' is not a valid value for entry function call convetion.", s)), + _ => Err(format!("'{s}' is not a valid value for entry function call convetion.")), } } } diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index 62a0f9fb034..28493c7700f 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -195,6 +195,6 @@ impl AArch64InlineAsmReg { (modifier.unwrap_or('v'), self as u32 - Self::v0 as u32) }; assert!(index < 32); - write!(out, "{}{}", prefix, index) + write!(out, "{prefix}{index}") } } diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index 0db3eb6fcac..ec7429a3065 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -249,7 +249,7 @@ impl ArmInlineAsmReg { let index = self as u32 - Self::q0 as u32; assert!(index < 16); let index = index * 2 + (modifier == 'f') as u32; - write!(out, "d{}", index) + write!(out, "d{index}") } else { out.write_str(self.name()) } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 65d2cd64bf6..7f01f33d39c 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -679,13 +679,13 @@ impl fmt::Display for InlineAsmType { Self::I128 => f.write_str("i128"), Self::F32 => f.write_str("f32"), Self::F64 => f.write_str("f64"), - Self::VecI8(n) => write!(f, "i8x{}", n), - Self::VecI16(n) => write!(f, "i16x{}", n), - Self::VecI32(n) => write!(f, "i32x{}", n), - Self::VecI64(n) => write!(f, "i64x{}", n), - Self::VecI128(n) => write!(f, "i128x{}", n), - Self::VecF32(n) => write!(f, "f32x{}", n), - Self::VecF64(n) => write!(f, "f64x{}", n), + Self::VecI8(n) => write!(f, "i8x{n}"), + Self::VecI16(n) => write!(f, "i16x{n}"), + Self::VecI32(n) => write!(f, "i32x{n}"), + Self::VecI64(n) => write!(f, "i64x{n}"), + Self::VecI128(n) => write!(f, "i128x{n}"), + Self::VecF32(n) => write!(f, "f32x{n}"), + Self::VecF64(n) => write!(f, "f64x{n}"), } } } diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index 238c365093f..5eae07f141f 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -357,28 +357,28 @@ impl X86InlineAsmReg { if self as u32 <= Self::dx as u32 { let root = ['a', 'b', 'c', 'd'][self as usize - Self::ax as usize]; match modifier.unwrap_or(reg_default_modifier) { - 'l' => write!(out, "{}l", root), - 'h' => write!(out, "{}h", root), - 'x' => write!(out, "{}x", root), - 'e' => write!(out, "e{}x", root), - 'r' => write!(out, "r{}x", root), + 'l' => write!(out, "{root}l"), + 'h' => write!(out, "{root}h"), + 'x' => write!(out, "{root}x"), + 'e' => write!(out, "e{root}x"), + 'r' => write!(out, "r{root}x"), _ => unreachable!(), } } else if self as u32 <= Self::di as u32 { let root = self.name(); match modifier.unwrap_or(reg_default_modifier) { - 'l' => write!(out, "{}l", root), - 'x' => write!(out, "{}", root), - 'e' => write!(out, "e{}", root), - 'r' => write!(out, "r{}", root), + 'l' => write!(out, "{root}l"), + 'x' => write!(out, "{root}"), + 'e' => write!(out, "e{root}"), + 'r' => write!(out, "r{root}"), _ => unreachable!(), } } else if self as u32 <= Self::r15 as u32 { let root = self.name(); match modifier.unwrap_or(reg_default_modifier) { - 'l' => write!(out, "{}b", root), - 'x' => write!(out, "{}w", root), - 'e' => write!(out, "{}d", root), + 'l' => write!(out, "{root}b"), + 'x' => write!(out, "{root}w"), + 'e' => write!(out, "{root}d"), 'r' => out.write_str(root), _ => unreachable!(), } @@ -387,15 +387,15 @@ impl X86InlineAsmReg { } else if self as u32 <= Self::xmm15 as u32 { let prefix = modifier.unwrap_or('x'); let index = self as u32 - Self::xmm0 as u32; - write!(out, "{}{}", prefix, index) + write!(out, "{prefix}{index}") } else if self as u32 <= Self::ymm15 as u32 { let prefix = modifier.unwrap_or('y'); let index = self as u32 - Self::ymm0 as u32; - write!(out, "{}{}", prefix, index) + write!(out, "{prefix}{index}") } else if self as u32 <= Self::zmm31 as u32 { let prefix = modifier.unwrap_or('z'); let index = self as u32 - Self::zmm0 as u32; - write!(out, "{}{}", prefix, index) + write!(out, "{prefix}{index}") } else { out.write_str(self.name()) } diff --git a/compiler/rustc_target/src/spec/aarch64_fuchsia.rs b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs index 4634433c4a9..ddecbb1a8c4 100644 --- a/compiler/rustc_target/src/spec/aarch64_fuchsia.rs +++ b/compiler/rustc_target/src/spec/aarch64_fuchsia.rs @@ -1,15 +1 @@ -use crate::spec::{SanitizerSet, Target, TargetOptions}; - -pub fn target() -> Target { - Target { - llvm_target: "aarch64-fuchsia".into(), - pointer_width: 64, - data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), - arch: "aarch64".into(), - options: TargetOptions { - max_atomic_width: Some(128), - supported_sanitizers: SanitizerSet::ADDRESS | SanitizerSet::CFI, - ..super::fuchsia_base::opts() - }, - } -} +pub use crate::spec::aarch64_unknown_fuchsia::target; diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/aarch64_unknown_fuchsia.rs new file mode 100644 index 00000000000..ef2ab304f9e --- /dev/null +++ b/compiler/rustc_target/src/spec/aarch64_unknown_fuchsia.rs @@ -0,0 +1,17 @@ +use crate::spec::{SanitizerSet, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "aarch64-unknown-fuchsia".into(), + pointer_width: 64, + data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".into(), + arch: "aarch64".into(), + options: TargetOptions { + max_atomic_width: Some(128), + supported_sanitizers: SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::SHADOWCALLSTACK, + ..super::fuchsia_base::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/abi.rs b/compiler/rustc_target/src/spec/abi.rs index cb2a0c04c6a..d4f7ed31b89 100644 --- a/compiler/rustc_target/src/spec/abi.rs +++ b/compiler/rustc_target/src/spec/abi.rs @@ -149,7 +149,7 @@ pub fn is_stable(name: &str) -> Result<(), AbiDisabled> { match name { // Stable "Rust" | "C" | "cdecl" | "stdcall" | "fastcall" | "aapcs" | "win64" | "sysv64" - | "system" => Ok(()), + | "system" | "efiapi" => Ok(()), "rust-intrinsic" => Err(AbiDisabled::Unstable { feature: sym::intrinsics, explain: "intrinsics are subject to change", @@ -198,10 +198,6 @@ pub fn is_stable(name: &str) -> Result<(), AbiDisabled> { feature: sym::abi_avr_interrupt, explain: "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change", }), - "efiapi" => Err(AbiDisabled::Unstable { - feature: sym::abi_efiapi, - explain: "efiapi ABI is experimental and subject to change", - }), "C-cmse-nonsecure-call" => Err(AbiDisabled::Unstable { feature: sym::abi_c_cmse_nonsecure_call, explain: "C-cmse-nonsecure-call ABI is experimental and subject to change", diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index 44644c4733e..5c6dcc0aba9 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -76,7 +76,7 @@ impl Arch { fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs { let platform_name: StaticCow<str> = match abi { - "sim" => format!("{}-simulator", os).into(), + "sim" => format!("{os}-simulator").into(), "macabi" => "mac-catalyst".into(), _ => os.into(), }; @@ -193,7 +193,7 @@ fn macos_deployment_target(arch: Arch) -> (u32, u32) { fn macos_lld_platform_version(arch: Arch) -> String { let (major, minor) = macos_deployment_target(arch); - format!("{}.{}", major, minor) + format!("{major}.{minor}") } pub fn macos_llvm_target(arch: Arch) -> String { @@ -252,7 +252,7 @@ pub fn ios_llvm_target(arch: Arch) -> String { fn ios_lld_platform_version() -> String { let (major, minor) = ios_deployment_target(); - format!("{}.{}", major, minor) + format!("{major}.{minor}") } pub fn ios_sim_llvm_target(arch: Arch) -> String { @@ -266,7 +266,7 @@ fn tvos_deployment_target() -> (u32, u32) { fn tvos_lld_platform_version() -> String { let (major, minor) = tvos_deployment_target(); - format!("{}.{}", major, minor) + format!("{major}.{minor}") } fn watchos_deployment_target() -> (u32, u32) { @@ -275,7 +275,7 @@ fn watchos_deployment_target() -> (u32, u32) { fn watchos_lld_platform_version() -> String { let (major, minor) = watchos_deployment_target(); - format!("{}.{}", major, minor) + format!("{major}.{minor}") } pub fn watchos_sim_llvm_target(arch: Arch) -> String { diff --git a/compiler/rustc_target/src/spec/bpf_base.rs b/compiler/rustc_target/src/spec/bpf_base.rs index baf36587147..2b00cda44b5 100644 --- a/compiler/rustc_target/src/spec/bpf_base.rs +++ b/compiler/rustc_target/src/spec/bpf_base.rs @@ -6,7 +6,7 @@ pub fn opts(endian: Endian) -> TargetOptions { allow_asm: true, endian, linker_flavor: LinkerFlavor::Bpf, - atomic_cas: false, + atomic_cas: true, dynamic_linking: true, no_builtins: true, panic_strategy: PanicStrategy::Abort, @@ -19,6 +19,10 @@ pub fn opts(endian: Endian) -> TargetOptions { obj_is_bitcode: true, requires_lto: false, singlethread: true, + // When targeting the `v3` cpu in llvm, 32-bit atomics are also supported. + // But making this value change based on the target cpu can be mostly confusing + // and would require a bit of a refactor. + min_atomic_width: Some(64), max_atomic_width: Some(64), ..Default::default() } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index b7ec1612e8e..1e80b8b759d 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -840,7 +840,7 @@ impl fmt::Display for SanitizerSet { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut first = true; for s in *self { - let name = s.as_str().unwrap_or_else(|| panic!("unrecognized sanitizer {:?}", s)); + let name = s.as_str().unwrap_or_else(|| panic!("unrecognized sanitizer {s:?}")); if !first { f.write_str(", ")?; } @@ -981,7 +981,7 @@ impl fmt::Display for StackProtector { } macro_rules! supported_targets { - ( $(($triple:literal, $module:ident ),)+ ) => { + ( $(($triple:literal, $module:ident),)+ ) => { $(mod $module;)+ /// List of supported targets @@ -1109,8 +1109,12 @@ supported_targets! { ("x86_64-apple-darwin", x86_64_apple_darwin), ("i686-apple-darwin", i686_apple_darwin), + // FIXME(#106649): Remove aarch64-fuchsia in favor of aarch64-unknown-fuchsia ("aarch64-fuchsia", aarch64_fuchsia), + ("aarch64-unknown-fuchsia", aarch64_unknown_fuchsia), + // FIXME(#106649): Remove x86_64-fuchsia in favor of x86_64-unknown-fuchsia ("x86_64-fuchsia", x86_64_fuchsia), + ("x86_64-unknown-fuchsia", x86_64_unknown_fuchsia), ("avr-unknown-gnu-atmega328", avr_unknown_gnu_atmega328), @@ -2074,7 +2078,7 @@ impl Target { let mut get_req_field = |name: &str| { obj.remove(name) .and_then(|j| j.as_str().map(str::to_string)) - .ok_or_else(|| format!("Field {} in target specification is required", name)) + .ok_or_else(|| format!("Field {name} in target specification is required")) }; let mut base = Target { @@ -2480,7 +2484,7 @@ impl Target { if let Some(s) = fp.as_str() { base.frame_pointer = s .parse() - .map_err(|()| format!("'{}' is not a valid value for frame-pointer", s))?; + .map_err(|()| format!("'{s}' is not a valid value for frame-pointer"))?; } else { incorrect_type.push("frame-pointer".into()) } @@ -2672,7 +2676,7 @@ impl Target { return load_file(&p); } - Err(format!("Could not find specification for target {:?}", target_triple)) + Err(format!("Could not find specification for target {target_triple:?}")) } TargetTriple::TargetJson { ref contents, .. } => { let obj = serde_json::from_str(contents).map_err(|e| e.to_string())?; @@ -2936,7 +2940,7 @@ impl TargetTriple { let contents = std::fs::read_to_string(&canonicalized_path).map_err(|err| { io::Error::new( io::ErrorKind::InvalidInput, - format!("target path {:?} is not a valid file: {}", canonicalized_path, err), + format!("target path {canonicalized_path:?} is not a valid file: {err}"), ) })?; let triple = canonicalized_path @@ -2971,7 +2975,7 @@ impl TargetTriple { let mut hasher = DefaultHasher::new(); content.hash(&mut hasher); let hash = hasher.finish(); - format!("{}-{}", triple, hash) + format!("{triple}-{hash}") } } } diff --git a/compiler/rustc_target/src/spec/solid_base.rs b/compiler/rustc_target/src/spec/solid_base.rs index c585a6cd58e..eaf72b7616c 100644 --- a/compiler/rustc_target/src/spec/solid_base.rs +++ b/compiler/rustc_target/src/spec/solid_base.rs @@ -3,7 +3,7 @@ use crate::spec::TargetOptions; pub fn opts(kernel: &str) -> TargetOptions { TargetOptions { - os: format!("solid_{}", kernel).into(), + os: format!("solid_{kernel}").into(), vendor: "kmc".into(), executables: false, frame_pointer: FramePointer::NonLeaf, diff --git a/compiler/rustc_target/src/spec/x86_64_fuchsia.rs b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs index 532dd6d0742..96fed097566 100644 --- a/compiler/rustc_target/src/spec/x86_64_fuchsia.rs +++ b/compiler/rustc_target/src/spec/x86_64_fuchsia.rs @@ -1,18 +1 @@ -use crate::spec::{SanitizerSet, StackProbeType, Target}; - -pub fn target() -> Target { - let mut base = super::fuchsia_base::opts(); - base.cpu = "x86-64".into(); - base.max_atomic_width = Some(64); - base.stack_probes = StackProbeType::X86; - base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI; - - Target { - llvm_target: "x86_64-fuchsia".into(), - pointer_width: 64, - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - .into(), - arch: "x86_64".into(), - options: base, - } -} +pub use crate::spec::x86_64_unknown_fuchsia::target; diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/x86_64_unknown_fuchsia.rs new file mode 100644 index 00000000000..a3231d19f4c --- /dev/null +++ b/compiler/rustc_target/src/spec/x86_64_unknown_fuchsia.rs @@ -0,0 +1,18 @@ +use crate::spec::{SanitizerSet, StackProbeType, Target}; + +pub fn target() -> Target { + let mut base = super::fuchsia_base::opts(); + base.cpu = "x86-64".into(); + base.max_atomic_width = Some(64); + base.stack_probes = StackProbeType::X86; + base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI; + + Target { + llvm_target: "x86_64-unknown-fuchsia".into(), + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + .into(), + arch: "x86_64".into(), + options: base, + } +} diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 19f404cb5b7..4405537c645 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,7 +1,6 @@ use rustc_errors::{fluent, ErrorGuaranteed, Handler, IntoDiagnostic}; use rustc_macros::Diagnostic; use rustc_middle::ty::{self, PolyTraitRef, Ty}; -use rustc_session::Limit; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] @@ -22,18 +21,6 @@ pub struct UnableToConstructConstantValue<'a> { } #[derive(Diagnostic)] -#[help] -#[diag(trait_selection_auto_deref_reached_recursion_limit, code = "E0055")] -pub struct AutoDerefReachedRecursionLimit<'a> { - #[primary_span] - #[label] - pub span: Span, - pub ty: Ty<'a>, - pub suggested_limit: Limit, - pub crate_name: Symbol, -} - -#[derive(Diagnostic)] #[diag(trait_selection_empty_on_clause_in_rustc_on_unimplemented, code = "E0232")] pub struct EmptyOnClauseInOnUnimplemented { #[primary_span] diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 6c70bbf7516..50c1787ef8c 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -4,7 +4,7 @@ use crate::traits::{self, ObligationCtxt}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_middle::arena::ArenaAllocatable; -use rustc_middle::infer::canonical::{Canonical, CanonicalizedQueryResponse, QueryResponse}; +use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; use rustc_middle::traits::query::Fallible; use rustc_middle::ty::{self, Ty, TypeFoldable, TypeVisitable}; use rustc_middle::ty::{GenericArg, ToPredicate}; @@ -102,7 +102,7 @@ pub trait InferCtxtBuilderExt<'tcx> { &mut self, canonical_key: &Canonical<'tcx, K>, operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Fallible<R>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, R>> + ) -> Fallible<CanonicalQueryResponse<'tcx, R>> where K: TypeFoldable<'tcx>, R: Debug + TypeFoldable<'tcx>, @@ -130,7 +130,7 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> { &mut self, canonical_key: &Canonical<'tcx, K>, operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Fallible<R>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, R>> + ) -> Fallible<CanonicalQueryResponse<'tcx, R>> where K: TypeFoldable<'tcx>, R: Debug + TypeFoldable<'tcx>, diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index a30d1df4ede..081ac966c69 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -35,7 +35,6 @@ extern crate rustc_middle; #[macro_use] extern crate smallvec; -pub mod autoderef; pub mod errors; pub mod infer; pub mod solve; diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 80115d78d88..c014d682a9a 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -3,7 +3,10 @@ use std::mem; use rustc_data_structures::fx::FxHashMap; use rustc_infer::{ infer::InferCtxt, - traits::{query::NoSolution, FulfillmentError, PredicateObligation, TraitEngine}, + traits::{ + query::NoSolution, FulfillmentError, FulfillmentErrorCode, PredicateObligation, + SelectionError, TraitEngine, + }, }; use rustc_middle::ty; @@ -45,32 +48,43 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { return errors; } - if self.obligations.is_empty() { - Vec::new() - } else { - unimplemented!("ambiguous obligations") - } + self.obligations + .drain(..) + .map(|obligation| FulfillmentError { + obligation: obligation.clone(), + code: FulfillmentErrorCode::CodeSelectionError(SelectionError::Unimplemented), + root_obligation: obligation, + }) + .collect() } fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { - let errors = Vec::new(); + let mut errors = Vec::new(); for i in 0.. { if !infcx.tcx.recursion_limit().value_within_limit(i) { unimplemented!("overflow") } let mut has_changed = false; - for o in mem::take(&mut self.obligations) { + for obligation in mem::take(&mut self.obligations) { let mut cx = EvalCtxt::new(infcx.tcx); - let (changed, certainty) = match cx.evaluate_goal(infcx, o.clone().into()) { + let (changed, certainty) = match cx.evaluate_goal(infcx, obligation.clone().into()) + { Ok(result) => result, - Err(NoSolution) => unimplemented!("error"), + Err(NoSolution) => { + errors.push(FulfillmentError { + obligation: obligation.clone(), + code: FulfillmentErrorCode::CodeAmbiguity, + root_obligation: obligation, + }); + continue; + } }; has_changed |= changed; match certainty { Certainty::Yes => {} - Certainty::Maybe(_) => self.obligations.push(o), + Certainty::Maybe(_) => self.obligations.push(obligation), } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index b50f42c4d94..3d649bea19d 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -131,8 +131,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { else { return }; - - let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); + let where_clause_bounds = tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_substs) + .predicates + .into_iter() + .map(|pred| goal.with(tcx, pred)); + + let nested_goals = obligations.into_iter().map(|o| o.into()).chain(where_clause_bounds).collect(); let Ok(trait_ref_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; let Some(assoc_def) = fetch_eligible_assoc_item_def( diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 10b45a77dab..c69cc39acb5 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -71,7 +71,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal: Goal<'tcx, TraitPredicate<'tcx>>, impl_def_id: DefId, ) { - let impl_trait_ref = acx.cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap(); + let tcx = acx.cx.tcx; + + let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap(); let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder }; if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs) .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp)) @@ -81,7 +83,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { acx.infcx.probe(|_| { let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); - let impl_trait_ref = impl_trait_ref.subst(acx.cx.tcx, impl_substs); + let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); let Ok(InferOk { obligations, .. }) = acx .infcx @@ -92,8 +94,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { else { return }; - - let nested_goals = obligations.into_iter().map(|o| o.into()).collect(); + let where_clause_bounds = tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_substs) + .predicates + .into_iter() + .map(|pred| goal.with(tcx, pred)); + + let nested_goals = + obligations.into_iter().map(|o| o.into()).chain(where_clause_bounds).collect(); let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return }; acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty); diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 26757965c95..258d2e2d28c 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -749,7 +749,7 @@ impl<'tcx> TypeVisitor<'tcx> for OrphanChecker<'tcx> { /// /// This means that we can completely ignore constants during the orphan check. /// - /// See `src/test/ui/coherence/const-generics-orphan-check-ok.rs` for examples. + /// See `tests/ui/coherence/const-generics-orphan-check-ok.rs` for examples. /// /// [^1]: This might not hold for function pointers or trait objects in the future. /// As these should be quite rare as const arguments and especially rare as impl diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index f8efe9bfa9f..71fb6058cd2 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -168,24 +168,27 @@ fn satisfied_from_param_env<'tcx>( param_env: ty::ParamEnv<'tcx>, infcx: &'a InferCtxt<'tcx>, + single_match: Option<Result<ty::Const<'tcx>, ()>>, } + impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> { type BreakTy = (); fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { debug!("is_const_evaluatable: candidate={:?}", c); - if let Ok(()) = self.infcx.commit_if_ok(|_| { + if self.infcx.probe(|_| { let ocx = ObligationCtxt::new_in_snapshot(self.infcx); - if let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()) - && let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct) + ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()).is_ok() + && ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok() && ocx.select_all_or_error().is_empty() - { - Ok(()) - } else { - Err(()) - } }) { - ControlFlow::BREAK - } else if let ty::ConstKind::Expr(e) = c.kind() { + self.single_match = match self.single_match { + None => Some(Ok(c)), + Some(Ok(o)) if o == c => Some(Ok(c)), + Some(_) => Some(Err(())), + }; + } + + if let ty::ConstKind::Expr(e) = c.kind() { e.visit_with(self) } else { // FIXME(generic_const_exprs): This doesn't recurse into `<T as Trait<U>>::ASSOC`'s substs. @@ -200,22 +203,29 @@ fn satisfied_from_param_env<'tcx>( } } + let mut single_match: Option<Result<ty::Const<'tcx>, ()>> = None; + for pred in param_env.caller_bounds() { match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(ce) => { let b_ct = tcx.expand_abstract_consts(ce); - let mut v = Visitor { ct, infcx, param_env }; - let result = b_ct.visit_with(&mut v); + let mut v = Visitor { ct, infcx, param_env, single_match }; + let _ = b_ct.visit_with(&mut v); - if let ControlFlow::Break(()) = result { - debug!("is_const_evaluatable: yes"); - return true; - } + single_match = v.single_match; } _ => {} // don't care } } + if let Some(Ok(c)) = single_match { + let ocx = ObligationCtxt::new(infcx); + assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok()); + assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok()); + assert!(ocx.select_all_or_error().is_empty()); + return true; + } + debug!("is_const_evaluatable: no"); false } diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index c028e89e4ea..369f80139a8 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -3,12 +3,13 @@ use std::fmt::Debug; use super::TraitEngine; use super::{ChalkFulfillmentContext, FulfillmentContext}; +use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt; use crate::traits::NormalizeExt; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::{ - Canonical, CanonicalVarValues, CanonicalizedQueryResponse, QueryResponse, + Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, }; use rustc_infer::infer::{InferCtxt, InferOk}; use rustc_infer::traits::query::Fallible; @@ -20,6 +21,7 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::ToPredicate; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_session::config::TraitSolver; use rustc_span::Span; pub trait TraitEngineExt<'tcx> { @@ -29,18 +31,18 @@ pub trait TraitEngineExt<'tcx> { impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { fn new(tcx: TyCtxt<'tcx>) -> Box<Self> { - if tcx.sess.opts.unstable_opts.chalk { - Box::new(ChalkFulfillmentContext::new()) - } else { - Box::new(FulfillmentContext::new()) + match tcx.sess.opts.unstable_opts.trait_solver { + TraitSolver::Classic => Box::new(FulfillmentContext::new()), + TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new()), + TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()), } } fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self> { - if tcx.sess.opts.unstable_opts.chalk { - Box::new(ChalkFulfillmentContext::new_in_snapshot()) - } else { - Box::new(FulfillmentContext::new_in_snapshot()) + match tcx.sess.opts.unstable_opts.trait_solver { + TraitSolver::Classic => Box::new(FulfillmentContext::new_in_snapshot()), + TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new_in_snapshot()), + TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()), } } } @@ -213,7 +215,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { &self, inference_vars: CanonicalVarValues<'tcx>, answer: T, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, T>> + ) -> Fallible<CanonicalQueryResponse<'tcx, T>> where T: Debug + TypeFoldable<'tcx>, Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs index 27c207528c7..ba9ee57d409 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs @@ -55,7 +55,7 @@ impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> { fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { self.infcx.probe(|_| { - if a.is_ty_infer() || b.is_ty_infer() { + if a.is_ty_var() || b.is_ty_var() { Ok(a) } else { self.infcx.super_combine_tys(self, a, b).or_else(|e| { @@ -71,10 +71,13 @@ impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> { a: ty::Const<'tcx>, b: ty::Const<'tcx>, ) -> RelateResult<'tcx, ty::Const<'tcx>> { - if a == b { - return Ok(a); - } - relate::super_relate_consts(self, a, b) // could do something similar here for constants! + self.infcx.probe(|_| { + if a.is_ct_infer() || b.is_ct_infer() { + Ok(a) + } else { + relate::super_relate_consts(self, a, b) // could do something similar here for constants! + } + }) } fn binders<T: Relate<'tcx>>( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 8a08c7533aa..0c7ffb056cc 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -33,13 +33,14 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::abstract_const::NotConstEvaluatable; -use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print}; use rustc_middle::ty::{ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, }; +use rustc_session::config::TraitSolver; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; @@ -770,7 +771,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ), } }; - + self.check_for_binding_assigned_block_without_tail_expression( + &obligation, + &mut err, + trait_predicate, + ); if self.suggest_add_reference_to_arg( &obligation, &mut err, @@ -868,6 +873,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } + if self.suggest_add_clone_to_arg(&obligation, &mut err, trait_predicate) { + err.emit(); + return; + } + if self.suggest_impl_trait(&mut err, span, &obligation, trait_predicate) { err.emit(); return; @@ -1092,15 +1102,19 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) - | ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) => { - let predicate = self.resolve_vars_if_possible(obligation.predicate); - struct_span_err!( - self.tcx.sess, + span_bug!( + span, + "outlives clauses should not error outside borrowck. obligation: `{:?}`", + obligation + ) + } + + ty::PredicateKind::Clause(ty::Clause::Projection(..)) => { + span_bug!( span, - E0280, - "the requirement `{}` is not satisfied", - predicate + "projection clauses should be implied from elsewhere. obligation: `{:?}`", + obligation ) } @@ -1167,7 +1181,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ty::PredicateKind::WellFormed(ty) => { - if !self.tcx.sess.opts.unstable_opts.chalk { + if self.tcx.sess.opts.unstable_opts.trait_solver == TraitSolver::Classic { // WF predicates cannot themselves make // errors. They can only block due to // ambiguity; otherwise, they always @@ -1179,7 +1193,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // which bounds actually failed to hold. self.tcx.sess.struct_span_err( span, - &format!("the type `{}` is not well-formed (chalk)", ty), + &format!("the type `{}` is not well-formed", ty), ) } } @@ -1215,6 +1229,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } + OutputTypeParameterMismatch( + found_trait_ref, + expected_trait_ref, + terr @ TypeError::CyclicTy(_), + ) => { + let self_ty = found_trait_ref.self_ty().skip_binder(); + let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() { + ( + ObligationCause::dummy_with_span(tcx.def_span(def_id)), + TypeError::CyclicTy(self_ty), + ) + } else { + (obligation.cause.clone(), terr) + }; + self.report_and_explain_type_error( + TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref), + terr, + ) + } OutputTypeParameterMismatch(found_trait_ref, expected_trait_ref, _) => { let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref); let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref); @@ -1387,7 +1420,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.note_obligation_cause(&mut err, &obligation); self.point_at_returns_when_relevant(&mut err, &obligation); - err.emit(); } } @@ -1692,7 +1724,19 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .and_then(|(predicate, _, normalized_term, expected_term)| { self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term) }) - .unwrap_or_else(|| format!("type mismatch resolving `{}`", predicate)); + .unwrap_or_else(|| { + with_forced_trimmed_paths!(format!( + "type mismatch resolving `{}`", + self.resolve_vars_if_possible(predicate) + .print(FmtPrinter::new_with_limit( + self.tcx, + Namespace::TypeNS, + rustc_session::Limit(10), + )) + .unwrap() + .into_buffer() + )) + }); let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}"); let secondary_span = match predicate.kind().skip_binder() { @@ -1723,7 +1767,20 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { kind: hir::ImplItemKind::Type(ty), .. }), - ) => Some((ty.span, format!("type mismatch resolving `{}`", predicate))), + ) => Some(( + ty.span, + with_forced_trimmed_paths!(format!( + "type mismatch resolving `{}`", + self.resolve_vars_if_possible(predicate) + .print(FmtPrinter::new_with_limit( + self.tcx, + Namespace::TypeNS, + rustc_session::Limit(5), + )) + .unwrap() + .into_buffer() + )), + )), _ => None, }), _ => None, @@ -2195,8 +2252,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { Ok(None) => { let ambiguities = ambiguity::recompute_applicable_impls(self.infcx, &obligation); - let has_non_region_infer = - trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_infer()); + let has_non_region_infer = trait_ref + .skip_binder() + .substs + .types() + .any(|t| !t.is_ty_or_numeric_infer()); // It doesn't make sense to talk about applicable impls if there are more // than a handful of them. if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { @@ -2247,23 +2307,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) = (body_id, subst.map(|subst| subst.unpack())) { - struct FindExprBySpan<'hir> { - span: Span, - result: Option<&'hir hir::Expr<'hir>>, - } - - impl<'v> hir::intravisit::Visitor<'v> for FindExprBySpan<'v> { - fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { - if self.span == ex.span { - self.result = Some(ex); - } else { - hir::intravisit::walk_expr(self, ex); - } - } - } - - let mut expr_finder = FindExprBySpan { span, result: None }; - + let mut expr_finder = FindExprBySpan::new(span); expr_finder.visit_expr(&self.tcx.hir().body(body_id).value); if let Some(hir::Expr { @@ -2750,6 +2794,36 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } +/// Crude way of getting back an `Expr` from a `Span`. +pub struct FindExprBySpan<'hir> { + pub span: Span, + pub result: Option<&'hir hir::Expr<'hir>>, + pub ty_result: Option<&'hir hir::Ty<'hir>>, +} + +impl<'hir> FindExprBySpan<'hir> { + fn new(span: Span) -> Self { + Self { span, result: None, ty_result: None } + } +} + +impl<'v> Visitor<'v> for FindExprBySpan<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if self.span == ex.span { + self.result = Some(ex); + } else { + hir::intravisit::walk_expr(self, ex); + } + } + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + if self.span == ty.span { + self.ty_result = Some(ty); + } else { + hir::intravisit::walk_ty(self, ty); + } + } +} + /// Look for type `param` in an ADT being used only through a reference to confirm that suggesting /// `param: ?Sized` would be a valid constraint. struct FindTypeParam { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 9bfe527647d..e599996230f 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -37,6 +37,21 @@ pub trait TypeErrCtxtExt<'tcx> { ) -> OnUnimplementedNote; } +/// The symbols which are always allowed in a format string +static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[ + kw::SelfUpper, + sym::ItemContext, + sym::from_method, + sym::from_desugaring, + sym::direct, + sym::cause, + sym::integral, + sym::integer_, + sym::float, + sym::_Self, + sym::crate_local, +]; + impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn impl_similar_to( &self, @@ -117,7 +132,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { Some(if movability.is_some() { "an async closure" } else { "a closure" }) }), hir::Node::Expr(hir::Expr { .. }) => { - let parent_hid = hir.get_parent_node(hir_id); + let parent_hid = hir.parent_id(hir_id); if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None } } _ => None, @@ -543,38 +558,26 @@ impl<'tcx> OnUnimplementedFormatString { Piece::NextArgument(a) => match a.position { Position::ArgumentNamed(s) => { match Symbol::intern(s) { - // `{Self}` is allowed - kw::SelfUpper => (), // `{ThisTraitsName}` is allowed s if s == trait_name => (), - // `{from_method}` is allowed - sym::from_method => (), - // `{from_desugaring}` is allowed - sym::from_desugaring => (), - // `{ItemContext}` is allowed - sym::ItemContext => (), - // `{integral}` and `{integer}` and `{float}` are allowed - sym::integral | sym::integer_ | sym::float => (), + s if ALLOWED_FORMAT_SYMBOLS.contains(&s) => (), // So is `{A}` if A is a type parameter - s => match generics.params.iter().find(|param| param.name == s) { - Some(_) => (), - None => { - let reported = struct_span_err!( - tcx.sess, - span, - E0230, - "there is no parameter `{}` on {}", - s, - if trait_def_id == item_def_id { - format!("trait `{}`", trait_name) - } else { - "impl".to_string() - } - ) - .emit(); - result = Err(reported); - } - }, + s if generics.params.iter().any(|param| param.name == s) => (), + s => { + result = Err(struct_span_err!( + tcx.sess, + span, + E0230, + "there is no parameter `{}` on {}", + s, + if trait_def_id == item_def_id { + format!("trait `{}`", trait_name) + } else { + "impl".to_string() + } + ) + .emit()); + } } } // `{:1}` and `{}` are not to be used diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 7c21a1047bc..1b98ead29f8 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1,13 +1,15 @@ // ignore-tidy-filelength -use super::{DefIdOrName, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation}; +use super::{ + DefIdOrName, FindExprBySpan, Obligation, ObligationCause, ObligationCauseCode, + PredicateObligation, +}; -use crate::autoderef::Autoderef; use crate::infer::InferCtxt; use crate::traits::{NormalizeExt, ObligationCtxt}; use hir::def::CtorOf; -use hir::HirId; +use hir::{Expr, HirId}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ @@ -33,7 +35,7 @@ use rustc_middle::ty::{ TypeSuperFoldable, TypeVisitable, TypeckResults, }; use rustc_span::symbol::{sym, Ident, Symbol}; -use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span, DUMMY_SP}; +use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP}; use rustc_target::spec::abi; use std::ops::Deref; @@ -196,6 +198,20 @@ pub trait TypeErrCtxtExt<'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool; + fn check_for_binding_assigned_block_without_tail_expression( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ); + + fn suggest_add_clone_to_arg( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool; + fn suggest_add_reference_to_arg( &self, obligation: &PredicateObligation<'tcx>, @@ -248,7 +264,7 @@ pub trait TypeErrCtxtExt<'tcx> { fn point_at_returns_when_relevant( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, obligation: &PredicateObligation<'tcx>, ); @@ -733,26 +749,30 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } if let ty::Ref(region, base_ty, mutbl) = *real_ty.skip_binder().kind() { - let mut autoderef = Autoderef::new( - self, - obligation.param_env, - obligation.cause.body_id, - span, - base_ty, - ); - if let Some(steps) = autoderef.find_map(|(ty, steps)| { - // Re-add the `&` - let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); - - // Remapping bound vars here - let real_trait_pred_and_ty = - real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty)); - let obligation = self.mk_trait_obligation_with_new_self_ty( - obligation.param_env, - real_trait_pred_and_ty, - ); - Some(steps).filter(|_| self.predicate_may_hold(&obligation)) - }) { + let autoderef = (self.autoderef_steps)(base_ty); + if let Some(steps) = + autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| { + // Re-add the `&` + let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); + + // Remapping bound vars here + let real_trait_pred_and_ty = + real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty)); + let obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + real_trait_pred_and_ty, + ); + if obligations + .iter() + .chain([&obligation]) + .all(|obligation| self.predicate_may_hold(obligation)) + { + Some(steps) + } else { + None + } + }) + { if steps > 0 { // Don't care about `&mut` because `DerefMut` is used less // often and user will not expect autoderef happens. @@ -838,8 +858,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let hir = self.tcx.hir(); let hir_id = hir.local_def_id_to_hir_id(def_id.as_local()?); - let parent_node = hir.get_parent_node(hir_id); - match hir.find(parent_node) { + match hir.find_parent(hir_id) { Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => { get_name(err, &local.pat.kind) } @@ -1033,6 +1052,115 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { true } + fn check_for_binding_assigned_block_without_tail_expression( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) { + let mut span = obligation.cause.span; + while span.from_expansion() { + // Remove all the desugaring and macro contexts. + span.remove_mark(); + } + let mut expr_finder = FindExprBySpan::new(span); + let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else { return; }; + expr_finder.visit_expr(&body); + let Some(expr) = expr_finder.result else { return; }; + let Some(typeck) = &self.typeck_results else { return; }; + let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else { return; }; + if !ty.is_unit() { + return; + }; + let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else { return; }; + let hir::def::Res::Local(hir_id) = path.res else { return; }; + let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else { + return; + }; + let Some(hir::Node::Local(hir::Local { + ty: None, + init: Some(init), + .. + })) = self.tcx.hir().find_parent(pat.hir_id) else { return; }; + let hir::ExprKind::Block(block, None) = init.kind else { return; }; + if block.expr.is_some() { + return; + } + let [.., stmt] = block.stmts else { + err.span_label(block.span, "this empty block is missing a tail expression"); + return; + }; + let hir::StmtKind::Semi(tail_expr) = stmt.kind else { return; }; + let Some(ty) = typeck.expr_ty_opt(tail_expr) else { + err.span_label(block.span, "this block is missing a tail expression"); + return; + }; + let ty = self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(ty)); + let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, ty)); + + let new_obligation = + self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self); + if self.predicate_must_hold_modulo_regions(&new_obligation) { + err.span_suggestion_short( + stmt.span.with_lo(tail_expr.span.hi()), + "remove this semicolon", + "", + Applicability::MachineApplicable, + ); + } else { + err.span_label(block.span, "this block is missing a tail expression"); + } + } + + fn suggest_add_clone_to_arg( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); + let ty = self.tcx.erase_late_bound_regions(self_ty); + let owner = self.tcx.hir().get_parent_item(obligation.cause.body_id); + let Some(generics) = self.tcx.hir().get_generics(owner.def_id) else { return false }; + let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false }; + let ty::Param(param) = inner_ty.kind() else { return false }; + let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = obligation.cause.code() else { return false }; + let arg_node = self.tcx.hir().get(*arg_hir_id); + let Node::Expr(Expr { kind: hir::ExprKind::Path(_), ..}) = arg_node else { return false }; + + let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None); + let has_clone = |ty| { + self.type_implements_trait(clone_trait, [ty], obligation.param_env) + .must_apply_modulo_regions() + }; + + let new_obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)), + ); + + if self.predicate_may_hold(&new_obligation) && has_clone(ty) { + if !has_clone(param.to_ty(self.tcx)) { + suggest_constraining_type_param( + self.tcx, + generics, + err, + param.name.as_str(), + "Clone", + Some(clone_trait), + ); + } + err.span_suggestion_verbose( + obligation.cause.span.shrink_to_hi(), + "consider using clone here", + ".clone()".to_string(), + Applicability::MaybeIncorrect, + ); + return true; + } + false + } + fn suggest_add_reference_to_arg( &self, obligation: &PredicateObligation<'tcx>, @@ -1233,57 +1361,117 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { - let span = obligation.cause.span; + let mut span = obligation.cause.span; + let mut trait_pred = trait_pred; + let mut code = obligation.cause.code(); + while let Some((c, Some(parent_trait_pred))) = code.parent() { + // We want the root obligation, in order to detect properly handle + // `for _ in &mut &mut vec![] {}`. + code = c; + trait_pred = parent_trait_pred; + } + while span.desugaring_kind().is_some() { + // Remove all the hir desugaring contexts while maintaining the macro contexts. + span.remove_mark(); + } + let mut expr_finder = super::FindExprBySpan::new(span); + let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else { + return false; + }; + expr_finder.visit_expr(&body); + let mut maybe_suggest = |suggested_ty, count, suggestions| { + // Remapping bound vars here + let trait_pred_and_suggested_ty = + trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty)); + + let new_obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + trait_pred_and_suggested_ty, + ); - let mut suggested = false; - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - let refs_number = - snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count(); - if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) { - // Do not suggest removal of borrow from type arguments. - return false; + if self.predicate_may_hold(&new_obligation) { + let msg = if count == 1 { + "consider removing the leading `&`-reference".to_string() + } else { + format!("consider removing {count} leading `&`-references") + }; + + err.multipart_suggestion_verbose( + &msg, + suggestions, + Applicability::MachineApplicable, + ); + true + } else { + false } + }; - // Skipping binder here, remapping below - let mut suggested_ty = trait_pred.self_ty().skip_binder(); + // Maybe suggest removal of borrows from types in type parameters, like in + // `src/test/ui/not-panic/not-panic-safe.rs`. + let mut count = 0; + let mut suggestions = vec![]; + // Skipping binder here, remapping below + let mut suggested_ty = trait_pred.self_ty().skip_binder(); + if let Some(mut hir_ty) = expr_finder.ty_result { + while let hir::TyKind::Ref(_, mut_ty) = &hir_ty.kind { + count += 1; + let span = hir_ty.span.until(mut_ty.ty.span); + suggestions.push((span, String::new())); - for refs_remaining in 0..refs_number { let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else { break; }; suggested_ty = *inner_ty; - // Remapping bound vars here - let trait_pred_and_suggested_ty = - trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty)); + hir_ty = mut_ty.ty; - let new_obligation = self.mk_trait_obligation_with_new_self_ty( - obligation.param_env, - trait_pred_and_suggested_ty, - ); + if maybe_suggest(suggested_ty, count, suggestions.clone()) { + return true; + } + } + } - if self.predicate_may_hold(&new_obligation) { - let sp = self - .tcx - .sess - .source_map() - .span_take_while(span, |c| c.is_whitespace() || *c == '&'); + // Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`. + let Some(mut expr) = expr_finder.result else { return false; }; + let mut count = 0; + let mut suggestions = vec![]; + // Skipping binder here, remapping below + let mut suggested_ty = trait_pred.self_ty().skip_binder(); + 'outer: loop { + while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind { + count += 1; + let span = if expr.span.eq_ctxt(borrowed.span) { + expr.span.until(borrowed.span) + } else { + expr.span.with_hi(expr.span.lo() + BytePos(1)) + }; + suggestions.push((span, String::new())); - let remove_refs = refs_remaining + 1; + let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else { + break 'outer; + }; + suggested_ty = *inner_ty; - let msg = if remove_refs == 1 { - "consider removing the leading `&`-reference".to_string() - } else { - format!("consider removing {} leading `&`-references", remove_refs) - }; + expr = borrowed; - err.span_suggestion_short(sp, &msg, "", Applicability::MachineApplicable); - suggested = true; - break; + if maybe_suggest(suggested_ty, count, suggestions.clone()) { + return true; } } + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let hir::def::Res::Local(hir_id) = path.res + && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(hir_id) + && let Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id) + && let None = local.ty + && let Some(binding_expr) = local.init + { + expr = binding_expr; + } else { + break 'outer; + } } - suggested + false } fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) { @@ -1394,6 +1582,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .source_map() .span_take_while(span, |c| c.is_whitespace() || *c == '&'); if points_at_arg && mutability.is_not() && refs_number > 0 { + // If we have a call like foo(&mut buf), then don't suggest foo(&mut mut buf) + if snippet + .trim_start_matches(|c: char| c.is_whitespace() || c == '&') + .starts_with("mut") + { + return; + } err.span_suggestion_verbose( sp, "consider changing this borrow's mutability", @@ -1421,7 +1616,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { let hir = self.tcx.hir(); - let parent_node = hir.get_parent_node(obligation.cause.body_id); + let parent_node = hir.parent_id(obligation.cause.body_id); let node = hir.find(parent_node); if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. })) = node && let hir::ExprKind::Block(blk, _) = &hir.body(*body_id).value.kind @@ -1458,7 +1653,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> { let hir = self.tcx.hir(); - let parent_node = hir.get_parent_node(obligation.cause.body_id); + let parent_node = hir.parent_id(obligation.cause.body_id); let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) = hir.find(parent_node) else { return None; }; @@ -1483,7 +1678,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } let hir = self.tcx.hir(); - let fn_hir_id = hir.get_parent_node(obligation.cause.body_id); + let fn_hir_id = hir.parent_id(obligation.cause.body_id); let node = hir.find(fn_hir_id); let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), @@ -1686,7 +1881,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn point_at_returns_when_relevant( &self, - err: &mut Diagnostic, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, obligation: &PredicateObligation<'tcx>, ) { match obligation.cause.code().peel_derives() { @@ -1695,7 +1890,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } let hir = self.tcx.hir(); - let parent_node = hir.get_parent_node(obligation.cause.body_id); + let parent_node = hir.parent_id(obligation.cause.body_id); let node = hir.find(parent_node); if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) = node @@ -1708,7 +1903,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { for expr in &visitor.returns { if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) { let ty = self.resolve_vars_if_possible(returned_ty); - err.span_label(expr.span, &format!("this returned value is of type `{}`", ty)); + if ty.references_error() { + // don't print out the [type error] here + err.delay_as_bug(); + } else { + err.span_label( + expr.span, + &format!("this returned value is of type `{}`", ty), + ); + } } } } @@ -2291,7 +2494,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let expr = hir.expect_expr(expr_id); debug!("target_ty evaluated from {:?}", expr); - let parent = hir.get_parent_node(expr_id); + let parent = hir.parent_id(expr_id); if let Some(hir::Node::Expr(e)) = hir.find(parent) { let parent_span = hir.span(parent); let parent_did = parent.owner.to_def_id(); @@ -2482,11 +2685,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } ObligationCauseCode::ObjectCastObligation(concrete_ty, object_ty) => { - err.note(&format!( - "required for the cast from `{}` to the object type `{}`", - self.ty_to_string(concrete_ty), - self.ty_to_string(object_ty) - )); + let (concrete_ty, concrete_file) = + self.tcx.short_ty_string(self.resolve_vars_if_possible(concrete_ty)); + let (object_ty, object_file) = + self.tcx.short_ty_string(self.resolve_vars_if_possible(object_ty)); + err.note(&with_forced_trimmed_paths!(format!( + "required for the cast from `{concrete_ty}` to the object type `{object_ty}`", + ))); + if let Some(file) = concrete_file { + err.note(&format!( + "the full name for the casted type has been written to '{}'", + file.display(), + )); + } + if let Some(file) = object_file { + err.note(&format!( + "the full name for the object type has been written to '{}'", + file.display(), + )); + } } ObligationCauseCode::Coercion { source: _, target } => { err.note(&format!("required by cast to type `{}`", self.ty_to_string(target))); @@ -2512,7 +2729,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } ObligationCauseCode::VariableType(hir_id) => { - let parent_node = self.tcx.hir().get_parent_node(hir_id); + let parent_node = self.tcx.hir().parent_id(hir_id); match self.tcx.hir().find(parent_node) { Some(Node::Local(hir::Local { ty: Some(ty), .. })) => { err.span_suggestion_verbose( @@ -2809,7 +3026,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // FIXME: we should do something else so that it works even on crate foreign // auto traits. is_auto_trait = matches!(is_auto, hir::IsAuto::Yes); - err.span_note(ident.span, &msg) + err.span_note(ident.span, &msg); } Some(Node::Item(hir::Item { kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), @@ -2820,9 +3037,29 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { spans.push(trait_ref.path.span); } spans.push(self_ty.span); - err.span_note(spans, &msg) + let mut spans: MultiSpan = spans.into(); + if matches!( + self_ty.span.ctxt().outer_expn_data().kind, + ExpnKind::Macro(MacroKind::Derive, _) + ) || matches!( + of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind), + Some(ExpnKind::Macro(MacroKind::Derive, _)) + ) { + spans.push_span_label( + data.span, + "unsatisfied trait bound introduced in this `derive` macro", + ); + } else if !data.span.is_dummy() && !data.span.overlaps(self_ty.span) { + spans.push_span_label( + data.span, + "unsatisfied trait bound introduced here", + ); + } + err.span_note(spans, &msg); + } + _ => { + err.note(&msg); } - _ => err.note(&msg), }; if let Some(file) = file { @@ -2992,7 +3229,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { span: Span, ) { let body_hir_id = obligation.cause.body_id; - let item_id = self.tcx.hir().get_parent_node(body_hir_id); + let item_id = self.tcx.hir().parent_id(body_hir_id); if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(self.tcx.hir().local_def_id(item_id)) @@ -3219,7 +3456,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id) - && let parent_hir_id = self.tcx.hir().get_parent_node(binding.hir_id) + && let parent_hir_id = self.tcx.hir().parent_id(binding.hir_id) && let Some(hir::Node::Local(local)) = self.tcx.hir().find(parent_hir_id) && let Some(binding_expr) = local.init { @@ -3287,8 +3524,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id) - && let parent_hir_id = self.tcx.hir().get_parent_node(binding.hir_id) - && let Some(parent) = self.tcx.hir().find(parent_hir_id) + && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id) { // We've reached the root of the method call chain... if let hir::Node::Local(local) = parent diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index c30531fa906..37b40a2f75a 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -102,7 +102,7 @@ pub enum TraitQueryMode { /// spans etc. passed in and hence can do reasonable /// error reporting on their own. Standard, - /// Canonicalized queries get dummy spans and hence + /// Canonical queries get dummy spans and hence /// must generally propagate errors to /// pre-canonicalization callsites. Canonical, @@ -450,9 +450,6 @@ pub fn impossible_predicates<'tcx>( } let errors = ocx.select_all_or_error(); - // Clean up after ourselves - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - let result = !errors.is_empty(); debug!("impossible_predicates = {:?}", result); result diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index f7614997585..81966f3fcb2 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -282,7 +282,7 @@ fn project_and_unify_type<'cx, 'tcx>( }; debug!(?normalized, ?obligations, "project_and_unify_type result"); let actual = obligation.predicate.term; - // For an example where this is necessary see src/test/ui/impl-trait/nested-return-type2.rs + // For an example where this is necessary see tests/ui/impl-trait/nested-return-type2.rs // This allows users to omit re-mentioning all bounds on an associated type and just use an // `impl Trait` for the assoc type to add more bounds. let InferOk { value: actual, obligations: new } = diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 86b015767f0..e6db96c9e55 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -1,4 +1,4 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; use crate::traits::query::Fallible; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; @@ -16,8 +16,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> { + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, ()>> { tcx.type_op_ascribe_user_type(canonicalized) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs index 490114aacd1..8c9b9610cb6 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs @@ -1,4 +1,4 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; use crate::traits::query::Fallible; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; @@ -16,8 +16,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> { + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, ()>> { tcx.type_op_eq(canonicalized) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index 2a3319f0f26..18d7c9b1936 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -1,4 +1,4 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; use crate::traits::query::Fallible; use rustc_infer::traits::query::OutlivesBound; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt}; @@ -27,8 +27,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>> { + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, Self::QueryResponse>> { // FIXME this `unchecked_map` is only necessary because the // query is defined as taking a `ParamEnvAnd<Ty>`; it should // take an `ImpliedOutlivesBounds` instead diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 29ae8ae6b6e..97002b461aa 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -1,10 +1,10 @@ use crate::infer::canonical::{ - Canonicalized, CanonicalizedQueryResponse, OriginalQueryValues, QueryRegionConstraints, + Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints, }; use crate::infer::{InferCtxt, InferOk}; use crate::traits::query::Fallible; use crate::traits::ObligationCause; -use rustc_infer::infer::canonical::{Canonical, Certainty}; +use rustc_infer::infer::canonical::Certainty; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::PredicateObligations; use rustc_middle::ty::fold::TypeFoldable; @@ -73,8 +73,8 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<'tcx> + 'tcx { /// not captured in the return value. fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>>; + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, Self::QueryResponse>>; fn fully_perform_into( query_key: ParamEnvAnd<'tcx, Self>, diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index e92ca7325d3..8f0b4de31e6 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -1,4 +1,4 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; use crate::traits::query::Fallible; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt}; @@ -18,8 +18,8 @@ where fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>> { + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, Self::QueryResponse>> { T::type_op_method(tcx, canonicalized) } } @@ -27,15 +27,15 @@ where pub trait Normalizable<'tcx>: fmt::Debug + TypeFoldable<'tcx> + Lift<'tcx> + Copy { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>>; + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, Self>>; } impl<'tcx> Normalizable<'tcx> for Ty<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>> { + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, Self>> { tcx.type_op_normalize_ty(canonicalized) } } @@ -43,8 +43,8 @@ impl<'tcx> Normalizable<'tcx> for Ty<'tcx> { impl<'tcx> Normalizable<'tcx> for ty::Predicate<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>> { + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, Self>> { tcx.type_op_normalize_predicate(canonicalized) } } @@ -52,8 +52,8 @@ impl<'tcx> Normalizable<'tcx> for ty::Predicate<'tcx> { impl<'tcx> Normalizable<'tcx> for ty::PolyFnSig<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>> { + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, Self>> { tcx.type_op_normalize_poly_fn_sig(canonicalized) } } @@ -61,8 +61,8 @@ impl<'tcx> Normalizable<'tcx> for ty::PolyFnSig<'tcx> { impl<'tcx> Normalizable<'tcx> for ty::FnSig<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self>> { + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, Self>> { tcx.type_op_normalize_fn_sig(canonicalized) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index b63382429d0..0d42cd8250a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -1,4 +1,4 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; use crate::traits::query::dropck_outlives::{trivial_dropck_outlives, DropckOutlivesResult}; use crate::traits::query::Fallible; use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; @@ -30,8 +30,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, Self::QueryResponse>> { + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, Self::QueryResponse>> { // Subtle: note that we are not invoking // `infcx.at(...).dropck_outlives(...)` here, but rather the // underlying `dropck_outlives` query. This same underlying diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 68434c2b68d..b63da28e274 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -1,4 +1,4 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; use crate::traits::query::Fallible; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; @@ -32,8 +32,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - mut canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> { + mut canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, ()>> { match canonicalized.value.value.predicate.kind().skip_binder() { ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => { canonicalized.value.param_env.remap_constness_with(pred.constness); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs index 57290b66914..c51292eba14 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs @@ -1,4 +1,4 @@ -use crate::infer::canonical::{Canonicalized, CanonicalizedQueryResponse}; +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; use crate::traits::query::Fallible; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; @@ -13,8 +13,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> { + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Fallible<CanonicalQueryResponse<'tcx, ()>> { tcx.type_op_subtype(canonicalized) } } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index c54d901e9b1..8c291d1595d 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -174,7 +174,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .param_env .caller_bounds() .iter() - .filter_map(|o| o.to_opt_poly_trait_pred()); + .filter_map(|p| p.to_opt_poly_trait_pred()) + .filter(|p| !p.references_error()); // Micro-optimization: filter out predicates relating to different traits. let matching_bounds = @@ -254,18 +255,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // touch bound regions, they just capture the in-scope // type/region parameters match *obligation.self_ty().skip_binder().kind() { - ty::Closure(_, closure_substs) => { + ty::Closure(def_id, closure_substs) => { + let is_const = self.tcx().is_const_fn_raw(def_id); debug!(?kind, ?obligation, "assemble_unboxed_candidates"); match self.infcx.closure_kind(closure_substs) { Some(closure_kind) => { debug!(?closure_kind, "assemble_unboxed_candidates"); if closure_kind.extends(kind) { - candidates.vec.push(ClosureCandidate); + candidates.vec.push(ClosureCandidate { is_const }); } } None => { debug!("assemble_unboxed_candidates: closure_kind not yet known"); - candidates.vec.push(ClosureCandidate); + candidates.vec.push(ClosureCandidate { is_const }); } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 466641ea6df..a41d10f1043 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -15,6 +15,7 @@ use rustc_middle::ty::{ self, Binder, GenericArg, GenericArgKind, GenericParamDefKind, InternalSubsts, SubstsRef, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, }; +use rustc_session::config::TraitSolver; use rustc_span::def_id::DefId; use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; @@ -83,7 +84,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Object(data) } - ClosureCandidate => { + ClosureCandidate { .. } => { let vtable_closure = self.confirm_closure_candidate(obligation)?; ImplSource::Closure(vtable_closure) } @@ -767,8 +768,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations"); // FIXME: Chalk - - if !self.tcx().sess.opts.unstable_opts.chalk { + if self.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Chalk { nested.push(obligation.with( self.tcx(), ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, substs, kind)), diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 760b4585f4e..305902af7c8 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -755,7 +755,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // contain the "'static" lifetime (any other lifetime // would either be late-bound or local), so it is guaranteed // to outlive any other lifetime - if pred.0.is_global() && !pred.0.has_late_bound_regions() { + if pred.0.is_global() && !pred.0.has_late_bound_vars() { Ok(EvaluatedToOk) } else { Ok(EvaluatedToOkModuloRegions) @@ -1365,15 +1365,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // const param ParamCandidate(trait_pred) if trait_pred.is_const_if_const() => {} // const projection - ProjectionCandidate(_, ty::BoundConstness::ConstIfConst) => {} + ProjectionCandidate(_, ty::BoundConstness::ConstIfConst) // auto trait impl - AutoImplCandidate => {} + | AutoImplCandidate // generator / future, this will raise error in other places // or ignore error with const_async_blocks feature - GeneratorCandidate => {} - FutureCandidate => {} + | GeneratorCandidate + | FutureCandidate // FnDef where the function is const - FnPointerCandidate { is_const: true } => {} + | FnPointerCandidate { is_const: true } + | ConstDestructCandidate(_) + | ClosureCandidate { is_const: true } => {} + FnPointerCandidate { is_const: false } => { if let ty::FnDef(def_id, _) = obligation.self_ty().skip_binder().kind() && tcx.trait_of_item(*def_id).is_some() { // Trait methods are not seen as const unless the trait is implemented as const. @@ -1382,7 +1385,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { continue } } - ConstDestructCandidate(_) => {} + _ => { // reject all other types of candidates continue; @@ -1785,9 +1788,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Check if a bound would previously have been removed when normalizing // the param_env so that it can be given the lowest priority. See // #50825 for the motivation for this. - let is_global = |cand: &ty::PolyTraitPredicate<'tcx>| { - cand.is_global() && !cand.has_late_bound_regions() - }; + let is_global = + |cand: &ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_late_bound_vars(); // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, // `DiscriminantKindCandidate`, `ConstDestructCandidate` @@ -1845,7 +1847,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ( ParamCandidate(ref cand), ImplCandidate(..) - | ClosureCandidate + | ClosureCandidate { .. } | GeneratorCandidate | FutureCandidate | FnPointerCandidate { .. } @@ -1864,7 +1866,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ( ImplCandidate(_) - | ClosureCandidate + | ClosureCandidate { .. } | GeneratorCandidate | FutureCandidate | FnPointerCandidate { .. } @@ -1895,7 +1897,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ( ObjectCandidate(_) | ProjectionCandidate(..), ImplCandidate(..) - | ClosureCandidate + | ClosureCandidate { .. } | GeneratorCandidate | FutureCandidate | FnPointerCandidate { .. } @@ -1908,7 +1910,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ( ImplCandidate(..) - | ClosureCandidate + | ClosureCandidate { .. } | GeneratorCandidate | FutureCandidate | FnPointerCandidate { .. } @@ -1990,7 +1992,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Everything else is ambiguous ( ImplCandidate(_) - | ClosureCandidate + | ClosureCandidate { .. } | GeneratorCandidate | FutureCandidate | FnPointerCandidate { .. } @@ -2000,7 +2002,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinCandidate { has_nested: true } | TraitAliasCandidate, ImplCandidate(_) - | ClosureCandidate + | ClosureCandidate { .. } | GeneratorCandidate | FutureCandidate | FnPointerCandidate { .. } @@ -2378,6 +2380,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let impl_substs = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id); let impl_trait_ref = impl_trait_ref.subst(self.tcx(), impl_substs); + if impl_trait_ref.references_error() { + return Err(()); + } debug!(?impl_trait_ref); diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 0e0a883d9f5..fec4047ff49 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -232,7 +232,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( // The obligation comes not from the current `impl` nor the `trait` being implemented, // but rather from a "second order" obligation, where an associated type has a // projection coming from another associated type. See - // `src/test/ui/associated-types/point-at-type-on-obligation-failure.rs` and + // `tests/ui/associated-types/point-at-type-on-obligation-failure.rs` and // `traits-assoc-type-in-supertrait-bad.rs`. if let Some(ty::Alias(ty::Projection, projection_ty)) = proj.term.ty().map(|ty| ty.kind()) && let Some(&impl_item_id) = @@ -640,7 +640,7 @@ impl<'tcx> WfPredicates<'tcx> { // hidden type that is not actually well formed and // can cause compiler crashes when the user abuses unsafe // code to procure such a closure. - // See src/test/ui/type-alias-impl-trait/wf_check_closures.rs + // See tests/ui/type-alias-impl-trait/wf_check_closures.rs let obligations = self.nominal_obligations(did, substs); self.out.extend(obligations); } diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index f8f74b732ef..f127ef8343f 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -82,7 +82,7 @@ pub fn codegen_select_candidate<'tcx>( // Opaque types may have gotten their hidden types constrained, but we can ignore them safely // as they will get constrained elsewhere, too. // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + let _ = infcx.take_opaque_types(); Ok(&*tcx.arena.alloc(impl_source)) } diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index 3f661ce6923..481b56e111e 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -189,7 +189,7 @@ fn dtorck_constraint_for_ty<'tcx>( tcx.sess.delay_span_bug( span, - &format!("upvar_tys for closure not found. Expected capture information for closure {}", ty,), + &format!("upvar_tys for closure not found. Expected capture information for closure {ty}",), ); return Err(NoSolution); } @@ -231,7 +231,7 @@ fn dtorck_constraint_for_ty<'tcx>( // be fully resolved. tcx.sess.delay_span_bug( span, - &format!("upvar_tys for generator not found. Expected capture information for generator {}", ty,), + &format!("upvar_tys for generator not found. Expected capture information for generator {ty}",), ); return Err(NoSolution); } diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 6e6bc62a040..5cad2c2ccb0 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -47,7 +47,7 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq + // us a test case. debug_assert_eq!(normalized_value, resolved_value); let erased = infcx.tcx.erase_regions(resolved_value); - debug_assert!(!erased.needs_infer(), "{:?}", erased); + debug_assert!(!erased.needs_infer(), "{erased:?}"); Ok(erased) } Err(NoSolution) => Err(NoSolution), diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index 7f964afde80..aa5c83ac2e6 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -4,8 +4,8 @@ use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt}; use rustc_infer::traits::ObligationCauseCode; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable}; -use rustc_middle::ty::{ParamEnvAnd, Predicate, ToPredicate}; -use rustc_middle::ty::{UserSelfTy, UserSubsts}; +use rustc_middle::ty::{ParamEnvAnd, Predicate}; +use rustc_middle::ty::{UserSelfTy, UserSubsts, UserType}; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; @@ -50,13 +50,46 @@ pub fn type_op_ascribe_user_type_with_span<'tcx>( key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>, span: Option<Span>, ) -> Result<(), NoSolution> { - let (param_env, AscribeUserType { mir_ty, def_id, user_substs }) = key.into_parts(); - debug!( - "type_op_ascribe_user_type: mir_ty={:?} def_id={:?} user_substs={:?}", - mir_ty, def_id, user_substs - ); + let (param_env, AscribeUserType { mir_ty, user_ty }) = key.into_parts(); + debug!("type_op_ascribe_user_type: mir_ty={:?} user_ty={:?}", mir_ty, user_ty); let span = span.unwrap_or(DUMMY_SP); + match user_ty { + UserType::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?, + UserType::TypeOf(def_id, user_substs) => { + relate_mir_and_user_substs(ocx, param_env, span, mir_ty, def_id, user_substs)? + } + }; + Ok(()) +} + +#[instrument(level = "debug", skip(ocx, param_env, span))] +fn relate_mir_and_user_ty<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + mir_ty: Ty<'tcx>, + user_ty: Ty<'tcx>, +) -> Result<(), NoSolution> { + let cause = ObligationCause::dummy_with_span(span); + let user_ty = ocx.normalize(&cause, param_env, user_ty); + ocx.eq(&cause, param_env, mir_ty, user_ty)?; + // FIXME(#104764): We should check well-formedness before normalization. + let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(user_ty.into())); + ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate)); + + Ok(()) +} + +#[instrument(level = "debug", skip(ocx, param_env, span))] +fn relate_mir_and_user_substs<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + mir_ty: Ty<'tcx>, + def_id: hir::def_id::DefId, + user_substs: UserSubsts<'tcx>, +) -> Result<(), NoSolution> { let UserSubsts { user_self_ty, substs } = user_substs; let tcx = ocx.infcx.tcx; let cause = ObligationCause::dummy_with_span(span); @@ -91,13 +124,13 @@ pub fn type_op_ascribe_user_type_with_span<'tcx>( } if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { + let self_ty = ocx.normalize(&cause, param_env, self_ty); let impl_self_ty = tcx.bound_type_of(impl_def_id).subst(tcx, substs); let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty); ocx.eq(&cause, param_env, self_ty, impl_self_ty)?; - let predicate: Predicate<'tcx> = - ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into())).to_predicate(tcx); + let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into())); ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate)); } @@ -112,8 +145,7 @@ pub fn type_op_ascribe_user_type_with_span<'tcx>( // them? This would only be relevant if some input // type were ill-formed but did not appear in `ty`, // which...could happen with normalization... - let predicate: Predicate<'tcx> = - ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())).to_predicate(tcx); + let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())); ocx.register_obligation(Obligation::new(tcx, cause, param_env, predicate)); Ok(()) } diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs index 07035ebdfb1..f8d05bc89d2 100644 --- a/compiler/rustc_transmute/src/layout/mod.rs +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -24,7 +24,7 @@ impl fmt::Debug for Byte { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self { Self::Uninit => f.write_str("??u8"), - Self::Init(b) => write!(f, "{:#04x}u8", b), + Self::Init(b) => write!(f, "{b:#04x}u8"), } } } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 73d2d278f93..dc1dd1bfaf8 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -35,7 +35,7 @@ fn fn_sig_for_fn_abi<'tcx>( // HACK(davidtwco,eddyb): This is a workaround for polymorphization considering // parameters unused if they show up in the signature, but not in the `mir::Body` // (i.e. due to being inside a projection that got normalized, see - // `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping + // `tests/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping // track of a polymorphization `ParamEnv` to allow normalizing later. // // We normalize the `fn_sig` again after substituting at a later point. @@ -254,7 +254,12 @@ fn adjust_for_rust_scalar<'tcx>( // The aliasing rules for `Box<T>` are still not decided, but currently we emit // `noalias` for it. This can be turned off using an unstable flag. // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326 - let noalias_for_box = cx.tcx.sess.opts.unstable_opts.box_noalias.unwrap_or(true); + let noalias_for_box = cx.tcx.sess.opts.unstable_opts.box_noalias; + + // LLVM prior to version 12 had known miscompiles in the presence of noalias attributes + // (see #54878), so it was conditionally disabled, but we don't support earlier + // versions at all anymore. We still support turning it off using -Zmutable-noalias. + let noalias_mut_ref = cx.tcx.sess.opts.unstable_opts.mutable_noalias; // `&mut` pointer parameters never alias other parameters, // or mutable global data @@ -263,15 +268,9 @@ fn adjust_for_rust_scalar<'tcx>( // and can be marked as both `readonly` and `noalias`, as // LLVM's definition of `noalias` is based solely on memory // dependencies rather than pointer equality - // - // Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute - // for UniqueBorrowed arguments, so that the codegen backend can decide whether - // or not to actually emit the attribute. It can also be controlled with the - // `-Zmutable-noalias` debugging option. let no_alias = match kind { - PointerKind::SharedMutable - | PointerKind::UniqueBorrowed - | PointerKind::UniqueBorrowedPinned => false, + PointerKind::SharedMutable | PointerKind::UniqueBorrowedPinned => false, + PointerKind::UniqueBorrowed => noalias_mut_ref, PointerKind::UniqueOwned => noalias_for_box, PointerKind::Frozen => true, }; @@ -284,10 +283,6 @@ fn adjust_for_rust_scalar<'tcx>( if kind == PointerKind::Frozen && !is_return { attrs.set(ArgAttribute::ReadOnly); } - - if kind == PointerKind::UniqueBorrowed && !is_return { - attrs.set(ArgAttribute::NoAliasMutRef); - } } } } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index c8c6acaa453..6aa016133ca 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -155,17 +155,37 @@ fn layout_of_uncached<'tcx>( } let unsized_part = tcx.struct_tail_erasing_lifetimes(pointee, param_env); - let metadata = match unsized_part.kind() { - ty::Foreign(..) => { + + let metadata = if let Some(metadata_def_id) = tcx.lang_items().metadata_type() { + let metadata_ty = tcx.normalize_erasing_regions( + param_env, + tcx.mk_projection(metadata_def_id, [pointee]), + ); + let metadata_layout = cx.layout_of(metadata_ty)?; + // If the metadata is a 1-zst, then the pointer is thin. + if metadata_layout.is_zst() && metadata_layout.align.abi.bytes() == 1 { return Ok(tcx.intern_layout(LayoutS::scalar(cx, data_ptr))); } - ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)), - ty::Dynamic(..) => { - let mut vtable = scalar_unit(Pointer); - vtable.valid_range_mut().start = 1; - vtable + + let Abi::Scalar(metadata) = metadata_layout.abi else { + return Err(LayoutError::Unknown(unsized_part)); + }; + metadata + } else { + match unsized_part.kind() { + ty::Foreign(..) => { + return Ok(tcx.intern_layout(LayoutS::scalar(cx, data_ptr))); + } + ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)), + ty::Dynamic(..) => { + let mut vtable = scalar_unit(Pointer); + vtable.valid_range_mut().start = 1; + vtable + } + _ => { + return Err(LayoutError::Unknown(unsized_part)); + } } - _ => return Err(LayoutError::Unknown(unsized_part)), }; // Effectively a (ptr, meta) tuple. diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index e2d10f550c3..87923ebbe4b 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -2,6 +2,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::{self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt}; +use rustc_session::config::TraitSolver; use rustc_trait_selection::traits; fn sized_constraint_for_ty<'tcx>( @@ -121,7 +122,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { // are any errors at that point, so outside of type inference you can be // sure that this will succeed without errors anyway. - if tcx.sess.opts.unstable_opts.chalk { + if tcx.sess.opts.unstable_opts.trait_solver == TraitSolver::Chalk { let environment = well_formed_types_in_env(tcx, def_id); predicates.extend(environment); } @@ -161,7 +162,7 @@ fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> { kind: hir::ImplItemKind::Type(..) | hir::ImplItemKind::Fn(..), .. }) => { - let parent_hir_id = tcx.hir().get_parent_node(hir_id); + let parent_hir_id = tcx.hir().parent_id(hir_id); match tcx.hir().get(parent_hir_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(hir::Impl { constness, .. }), diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index dd36a5c7a21..44004cb0be1 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -241,22 +241,30 @@ bitflags! { /// Basically anything but `ReLateBound` and `ReErased`. const HAS_FREE_REGIONS = 1 << 14; - /// Does this have any `ReLateBound` regions? Used to check - /// if a global bound is safe to evaluate. + /// Does this have any `ReLateBound` regions? const HAS_RE_LATE_BOUND = 1 << 15; + /// Does this have any `Bound` types? + const HAS_TY_LATE_BOUND = 1 << 16; + /// Does this have any `ConstKind::Bound` consts? + const HAS_CT_LATE_BOUND = 1 << 17; + /// Does this have any bound variables? + /// Used to check if a global bound is safe to evaluate. + const HAS_LATE_BOUND = TypeFlags::HAS_RE_LATE_BOUND.bits + | TypeFlags::HAS_TY_LATE_BOUND.bits + | TypeFlags::HAS_CT_LATE_BOUND.bits; /// Does this have any `ReErased` regions? - const HAS_RE_ERASED = 1 << 16; + const HAS_RE_ERASED = 1 << 18; /// Does this value have parameters/placeholders/inference variables which could be /// replaced later, in a way that would change the results of `impl` specialization? - const STILL_FURTHER_SPECIALIZABLE = 1 << 17; + const STILL_FURTHER_SPECIALIZABLE = 1 << 19; /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`? - const HAS_TY_FRESH = 1 << 18; + const HAS_TY_FRESH = 1 << 20; /// Does this value have `InferConst::Fresh`? - const HAS_CT_FRESH = 1 << 19; + const HAS_CT_FRESH = 1 << 21; } } @@ -718,9 +726,9 @@ impl fmt::Debug for InferTy { TyVar(ref v) => v.fmt(f), IntVar(ref v) => v.fmt(f), FloatVar(ref v) => v.fmt(f), - FreshTy(v) => write!(f, "FreshTy({:?})", v), - FreshIntTy(v) => write!(f, "FreshIntTy({:?})", v), - FreshFloatTy(v) => write!(f, "FreshFloatTy({:?})", v), + FreshTy(v) => write!(f, "FreshTy({v:?})"), + FreshIntTy(v) => write!(f, "FreshIntTy({v:?})"), + FreshFloatTy(v) => write!(f, "FreshFloatTy({v:?})"), } } } @@ -743,9 +751,9 @@ impl fmt::Display for InferTy { TyVar(_) => write!(f, "_"), IntVar(_) => write!(f, "{}", "{integer}"), FloatVar(_) => write!(f, "{}", "{float}"), - FreshTy(v) => write!(f, "FreshTy({})", v), - FreshIntTy(v) => write!(f, "FreshIntTy({})", v), - FreshFloatTy(v) => write!(f, "FreshFloatTy({})", v), + FreshTy(v) => write!(f, "FreshTy({v})"), + FreshIntTy(v) => write!(f, "FreshIntTy({v})"), + FreshFloatTy(v) => write!(f, "FreshFloatTy({v})"), } } } diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index f30ae82d7cd..b944cbd698d 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -1028,10 +1028,10 @@ impl<I: Interner> hash::Hash for RegionKind<I> { impl<I: Interner> fmt::Debug for RegionKind<I> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ReEarlyBound(data) => write!(f, "ReEarlyBound({:?})", data), + ReEarlyBound(data) => write!(f, "ReEarlyBound({data:?})"), ReLateBound(binder_id, bound_region) => { - write!(f, "ReLateBound({:?}, {:?})", binder_id, bound_region) + write!(f, "ReLateBound({binder_id:?}, {bound_region:?})") } ReFree(fr) => fr.fmt(f), @@ -1040,7 +1040,7 @@ impl<I: Interner> fmt::Debug for RegionKind<I> { ReVar(vid) => vid.fmt(f), - RePlaceholder(placeholder) => write!(f, "RePlaceholder({:?})", placeholder), + RePlaceholder(placeholder) => write!(f, "RePlaceholder({placeholder:?})"), ReErased => f.write_str("ReErased"), } |
