diff options
| author | Boxy <rust@boxyuwu.dev> | 2025-01-28 11:57:19 +0000 |
|---|---|---|
| committer | Boxy <rust@boxyuwu.dev> | 2025-01-28 11:57:19 +0000 |
| commit | 815c5d4eee36e836c7b75aa9288a58c4e8e7830b (patch) | |
| tree | d4e7e89bcb0f3fd8e1ad2004c2c4a3dab933aaed /compiler | |
| parent | 62102ee041d0681e507e16c6d17ff5798b3a9d9f (diff) | |
| parent | 66d6064f9eb888018775e08f84747ee6f39ba28e (diff) | |
Merge from rustc
Diffstat (limited to 'compiler')
428 files changed, 5835 insertions, 4104 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 8e73df63ef5..ad942e9b494 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -28,6 +28,7 @@ use rustc_data_structures::packed::Pu128; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lrc; +use rustc_data_structures::tagged_ptr::Tag; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::AttrId; use rustc_span::source_map::{Spanned, respan}; @@ -99,7 +100,7 @@ pub struct Path { impl PartialEq<Symbol> for Path { #[inline] fn eq(&self, symbol: &Symbol) -> bool { - self.segments.len() == 1 && { self.segments[0].ident.name == *symbol } + matches!(&self.segments[..], [segment] if segment.ident.name == *symbol) } } @@ -120,13 +121,13 @@ impl Path { } pub fn is_global(&self) -> bool { - !self.segments.is_empty() && self.segments[0].ident.name == kw::PathRoot + self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot) } /// If this path is a single identifier with no arguments, does not ensure /// that the path resolves to a const param, the caller should check this. pub fn is_potential_trivial_const_arg(&self) -> bool { - self.segments.len() == 1 && self.segments[0].args.is_none() + matches!(self.segments[..], [PathSegment { args: None, .. }]) } } @@ -287,6 +288,7 @@ impl ParenthesizedArgs { } } +use crate::AstDeref; pub use crate::node_id::{CRATE_NODE_ID, DUMMY_NODE_ID, NodeId}; /// Modifiers on a trait bound like `~const`, `?` and `!`. @@ -2165,6 +2167,14 @@ impl Ty { } final_ty } + + pub fn is_maybe_parenthesised_infer(&self) -> bool { + match &self.kind { + TyKind::Infer => true, + TyKind::Paren(inner) => inner.ast_deref().is_maybe_parenthesised_infer(), + _ => false, + } + } } #[derive(Clone, Encodable, Decodable, Debug)] @@ -2269,10 +2279,32 @@ impl TyKind { /// Syntax used to declare a trait object. #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[repr(u8)] pub enum TraitObjectSyntax { - Dyn, - DynStar, - None, + // SAFETY: When adding new variants make sure to update the `Tag` impl. + Dyn = 0, + DynStar = 1, + None = 2, +} + +/// SAFETY: `TraitObjectSyntax` only has 3 data-less variants which means +/// it can be represented with a `u2`. We use `repr(u8)` to guarantee the +/// discriminants of the variants are no greater than `3`. +unsafe impl Tag for TraitObjectSyntax { + const BITS: u32 = 2; + + fn into_usize(self) -> usize { + self as u8 as usize + } + + unsafe fn from_usize(tag: usize) -> Self { + match tag { + 0 => TraitObjectSyntax::Dyn, + 1 => TraitObjectSyntax::DynStar, + 2 => TraitObjectSyntax::None, + _ => unreachable!(), + } + } } #[derive(Clone, Encodable, Decodable, Debug)] diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 51f18580013..df2f4b88712 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -302,7 +302,7 @@ impl AttrItem { impl MetaItem { /// For a single-segment meta item, returns its name; otherwise, returns `None`. pub fn ident(&self) -> Option<Ident> { - if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None } + if let [PathSegment { ident, .. }] = self.path.segments[..] { Some(ident) } else { None } } pub fn name_or_empty(&self) -> Symbol { diff --git a/compiler/rustc_ast/src/entry.rs b/compiler/rustc_ast/src/entry.rs index ab1413d6080..12cbb3b2a15 100644 --- a/compiler/rustc_ast/src/entry.rs +++ b/compiler/rustc_ast/src/entry.rs @@ -18,12 +18,6 @@ pub enum EntryPointType { /// fn main() {} /// ``` RustcMainAttr, - /// This is a function with the `#[start]` attribute. - /// ```ignore (clashes with test entrypoint) - /// #[start] - /// fn main() {} - /// ``` - Start, /// This function is **not** an entrypoint but simply named `main` (not at the root). /// This is only used for diagnostics. /// ``` @@ -40,9 +34,7 @@ pub fn entry_point_type( at_root: bool, name: Option<Symbol>, ) -> EntryPointType { - if attr::contains_name(attrs, sym::start) { - EntryPointType::Start - } else if attr::contains_name(attrs, sym::rustc_main) { + if attr::contains_name(attrs, sym::rustc_main) { EntryPointType::RustcMainAttr } else if let Some(name) = name && name == sym::main diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index aa88e8369d5..3459d39131a 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1813,10 +1813,10 @@ pub fn walk_flat_map_stmt<T: MutVisitor>( .into_iter() .map(|kind| Stmt { id, kind, span }) .collect(); - match stmts.len() { - 0 => {} - 1 => vis.visit_span(&mut stmts[0].span), - 2.. => panic!( + match &mut stmts[..] { + [] => {} + [stmt] => vis.visit_span(&mut stmt.span), + _ => panic!( "cloning statement `NodeId`s is prohibited by default, \ the visitor should implement custom statement visiting" ), diff --git a/compiler/rustc_ast/src/util/comments.rs b/compiler/rustc_ast/src/util/comments.rs index f39142f08ba..e12818623d8 100644 --- a/compiler/rustc_ast/src/util/comments.rs +++ b/compiler/rustc_ast/src/util/comments.rs @@ -39,7 +39,7 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol { let mut i = 0; let mut j = lines.len(); // first line of all-stars should be omitted - if !lines.is_empty() && lines[0].chars().all(|c| c == '*') { + if lines.first().is_some_and(|line| line.chars().all(|c| c == '*')) { i += 1; } @@ -97,7 +97,7 @@ pub fn beautify_doc_string(data: Symbol, kind: CommentKind) -> Symbol { return None; } } - if lines.is_empty() { None } else { Some(lines[0][..i].into()) } + Some(lines.first()?[..i].to_string()) } let data_s = data.as_str(); diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 266e77c0e02..f9fe4938ca8 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -72,7 +72,7 @@ impl<'hir> LoweringContext<'_, 'hir> { if let Some(local_sig_id) = def_id.as_local() { // The value may be missing due to recursive delegation. // Error will be emitted later during HIR ty lowering. - self.resolver.delegation_fn_sigs.get(&local_sig_id).map_or(false, |sig| sig.has_self) + self.resolver.delegation_fn_sigs.get(&local_sig_id).is_some_and(|sig| sig.has_self) } else { match self.tcx.def_kind(def_id) { DefKind::Fn => false, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index a76ca6772e5..f31e2c65c79 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -2159,7 +2159,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let path = hir::ExprKind::Path(hir::QPath::TypeRelative( self.arena.alloc(self.ty(span, hir::TyKind::Path(qpath))), self.arena.alloc(hir::PathSegment::new( - Ident::new(name, span), + Ident::new(name, self.lower_span(span)), self.next_id(), Res::Err, )), diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 29d4fb9ef25..f8fb21d5cd0 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -1,3 +1,4 @@ +use intravisit::InferKind; use rustc_data_structures::sorted_map::SortedMap; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LocalDefIdMap}; @@ -78,24 +79,31 @@ impl<'a, 'hir> NodeCollector<'a, 'hir> { // Make sure that the DepNode of some node coincides with the HirId // owner of that node. - if cfg!(debug_assertions) && hir_id.owner != self.owner { - span_bug!( - span, - "inconsistent HirId at `{:?}` for `{:?}`: \ + if cfg!(debug_assertions) { + if hir_id.owner != self.owner { + span_bug!( + span, + "inconsistent HirId at `{:?}` for `{node:?}`: \ current_dep_node_owner={} ({:?}), hir_id.owner={} ({:?})", - self.tcx.sess.source_map().span_to_diagnostic_string(span), - node, - self.tcx - .definitions_untracked() - .def_path(self.owner.def_id) - .to_string_no_crate_verbose(), - self.owner, - self.tcx - .definitions_untracked() - .def_path(hir_id.owner.def_id) - .to_string_no_crate_verbose(), - hir_id.owner, - ) + self.tcx.sess.source_map().span_to_diagnostic_string(span), + self.tcx + .definitions_untracked() + .def_path(self.owner.def_id) + .to_string_no_crate_verbose(), + self.owner, + self.tcx + .definitions_untracked() + .def_path(hir_id.owner.def_id) + .to_string_no_crate_verbose(), + hir_id.owner, + ) + } + if self.tcx.sess.opts.incremental.is_some() + && span.parent().is_none() + && !span.is_dummy() + { + span_bug!(span, "span without a parent: {:#?}, {node:?}", span.data()) + } } self.nodes[hir_id.local_id] = ParentedNode { parent: self.parent_node, node }; @@ -258,14 +266,6 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir>) { - self.insert(const_arg.span(), const_arg.hir_id, Node::ConstArg(const_arg)); - - self.with_parent(const_arg.hir_id, |this| { - intravisit::walk_const_arg(this, const_arg); - }); - } - fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { self.insert(expr.span, expr.hir_id, Node::Expr(expr)); @@ -295,22 +295,41 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { intravisit::walk_path_segment(self, path_segment); } - fn visit_ty(&mut self, ty: &'hir Ty<'hir>) { - self.insert(ty.span, ty.hir_id, Node::Ty(ty)); + fn visit_ty(&mut self, ty: &'hir Ty<'hir, AmbigArg>) { + self.insert(ty.span, ty.hir_id, Node::Ty(ty.as_unambig_ty())); self.with_parent(ty.hir_id, |this| { intravisit::walk_ty(this, ty); }); } - fn visit_infer(&mut self, inf: &'hir InferArg) { - self.insert(inf.span, inf.hir_id, Node::Infer(inf)); + fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir, AmbigArg>) { + self.insert( + const_arg.as_unambig_ct().span(), + const_arg.hir_id, + Node::ConstArg(const_arg.as_unambig_ct()), + ); - self.with_parent(inf.hir_id, |this| { - intravisit::walk_inf(this, inf); + self.with_parent(const_arg.hir_id, |this| { + intravisit::walk_ambig_const_arg(this, const_arg); }); } + fn visit_infer( + &mut self, + inf_id: HirId, + inf_span: Span, + kind: InferKind<'hir>, + ) -> Self::Result { + match kind { + InferKind::Ty(ty) => self.insert(inf_span, inf_id, Node::Ty(ty)), + InferKind::Const(ct) => self.insert(inf_span, inf_id, Node::ConstArg(ct)), + InferKind::Ambig(inf) => self.insert(inf_span, inf_id, Node::Infer(inf)), + } + + self.visit_id(inf_id); + } + fn visit_trait_ref(&mut self, tr: &'hir TraitRef<'hir>) { self.insert(tr.path.span, tr.hir_ref_id, Node::TraitRef(tr)); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 61d7da429f8..74870d74150 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1092,6 +1092,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // this as a special case. return self.lower_fn_body(decl, |this| { if attrs.iter().any(|a| a.name_or_empty() == sym::rustc_intrinsic) { + let span = this.lower_span(span); let empty_block = hir::Block { hir_id: this.next_id(), stmts: &[], diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0e28590bd66..b9f1a4220b8 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -48,6 +48,7 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; +use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; @@ -1083,17 +1084,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { match arg { ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(lt)), ast::GenericArg::Type(ty) => { + // We cannot just match on `TyKind::Infer` as `(_)` is represented as + // `TyKind::Paren(TyKind::Infer)` and should also be lowered to `GenericArg::Infer` + if ty.is_maybe_parenthesised_infer() { + return GenericArg::Infer(hir::InferArg { + hir_id: self.lower_node_id(ty.id), + span: self.lower_span(ty.span), + }); + } + match &ty.kind { - TyKind::Infer if self.tcx.features().generic_arg_infer() => { - return GenericArg::Infer(hir::InferArg { - hir_id: self.lower_node_id(ty.id), - span: self.lower_span(ty.span), - }); - } // We parse const arguments as path types as we cannot distinguish them during // parsing. We try to resolve that ambiguity by attempting resolution in both the // type and value namespaces. If we resolved the path in the value namespace, we // transform it into a generic const argument. + // + // FIXME: Should we be handling `(PATH_TO_CONST)`? TyKind::Path(None, path) => { if let Some(res) = self .resolver @@ -1110,15 +1116,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let ct = self.lower_const_path_to_const_arg(path, res, ty.id, ty.span); - return GenericArg::Const(ct); + return GenericArg::Const(ct.try_as_ambig_ct().unwrap()); } } } _ => {} } - GenericArg::Type(self.lower_ty(ty, itctx)) + GenericArg::Type(self.lower_ty(ty, itctx).try_as_ambig_ty().unwrap()) + } + ast::GenericArg::Const(ct) => { + GenericArg::Const(self.lower_anon_const_to_const_arg(ct).try_as_ambig_ct().unwrap()) } - ast::GenericArg::Const(ct) => GenericArg::Const(self.lower_anon_const_to_const_arg(ct)), } } @@ -1158,7 +1166,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let lifetime_bound = this.elided_dyn_bound(t.span); (bounds, lifetime_bound) }); - let kind = hir::TyKind::TraitObject(bounds, lifetime_bound, TraitObjectSyntax::None); + let kind = hir::TyKind::TraitObject( + bounds, + TaggedRef::new(lifetime_bound, TraitObjectSyntax::None), + ); return hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.next_id() }; } @@ -1185,7 +1196,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> { let kind = match &t.kind { - TyKind::Infer => hir::TyKind::Infer, + TyKind::Infer => hir::TyKind::Infer(()), TyKind::Err(guar) => hir::TyKind::Err(*guar), TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), @@ -1309,7 +1320,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span)); (bounds, lifetime_bound) }); - hir::TyKind::TraitObject(bounds, lifetime_bound, *kind) + hir::TyKind::TraitObject(bounds, TaggedRef::new(lifetime_bound, *kind)) } TyKind::ImplTrait(def_node_id, bounds) => { let span = t.span; @@ -2041,7 +2052,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) .stash(c.value.span, StashKey::UnderscoreForArrayLengths); } - let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span)); + let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span), ()); self.arena.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind }) } _ => self.lower_anon_const_to_const_arg(c), @@ -2365,8 +2376,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir_id = self.next_id(); hir::TyKind::TraitObject( arena_vec![self; principal], - self.elided_dyn_bound(span), - TraitObjectSyntax::None, + TaggedRef::new(self.elided_dyn_bound(span), TraitObjectSyntax::None), ) } _ => hir::TyKind::Path(hir::QPath::Resolved(None, path)), diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index abd314ae74c..3c78ed0497d 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -375,24 +375,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { expr: &Expr, allow_paths: bool, ) -> &'hir hir::PatExpr<'hir> { + let span = self.lower_span(expr.span); let err = |guar| hir::PatExprKind::Lit { - lit: self.arena.alloc(respan(self.lower_span(expr.span), LitKind::Err(guar))), + lit: self.arena.alloc(respan(span, LitKind::Err(guar))), negated: false, }; let kind = match &expr.kind { ExprKind::Lit(lit) => { - hir::PatExprKind::Lit { lit: self.lower_lit(lit, expr.span), negated: false } + hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: false } } ExprKind::ConstBlock(c) => hir::PatExprKind::ConstBlock(self.lower_const_block(c)), ExprKind::IncludedBytes(bytes) => hir::PatExprKind::Lit { - lit: self.arena.alloc(respan( - self.lower_span(expr.span), - LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked), - )), + lit: self + .arena + .alloc(respan(span, LitKind::ByteStr(Arc::clone(bytes), StrStyle::Cooked))), negated: false, }, ExprKind::Err(guar) => err(*guar), - ExprKind::Dummy => span_bug!(expr.span, "lowered ExprKind::Dummy"), + ExprKind::Dummy => span_bug!(span, "lowered ExprKind::Dummy"), ExprKind::Path(qself, path) if allow_paths => hir::PatExprKind::Path(self.lower_qpath( expr.id, qself, @@ -403,21 +403,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { None, )), ExprKind::Unary(UnOp::Neg, inner) if let ExprKind::Lit(lit) = &inner.kind => { - hir::PatExprKind::Lit { lit: self.lower_lit(lit, expr.span), negated: true } + hir::PatExprKind::Lit { lit: self.lower_lit(lit, span), negated: true } } _ => { let pattern_from_macro = expr.is_approximately_pattern(); let guar = self.dcx().emit_err(ArbitraryExpressionInPattern { - span: expr.span, + span, pattern_from_macro_note: pattern_from_macro, }); err(guar) } }; - self.arena.alloc(hir::PatExpr { - hir_id: self.lower_node_id(expr.id), - span: expr.span, - kind, - }) + self.arena.alloc(hir::PatExpr { hir_id: self.lower_node_id(expr.id), span, kind }) } } diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 043144a5464..75abff7461b 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -525,7 +525,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])), }; - let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))]; + let args = smallvec![GenericArg::Type( + self.arena.alloc(self.ty_tup(*inputs_span, inputs)).try_as_ambig_ty().unwrap() + )]; // If we have a bound like `async Fn() -> T`, make sure that we mark the // `Output = T` associated type bound with the right feature gates. diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 94746212138..80b99f94485 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -83,7 +83,7 @@ impl<'a> PostExpansionVisitor<'a> { feature_err_issue(&self.sess, feature, span, GateIssue::Language, explain).emit(); } Err(abi::AbiDisabled::Unrecognized) => { - if self.sess.opts.pretty.map_or(true, |ppm| ppm.needs_hir()) { + if self.sess.opts.pretty.is_none_or(|ppm| ppm.needs_hir()) { self.sess.dcx().span_delayed_bug( span, format!( @@ -230,18 +230,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } - ast::ItemKind::Fn(..) => { - if attr::contains_name(&i.attrs, sym::start) { - gate!( - &self, - start, - i.span, - "`#[start]` functions are experimental and their signature may change \ - over time" - ); - } - } - ast::ItemKind::Struct(..) | ast::ItemKind::Enum(..) | ast::ItemKind::Union(..) => { for attr in attr::filter_by_name(&i.attrs, sym::repr) { for item in attr.meta_item_list().unwrap_or_else(ThinVec::new) { diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index def6b16ee8a..b4027a096c5 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -35,13 +35,25 @@ pub enum InstructionSetAttr { ArmT32, } -#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)] +#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic, Default)] pub enum OptimizeAttr { - None, + /// No `#[optimize(..)]` attribute + #[default] + Default, + /// `#[optimize(none)]` + DoNotOptimize, + /// `#[optimize(speed)]` Speed, + /// `#[optimize(size)]` Size, } +impl OptimizeAttr { + pub fn do_not_optimize(&self) -> bool { + matches!(self, Self::DoNotOptimize) + } +} + #[derive(Clone, Debug, Encodable, Decodable)] pub enum DiagnosticAttribute { // tidy-alphabetical-start diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 124f0aa3eff..28c381160b8 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -166,7 +166,7 @@ pub fn parse_repr_attr(sess: &Session, attr: &impl AttributeExt) -> Vec<ReprAttr // the `check_mod_attrs` pass, but this pass doesn't always run // (e.g. if we only pretty-print the source), so we have to gate // the `span_delayed_bug` call as follows: - if sess.opts.pretty.map_or(true, |pp| pp.needs_analysis()) { + if sess.opts.pretty.is_none_or(|pp| pp.needs_analysis()) { dcx.span_delayed_bug(item.span(), "unrecognized representation hint"); } } diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index ada20e5c614..c00e6dde919 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -92,6 +92,9 @@ borrowck_lifetime_constraints_error = borrowck_limitations_implies_static = due to current limitations in the borrow checker, this implies a `'static` lifetime +borrowck_long_type_consider_verbose = consider using `--verbose` to print the full type name to the console +borrowck_long_type_full_path = the full type name has been written to '{$path}' + borrowck_move_closure_suggestion = consider adding 'move' keyword before the nested closure diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index da59f9f9ebd..07dcbba019a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -39,7 +39,9 @@ use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::error_reporting::traits::call_kind::CallKind; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; +use rustc_trait_selection::traits::{ + Obligation, ObligationCause, ObligationCtxt, supertrait_def_ids, +}; use tracing::{debug, instrument}; use super::explain_borrow::{BorrowExplanation, LaterUseKind}; @@ -287,6 +289,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { None => "value".to_owned(), }; if needs_note { + let mut path = None; + let ty = self.infcx.tcx.short_ty_string(ty, &mut path); if let Some(local) = place.as_local() { let span = self.body.local_decls[local].source_info.span; err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { @@ -302,6 +306,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { place: ¬e_msg, }); }; + if let Some(path) = path { + err.subdiagnostic(crate::session_diagnostics::LongTypePath { + path: path.display().to_string(), + }); + } } if let UseSpans::FnSelfUse { @@ -658,8 +667,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { clause.as_trait_clause().is_some_and(|tc| { tc.self_ty().skip_binder().is_param(param.index) && tc.polarity() == ty::PredicatePolarity::Positive - && tcx - .supertrait_def_ids(tc.def_id()) + && supertrait_def_ids(tcx, tc.def_id()) .flat_map(|trait_did| tcx.associated_items(trait_did).in_definition_order()) .any(|item| item.fn_has_self_parameter) }) @@ -2480,7 +2488,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // To support cases like `|| { v.call(|this| v.get()) }` // FIXME: actually support such cases (need to figure out how to move from the // capture place to original local). - && self.res.as_ref().map_or(true, |(prev_res, _)| prev_res.span.contains(ex.span)) + && self.res.as_ref().is_none_or(|(prev_res, _)| prev_res.span.contains(ex.span)) { self.res = Some((ex, closure)); } diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index 5c0c1d0eb86..2656e0bb6a4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -131,10 +131,10 @@ impl<'tcx> BorrowExplanation<'tcx> { && let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span) { suggest_rewrite_if_let(tcx, expr, &pat, init, conseq, alt, err); - } else if path_span.map_or(true, |path_span| path_span == var_or_use_span) { + } else if path_span.is_none_or(|path_span| path_span == var_or_use_span) { // We can use `var_or_use_span` if either `path_span` is not present, or both // spans are the same. - if borrow_span.map_or(true, |sp| !sp.overlaps(var_or_use_span)) { + if borrow_span.is_none_or(|sp| !sp.overlaps(var_or_use_span)) { err.span_label( var_or_use_span, format!("{borrow_desc}borrow later {message}"), diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index beacbdbd3fa..14a900f38e9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -596,12 +596,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.suggest_cloning(err, place_ty, expr, None); } + let mut path = None; + let ty = self.infcx.tcx.short_ty_string(place_ty, &mut path); err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { is_partial_move: false, - ty: place_ty, + ty, place: &place_desc, span, }); + if let Some(path) = path { + err.subdiagnostic(crate::session_diagnostics::LongTypePath { + path: path.display().to_string(), + }); + } } else { binds_to.sort(); binds_to.dedup(); @@ -628,12 +635,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.suggest_cloning(err, place_ty, expr, Some(use_spans)); } + let mut path = None; + let ty = self.infcx.tcx.short_ty_string(place_ty, &mut path); err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { is_partial_move: false, - ty: place_ty, + ty, place: &place_desc, span: use_span, }); + if let Some(path) = path { + err.subdiagnostic(crate::session_diagnostics::LongTypePath { + path: path.display().to_string(), + }); + } use_spans.args_subdiag(err, |args_span| { crate::session_diagnostics::CaptureArgLabel::MoveOutPlace { @@ -831,12 +845,19 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { self.suggest_cloning(err, bind_to.ty, expr, None); } + let mut path = None; + let ty = self.infcx.tcx.short_ty_string(bind_to.ty, &mut path); err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { is_partial_move: false, - ty: bind_to.ty, + ty, place: place_desc, span: binding_span, }); + if let Some(path) = path { + err.subdiagnostic(crate::session_diagnostics::LongTypePath { + path: path.display().to_string(), + }); + } } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index f0baa20648c..3c4e4c29197 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -8,7 +8,7 @@ use rustc_hir::QPath::Resolved; use rustc_hir::WherePredicateKind::BoundPredicate; use rustc_hir::def::Res::Def; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate}; use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound}; use rustc_middle::bug; @@ -887,7 +887,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if alias_ty.span.desugaring_kind().is_some() { // Skip `async` desugaring `impl Future`. } - if let TyKind::TraitObject(_, lt, _) = alias_ty.kind { + if let TyKind::TraitObject(_, lt) = alias_ty.kind { if lt.ident.name == kw::Empty { spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string())); } else { @@ -987,7 +987,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { for found_did in found_dids { let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); - hir_v.visit_ty(self_ty); + hir_v.visit_ty_unambig(self_ty); debug!("trait spans found: {:?}", traits); for span in &traits { let mut multi_span: MultiSpan = vec![*span].into(); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 9349b46ec5b..ccd13badad7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -432,7 +432,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // must highlight the variable. // NOTE(eddyb) this is handled in/by the sole caller // (`give_name_if_anonymous_region_appears_in_arguments`). - hir::TyKind::Infer => None, + hir::TyKind::Infer(()) => None, _ => Some(argument_hir_ty), } @@ -615,7 +615,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } (GenericArgKind::Type(ty), hir::GenericArg::Type(hir_ty)) => { - search_stack.push((ty, hir_ty)); + search_stack.push((ty, hir_ty.as_unambig_ty())); } (GenericArgKind::Const(_ct), hir::GenericArg::Const(_hir_ct)) => { diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index a6d80149034..40e801d0388 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -1,7 +1,9 @@ use std::io; -use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options}; -use rustc_middle::mir::{Body, ClosureRegionRequirements, PassWhere}; +use rustc_middle::mir::pretty::{ + PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, +}; +use rustc_middle::mir::{Body, ClosureRegionRequirements}; use rustc_middle::ty::TyCtxt; use rustc_session::config::MirIncludeSpans; @@ -10,9 +12,6 @@ use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSe use crate::{BorrowckInferCtxt, RegionInferenceContext}; /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information. -// Note: this currently duplicates most of NLL MIR, with some additions for the localized outlives -// constraints. This is ok for now as this dump will change in the near future to an HTML file to -// become more useful. pub(crate) fn dump_polonius_mir<'tcx>( infcx: &BorrowckInferCtxt<'tcx>, body: &Body<'tcx>, @@ -26,12 +25,100 @@ pub(crate) fn dump_polonius_mir<'tcx>( return; } + if !dump_enabled(tcx, "polonius", body.source.def_id()) { + return; + } + let localized_outlives_constraints = localized_outlives_constraints .expect("missing localized constraints with `-Zpolonius=next`"); - // We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in - // #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example, - // they're always disabled in mir-opt tests to make working with blessed dumps easier. + let _: io::Result<()> = try { + let mut file = create_dump_file(tcx, "html", false, "polonius", &0, body)?; + emit_polonius_dump( + tcx, + body, + regioncx, + borrow_set, + localized_outlives_constraints, + closure_region_requirements, + &mut file, + )?; + }; +} + +/// The polonius dump consists of: +/// - the NLL MIR +/// - the list of polonius localized constraints +/// - a mermaid graph of the CFG +fn emit_polonius_dump<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + regioncx: &RegionInferenceContext<'tcx>, + borrow_set: &BorrowSet<'tcx>, + localized_outlives_constraints: LocalizedOutlivesConstraintSet, + closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // Prepare the HTML dump file prologue. + writeln!(out, "<!DOCTYPE html>")?; + writeln!(out, "<html>")?; + writeln!(out, "<head><title>Polonius MIR dump</title></head>")?; + writeln!(out, "<body>")?; + + // Section 1: the NLL + Polonius MIR. + writeln!(out, "<div>")?; + writeln!(out, "Raw MIR dump")?; + writeln!(out, "<code><pre>")?; + emit_html_mir( + tcx, + body, + regioncx, + borrow_set, + localized_outlives_constraints, + closure_region_requirements, + out, + )?; + writeln!(out, "</pre></code>")?; + writeln!(out, "</div>")?; + + // Section 2: mermaid visualization of the CFG. + writeln!(out, "<div>")?; + writeln!(out, "Control-flow graph")?; + writeln!(out, "<code><pre class='mermaid'>")?; + emit_mermaid_cfg(body, out)?; + writeln!(out, "</pre></code>")?; + writeln!(out, "</div>")?; + + // Finalize the dump with the HTML epilogue. + writeln!( + out, + "<script src='https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js'></script>" + )?; + writeln!(out, "<script>")?; + writeln!(out, "mermaid.initialize({{ startOnLoad: false, maxEdges: 100 }});")?; + writeln!(out, "mermaid.run({{ querySelector: '.mermaid' }})")?; + writeln!(out, "</script>")?; + writeln!(out, "</body>")?; + writeln!(out, "</html>")?; + + Ok(()) +} + +/// Emits the polonius MIR, as escaped HTML. +fn emit_html_mir<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, + regioncx: &RegionInferenceContext<'tcx>, + borrow_set: &BorrowSet<'tcx>, + localized_outlives_constraints: LocalizedOutlivesConstraintSet, + closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // Buffer the regular MIR dump to be able to escape it. + let mut buffer = Vec::new(); + + // We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z + // mir-include-spans` on the CLI still has priority. let options = PrettyPrintMirOptions { include_extra_comments: matches!( tcx.sess.opts.unstable_opts.mir_include_spans, @@ -39,12 +126,12 @@ pub(crate) fn dump_polonius_mir<'tcx>( ), }; - dump_mir_with_options( + dump_mir_to_writer( tcx, - false, "polonius", &0, body, + &mut buffer, |pass_where, out| { emit_polonius_mir( tcx, @@ -57,7 +144,27 @@ pub(crate) fn dump_polonius_mir<'tcx>( ) }, options, - ); + )?; + + // Escape the handful of characters that need it. We don't need to be particularly efficient: + // we're actually writing into a buffered writer already. Note that MIR dumps are valid UTF-8. + let buffer = String::from_utf8_lossy(&buffer); + for ch in buffer.chars() { + let escaped = match ch { + '>' => ">", + '<' => "<", + '&' => "&", + '\'' => "'", + '"' => """, + _ => { + // The common case, no escaping needed. + write!(out, "{}", ch)?; + continue; + } + }; + write!(out, "{}", escaped)?; + } + Ok(()) } /// Produces the actual NLL + Polonius MIR sections to emit during the dumping process. @@ -102,3 +209,55 @@ fn emit_polonius_mir<'tcx>( Ok(()) } + +/// Emits a mermaid flowchart of the CFG blocks and edges, similar to the graphviz version. +fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()> { + use rustc_middle::mir::{TerminatorEdges, TerminatorKind}; + + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Emit the block nodes. + for (block_idx, block) in body.basic_blocks.iter_enumerated() { + let block_idx = block_idx.as_usize(); + let cleanup = if block.is_cleanup { " (cleanup)" } else { "" }; + writeln!(out, "{block_idx}[\"bb{block_idx}{cleanup}\"]")?; + } + + // Emit the edges between blocks, from the terminator edges. + for (block_idx, block) in body.basic_blocks.iter_enumerated() { + let block_idx = block_idx.as_usize(); + let terminator = block.terminator(); + match terminator.edges() { + TerminatorEdges::None => {} + TerminatorEdges::Single(bb) => { + writeln!(out, "{block_idx} --> {}", bb.as_usize())?; + } + TerminatorEdges::Double(bb1, bb2) => { + if matches!(terminator.kind, TerminatorKind::FalseEdge { .. }) { + writeln!(out, "{block_idx} --> {}", bb1.as_usize())?; + writeln!(out, "{block_idx} -- imaginary --> {}", bb2.as_usize())?; + } else { + writeln!(out, "{block_idx} --> {}", bb1.as_usize())?; + writeln!(out, "{block_idx} -- unwind --> {}", bb2.as_usize())?; + } + } + TerminatorEdges::AssignOnReturn { return_, cleanup, .. } => { + for to_idx in return_ { + writeln!(out, "{block_idx} --> {}", to_idx.as_usize())?; + } + + if let Some(to_idx) = cleanup { + writeln!(out, "{block_idx} -- unwind --> {}", to_idx.as_usize())?; + } + } + TerminatorEdges::SwitchInt { targets, .. } => { + for to_idx in targets.all_targets() { + writeln!(out, "{block_idx} --> {}", to_idx.as_usize())?; + } + } + } + } + + Ok(()) +} diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index 4be5d0dbf42..2c37d2bc123 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -459,17 +459,24 @@ pub(crate) enum OnClosureNote<'a> { } #[derive(Subdiagnostic)] -pub(crate) enum TypeNoCopy<'a, 'tcx> { +#[note(borrowck_long_type_full_path)] +#[note(borrowck_long_type_consider_verbose)] +pub(crate) struct LongTypePath { + pub(crate) path: String, +} + +#[derive(Subdiagnostic)] +pub(crate) enum TypeNoCopy<'a> { #[label(borrowck_ty_no_impl_copy)] Label { is_partial_move: bool, - ty: Ty<'tcx>, + ty: String, place: &'a str, #[primary_span] span: Span, }, #[note(borrowck_ty_no_impl_copy)] - Note { is_partial_move: bool, ty: Ty<'tcx>, place: &'a str }, + Note { is_partial_move: bool, ty: String, place: &'a str }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs index 3903c45fda5..bffd9f38334 100644 --- a/compiler/rustc_borrowck/src/type_check/canonical.rs +++ b/compiler/rustc_borrowck/src/type_check/canonical.rs @@ -185,7 +185,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { CustomTypeOp::new( |ocx| { let structurally_normalize = |ty| { - ocx.structurally_normalize( + ocx.structurally_normalize_ty( &ObligationCause::misc( location.to_locations().span(body), body.source.def_id().expect_local(), @@ -230,7 +230,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::Boring, CustomTypeOp::new( |ocx| { - ocx.structurally_normalize( + ocx.structurally_normalize_ty( &ObligationCause::misc( location.to_locations().span(body), body.source.def_id().expect_local(), diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 49706db0e0b..82417a86dd9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -157,7 +157,7 @@ pub(crate) fn expand_deriving_coerce_pointee( { cx.dcx().emit_err(RequiresMaybeSized { span: pointee_ty_ident.span, - name: pointee_ty_ident.name.to_ident_string(), + name: pointee_ty_ident, }); return; } @@ -471,5 +471,5 @@ struct TooManyPointees { struct RequiresMaybeSized { #[primary_span] span: Span, - name: String, + name: Ident, } diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 6071d36f8eb..0918403b855 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -16,6 +16,7 @@ #![feature(proc_macro_internals)] #![feature(proc_macro_quote)] #![feature(rustdoc_internals)] +#![feature(string_from_utf8_lossy_owned)] #![feature(try_blocks)] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs index 123b96f6bca..d163da3ddea 100644 --- a/compiler/rustc_builtin_macros/src/source_util.rs +++ b/compiler/rustc_builtin_macros/src/source_util.rs @@ -13,7 +13,7 @@ use rustc_expand::base::{ use rustc_expand::module::DirOwnership; use rustc_lint_defs::BuiltinLintDiag; use rustc_parse::parser::{ForceCollect, Parser}; -use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal}; +use rustc_parse::{new_parser_from_file, unwrap_or_emit_fatal, utf8_error}; use rustc_session::lint::builtin::INCOMPLETE_INCLUDE; use rustc_span::source_map::SourceMap; use rustc_span::{Pos, Span, Symbol}; @@ -209,9 +209,10 @@ pub(crate) fn expand_include_str( let interned_src = Symbol::intern(src); MacEager::expr(cx.expr_str(cx.with_def_site_ctxt(bsp), interned_src)) } - Err(_) => { - let guar = cx.dcx().span_err(sp, format!("`{path}` wasn't a utf-8 file")); - DummyResult::any(sp, guar) + Err(utf8err) => { + let mut err = cx.dcx().struct_span_err(sp, format!("`{path}` wasn't a utf-8 file")); + utf8_error(cx.source_map(), path.as_str(), None, &mut err, utf8err, &bytes[..]); + DummyResult::any(sp, err.emit()) } }, Err(dummy) => dummy, @@ -273,7 +274,7 @@ fn load_binary_file( .and_then(|path| path.into_os_string().into_string().ok()); if let Some(new_path) = new_path { - err.span_suggestion( + err.span_suggestion_verbose( path_span, "there is a file with the same name in a different directory", format!("\"{}\"", new_path.replace('\\', "/").escape_debug()), diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 46446598943..31b068bd33d 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -204,11 +204,11 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { ast::mut_visit::walk_item(self, item); self.depth -= 1; - // Remove any #[rustc_main] or #[start] from the AST so it doesn't + // Remove any #[rustc_main] from the AST so it doesn't // clash with the one we're going to add, but mark it as // #[allow(dead_code)] to avoid printing warnings. match entry_point_type(&item, self.depth == 0) { - EntryPointType::MainNamed | EntryPointType::RustcMainAttr | EntryPointType::Start => { + EntryPointType::MainNamed | EntryPointType::RustcMainAttr => { let allow_dead_code = attr::mk_attr_nested_word( &self.sess.psess.attr_id_generator, ast::AttrStyle::Outer, @@ -217,8 +217,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> { sym::dead_code, self.def_site, ); - item.attrs - .retain(|attr| !attr.has_name(sym::rustc_main) && !attr.has_name(sym::start)); + item.attrs.retain(|attr| !attr.has_name(sym::rustc_main)); item.attrs.push(allow_dead_code); } EntryPointType::None | EntryPointType::OtherMain => {} diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs index 8de419a0c4e..bcb2b4881eb 100644 --- a/compiler/rustc_codegen_cranelift/build_system/tests.rs +++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs @@ -151,7 +151,7 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[ apply_patches( &runner.dirs, "coretests", - &runner.stdlib_source.join("library/core/tests"), + &runner.stdlib_source.join("library/coretests"), &LIBCORE_TESTS_SRC.to_path(&runner.dirs), ); diff --git a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch b/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch deleted file mode 100644 index 161173d4765..00000000000 --- a/compiler/rustc_codegen_cranelift/patches/0022-coretests-Disable-not-compiling-tests.patch +++ /dev/null @@ -1,44 +0,0 @@ -From f6befc4bb51d84f5f1cf35938a168c953d421350 Mon Sep 17 00:00:00 2001 -From: bjorn3 <bjorn3@users.noreply.github.com> -Date: Sun, 24 Nov 2019 15:10:23 +0100 -Subject: [PATCH] [core] Disable not compiling tests - ---- - library/core/tests/Cargo.toml | 8 ++++++++ - library/core/tests/num/flt2dec/mod.rs | 1 - - library/core/tests/num/int_macros.rs | 2 ++ - library/core/tests/num/uint_macros.rs | 2 ++ - library/core/tests/ptr.rs | 2 ++ - library/core/tests/slice.rs | 2 ++ - 6 files changed, 16 insertions(+), 1 deletion(-) - create mode 100644 library/core/tests/Cargo.toml - -diff --git a/Cargo.toml b/Cargo.toml -new file mode 100644 -index 0000000..46fd999 ---- /dev/null -+++ b/Cargo.toml -@@ -0,0 +1,12 @@ -+[package] -+name = "coretests" -+version = "0.0.0" -+edition = "2021" -+ -+[lib] -+name = "coretests" -+path = "lib.rs" -+ -+[dependencies] -+rand = { version = "0.8.5", default-features = false } -+rand_xorshift = { version = "0.3.0", default-features = false } -diff --git a/lib.rs b/lib.rs -index 42a26ae..5ac1042 100644 ---- a/lib.rs -+++ b/lib.rs -@@ -1,3 +1,4 @@ -+#![cfg(test)] - // tidy-alphabetical-start - #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] - #![cfg_attr(test, feature(cfg_match))] --- -2.21.0 (Apple Git-122) diff --git a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch index 06840624cef..4a06dc3f7ef 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-coretests-128bit-atomic-operations.patch @@ -10,21 +10,20 @@ Cranelift doesn't support them yet library/core/tests/atomic.rs | 4 --- 4 files changed, 4 insertions(+), 50 deletions(-) -diff --git a/lib.rs b/lib.rs +diff --git a/tests/lib.rs b/tests/lib.rs index 1e336bf..35e6f54 100644 ---- a/lib.rs -+++ b/lib.rs -@@ -2,6 +2,5 @@ - #![cfg(test)] +--- a/tests/lib.rs ++++ b/tests/lib.rs +@@ -2,5 +2,4 @@ // tidy-alphabetical-start -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_match))] #![feature(alloc_layout_extra)] #![feature(array_chunks)] -diff --git a/atomic.rs b/atomic.rs +diff --git a/tests/atomic.rs b/tests/atomic.rs index b735957..ea728b6 100644 ---- a/atomic.rs -+++ b/atomic.rs +--- a/tests/atomic.rs ++++ b/tests/atomic.rs @@ -185,10 +185,6 @@ fn atomic_alignment() { assert_eq!(align_of::<AtomicU64>(), size_of::<AtomicU64>()); #[cfg(target_has_atomic = "64")] diff --git a/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch b/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch index b98326c54a6..5a38dffa24f 100644 --- a/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch +++ b/compiler/rustc_codegen_cranelift/patches/0028-coretests-Disable-long-running-tests.patch @@ -7,10 +7,10 @@ Subject: [PATCH] Disable long running tests library/core/tests/slice.rs | 2 ++ 1 file changed, 2 insertions(+) -diff --git a/slice.rs b/slice.rs +diff --git a/tests/slice.rs b/tests/slice.rs index 8402833..84592e0 100644 ---- a/slice.rs -+++ b/slice.rs +--- a/tests/slice.rs ++++ b/tests/slice.rs @@ -1809,6 +1809,7 @@ fn sort_unstable() { } } diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain index e4c3dd708fd..9c6aad3490d 100644 --- a/compiler/rustc_codegen_cranelift/rust-toolchain +++ b/compiler/rustc_codegen_cranelift/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2025-01-10" +channel = "nightly-2025-01-20" components = ["rust-src", "rustc-dev", "llvm-tools"] profile = "minimal" diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index e569da90cf7..41aa011e805 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -176,12 +176,11 @@ diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-supp index 9607ff02f96..b7d97caf9a2 100644 --- a/src/tools/run-make-support/src/external_deps/rustdoc.rs +++ b/src/tools/run-make-support/src/external_deps/rustdoc.rs -@@ -34,8 +34,6 @@ pub fn bare() -> Self { +@@ -34,7 +34,6 @@ pub fn bare() -> Self { #[track_caller] pub fn new() -> Self { let mut cmd = setup_common(); -- let target_rpath_dir = env_var_os("TARGET_RPATH_DIR"); -- cmd.arg(format!("-L{}", target_rpath_dir.to_string_lossy())); +- cmd.arg("-L").arg(env_var_os("TARGET_RPATH_DIR")); Self { cmd } } diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 2c99597922e..fdcd9caf4ac 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -126,7 +126,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { &mut self, name: &str, params: Vec<AbiParam>, - returns: Vec<AbiParam>, + mut returns: Vec<AbiParam>, args: &[Value], ) -> Cow<'_, [Value]> { // Pass i128 arguments by-ref on Windows. @@ -150,15 +150,19 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { (params, args.into()) }; - // Return i128 using a return area pointer on Windows and s390x. - let adjust_ret_param = - if self.tcx.sess.target.is_like_windows || self.tcx.sess.target.arch == "s390x" { - returns.len() == 1 && returns[0].value_type == types::I128 - } else { - false - }; + let ret_single_i128 = returns.len() == 1 && returns[0].value_type == types::I128; + if ret_single_i128 && self.tcx.sess.target.is_like_windows { + // Return i128 using the vector ABI on Windows + returns[0].value_type = types::I64X2; + + let ret = self.lib_call_unadjusted(name, params, returns, &args)[0]; - if adjust_ret_param { + // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 + let ret_ptr = self.create_stack_slot(16, 16); + ret_ptr.store(self, ret, MemFlags::trusted()); + Cow::Owned(vec![ret_ptr.load(self, types::I128, MemFlags::trusted())]) + } else if ret_single_i128 && self.tcx.sess.target.arch == "s390x" { + // Return i128 using a return area pointer on s390x. let mut params = params; let mut args = args.to_vec(); diff --git a/compiler/rustc_codegen_cranelift/src/cast.rs b/compiler/rustc_codegen_cranelift/src/cast.rs index 0b5cb1547fc..4463631c524 100644 --- a/compiler/rustc_codegen_cranelift/src/cast.rs +++ b/compiler/rustc_codegen_cranelift/src/cast.rs @@ -96,25 +96,9 @@ pub(crate) fn clif_int_or_float_cast( }, ); - if fx.tcx.sess.target.is_like_windows { - let ret = fx.lib_call( - &name, - vec![AbiParam::new(from_ty)], - vec![AbiParam::new(types::I64X2)], - &[from], - )[0]; - // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 - let ret_ptr = fx.create_stack_slot(16, 16); - ret_ptr.store(fx, ret, MemFlags::trusted()); - ret_ptr.load(fx, types::I128, MemFlags::trusted()) - } else { - fx.lib_call( - &name, - vec![AbiParam::new(from_ty)], - vec![AbiParam::new(types::I128)], - &[from], - )[0] - } + fx.lib_call(&name, vec![AbiParam::new(from_ty)], vec![AbiParam::new(types::I128)], &[ + from, + ])[0] } else if to_ty == types::I8 || to_ty == types::I16 { // FIXME implement fcvt_to_*int_sat.i8/i16 let val = if to_signed { diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index dcfd7ddabbc..df5a79086fa 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -33,28 +33,14 @@ pub(crate) fn maybe_codegen<'tcx>( (BinOp::Rem, true) => "__modti3", _ => unreachable!(), }; - if fx.tcx.sess.target.is_like_windows { - let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; - let ret = fx.lib_call( - name, - vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], - vec![AbiParam::new(types::I64X2)], - &args, - )[0]; - // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 - let ret_place = CPlace::new_stack_slot(fx, lhs.layout()); - ret_place.to_ptr().store(fx, ret, MemFlags::trusted()); - Some(ret_place.to_cvalue(fx)) - } else { - let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; - let ret_val = fx.lib_call( - name, - vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], - vec![AbiParam::new(types::I128)], - &args, - )[0]; - Some(CValue::by_val(ret_val, lhs.layout())) - } + let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; + let ret_val = fx.lib_call( + name, + vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], + vec![AbiParam::new(types::I128)], + &args, + )[0]; + Some(CValue::by_val(ret_val, lhs.layout())) } BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne | BinOp::Cmp => None, BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None, diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index fe578e44770..27adf6318e2 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -333,9 +333,17 @@ fn make_module(sess: &Session, name: String) -> UnwindModule<ObjectModule> { let mut builder = ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap(); + + // Disable function sections by default on MSVC as it causes significant slowdowns with link.exe. + // Maybe link.exe has exponential behavior when there are many sections with the same name? Also + // explicitly disable it on MinGW as rustc already disables it by default on MinGW and as such + // isn't tested. If rustc enables it in the future on MinGW, we can re-enable it too once it has + // been on MinGW. + let default_function_sections = sess.target.function_sections && !sess.target.is_like_windows; builder.per_function_section( - sess.opts.unstable_opts.function_sections.unwrap_or(sess.target.function_sections), + sess.opts.unstable_opts.function_sections.unwrap_or(default_function_sections), ); + UnwindModule::new(ObjectModule::new(builder), true) } @@ -668,7 +676,7 @@ pub(crate) fn run_aot( .to_owned(); let cgus = if tcx.sess.opts.output_types.should_codegen() { - tcx.collect_and_partition_mono_items(()).1 + tcx.collect_and_partition_mono_items(()).codegen_units } else { // If only `--emit metadata` is used, we shouldn't perform any codegen. // Also `tcx.collect_and_partition_mono_items` may panic in that case. diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index 2e5813556aa..26f14532b45 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -1,4 +1,5 @@ //! Codegen of intrinsics. This includes `extern "rust-intrinsic"`, +//! functions marked with the `#[rustc_intrinsic]` attribute //! and LLVM intrinsics that have symbol names starting with `llvm.`. macro_rules! intrinsic_args { diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs index e6bf0d5b47e..f6843496895 100644 --- a/compiler/rustc_codegen_cranelift/src/main_shim.rs +++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs @@ -1,7 +1,7 @@ use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use rustc_hir::LangItem; use rustc_middle::ty::{AssocKind, GenericArg}; -use rustc_session::config::{EntryFnType, sigpipe}; +use rustc_session::config::EntryFnType; use rustc_span::{DUMMY_SP, Ident}; use crate::prelude::*; @@ -14,10 +14,9 @@ pub(crate) fn maybe_create_entry_wrapper( is_jit: bool, is_primary_cgu: bool, ) { - let (main_def_id, (is_main_fn, sigpipe)) = match tcx.entry_fn(()) { + let (main_def_id, sigpipe) = match tcx.entry_fn(()) { Some((def_id, entry_ty)) => (def_id, match entry_ty { - EntryFnType::Main { sigpipe } => (true, sigpipe), - EntryFnType::Start => (false, sigpipe::DEFAULT), + EntryFnType::Main { sigpipe } => sigpipe, }), None => return, }; @@ -31,14 +30,13 @@ pub(crate) fn maybe_create_entry_wrapper( return; } - create_entry_fn(tcx, module, main_def_id, is_jit, is_main_fn, sigpipe); + create_entry_fn(tcx, module, main_def_id, is_jit, sigpipe); fn create_entry_fn( tcx: TyCtxt<'_>, m: &mut dyn Module, rust_main_def_id: DefId, ignore_lang_start_wrapper: bool, - is_main_fn: bool, sigpipe: u8, ) { let main_ret_ty = tcx.fn_sig(rust_main_def_id).no_bound_vars().unwrap().output(); @@ -94,8 +92,8 @@ pub(crate) fn maybe_create_entry_wrapper( let main_func_ref = m.declare_func_in_func(main_func_id, &mut bcx.func); - let result = if is_main_fn && ignore_lang_start_wrapper { - // regular main fn, but ignoring #[lang = "start"] as we are running in the jit + let result = if ignore_lang_start_wrapper { + // ignoring #[lang = "start"] as we are running in the jit // FIXME set program arguments somehow let call_inst = bcx.ins().call(main_func_ref, &[]); let call_results = bcx.func.dfg.inst_results(call_inst).to_owned(); @@ -133,7 +131,8 @@ pub(crate) fn maybe_create_entry_wrapper( types::I64 => bcx.ins().sextend(types::I64, res), _ => unimplemented!("16bit systems are not yet supported"), } - } else if is_main_fn { + } else { + // Regular main fn invoked via start lang item. let start_def_id = tcx.require_lang_item(LangItem::Start, None); let start_instance = Instance::expect_resolve( tcx, @@ -150,10 +149,6 @@ pub(crate) fn maybe_create_entry_wrapper( let call_inst = bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv, arg_sigpipe]); bcx.inst_results(call_inst)[0] - } else { - // using user-defined start fn - let call_inst = bcx.ins().call(main_func_ref, &[arg_argc, arg_argv]); - bcx.inst_results(call_inst)[0] }; bcx.ins().return_(&[result]); diff --git a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml index 73ec6b84a15..f96912e6b7a 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/ci.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/ci.yml @@ -13,14 +13,14 @@ env: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: libgccjit_version: - - { gcc: "gcc-13.deb" } - - { gcc: "gcc-13-without-int128.deb" } + - { gcc: "gcc-15.deb" } + - { gcc: "gcc-15-without-int128.deb" } commands: [ "--std-tests", # FIXME: re-enable asm tests when GCC can emit in the right syntax. @@ -108,13 +108,13 @@ jobs: cargo clippy --all-targets --features master -- -D warnings duplicates: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - run: python tools/check_intrinsics_duplicates.py build_system: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Test build system diff --git a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml index f33d9fcc582..d080bbfe91f 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/failures.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/failures.yml @@ -13,7 +13,7 @@ env: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -56,12 +56,12 @@ jobs: - name: Download artifact if: matrix.libgccjit_version.gcc != 'libgccjit12.so' - run: curl -LO https://github.com/rust-lang/gcc/releases/latest/download/gcc-13.deb + run: curl -LO https://github.com/rust-lang/gcc/releases/latest/download/gcc-15.deb - name: Setup path to libgccjit if: matrix.libgccjit_version.gcc != 'libgccjit12.so' run: | - sudo dpkg --force-overwrite -i gcc-13.deb + sudo dpkg --force-overwrite -i gcc-15.deb echo 'gcc-path = "/usr/lib"' > config.toml echo "LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV echo "LD_LIBRARY_PATH=/usr/lib" >> $GITHUB_ENV diff --git a/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml b/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml index 4c2ce91e86e..bb9e020dc6a 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/gcc12.yml @@ -17,7 +17,7 @@ env: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false diff --git a/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml b/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml index 07bb372b360..ed1fc02bd91 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/m68k.yml @@ -17,7 +17,7 @@ env: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -47,17 +47,17 @@ jobs: - name: Install packages run: | sudo apt-get update - sudo apt-get install qemu qemu-user-static + sudo apt-get install qemu-system qemu-user-static - name: Download artifact - run: curl -LO https://github.com/cross-cg-gcc-tools/cross-gcc/releases/latest/download/gcc-m68k-13.deb + run: curl -LO https://github.com/cross-cg-gcc-tools/cross-gcc/releases/latest/download/gcc-m68k-15.deb - name: Download VM artifact run: curl -LO https://github.com/cross-cg-gcc-tools/vms/releases/latest/download/debian-m68k.img - name: Setup path to libgccjit run: | - sudo dpkg -i gcc-m68k-13.deb + sudo dpkg -i gcc-m68k-15.deb echo 'gcc-path = "/usr/lib/"' > config.toml - name: Set env diff --git a/compiler/rustc_codegen_gcc/.github/workflows/release.yml b/compiler/rustc_codegen_gcc/.github/workflows/release.yml index 60e0943c87d..886ce90b471 100644 --- a/compiler/rustc_codegen_gcc/.github/workflows/release.yml +++ b/compiler/rustc_codegen_gcc/.github/workflows/release.yml @@ -13,7 +13,7 @@ env: jobs: build: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -37,11 +37,11 @@ jobs: run: sudo apt-get install ninja-build ripgrep - name: Download artifact - run: curl -LO https://github.com/rust-lang/gcc/releases/latest/download/gcc-13.deb + run: curl -LO https://github.com/rust-lang/gcc/releases/latest/download/gcc-15.deb - name: Setup path to libgccjit run: | - sudo dpkg --force-overwrite -i gcc-13.deb + sudo dpkg --force-overwrite -i gcc-15.deb echo 'gcc-path = "/usr/lib/"' > config.toml - name: Set env @@ -76,4 +76,9 @@ jobs: - name: Run y.sh cargo build run: | EMBED_LTO_BITCODE=1 CHANNEL="release" ./y.sh cargo build --release --manifest-path tests/hello-world/Cargo.toml - # TODO: grep the asm output for "call my_func" and fail if it is found. + call_found=$(objdump -dj .text tests/hello-world/target/release/hello_world | grep -c "call .*mylib.*my_func" ) ||: + if [ $call_found -gt 0 ]; then + echo "ERROR: call my_func found in asm" + echo "Test is done with LTO enabled, hence inlining should occur across crates" + exit 1 + fi diff --git a/compiler/rustc_codegen_gcc/build_system/src/build.rs b/compiler/rustc_codegen_gcc/build_system/src/build.rs index d0ced211a61..e98377f15a9 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/build.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/build.rs @@ -150,6 +150,8 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu "debug" }; + // We have a different environment variable than RUSTFLAGS to make sure those flags are only + // sent to rustc_codegen_gcc and not the LLVM backend. if let Ok(cg_rustflags) = std::env::var("CG_RUSTFLAGS") { rustflags.push(' '); rustflags.push_str(&cg_rustflags); @@ -184,8 +186,12 @@ pub fn build_sysroot(env: &HashMap<String, String>, config: &ConfigInfo) -> Resu fn build_codegen(args: &mut BuildArg) -> Result<(), String> { let mut env = HashMap::new(); - env.insert("LD_LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone()); - env.insert("LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone()); + let gcc_path = + args.config_info.gcc_path.clone().expect( + "The config module should have emitted an error if the GCC path wasn't provided", + ); + env.insert("LD_LIBRARY_PATH".to_string(), gcc_path.clone()); + env.insert("LIBRARY_PATH".to_string(), gcc_path); if args.config_info.no_default_features { env.insert("RUSTFLAGS".to_string(), "-Csymbol-mangling-version=v0".to_string()); diff --git a/compiler/rustc_codegen_gcc/build_system/src/config.rs b/compiler/rustc_codegen_gcc/build_system/src/config.rs index 37b4b68950e..4f9fcc97151 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/config.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/config.rs @@ -112,7 +112,7 @@ pub struct ConfigInfo { pub sysroot_panic_abort: bool, pub cg_backend_path: String, pub sysroot_path: String, - pub gcc_path: String, + pub gcc_path: Option<String>, config_file: Option<String>, // This is used in particular in rust compiler bootstrap because it doesn't run at the root // of the `cg_gcc` folder, making it complicated for us to get access to local files we need @@ -173,6 +173,14 @@ impl ConfigInfo { "--release-sysroot" => self.sysroot_release_channel = true, "--release" => self.channel = Channel::Release, "--sysroot-panic-abort" => self.sysroot_panic_abort = true, + "--gcc-path" => match args.next() { + Some(arg) if !arg.is_empty() => { + self.gcc_path = Some(arg.into()); + } + _ => { + return Err("Expected a value after `--gcc-path`, found nothing".to_string()); + } + }, "--cg_gcc-path" => match args.next() { Some(arg) if !arg.is_empty() => { self.cg_gcc_path = Some(arg.into()); @@ -260,8 +268,9 @@ impl ConfigInfo { create_symlink(&libgccjit_so, output_dir.join(&format!("{}.0", libgccjit_so_name)))?; } - self.gcc_path = output_dir.display().to_string(); - println!("Using `{}` as path for libgccjit", self.gcc_path); + let gcc_path = output_dir.display().to_string(); + println!("Using `{}` as path for libgccjit", gcc_path); + self.gcc_path = Some(gcc_path); Ok(()) } @@ -273,6 +282,15 @@ impl ConfigInfo { } pub fn setup_gcc_path(&mut self) -> Result<(), String> { + // If the user used the `--gcc-path` option, no need to look at `config.toml` content + // since we already have everything we need. + if let Some(gcc_path) = &self.gcc_path { + println!( + "`--gcc-path` was provided, ignoring config file. Using `{}` as path for libgccjit", + gcc_path + ); + return Ok(()); + } let config_file = match self.config_file.as_deref() { Some(config_file) => config_file.into(), None => self.compute_path("config.toml"), @@ -283,12 +301,15 @@ impl ConfigInfo { self.download_gccjit_if_needed()?; return Ok(()); } - self.gcc_path = match gcc_path { - Some(path) => path, - None => { - return Err(format!("missing `gcc-path` value from `{}`", config_file.display(),)); - } + let Some(gcc_path) = gcc_path else { + return Err(format!("missing `gcc-path` value from `{}`", config_file.display())); }; + println!( + "GCC path retrieved from `{}`. Using `{}` as path for libgccjit", + config_file.display(), + gcc_path + ); + self.gcc_path = Some(gcc_path); Ok(()) } @@ -299,10 +320,17 @@ impl ConfigInfo { ) -> Result<(), String> { env.insert("CARGO_INCREMENTAL".to_string(), "0".to_string()); - if self.gcc_path.is_empty() && !use_system_gcc { - self.setup_gcc_path()?; - } - env.insert("GCC_PATH".to_string(), self.gcc_path.clone()); + let gcc_path = if !use_system_gcc { + if self.gcc_path.is_none() { + self.setup_gcc_path()?; + } + self.gcc_path.clone().expect( + "The config module should have emitted an error if the GCC path wasn't provided", + ) + } else { + String::new() + }; + env.insert("GCC_PATH".to_string(), gcc_path.clone()); if self.cargo_target_dir.is_empty() { match env.get("CARGO_TARGET_DIR").filter(|dir| !dir.is_empty()) { @@ -381,6 +409,8 @@ impl ConfigInfo { } // This environment variable is useful in case we want to change options of rustc commands. + // We have a different environment variable than RUSTFLAGS to make sure those flags are + // only sent to rustc_codegen_gcc and not the LLVM backend. if let Some(cg_rustflags) = env.get("CG_RUSTFLAGS") { rustflags.extend_from_slice(&split_args(&cg_rustflags)?); } @@ -414,7 +444,7 @@ impl ConfigInfo { "{target}:{sysroot}:{gcc_path}", target = self.cargo_target_dir, sysroot = sysroot.display(), - gcc_path = self.gcc_path, + gcc_path = gcc_path, ); env.insert("LIBRARY_PATH".to_string(), ld_library_path.clone()); env.insert("LD_LIBRARY_PATH".to_string(), ld_library_path.clone()); @@ -459,6 +489,7 @@ impl ConfigInfo { --release-sysroot : Build sysroot in release mode --sysroot-panic-abort : Build the sysroot without unwinding support --config-file : Location of the config file to be used + --gcc-path : Location of the GCC root folder --cg_gcc-path : Location of the rustc_codegen_gcc root folder (used when ran from another directory) --no-default-features : Add `--no-default-features` flag to cargo commands diff --git a/compiler/rustc_codegen_gcc/build_system/src/info.rs b/compiler/rustc_codegen_gcc/build_system/src/info.rs index ea38791d38c..bd891de2eb4 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/info.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/info.rs @@ -14,6 +14,8 @@ pub fn run() -> Result<(), String> { } config.no_download = true; config.setup_gcc_path()?; - println!("{}", config.gcc_path); + if let Some(gcc_path) = config.gcc_path { + println!("{}", gcc_path); + } Ok(()) } diff --git a/compiler/rustc_codegen_gcc/build_system/src/main.rs b/compiler/rustc_codegen_gcc/build_system/src/main.rs index 3a860e2b136..39361718306 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/main.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/main.rs @@ -34,11 +34,11 @@ Options: --help : Displays this help message. Commands: - cargo : Executes a cargo command. + cargo : Executes a cargo command. rustc : Compiles the program using the GCC compiler. clean : Cleans the build directory, removing all compiled files and artifacts. prepare : Prepares the environment for building, including fetching dependencies and setting up configurations. - build : Compiles the project. + build : Compiles the project. test : Runs tests for the project. info : Displays information about the build environment and project configuration. clone-gcc : Clones the GCC compiler from a specified source. diff --git a/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs index 7cc7336612c..6c29c7d1825 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/test.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/test.rs @@ -426,19 +426,6 @@ fn std_tests(env: &Env, args: &TestArg) -> Result<(), String> { run_command_with_env(&command, None, Some(env))?; maybe_run_command_in_vm(&[&cargo_target_dir.join("track-caller-attribute")], env, args)?; - // FIXME: create a function "display_if_not_quiet" or something along the line. - println!("[AOT] mod_bench"); - let mut command = args.config_info.rustc_command_vec(); - command.extend_from_slice(&[ - &"example/mod_bench.rs", - &"--crate-type", - &"bin", - &"--target", - &args.config_info.target_triple, - ]); - run_command_with_env(&command, None, Some(env))?; - // FIXME: the compiled binary is not run. - Ok(()) } @@ -696,19 +683,6 @@ fn test_libcore(env: &Env, args: &TestArg) -> Result<(), String> { Ok(()) } -// echo "[BENCH COMPILE] mod_bench" -// -// COMPILE_MOD_BENCH_INLINE="$RUSTC example/mod_bench.rs --crate-type bin -Zmir-opt-level=3 -O --crate-name mod_bench_inline" -// COMPILE_MOD_BENCH_LLVM_0="rustc example/mod_bench.rs --crate-type bin -Copt-level=0 -o $cargo_target_dir/mod_bench_llvm_0 -Cpanic=abort" -// COMPILE_MOD_BENCH_LLVM_1="rustc example/mod_bench.rs --crate-type bin -Copt-level=1 -o $cargo_target_dir/mod_bench_llvm_1 -Cpanic=abort" -// COMPILE_MOD_BENCH_LLVM_2="rustc example/mod_bench.rs --crate-type bin -Copt-level=2 -o $cargo_target_dir/mod_bench_llvm_2 -Cpanic=abort" -// COMPILE_MOD_BENCH_LLVM_3="rustc example/mod_bench.rs --crate-type bin -Copt-level=3 -o $cargo_target_dir/mod_bench_llvm_3 -Cpanic=abort" -// -// Use 100 runs, because a single compilations doesn't take more than ~150ms, so it isn't very slow -// hyperfine --runs ${COMPILE_RUNS:-100} "$COMPILE_MOD_BENCH_INLINE" "$COMPILE_MOD_BENCH_LLVM_0" "$COMPILE_MOD_BENCH_LLVM_1" "$COMPILE_MOD_BENCH_LLVM_2" "$COMPILE_MOD_BENCH_LLVM_3" -// echo "[BENCH RUN] mod_bench" -// hyperfine --runs ${RUN_RUNS:-10} $cargo_target_dir/mod_bench{,_inline} $cargo_target_dir/mod_bench_llvm_* - fn extended_rand_tests(env: &Env, args: &TestArg) -> Result<(), String> { if !args.is_using_gcc_master_branch() { println!("Not using GCC master branch. Skipping `extended_rand_tests`."); @@ -1255,8 +1229,11 @@ pub fn run() -> Result<(), String> { if !args.use_system_gcc { args.config_info.setup_gcc_path()?; - env.insert("LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone()); - env.insert("LD_LIBRARY_PATH".to_string(), args.config_info.gcc_path.clone()); + let gcc_path = args.config_info.gcc_path.clone().expect( + "The config module should have emitted an error if the GCC path wasn't provided", + ); + env.insert("LIBRARY_PATH".to_string(), gcc_path.clone()); + env.insert("LD_LIBRARY_PATH".to_string(), gcc_path); } build_if_no_backend(&env, &args)?; diff --git a/compiler/rustc_codegen_gcc/example/alloc_example.rs b/compiler/rustc_codegen_gcc/example/alloc_example.rs index 6ed8b9157f2..9a0b46d5b22 100644 --- a/compiler/rustc_codegen_gcc/example/alloc_example.rs +++ b/compiler/rustc_codegen_gcc/example/alloc_example.rs @@ -1,5 +1,6 @@ -#![feature(start, core_intrinsics, alloc_error_handler, lang_items)] +#![feature(core_intrinsics, alloc_error_handler, lang_items)] #![no_std] +#![no_main] #![allow(internal_features)] extern crate alloc; @@ -37,8 +38,8 @@ unsafe extern "C" fn _Unwind_Resume() { core::intrinsics::unreachable(); } -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(_argc: core::ffi::c_int, _argv: *const *const u8) -> core::ffi::c_int { let world: Box<&str> = Box::new("Hello World!\0"); unsafe { puts(*world as *const str as *const u8); diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs index 1d51e0a1856..4cbe66c5e4c 100644 --- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs +++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs @@ -1,7 +1,7 @@ // Adapted from https://github.com/sunfishcode/mir2cranelift/blob/master/rust-examples/nocore-hello-world.rs #![feature( - no_core, unboxed_closures, start, lang_items, never_type, linkage, + no_core, unboxed_closures, lang_items, never_type, linkage, extern_types, thread_local )] #![no_core] diff --git a/compiler/rustc_codegen_gcc/example/mod_bench.rs b/compiler/rustc_codegen_gcc/example/mod_bench.rs deleted file mode 100644 index e8a9cade747..00000000000 --- a/compiler/rustc_codegen_gcc/example/mod_bench.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![feature(start, core_intrinsics, lang_items)] -#![no_std] -#![allow(internal_features)] - -#[link(name = "c")] -extern "C" {} - -#[panic_handler] -fn panic_handler(_: &core::panic::PanicInfo<'_>) -> ! { - core::intrinsics::abort(); -} - -#[lang="eh_personality"] -fn eh_personality(){} - -// Required for rustc_codegen_llvm -#[no_mangle] -unsafe extern "C" fn _Unwind_Resume() { - core::intrinsics::unreachable(); -} - -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { - for i in 2..100_000_000 { - black_box((i + 1) % i); - } - - 0 -} - -#[inline(never)] -fn black_box(i: u32) { - if i != 1 { - core::intrinsics::abort(); - } -} diff --git a/compiler/rustc_codegen_gcc/libgccjit.version b/compiler/rustc_codegen_gcc/libgccjit.version index ff58accec1d..417fd5b0393 100644 --- a/compiler/rustc_codegen_gcc/libgccjit.version +++ b/compiler/rustc_codegen_gcc/libgccjit.version @@ -1 +1 @@ -45648c2edd4ecd862d9f08196d3d6c6ccba79f07 +e607be166673a8de9fc07f6f02c60426e556c5f2 diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl index 85fa17a6ba5..882fff8673a 100644 --- a/compiler/rustc_codegen_gcc/messages.ftl +++ b/compiler/rustc_codegen_gcc/messages.ftl @@ -5,9 +5,6 @@ codegen_gcc_unknown_ctarget_feature_prefix = codegen_gcc_invalid_minimum_alignment = invalid minimum global alignment: {$err} -codegen_gcc_lto_not_supported = - LTO is not supported. You may get a linker error. - codegen_gcc_forbidden_ctarget_feature = target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason} diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index f43743fc2a4..bd5d6ba387c 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -64,6 +64,11 @@ impl<'gcc, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { if type_is_pointer(typ) { self.context.new_null(typ) } else { self.const_int(typ, 0) } } + fn is_undef(&self, _val: RValue<'gcc>) -> bool { + // FIXME: actually check for undef + false + } + fn const_undef(&self, typ: Type<'gcc>) -> RValue<'gcc> { let local = self.current_func.borrow().expect("func").new_local(None, typ, "undefined"); if typ.is_struct().is_some() { diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index c81c53359fd..30732c74eb3 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -513,7 +513,6 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } else { // If the symbol already exists, it is an error: for example, the user wrote // #[no_mangle] extern "C" fn main(..) {..} - // instead of #[start] None } } diff --git a/compiler/rustc_codegen_gcc/src/debuginfo.rs b/compiler/rustc_codegen_gcc/src/debuginfo.rs index d3aeb7f3bde..4b84b1dbfd3 100644 --- a/compiler/rustc_codegen_gcc/src/debuginfo.rs +++ b/compiler/rustc_codegen_gcc/src/debuginfo.rs @@ -113,15 +113,15 @@ fn make_mir_scope<'gcc, 'tcx>( let scope_data = &mir.source_scopes[scope]; let parent_scope = if let Some(parent) = scope_data.parent_scope { make_mir_scope(cx, _instance, mir, variables, debug_context, instantiated, parent); - debug_context.scopes[parent].unwrap() + debug_context.scopes[parent] } else { // The root is the function itself. let file = cx.sess().source_map().lookup_source_file(mir.span.lo()); - debug_context.scopes[scope] = Some(DebugScope { + debug_context.scopes[scope] = DebugScope { file_start_pos: file.start_pos, file_end_pos: file.end_position(), - ..debug_context.scopes[scope].unwrap() - }); + ..debug_context.scopes[scope] + }; instantiated.insert(scope); return; }; @@ -130,7 +130,7 @@ fn make_mir_scope<'gcc, 'tcx>( if !vars.contains(scope) && scope_data.inlined.is_none() { // Do not create a DIScope if there are no variables defined in this // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. - debug_context.scopes[scope] = Some(parent_scope); + debug_context.scopes[scope] = parent_scope; instantiated.insert(scope); return; } @@ -157,12 +157,12 @@ fn make_mir_scope<'gcc, 'tcx>( // TODO(tempdragon): dbg_scope: Add support for scope extension here. inlined_at.or(p_inlined_at); - debug_context.scopes[scope] = Some(DebugScope { + debug_context.scopes[scope] = DebugScope { dbg_scope, inlined_at, file_start_pos: loc.file.start_pos, file_end_pos: loc.file.end_position(), - }); + }; instantiated.insert(scope); } @@ -232,12 +232,12 @@ impl<'gcc, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } // Initialize fn debug context (including scopes). - let empty_scope = Some(DebugScope { + let empty_scope = DebugScope { dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)), inlined_at: None, file_start_pos: BytePos(0), file_end_pos: BytePos(0), - }); + }; let mut fn_debug_context = FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, mir.source_scopes.as_slice()), inlined_function_scopes: Default::default(), diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index f6ad0c79de5..38019faa7a9 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -444,7 +444,6 @@ impl WriteBackendMethods for GccCodegenBackend { } fn autodiff( _cgcx: &CodegenContext<Self>, - _tcx: TyCtxt<'_>, _module: &ModuleCodegen<Self::Module>, _diff_fncs: Vec<AutoDiffItem>, _config: &ModuleConfig, diff --git a/compiler/rustc_codegen_gcc/tests/run/abort1.rs b/compiler/rustc_codegen_gcc/tests/run/abort1.rs index 696197d7377..385e41a6881 100644 --- a/compiler/rustc_codegen_gcc/tests/run/abort1.rs +++ b/compiler/rustc_codegen_gcc/tests/run/abort1.rs @@ -3,11 +3,12 @@ // Run-time: // status: signal -#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)] +#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -49,7 +50,7 @@ fn test_fail() -> ! { unsafe { intrinsics::abort() }; } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { test_fail(); } diff --git a/compiler/rustc_codegen_gcc/tests/run/abort2.rs b/compiler/rustc_codegen_gcc/tests/run/abort2.rs index 714cd6c0f38..6c66a930e07 100644 --- a/compiler/rustc_codegen_gcc/tests/run/abort2.rs +++ b/compiler/rustc_codegen_gcc/tests/run/abort2.rs @@ -3,11 +3,12 @@ // Run-time: // status: signal -#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)] +#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -50,8 +51,8 @@ fn fail() -> i32 { 0 } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { fail(); 0 } diff --git a/compiler/rustc_codegen_gcc/tests/run/array.rs b/compiler/rustc_codegen_gcc/tests/run/array.rs index c3c08c29c6d..e18a4ced6bc 100644 --- a/compiler/rustc_codegen_gcc/tests/run/array.rs +++ b/compiler/rustc_codegen_gcc/tests/run/array.rs @@ -7,10 +7,11 @@ // 5 // 10 -#![feature(no_core, start)] +#![feature(no_core)] #![no_std] #![no_core] +#![no_main] extern crate mini_core; @@ -28,8 +29,8 @@ fn make_array() -> [u8; 3] { [42, 10, 5] } -#[start] -fn main(argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let array = [42, 7, 5]; let array2 = make_array(); unsafe { diff --git a/compiler/rustc_codegen_gcc/tests/run/assign.rs b/compiler/rustc_codegen_gcc/tests/run/assign.rs index 2a47f0c2966..4d414c577a6 100644 --- a/compiler/rustc_codegen_gcc/tests/run/assign.rs +++ b/compiler/rustc_codegen_gcc/tests/run/assign.rs @@ -6,10 +6,11 @@ // 10 #![allow(internal_features, unused_attributes)] -#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs, track_caller)] +#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs, track_caller)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -142,8 +143,8 @@ fn inc(num: isize) -> isize { } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { argc = inc(argc); unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc); diff --git a/compiler/rustc_codegen_gcc/tests/run/closure.rs b/compiler/rustc_codegen_gcc/tests/run/closure.rs index 46c47bc54ed..c7a236f74f9 100644 --- a/compiler/rustc_codegen_gcc/tests/run/closure.rs +++ b/compiler/rustc_codegen_gcc/tests/run/closure.rs @@ -8,10 +8,11 @@ // Int argument: 2 // Both args: 11 -#![feature(no_core, start)] +#![feature(no_core)] #![no_std] #![no_core] +#![no_main] extern crate mini_core; @@ -22,8 +23,8 @@ mod libc { } } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let string = "Arg: %d\n\0"; let mut closure = || { unsafe { diff --git a/compiler/rustc_codegen_gcc/tests/run/condition.rs b/compiler/rustc_codegen_gcc/tests/run/condition.rs index 039ef94eaa7..b02359702ed 100644 --- a/compiler/rustc_codegen_gcc/tests/run/condition.rs +++ b/compiler/rustc_codegen_gcc/tests/run/condition.rs @@ -5,10 +5,11 @@ // stdout: true // 1 -#![feature(no_core, start)] +#![feature(no_core)] #![no_std] #![no_core] +#![no_main] extern crate mini_core; @@ -19,8 +20,8 @@ mod libc { } } -#[start] -fn main(argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { unsafe { if argc == 1 { libc::printf(b"true\n\0" as *const u8 as *const i8); diff --git a/compiler/rustc_codegen_gcc/tests/run/empty_main.rs b/compiler/rustc_codegen_gcc/tests/run/empty_main.rs index e66a859ad69..042e44080c5 100644 --- a/compiler/rustc_codegen_gcc/tests/run/empty_main.rs +++ b/compiler/rustc_codegen_gcc/tests/run/empty_main.rs @@ -3,11 +3,12 @@ // Run-time: // status: 0 -#![feature(auto_traits, lang_items, no_core, start)] +#![feature(auto_traits, lang_items, no_core)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -34,7 +35,7 @@ pub(crate) unsafe auto trait Freeze {} * Code */ -#[start] -fn main(_argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { 0 } diff --git a/compiler/rustc_codegen_gcc/tests/run/exit.rs b/compiler/rustc_codegen_gcc/tests/run/exit.rs index bf1cbeef302..9a7c91c0adb 100644 --- a/compiler/rustc_codegen_gcc/tests/run/exit.rs +++ b/compiler/rustc_codegen_gcc/tests/run/exit.rs @@ -3,11 +3,12 @@ // Run-time: // status: 2 -#![feature(auto_traits, lang_items, no_core, start, intrinsics)] +#![feature(auto_traits, lang_items, no_core, intrinsics)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] mod libc { #[link(name = "c")] @@ -41,8 +42,8 @@ pub(crate) unsafe auto trait Freeze {} * Code */ -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { unsafe { libc::exit(2); } diff --git a/compiler/rustc_codegen_gcc/tests/run/exit_code.rs b/compiler/rustc_codegen_gcc/tests/run/exit_code.rs index be7a233efda..c50d2b0d710 100644 --- a/compiler/rustc_codegen_gcc/tests/run/exit_code.rs +++ b/compiler/rustc_codegen_gcc/tests/run/exit_code.rs @@ -3,11 +3,12 @@ // Run-time: // status: 1 -#![feature(auto_traits, lang_items, no_core, start)] +#![feature(auto_traits, lang_items, no_core)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -34,7 +35,7 @@ pub(crate) unsafe auto trait Freeze {} * Code */ -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { 1 } diff --git a/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs b/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs index ed1bf72bb27..98b351e5044 100644 --- a/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs +++ b/compiler/rustc_codegen_gcc/tests/run/fun_ptr.rs @@ -4,10 +4,11 @@ // status: 0 // stdout: 1 -#![feature(no_core, start)] +#![feature(no_core)] #![no_std] #![no_core] +#![no_main] extern crate mini_core; @@ -26,8 +27,8 @@ fn call_func(func: fn(i16) -> i8, param: i16) -> i8 { func(param) } -#[start] -fn main(argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { unsafe { let result = call_func(i16_as_i8, argc as i16) as isize; libc::printf(b"%ld\n\0" as *const u8 as *const i8, result); diff --git a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs index 3ae79338216..9be64f991ee 100644 --- a/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs +++ b/compiler/rustc_codegen_gcc/tests/run/mut_ref.rs @@ -8,10 +8,11 @@ // 11 #![allow(internal_features, unused_attributes)] -#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs, track_caller)] +#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs, track_caller)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -148,8 +149,8 @@ fn update_num(num: &mut isize) { *num = *num + 5; } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let mut test = test(argc); unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field); diff --git a/compiler/rustc_codegen_gcc/tests/run/operations.rs b/compiler/rustc_codegen_gcc/tests/run/operations.rs index 0e44fc580b8..c92d3cc0b8f 100644 --- a/compiler/rustc_codegen_gcc/tests/run/operations.rs +++ b/compiler/rustc_codegen_gcc/tests/run/operations.rs @@ -6,10 +6,11 @@ // 10 #![allow(internal_features, unused_attributes)] -#![feature(auto_traits, lang_items, no_core, start, intrinsics, arbitrary_self_types, rustc_attrs)] +#![feature(auto_traits, lang_items, no_core, intrinsics, arbitrary_self_types, rustc_attrs)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -231,8 +232,8 @@ pub fn panic_const_mul_overflow() -> ! { * Code */ -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, 40 + argc); libc::printf(b"%ld\n\0" as *const u8 as *const i8, 40 - argc); diff --git a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs index 2b8812ad51c..0ba49e7187f 100644 --- a/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs +++ b/compiler/rustc_codegen_gcc/tests/run/ptr_cast.rs @@ -4,10 +4,11 @@ // status: 0 // stdout: 1 -#![feature(no_core, start)] +#![feature(no_core)] #![no_std] #![no_core] +#![no_main] extern crate mini_core; @@ -24,8 +25,8 @@ fn make_array() -> [u8; 3] { [42, 10, 5] } -#[start] -fn main(argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { unsafe { let ptr = ONE as *mut usize; let value = ptr as usize; diff --git a/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs b/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs index f2a5a2e4384..3cc1e274001 100644 --- a/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs +++ b/compiler/rustc_codegen_gcc/tests/run/return-tuple.rs @@ -6,11 +6,12 @@ // 10 // 42 -#![feature(auto_traits, lang_items, no_core, start, intrinsics)] +#![feature(auto_traits, lang_items, no_core, intrinsics)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] #[lang = "copy"] pub unsafe trait Copy {} @@ -61,8 +62,8 @@ fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u3 ) } -#[start] -fn main(argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let (a, b, c, d, e, f, g, h, i, j) = int_cast(10, 42); unsafe { libc::printf(b"%d\n\0" as *const u8 as *const i8, c); diff --git a/compiler/rustc_codegen_gcc/tests/run/slice.rs b/compiler/rustc_codegen_gcc/tests/run/slice.rs index fba93fc1554..825fcb8a081 100644 --- a/compiler/rustc_codegen_gcc/tests/run/slice.rs +++ b/compiler/rustc_codegen_gcc/tests/run/slice.rs @@ -4,10 +4,11 @@ // status: 0 // stdout: 5 -#![feature(no_core, start)] +#![feature(no_core)] #![no_std] #![no_core] +#![no_main] extern crate mini_core; @@ -26,8 +27,8 @@ fn index_slice(s: &[u32]) -> u32 { } } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let array = [42, 7, 5]; unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, index_slice(&array)); diff --git a/compiler/rustc_codegen_gcc/tests/run/static.rs b/compiler/rustc_codegen_gcc/tests/run/static.rs index a17ea2a4893..80c8782c4b1 100644 --- a/compiler/rustc_codegen_gcc/tests/run/static.rs +++ b/compiler/rustc_codegen_gcc/tests/run/static.rs @@ -9,11 +9,12 @@ // 12 // 1 -#![feature(auto_traits, lang_items, no_core, start, intrinsics, rustc_attrs)] +#![feature(auto_traits, lang_items, no_core, intrinsics, rustc_attrs)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -98,8 +99,8 @@ static mut WITH_REF: WithRef = WithRef { refe: unsafe { &TEST }, }; -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, CONSTANT); libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST2.field); diff --git a/compiler/rustc_codegen_gcc/tests/run/structs.rs b/compiler/rustc_codegen_gcc/tests/run/structs.rs index d6455667400..59b8f358863 100644 --- a/compiler/rustc_codegen_gcc/tests/run/structs.rs +++ b/compiler/rustc_codegen_gcc/tests/run/structs.rs @@ -5,11 +5,12 @@ // stdout: 1 // 2 -#![feature(auto_traits, lang_items, no_core, start, intrinsics)] +#![feature(auto_traits, lang_items, no_core, intrinsics)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -55,8 +56,8 @@ fn one() -> isize { 1 } -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let test = Test { field: one(), }; diff --git a/compiler/rustc_codegen_gcc/tests/run/tuple.rs b/compiler/rustc_codegen_gcc/tests/run/tuple.rs index 8a7d85ae867..ed60a56a68c 100644 --- a/compiler/rustc_codegen_gcc/tests/run/tuple.rs +++ b/compiler/rustc_codegen_gcc/tests/run/tuple.rs @@ -4,11 +4,12 @@ // status: 0 // stdout: 3 -#![feature(auto_traits, lang_items, no_core, start, intrinsics)] +#![feature(auto_traits, lang_items, no_core, intrinsics)] #![allow(internal_features)] #![no_std] #![no_core] +#![no_main] /* * Core @@ -42,8 +43,8 @@ mod libc { * Code */ -#[start] -fn main(mut argc: isize, _argv: *const *const u8) -> isize { +#[no_mangle] +extern "C" fn main(argc: i32, _argv: *const *const u8) -> i32 { let test: (isize, isize, isize) = (3, 1, 4); unsafe { libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.0); diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index c44d1a5e5c2..94f21ac5f57 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -9,6 +9,8 @@ test = false [dependencies] # tidy-alphabetical-start bitflags = "2.4.1" +# To avoid duplicate dependencies, this should match the version of gimli used +# by `rustc_codegen_ssa` via its `thorin-dwp` dependency. gimli = "0.30" itertools = "0.12" libc = "0.2" diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 95e0481b035..3d7afa17bdf 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -333,9 +333,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( let mut to_add = SmallVec::<[_; 16]>::new(); match codegen_fn_attrs.optimize { - OptimizeAttr::None => { + OptimizeAttr::Default => { to_add.extend(default_optimisation_attrs(cx)); } + OptimizeAttr::DoNotOptimize => { + to_add.push(llvm::AttributeKind::OptimizeNone.create_attr(cx.llcx)); + } OptimizeAttr::Size => { to_add.push(llvm::AttributeKind::MinSize.create_attr(cx.llcx)); to_add.push(llvm::AttributeKind::OptimizeForSize.create_attr(cx.llcx)); @@ -343,12 +346,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( OptimizeAttr::Speed => {} } - let inline = - if codegen_fn_attrs.inline == InlineAttr::None && instance.def.requires_inline(cx.tcx) { - InlineAttr::Hint - } else { - codegen_fn_attrs.inline - }; + // `optnone` requires `noinline` + let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { + (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, + (InlineAttr::None, _) if instance.def.requires_inline(cx.tcx) => InlineAttr::Hint, + (inline, _) => inline, + }; to_add.extend(inline_attr(cx, inline)); // The `uwtable` attribute according to LLVM is: diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 509b24dd703..4706744f353 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -769,12 +769,9 @@ pub(crate) unsafe fn codegen( } } - // Two things to note: - // - If object files are just LLVM bitcode we write bitcode, copy it to - // the .o file, and delete the bitcode if it wasn't otherwise - // requested. - // - If we don't have the integrated assembler then we need to emit - // asm from LLVM and use `gcc` to create the object file. + // Note that if object files are just LLVM bitcode we write bitcode, + // copy it to the .o file, and delete the bitcode if it wasn't + // otherwise requested. let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); let bc_summary_out = diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5a34b52e6ef..d2de62b17f0 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::borrow::{Borrow, Cow}; use std::ops::Deref; use std::{iter, ptr}; @@ -31,20 +31,22 @@ use tracing::{debug, instrument}; use crate::abi::FnAbiLlvmExt; use crate::attributes; use crate::common::Funclet; -use crate::context::CodegenCx; +use crate::context::{CodegenCx, SimpleCx}; use crate::llvm::{self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, True}; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -// All Builders must have an llfn associated with them #[must_use] -pub(crate) struct Builder<'a, 'll, 'tcx> { +pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow<SimpleCx<'ll>>> { pub llbuilder: &'ll mut llvm::Builder<'ll>, - pub cx: &'a CodegenCx<'ll, 'tcx>, + pub cx: &'a CX, } -impl Drop for Builder<'_, '_, '_> { +pub(crate) type SBuilder<'a, 'll> = GenericBuilder<'a, 'll, SimpleCx<'ll>>; +pub(crate) type Builder<'a, 'll, 'tcx> = GenericBuilder<'a, 'll, CodegenCx<'ll, 'tcx>>; + +impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> Drop for GenericBuilder<'a, 'll, CX> { fn drop(&mut self) { unsafe { llvm::LLVMDisposeBuilder(&mut *(self.llbuilder as *mut _)); @@ -52,6 +54,112 @@ impl Drop for Builder<'_, '_, '_> { } } +impl<'a, 'll> SBuilder<'a, 'll> { + fn call( + &mut self, + llty: &'ll Type, + llfn: &'ll Value, + args: &[&'ll Value], + funclet: Option<&Funclet<'ll>>, + ) -> &'ll Value { + debug!("call {:?} with args ({:?})", llfn, args); + + let args = self.check_call("call", llty, llfn, args); + let funclet_bundle = funclet.map(|funclet| funclet.bundle()); + let mut bundles: SmallVec<[_; 2]> = SmallVec::new(); + if let Some(funclet_bundle) = funclet_bundle { + bundles.push(funclet_bundle); + } + + let call = unsafe { + llvm::LLVMBuildCallWithOperandBundles( + self.llbuilder, + llty, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + bundles.as_ptr(), + bundles.len() as c_uint, + c"".as_ptr(), + ) + }; + call + } + + fn with_scx(scx: &'a SimpleCx<'ll>) -> Self { + // Create a fresh builder from the simple context. + let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(scx.llcx) }; + SBuilder { llbuilder, cx: scx } + } +} +impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> { + pub(crate) fn bitcast(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { + unsafe { llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty, UNNAMED) } + } + + fn ret_void(&mut self) { + unsafe { + llvm::LLVMBuildRetVoid(self.llbuilder); + } + } + + fn ret(&mut self, v: &'ll Value) { + unsafe { + llvm::LLVMBuildRet(self.llbuilder, v); + } + } +} +impl<'a, 'll> SBuilder<'a, 'll> { + fn build(cx: &'a SimpleCx<'ll>, llbb: &'ll BasicBlock) -> SBuilder<'a, 'll> { + let bx = SBuilder::with_scx(cx); + unsafe { + llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb); + } + bx + } + + fn check_call<'b>( + &mut self, + typ: &str, + fn_ty: &'ll Type, + llfn: &'ll Value, + args: &'b [&'ll Value], + ) -> Cow<'b, [&'ll Value]> { + assert!( + self.cx.type_kind(fn_ty) == TypeKind::Function, + "builder::{typ} not passed a function, but {fn_ty:?}" + ); + + let param_tys = self.cx.func_params_types(fn_ty); + + let all_args_match = iter::zip(¶m_tys, args.iter().map(|&v| self.cx.val_ty(v))) + .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty); + + if all_args_match { + return Cow::Borrowed(args); + } + + let casted_args: Vec<_> = iter::zip(param_tys, args) + .enumerate() + .map(|(i, (expected_ty, &actual_val))| { + let actual_ty = self.cx.val_ty(actual_val); + if expected_ty != actual_ty { + debug!( + "type mismatch in function call of {:?}. \ + Expected {:?} for param {}, got {:?}; injecting bitcast", + llfn, expected_ty, i, actual_ty + ); + self.bitcast(actual_val, expected_ty) + } else { + actual_val + } + }) + .collect(); + + Cow::Owned(casted_args) + } +} + /// Empty string, to be used where LLVM expects an instruction name, indicating /// that the instruction is to be left unnamed (i.e. numbered, in textual IR). // FIXME(eddyb) pass `&CStr` directly to FFI once it's a thin pointer. @@ -1222,6 +1330,14 @@ impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> { } impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { + fn build(cx: &'a CodegenCx<'ll, 'tcx>, llbb: &'ll BasicBlock) -> Builder<'a, 'll, 'tcx> { + let bx = Builder::with_cx(cx); + unsafe { + llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb); + } + bx + } + fn with_cx(cx: &'a CodegenCx<'ll, 'tcx>) -> Self { // Create a fresh builder from the crate context. let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(cx.llcx) }; @@ -1231,13 +1347,16 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn llfn(&self) -> &'ll Value { unsafe { llvm::LLVMGetBasicBlockParent(self.llbb()) } } +} +impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> { fn position_at_start(&mut self, llbb: &'ll BasicBlock) { unsafe { llvm::LLVMRustPositionBuilderAtStart(self.llbuilder, llbb); } } - +} +impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn align_metadata(&mut self, load: &'ll Value, align: Align) { unsafe { let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))]; @@ -1259,7 +1378,8 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { self.set_metadata(inst, llvm::MD_unpredictable, md); } } - +} +impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> { pub(crate) fn minnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { unsafe { llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs) } } @@ -1360,7 +1480,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { let ret = unsafe { llvm::LLVMBuildCatchRet(self.llbuilder, funclet.cleanuppad(), unwind) }; ret.expect("LLVM does not have support for catchret") } +} +impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn check_call<'b>( &mut self, typ: &str, @@ -1401,11 +1523,13 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { Cow::Owned(casted_args) } - +} +impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> { pub(crate) fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) } } - +} +impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value { let (ty, f) = self.cx.get_intrinsic(intrinsic); self.call(ty, None, None, f, args, None, None) @@ -1423,7 +1547,8 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { self.call_intrinsic(intrinsic, &[self.cx.const_u64(size), ptr]); } - +} +impl<'a, 'll, CX: Borrow<SimpleCx<'ll>>> GenericBuilder<'a, 'll, CX> { pub(crate) fn phi( &mut self, ty: &'ll Type, @@ -1443,7 +1568,8 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); } } - +} +impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn fptoint_sat(&mut self, signed: bool, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { let src_ty = self.cx.val_ty(val); let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector { diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 38f7eaa090f..6b17b5f6989 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -3,20 +3,19 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, AutoDiffItem, DiffActivity, DiffMode}; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::back::write::ModuleConfig; -use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; use rustc_errors::FatalError; -use rustc_middle::ty::TyCtxt; use rustc_session::config::Lto; use tracing::{debug, trace}; use crate::back::write::{llvm_err, llvm_optimize}; -use crate::builder::Builder; -use crate::declare::declare_raw_fn; +use crate::builder::SBuilder; +use crate::context::SimpleCx; +use crate::declare::declare_simple_fn; use crate::errors::LlvmError; use crate::llvm::AttributePlace::Function; use crate::llvm::{Metadata, True}; use crate::value::Value; -use crate::{CodegenContext, LlvmCodegenBackend, ModuleLlvm, attributes, context, llvm}; +use crate::{CodegenContext, LlvmCodegenBackend, ModuleLlvm, attributes, llvm}; fn get_params(fnc: &Value) -> Vec<&Value> { unsafe { @@ -38,8 +37,8 @@ fn get_params(fnc: &Value) -> Vec<&Value> { /// [^1]: <https://enzyme.mit.edu/getting_started/CallingConvention/> // FIXME(ZuseZ4): `outer_fn` should include upstream safety checks to // cover some assumptions of enzyme/autodiff, which could lead to UB otherwise. -fn generate_enzyme_call<'ll, 'tcx>( - cx: &context::CodegenCx<'ll, 'tcx>, +fn generate_enzyme_call<'ll>( + cx: &SimpleCx<'ll>, fn_to_diff: &'ll Value, outer_fn: &'ll Value, attrs: AutoDiffAttrs, @@ -112,7 +111,7 @@ fn generate_enzyme_call<'ll, 'tcx>( //FIXME(ZuseZ4): the CC/Addr/Vis values are best effort guesses, we should look at tests and // think a bit more about what should go here. let cc = llvm::LLVMGetFunctionCallConv(outer_fn); - let ad_fn = declare_raw_fn( + let ad_fn = declare_simple_fn( cx, &ad_name, llvm::CallConv::try_from(cc).expect("invalid callconv"), @@ -132,7 +131,7 @@ fn generate_enzyme_call<'ll, 'tcx>( llvm::LLVMRustEraseInstFromParent(br); let last_inst = llvm::LLVMRustGetLastInstruction(entry).unwrap(); - let mut builder = Builder::build(cx, entry); + let mut builder = SBuilder::build(cx, entry); let num_args = llvm::LLVMCountParams(&fn_to_diff); let mut args = Vec::with_capacity(num_args as usize + 1); @@ -236,7 +235,7 @@ fn generate_enzyme_call<'ll, 'tcx>( } } - let call = builder.call(enzyme_ty, None, None, ad_fn, &args, None, None); + let call = builder.call(enzyme_ty, ad_fn, &args, None); // This part is a bit iffy. LLVM requires that a call to an inlineable function has some // metadata attachted to it, but we just created this code oota. Given that the @@ -274,10 +273,9 @@ fn generate_enzyme_call<'ll, 'tcx>( } } -pub(crate) fn differentiate<'ll, 'tcx>( +pub(crate) fn differentiate<'ll>( module: &'ll ModuleCodegen<ModuleLlvm>, cgcx: &CodegenContext<LlvmCodegenBackend>, - tcx: TyCtxt<'tcx>, diff_items: Vec<AutoDiffItem>, config: &ModuleConfig, ) -> Result<(), FatalError> { @@ -286,8 +284,7 @@ pub(crate) fn differentiate<'ll, 'tcx>( } let diag_handler = cgcx.create_dcx(); - let (_, cgus) = tcx.collect_and_partition_mono_items(()); - let cx = context::CodegenCx::new(tcx, &cgus.first().unwrap(), &module.module_llvm); + let cx = SimpleCx { llmod: module.module_llvm.llmod(), llcx: module.module_llvm.llcx }; // Before dumping the module, we want all the TypeTrees to become part of the module. for item in diff_items.iter() { diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index adfe8aeb5c5..b4e9b9f44f4 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -126,6 +126,10 @@ impl<'ll, 'tcx> ConstCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMGetUndef(t) } } + fn is_undef(&self, v: &'ll Value) -> bool { + unsafe { llvm::LLVMIsUndef(v) == True } + } + fn const_poison(&self, t: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMGetPoison(t) } } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 65345751842..79381f35a3c 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1,11 +1,13 @@ use std::borrow::Borrow; use std::cell::{Cell, RefCell}; use std::ffi::{CStr, c_char, c_uint}; +use std::ops::Deref; use std::str; use rustc_abi::{HasDataLayout, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh}; +use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::errors as ssa_errors; use rustc_codegen_ssa::traits::*; use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; @@ -30,23 +32,46 @@ use smallvec::SmallVec; use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; -use crate::common::AsCCharPtr; +use crate::common::{self, AsCCharPtr}; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; use crate::llvm::{Metadata, MetadataType}; use crate::type_::Type; use crate::value::Value; use crate::{attributes, coverageinfo, debuginfo, llvm, llvm_util}; +/// `TyCtxt` (and related cache datastructures) can't be move between threads. +/// However, there are various cx related functions which we want to be available to the builder and +/// other compiler pieces. Here we define a small subset which has enough information and can be +/// moved around more freely. +pub(crate) struct SimpleCx<'ll> { + pub llmod: &'ll llvm::Module, + pub llcx: &'ll llvm::Context, +} + +impl<'ll> Borrow<SimpleCx<'ll>> for CodegenCx<'ll, '_> { + fn borrow(&self) -> &SimpleCx<'ll> { + &self.scx + } +} + +impl<'ll, 'tcx> Deref for CodegenCx<'ll, 'tcx> { + type Target = SimpleCx<'ll>; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.scx + } +} + /// There is one `CodegenCx` per codegen unit. Each one has its own LLVM /// `llvm::Context` so that several codegen units may be processed in parallel. /// All other LLVM data structures in the `CodegenCx` are tied to that `llvm::Context`. pub(crate) struct CodegenCx<'ll, 'tcx> { pub tcx: TyCtxt<'tcx>, + pub scx: SimpleCx<'ll>, pub use_dll_storage_attrs: bool, pub tls_model: llvm::ThreadLocalMode, - pub llmod: &'ll llvm::Module, - pub llcx: &'ll llvm::Context, pub codegen_unit: &'tcx CodegenUnit<'tcx>, /// Cache instances of monomorphic and polymorphic items @@ -553,10 +578,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { CodegenCx { tcx, + scx: SimpleCx { llcx, llmod }, use_dll_storage_attrs, tls_model, - llmod, - llcx, codegen_unit, instances: Default::default(), vtables: Default::default(), @@ -600,6 +624,11 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { llvm::set_section(g, c"llvm.metadata"); } } +} +impl<'ll> SimpleCx<'ll> { + pub(crate) fn val_ty(&self, v: &'ll Value) -> &'ll Type { + common::val_ty(v) + } pub(crate) fn get_metadata_value(&self, metadata: &'ll Metadata) -> &'ll Value { unsafe { llvm::LLVMMetadataAsValue(self.llcx, metadata) } @@ -625,6 +654,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { llvm::LLVMMDStringInContext2(self.llcx, name.as_ptr() as *const c_char, name.len()) }) } + + pub(crate) fn type_kind(&self, ty: &'ll Type) -> TypeKind { + unsafe { llvm::LLVMRustGetTypeKind(ty).to_generic() } + } } impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { @@ -751,7 +784,6 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } else { // If the symbol already exists, it is an error: for example, the user wrote // #[no_mangle] extern "C" fn main(..) {..} - // instead of #[start] None } } @@ -1179,6 +1211,20 @@ impl CodegenCx<'_, '_> { } } +// This is a duplication of the set_metadata function above. However, so far it's the only one +// shared between both contexts, so it doesn't seem worth it to make the Cx generic like we did it +// for the Builder. +impl SimpleCx<'_> { + #[allow(unused)] + /// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`. + pub(crate) fn set_metadata<'a>(&self, val: &'a Value, kind_id: MetadataType, md: &'a Metadata) { + unsafe { + let node = llvm::LLVMMetadataAsValue(&self.llcx, md); + llvm::LLVMSetMetadata(val, kind_id as c_uint, node); + } + } +} + impl HasDataLayout for CodegenCx<'_, '_> { #[inline] fn data_layout(&self) -> &TargetDataLayout { diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index b3ad2a0e409..fd22421c7fc 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -9,6 +9,7 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_index::IndexVec; use rustc_middle::mir; +use rustc_middle::mir::mono::MonoItemPartitions; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::RemapFileNameExt; use rustc_session::config::RemapPathScopeComponents; @@ -297,12 +298,13 @@ struct UsageSets<'tcx> { /// Prepare sets of definitions that are relevant to deciding whether something /// is an "unused function" for coverage purposes. fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> { - let (all_mono_items, cgus) = tcx.collect_and_partition_mono_items(()); + let MonoItemPartitions { all_mono_items, codegen_units } = + tcx.collect_and_partition_mono_items(()); // Obtain a MIR body for each function participating in codegen, via an // arbitrary instance. let mut def_ids_seen = FxHashSet::default(); - let def_and_mir_for_all_mono_fns = cgus + let def_and_mir_for_all_mono_fns = codegen_units .iter() .flat_map(|cgu| cgu.items().keys()) .filter_map(|item| match item { diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs index 5428d776f41..460a4664615 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs @@ -51,7 +51,7 @@ pub(crate) fn prepare_covfun_record<'tcx>( is_used: bool, ) -> Option<CovfunRecord<'tcx>> { let fn_cov_info = tcx.instance_mir(instance.def).function_coverage_info.as_deref()?; - let ids_info = tcx.coverage_ids_info(instance.def); + let ids_info = tcx.coverage_ids_info(instance.def)?; let expressions = prepare_expressions(fn_cov_info, ids_info, is_used); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 7311cd9d230..021108cd51c 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -8,7 +8,6 @@ use rustc_codegen_ssa::traits::{ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; -use rustc_middle::ty::layout::HasTyCtxt; use tracing::{debug, instrument}; use crate::builder::Builder; @@ -147,6 +146,10 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { debug!("function has a coverage statement but no coverage info"); return; }; + let Some(ids_info) = bx.tcx.coverage_ids_info(instance.def) else { + debug!("function has a coverage statement but no IDs info"); + return; + }; // Mark the instance as used in this CGU, for coverage purposes. // This includes functions that were not partitioned into this CGU, @@ -162,8 +165,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { // be smaller than the number originally inserted by the instrumentor, // if some high-numbered counters were removed by MIR optimizations. // If so, LLVM's profiler runtime will use fewer physical counters. - let num_counters = - bx.tcx().coverage_ids_info(instance.def).num_counters_after_mir_opts(); + let num_counters = ids_info.num_counters_after_mir_opts(); assert!( num_counters as usize <= function_coverage_info.num_counters, "num_counters disagreement: query says {num_counters} but function info only has {}", diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs index e545ce386ed..11eb9651af6 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/create_scope_map.rs @@ -9,7 +9,7 @@ use rustc_middle::mir::{Body, SourceScope}; use rustc_middle::ty::layout::{FnAbiOf, HasTypingEnv}; use rustc_middle::ty::{self, Instance}; use rustc_session::config::DebugInfo; -use rustc_span::{BytePos, hygiene}; +use rustc_span::{BytePos, DUMMY_SP, hygiene}; use super::metadata::file_metadata; use super::utils::DIB; @@ -85,23 +85,15 @@ fn make_mir_scope<'ll, 'tcx>( discriminators, parent, ); - if let Some(parent_scope) = debug_context.scopes[parent] { - parent_scope - } else { - // If the parent scope could not be represented then no children - // can be either. - debug_context.scopes[scope] = None; - instantiated.insert(scope); - return; - } + debug_context.scopes[parent] } else { // The root is the function itself. let file = cx.sess().source_map().lookup_source_file(mir.span.lo()); - debug_context.scopes[scope] = Some(DebugScope { + debug_context.scopes[scope] = DebugScope { file_start_pos: file.start_pos, file_end_pos: file.end_position(), - ..debug_context.scopes[scope].unwrap() - }); + ..debug_context.scopes[scope] + }; instantiated.insert(scope); return; }; @@ -112,7 +104,7 @@ fn make_mir_scope<'ll, 'tcx>( { // Do not create a DIScope if there are no variables defined in this // MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat. - debug_context.scopes[scope] = Some(parent_scope); + debug_context.scopes[scope] = parent_scope; instantiated.insert(scope); return; } @@ -145,14 +137,7 @@ fn make_mir_scope<'ll, 'tcx>( }, }; - let mut debug_scope = Some(DebugScope { - dbg_scope, - inlined_at: parent_scope.inlined_at, - file_start_pos: loc.file.start_pos, - file_end_pos: loc.file.end_position(), - }); - - if let Some((_, callsite_span)) = scope_data.inlined { + let inlined_at = scope_data.inlined.map(|(_, callsite_span)| { let callsite_span = hygiene::walk_chain_collapsed(callsite_span, mir.span); let callsite_scope = parent_scope.adjust_dbg_scope_for_span(cx, callsite_span); let loc = cx.dbg_loc(callsite_scope, parent_scope.inlined_at, callsite_span); @@ -175,29 +160,29 @@ fn make_mir_scope<'ll, 'tcx>( // Note further that we can't key this hashtable on the span itself, // because these spans could have distinct SyntaxContexts. We have // to key on exactly what we're giving to LLVM. - let inlined_at = match discriminators.entry(callsite_span.lo()) { + match discriminators.entry(callsite_span.lo()) { Entry::Occupied(mut o) => { *o.get_mut() += 1; + // NB: We have to emit *something* here or we'll fail LLVM IR verification + // in at least some circumstances (see issue #135322) so if the required + // discriminant cannot be encoded fall back to the dummy location. unsafe { llvm::LLVMRustDILocationCloneWithBaseDiscriminator(loc, *o.get()) } + .unwrap_or_else(|| { + cx.dbg_loc(callsite_scope, parent_scope.inlined_at, DUMMY_SP) + }) } Entry::Vacant(v) => { v.insert(0); - Some(loc) - } - }; - match inlined_at { - Some(inlined_at) => { - debug_scope.as_mut().unwrap().inlined_at = Some(inlined_at); - } - None => { - // LLVM has a maximum discriminator that it can encode (currently - // it uses 12 bits for 4096 possible values). If we exceed that - // there is little we can do but drop the debug info. - debug_scope = None; + loc } } - } + }); - debug_context.scopes[scope] = debug_scope; + debug_context.scopes[scope] = DebugScope { + dbg_scope, + inlined_at: inlined_at.or(parent_scope.inlined_at), + file_start_pos: loc.file.start_pos, + file_end_pos: loc.file.end_position(), + }; instantiated.insert(scope); } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index 88e43e1c678..8d782a618fc 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -442,7 +442,7 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> // (or if there is no allocator argument). ty::Adt(def, args) if def.is_box() - && args.get(1).map_or(true, |arg| cx.layout_of(arg.expect_ty()).is_1zst()) => + && args.get(1).is_none_or(|arg| cx.layout_of(arg.expect_ty()).is_1zst()) => { build_pointer_or_reference_di_node(cx, t, t.expect_boxed_ty(), unique_type_id) } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 755f4816acf..e6778411365 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -295,12 +295,12 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } // Initialize fn debug context (including scopes). - let empty_scope = Some(DebugScope { + let empty_scope = DebugScope { dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)), inlined_at: None, file_start_pos: BytePos(0), file_end_pos: BytePos(0), - }); + }; let mut fn_debug_context = FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes), inlined_function_scopes: Default::default(), diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index c72b5b5611f..bdc83267cca 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -21,26 +21,26 @@ use tracing::debug; use crate::abi::{FnAbi, FnAbiLlvmExt}; use crate::common::AsCCharPtr; -use crate::context::CodegenCx; +use crate::context::{CodegenCx, SimpleCx}; use crate::llvm::AttributePlace::Function; use crate::llvm::Visibility; use crate::type_::Type; use crate::value::Value; use crate::{attributes, llvm}; -/// Declare a function. +/// Declare a function with a SimpleCx. /// /// If there’s a value with the same name already declared, the function will /// update the declaration and return existing Value instead. -pub(crate) fn declare_raw_fn<'ll>( - cx: &CodegenCx<'ll, '_>, +pub(crate) fn declare_simple_fn<'ll>( + cx: &SimpleCx<'ll>, name: &str, callconv: llvm::CallConv, unnamed: llvm::UnnamedAddr, visibility: llvm::Visibility, ty: &'ll Type, ) -> &'ll Value { - debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); + debug!("declare_simple_fn(name={:?}, ty={:?})", name, ty); let llfn = unsafe { llvm::LLVMRustGetOrInsertFunction(cx.llmod, name.as_c_char_ptr(), name.len(), ty) }; @@ -49,6 +49,24 @@ pub(crate) fn declare_raw_fn<'ll>( llvm::SetUnnamedAddress(llfn, unnamed); llvm::set_visibility(llfn, visibility); + llfn +} + +/// Declare a function. +/// +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing Value instead. +pub(crate) fn declare_raw_fn<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + name: &str, + callconv: llvm::CallConv, + unnamed: llvm::UnnamedAddr, + visibility: llvm::Visibility, + ty: &'ll Type, +) -> &'ll Value { + debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); + let llfn = declare_simple_fn(cx, name, callconv, unnamed, visibility, ty); + let mut attrs = SmallVec::<[_; 4]>::new(); if cx.tcx.sess.opts.cg.no_redzone.unwrap_or(cx.tcx.sess.target.disable_redzone) { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index cabcfc9b42b..eab4a9f30c9 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1081,11 +1081,11 @@ fn codegen_emcc_try<'ll>( // Helper function to give a Block to a closure to codegen a shim function. // This is currently primarily used for the `try` intrinsic functions above. -fn gen_fn<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, +fn gen_fn<'a, 'll, 'tcx>( + cx: &'a CodegenCx<'ll, 'tcx>, name: &str, rust_fn_sig: ty::PolyFnSig<'tcx>, - codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>), + codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>), ) -> (&'ll Type, &'ll Value) { let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty()); let llty = fn_abi.llvm_type(cx); @@ -1104,9 +1104,9 @@ fn gen_fn<'ll, 'tcx>( // catch exceptions. // // This function is only generated once and is then cached. -fn get_rust_try_fn<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>), +fn get_rust_try_fn<'a, 'll, 'tcx>( + cx: &'a CodegenCx<'ll, 'tcx>, + codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>), ) -> (&'ll Type, &'ll Value) { if let Some(llfn) = cx.rust_try_fn.get() { return llfn; @@ -1182,6 +1182,60 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }}; } + /// Returns the bitwidth of the `$ty` argument if it is an `Int` type. + macro_rules! require_int_ty { + ($ty: expr, $diag: expr) => { + match $ty { + ty::Int(i) => i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), + _ => { + return_error!($diag); + } + } + }; + } + + /// Returns the bitwidth of the `$ty` argument if it is an `Int` or `Uint` type. + macro_rules! require_int_or_uint_ty { + ($ty: expr, $diag: expr) => { + match $ty { + ty::Int(i) => i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), + ty::Uint(i) => { + i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()) + } + _ => { + return_error!($diag); + } + } + }; + } + + /// Converts a vector mask, where each element has a bit width equal to the data elements it is used with, + /// down to an i1 based mask that can be used by llvm intrinsics. + /// + /// The rust simd semantics are that each element should either consist of all ones or all zeroes, + /// but this information is not available to llvm. Truncating the vector effectively uses the lowest bit, + /// but codegen for several targets is better if we consider the highest bit by shifting. + /// + /// For x86 SSE/AVX targets this is beneficial since most instructions with mask parameters only consider the highest bit. + /// So even though on llvm level we have an additional shift, in the final assembly there is no shift or truncate and + /// instead the mask can be used as is. + /// + /// For aarch64 and other targets there is a benefit because a mask from the sign bit can be more + /// efficiently converted to an all ones / all zeroes mask by comparing whether each element is negative. + fn vector_mask_to_bitmask<'a, 'll, 'tcx>( + bx: &mut Builder<'a, 'll, 'tcx>, + i_xn: &'ll Value, + in_elem_bitwidth: u64, + in_len: u64, + ) -> &'ll Value { + // Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position. + let shift_idx = bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _); + let shift_indices = vec![shift_idx; in_len as _]; + let i_xn_msb = bx.lshr(i_xn, bx.const_vector(shift_indices.as_slice())); + // Truncate vector to an <i1 x N> + bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len)) + } + let tcx = bx.tcx(); let sig = tcx.normalize_erasing_late_bound_regions(bx.typing_env(), callee_ty.fn_sig(tcx)); let arg_tys = sig.inputs(); @@ -1433,14 +1487,13 @@ fn generic_simd_intrinsic<'ll, 'tcx>( m_len, v_len }); - match m_elem_ty.kind() { - ty::Int(_) => {} - _ => return_error!(InvalidMonomorphization::MaskType { span, name, ty: m_elem_ty }), - } - // truncate the mask to a vector of i1s - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, m_len as u64); - let m_i1s = bx.trunc(args[0].immediate(), i1xn); + let in_elem_bitwidth = + require_int_ty!(m_elem_ty.kind(), InvalidMonomorphization::MaskType { + span, + name, + ty: m_elem_ty + }); + let m_i1s = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, m_len); return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); } @@ -1457,33 +1510,15 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let expected_bytes = in_len.div_ceil(8); // Integer vector <i{in_bitwidth} x in_len>: - let (i_xn, in_elem_bitwidth) = match in_elem.kind() { - ty::Int(i) => ( - args[0].immediate(), - i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), - ), - ty::Uint(i) => ( - args[0].immediate(), - i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), - ), - _ => return_error!(InvalidMonomorphization::VectorArgument { + let in_elem_bitwidth = + require_int_or_uint_ty!(in_elem.kind(), InvalidMonomorphization::VectorArgument { span, name, in_ty, in_elem - }), - }; + }); - // LLVM doesn't always know the inputs are `0` or `!0`, so we shift here so it optimizes to - // `pmovmskb` and similar on x86. - let shift_indices = - vec![ - bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _); - in_len as _ - ]; - let i_xn_msb = bx.lshr(i_xn, bx.const_vector(shift_indices.as_slice())); - // Truncate vector to an <i1 x N> - let i1xn = bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len)); + let i1xn = vector_mask_to_bitmask(bx, args[0].immediate(), in_elem_bitwidth, in_len); // Bitcast <i1 x N> to iN: let i_ = bx.bitcast(i1xn, bx.type_ix(in_len)); @@ -1704,28 +1739,21 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); - match element_ty2.kind() { - ty::Int(_) => (), - _ => { - return_error!(InvalidMonomorphization::ThirdArgElementType { - span, - name, - expected_element: element_ty2, - third_arg: arg_tys[2] - }); - } - } + let mask_elem_bitwidth = + require_int_ty!(element_ty2.kind(), InvalidMonomorphization::ThirdArgElementType { + span, + name, + expected_element: element_ty2, + third_arg: arg_tys[2] + }); // Alignment of T, must be a constant integer value: let alignment_ty = bx.type_i32(); let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32); // Truncate the mask vector to a vector of i1s: - let (mask, mask_ty) = { - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, in_len); - (bx.trunc(args[2].immediate(), i1xn), i1xn) - }; + let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len); + let mask_ty = bx.type_vector(bx.type_i1(), in_len); // Type of the vector of pointers: let llvm_pointer_vec_ty = llvm_vector_ty(bx, element_ty1, in_len); @@ -1810,27 +1838,21 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); - require!( - matches!(mask_elem.kind(), ty::Int(_)), - InvalidMonomorphization::ThirdArgElementType { + let m_elem_bitwidth = + require_int_ty!(mask_elem.kind(), InvalidMonomorphization::ThirdArgElementType { span, name, expected_element: values_elem, third_arg: mask_ty, - } - ); + }); + + let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len); + let mask_ty = bx.type_vector(bx.type_i1(), mask_len); // Alignment of T, must be a constant integer value: let alignment_ty = bx.type_i32(); let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32); - // Truncate the mask vector to a vector of i1s: - let (mask, mask_ty) = { - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, mask_len); - (bx.trunc(args[0].immediate(), i1xn), i1xn) - }; - let llvm_pointer = bx.type_ptr(); // Type of the vector of elements: @@ -1901,27 +1923,21 @@ fn generic_simd_intrinsic<'ll, 'tcx>( } ); - require!( - matches!(mask_elem.kind(), ty::Int(_)), - InvalidMonomorphization::ThirdArgElementType { + let m_elem_bitwidth = + require_int_ty!(mask_elem.kind(), InvalidMonomorphization::ThirdArgElementType { span, name, expected_element: values_elem, third_arg: mask_ty, - } - ); + }); + + let mask = vector_mask_to_bitmask(bx, args[0].immediate(), m_elem_bitwidth, mask_len); + let mask_ty = bx.type_vector(bx.type_i1(), mask_len); // Alignment of T, must be a constant integer value: let alignment_ty = bx.type_i32(); let alignment = bx.const_i32(bx.align_of(values_elem).bytes() as i32); - // Truncate the mask vector to a vector of i1s: - let (mask, mask_ty) = { - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, in_len); - (bx.trunc(args[0].immediate(), i1xn), i1xn) - }; - let ret_t = bx.type_void(); let llvm_pointer = bx.type_ptr(); @@ -1995,28 +2011,21 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ); // The element type of the third argument must be a signed integer type of any width: - match element_ty2.kind() { - ty::Int(_) => (), - _ => { - return_error!(InvalidMonomorphization::ThirdArgElementType { - span, - name, - expected_element: element_ty2, - third_arg: arg_tys[2] - }); - } - } + let mask_elem_bitwidth = + require_int_ty!(element_ty2.kind(), InvalidMonomorphization::ThirdArgElementType { + span, + name, + expected_element: element_ty2, + third_arg: arg_tys[2] + }); // Alignment of T, must be a constant integer value: let alignment_ty = bx.type_i32(); let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32); // Truncate the mask vector to a vector of i1s: - let (mask, mask_ty) = { - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, in_len); - (bx.trunc(args[2].immediate(), i1xn), i1xn) - }; + let mask = vector_mask_to_bitmask(bx, args[2].immediate(), mask_elem_bitwidth, in_len); + let mask_ty = bx.type_vector(bx.type_i1(), in_len); let ret_t = bx.type_void(); @@ -2164,8 +2173,13 @@ fn generic_simd_intrinsic<'ll, 'tcx>( }); args[0].immediate() } else { - match in_elem.kind() { - ty::Int(_) | ty::Uint(_) => {} + let bitwidth = match in_elem.kind() { + ty::Int(i) => { + i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()) + } + ty::Uint(i) => { + i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()) + } _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { span, name, @@ -2174,12 +2188,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>( in_elem, ret_ty }), - } + }; - // boolean reductions operate on vectors of i1s: - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, in_len as u64); - bx.trunc(args[0].immediate(), i1xn) + vector_mask_to_bitmask(bx, args[0].immediate(), bitwidth, in_len as _) }; return match in_elem.kind() { ty::Int(_) | ty::Uint(_) => { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 06afe8bb3ad..4a84fd29e44 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -237,7 +237,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { /// Generate autodiff rules fn autodiff( cgcx: &CodegenContext<Self>, - tcx: TyCtxt<'_>, module: &ModuleCodegen<Self::Module>, diff_fncs: Vec<AutoDiffItem>, config: &ModuleConfig, @@ -246,7 +245,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { let dcx = cgcx.create_dcx(); return Err(dcx.handle().emit_almost_fatal(AutoDiffWithoutLTO)); } - builder::autodiff::differentiate(module, cgcx, tcx, diff_fncs, config) + builder::autodiff::differentiate(module, cgcx, diff_fncs, config) } } diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index ec6c84f6f25..009d15a932f 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -741,8 +741,11 @@ pub mod debuginfo { pub type DIEnumerator = DIDescriptor; pub type DITemplateTypeParameter = DIDescriptor; - // These values **must** match with LLVMRustDIFlags!! bitflags! { + /// Must match the layout of `LLVMDIFlags` in the LLVM-C API. + /// + /// Each value declared here must also be covered by the static + /// assertions in `RustWrapper.cpp` used by `fromRust(LLVMDIFlags)`. #[repr(transparent)] #[derive(Clone, Copy, Default)] pub struct DIFlags: u32 { @@ -752,7 +755,7 @@ pub mod debuginfo { const FlagPublic = 3; const FlagFwdDecl = (1 << 2); const FlagAppleBlock = (1 << 3); - const FlagBlockByrefStruct = (1 << 4); + const FlagReservedBit4 = (1 << 4); const FlagVirtual = (1 << 5); const FlagArtificial = (1 << 6); const FlagExplicit = (1 << 7); @@ -763,10 +766,21 @@ pub mod debuginfo { const FlagStaticMember = (1 << 12); const FlagLValueReference = (1 << 13); const FlagRValueReference = (1 << 14); - const FlagExternalTypeRef = (1 << 15); + const FlagReserved = (1 << 15); + const FlagSingleInheritance = (1 << 16); + const FlagMultipleInheritance = (2 << 16); + const FlagVirtualInheritance = (3 << 16); const FlagIntroducedVirtual = (1 << 18); const FlagBitField = (1 << 19); const FlagNoReturn = (1 << 20); + // The bit at (1 << 21) is unused, but was `LLVMDIFlagMainSubprogram`. + const FlagTypePassByValue = (1 << 22); + const FlagTypePassByReference = (1 << 23); + const FlagEnumClass = (1 << 24); + const FlagThunk = (1 << 25); + const FlagNonTrivial = (1 << 26); + const FlagBigEndian = (1 << 27); + const FlagLittleEndian = (1 << 28); } } @@ -918,6 +932,7 @@ unsafe extern "C" { pub fn LLVMMetadataTypeInContext(C: &Context) -> &Type; // Operations on all values + pub fn LLVMIsUndef(Val: &Value) -> Bool; pub fn LLVMTypeOf(Val: &Value) -> &Type; pub fn LLVMGetValueName2(Val: &Value, Length: *mut size_t) -> *const c_char; pub fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t); diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 6aec078e0de..c56ad886120 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_target::callconv::{CastTarget, FnAbi, Reg}; use crate::abi::{FnAbiLlvmExt, LlvmType}; -use crate::context::CodegenCx; +use crate::context::{CodegenCx, SimpleCx}; pub(crate) use crate::llvm::Type; use crate::llvm::{Bool, False, Metadata, True}; use crate::type_of::LayoutLlvmExt; @@ -35,7 +35,8 @@ impl fmt::Debug for Type { } } -impl<'ll> CodegenCx<'ll, '_> { +impl<'ll> CodegenCx<'ll, '_> {} +impl<'ll> SimpleCx<'ll> { pub(crate) fn type_named_struct(&self, name: &str) -> &'ll Type { let name = SmallCStr::new(name); unsafe { llvm::LLVMStructCreateNamed(self.llcx, name.as_ptr()) } @@ -44,11 +45,9 @@ impl<'ll> CodegenCx<'ll, '_> { pub(crate) fn set_struct_body(&self, ty: &'ll Type, els: &[&'ll Type], packed: bool) { unsafe { llvm::LLVMStructSetBody(ty, els.as_ptr(), els.len() as c_uint, packed as Bool) } } - pub(crate) fn type_void(&self) -> &'ll Type { unsafe { llvm::LLVMVoidTypeInContext(self.llcx) } } - pub(crate) fn type_token(&self) -> &'ll Type { unsafe { llvm::LLVMTokenTypeInContext(self.llcx) } } @@ -75,7 +74,8 @@ impl<'ll> CodegenCx<'ll, '_> { args } } - +} +impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub(crate) fn type_bool(&self) -> &'ll Type { self.type_i8() } @@ -120,7 +120,8 @@ impl<'ll> CodegenCx<'ll, '_> { assert_eq!(size % unit_size, 0); self.type_array(self.type_from_integer(unit), size / unit_size) } - +} +impl<'ll> SimpleCx<'ll> { pub(crate) fn type_variadic_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type { unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, True) } } diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 3254b5d38e7..b22357acdc2 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" ar_archive_writer = "0.4.2" arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" +bstr = "1.11.3" # Pinned so `cargo update` bumps don't cause breakage. Please also update the # `cc` in `rustc_llvm` if you update the `cc` here. cc = "=1.2.7" @@ -59,5 +60,5 @@ default-features = false features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"] [target.'cfg(windows)'.dependencies.windows] -version = "0.57.0" +version = "0.59.0" features = ["Win32_Globalization"] diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 484f467068a..de37de09f5a 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -183,6 +183,8 @@ codegen_ssa_linker_file_stem = couldn't extract file stem from specified linker codegen_ssa_linker_not_found = linker `{$linker_path}` not found .note = {$error} +codegen_ssa_linker_output = {$inner} + codegen_ssa_linker_unsupported_modifier = `as-needed` modifier not supported for current linker codegen_ssa_linking_failed = linking with `{$linker_path}` failed: {$exit_status} @@ -211,7 +213,7 @@ codegen_ssa_msvc_missing_linker = the msvc targets depend on the msvc linker but codegen_ssa_multiple_external_func_decl = multiple declarations of external function `{$function}` from library `{$library_name}` have different calling conventions codegen_ssa_multiple_main_functions = entry symbol `main` declared multiple times - .help = did you use `#[no_mangle]` on `fn main`? Use `#[start]` instead + .help = did you use `#[no_mangle]` on `fn main`? Use `#![no_main]` to suppress the usual Rust-generated entry point codegen_ssa_no_field = no field `{$name}` diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index ab65319e3d3..27331ce4ca6 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -46,8 +46,12 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTr return; } - let available_cgus = - tcx.collect_and_partition_mono_items(()).1.iter().map(|cgu| cgu.name()).collect(); + let available_cgus = tcx + .collect_and_partition_mono_items(()) + .codegen_units + .iter() + .map(|cgu| cgu.name()) + .collect(); let mut ams = AssertModuleSource { tcx, diff --git a/compiler/rustc_codegen_ssa/src/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs index b3c5b86ccf4..63023fdba20 100644 --- a/compiler/rustc_codegen_ssa/src/back/command.rs +++ b/compiler/rustc_codegen_ssa/src/back/command.rs @@ -13,6 +13,7 @@ pub(crate) struct Command { args: Vec<OsString>, env: Vec<(OsString, OsString)>, env_remove: Vec<OsString>, + env_clear: bool, } #[derive(Clone)] @@ -36,7 +37,13 @@ impl Command { } fn _new(program: Program) -> Command { - Command { program, args: Vec::new(), env: Vec::new(), env_remove: Vec::new() } + Command { + program, + args: Vec::new(), + env: Vec::new(), + env_remove: Vec::new(), + env_clear: false, + } } pub(crate) fn arg<P: AsRef<OsStr>>(&mut self, arg: P) -> &mut Command { @@ -79,6 +86,11 @@ impl Command { self } + pub(crate) fn env_clear(&mut self) -> &mut Command { + self.env_clear = true; + self + } + fn _env_remove(&mut self, key: &OsStr) { self.env_remove.push(key.to_owned()); } @@ -106,6 +118,9 @@ impl Command { for k in &self.env_remove { ret.env_remove(k); } + if self.env_clear { + ret.env_clear(); + } ret } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index df35b5e8426..105a4cb81f0 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -15,12 +15,14 @@ use rustc_ast::CRATE_NODE_ID; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_errors::DiagCtxtHandle; +use rustc_errors::{DiagCtxtHandle, LintDiagnostic}; use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc_macros::LintDiagnostic; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; use rustc_metadata::{find_native_static_library, walk_native_lib_search_dirs}; use rustc_middle::bug; +use rustc_middle::lint::lint_level; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::SymbolExportKind; @@ -29,6 +31,7 @@ use rustc_session::config::{ OutputType, PrintKind, SplitDwarfKind, Strip, }; use rustc_session::cstore::DllImport; +use rustc_session::lint::builtin::LINKER_MESSAGES; use rustc_session::output::{check_file_is_writeable, invalid_output_for_target, out_filename}; use rustc_session::search_paths::PathKind; use rustc_session::utils::NativeLibKind; @@ -749,6 +752,14 @@ fn link_dwarf_object(sess: &Session, cg_results: &CodegenResults, executable_out } } +#[derive(LintDiagnostic)] +#[diag(codegen_ssa_linker_output)] +/// Translating this is kind of useless. We don't pass translation flags to the linker, so we'd just +/// end up with inconsistent languages within the same diagnostic. +struct LinkerOutput { + inner: String, +} + /// Create a dynamic library or executable. /// /// This will invoke the system linker/cc to create the resulting file. This links to all upstream @@ -981,6 +992,11 @@ fn link_natively( match prog { Ok(prog) => { + let is_msvc_link_exe = sess.target.is_like_msvc + && flavor == LinkerFlavor::Msvc(Lld::No) + // Match exactly "link.exe" + && linker_path.to_str() == Some("link.exe"); + if !prog.status.success() { let mut output = prog.stderr.clone(); output.extend_from_slice(&prog.stdout); @@ -991,22 +1007,16 @@ fn link_natively( command: cmd, escaped_output, verbose: sess.opts.verbose, + sysroot_dir: sess.sysroot.clone(), }; sess.dcx().emit_err(err); // If MSVC's `link.exe` was expected but the return code // is not a Microsoft LNK error then suggest a way to fix or // install the Visual Studio build tools. if let Some(code) = prog.status.code() { - if sess.target.is_like_msvc - && flavor == LinkerFlavor::Msvc(Lld::No) - // Respect the command line override - && sess.opts.cg.linker.is_none() - // Match exactly "link.exe" - && linker_path.to_str() == Some("link.exe") - // All Microsoft `link.exe` linking error codes are - // four digit numbers in the range 1000 to 9999 inclusive - && (code < 1000 || code > 9999) - { + // All Microsoft `link.exe` linking ror codes are + // four digit numbers in the range 1000 to 9999 inclusive + if is_msvc_link_exe && (code < 1000 || code > 9999) { let is_vs_installed = windows_registry::find_vs_version().is_ok(); let has_linker = windows_registry::find_tool(&sess.target.arch, "link.exe").is_some(); @@ -1028,8 +1038,49 @@ fn link_natively( sess.dcx().abort_if_errors(); } - info!("linker stderr:\n{}", escape_string(&prog.stderr)); - info!("linker stdout:\n{}", escape_string(&prog.stdout)); + + let stderr = escape_string(&prog.stderr); + let mut stdout = escape_string(&prog.stdout); + info!("linker stderr:\n{}", &stderr); + info!("linker stdout:\n{}", &stdout); + + // Hide some progress messages from link.exe that we don't care about. + // See https://github.com/chromium/chromium/blob/bfa41e41145ffc85f041384280caf2949bb7bd72/build/toolchain/win/tool_wrapper.py#L144-L146 + if is_msvc_link_exe { + if let Ok(str) = str::from_utf8(&prog.stdout) { + let mut output = String::with_capacity(str.len()); + for line in stdout.lines() { + if line.starts_with(" Creating library") + || line.starts_with("Generating code") + || line.starts_with("Finished generating code") + { + continue; + } + output += line; + output += "\r\n" + } + stdout = escape_string(output.trim().as_bytes()) + } + } + + let (level, src) = codegen_results.crate_info.lint_levels.linker_messages; + let lint = |msg| { + lint_level(sess, LINKER_MESSAGES, level, src, None, |diag| { + LinkerOutput { inner: msg }.decorate_lint(diag) + }) + }; + + if !prog.stderr.is_empty() { + // We already print `warning:` at the start of the diagnostic. Remove it from the linker output if present. + let stderr = stderr + .strip_prefix("warning: ") + .unwrap_or(&stderr) + .replace(": warning: ", ": "); + lint(format!("linker stderr: {stderr}")); + } + if !stdout.is_empty() { + lint(format!("linker stdout: {}", stdout)) + } } Err(e) => { let linker_not_found = e.kind() == io::ErrorKind::NotFound; diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index efccf7687a1..9fd984b6419 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -4,7 +4,6 @@ use std::sync::Arc; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_data_structures::memmap::Mmap; use rustc_errors::FatalError; -use rustc_middle::ty::TyCtxt; use super::write::CodegenContext; use crate::ModuleCodegen; @@ -89,13 +88,12 @@ impl<B: WriteBackendMethods> LtoModuleCodegen<B> { pub unsafe fn autodiff( self, cgcx: &CodegenContext<B>, - tcx: TyCtxt<'_>, diff_fncs: Vec<AutoDiffItem>, config: &ModuleConfig, ) -> Result<LtoModuleCodegen<B>, FatalError> { match &self { LtoModuleCodegen::Fat(module) => { - B::autodiff(cgcx, tcx, &module, diff_fncs, config)?; + B::autodiff(cgcx, &module, diff_fncs, config)?; } _ => panic!("autodiff called with non-fat LTO module"), } diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 60ab2919352..f8f7bb2dbc6 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -293,7 +293,7 @@ fn exported_symbols_provider_local( // external linkage is enough for monomorphization to be linked to. let need_visibility = tcx.sess.target.dynamic_linking && !tcx.sess.target.only_cdylib; - let (_, cgus) = tcx.collect_and_partition_mono_items(()); + let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; // The symbols created in this loop are sorted below it #[allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 544578b29f1..e438bd70c51 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -44,7 +44,8 @@ use crate::mir::operand::OperandValue; use crate::mir::place::PlaceRef; use crate::traits::*; use crate::{ - CachedModuleCodegen, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, errors, meth, mir, + CachedModuleCodegen, CodegenLintLevels, CompiledModule, CrateInfo, ModuleCodegen, ModuleKind, + errors, meth, mir, }; pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { @@ -490,8 +491,8 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let ptr_ty = cx.type_ptr(); let (arg_argc, arg_argv) = get_argc_argv(&mut bx); - let (start_fn, start_ty, args, instance) = if let EntryFnType::Main { sigpipe } = entry_type - { + let EntryFnType::Main { sigpipe } = entry_type; + let (start_fn, start_ty, args, instance) = { let start_def_id = cx.tcx().require_lang_item(LangItem::Start, None); let start_instance = ty::Instance::expect_resolve( cx.tcx(), @@ -512,10 +513,6 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( vec![rust_main, arg_argc, arg_argv, arg_sigpipe], Some(start_instance), ) - } else { - debug!("using user-defined start fn"); - let start_ty = cx.type_func(&[isize_ty, ptr_ty], isize_ty); - (rust_main, start_ty, vec![arg_argc, arg_argv], None) }; let result = bx.call(start_ty, None, None, start_fn, &args, None, instance); @@ -530,7 +527,8 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } -/// Obtain the `argc` and `argv` values to pass to the rust start function. +/// Obtain the `argc` and `argv` values to pass to the rust start function +/// (i.e., the "start" lang item). fn get_argc_argv<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(bx: &mut Bx) -> (Bx::Value, Bx::Value) { if bx.cx().sess().target.os.contains("uefi") { // Params for UEFI @@ -621,7 +619,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>( // Run the monomorphization collector and partition the collected items into // codegen units. - let codegen_units = tcx.collect_and_partition_mono_items(()).1; + let codegen_units = tcx.collect_and_partition_mono_items(()).codegen_units; // Force all codegen_unit queries so they are already either red or green // when compile_codegen_unit accesses them. We are not able to re-execute @@ -927,6 +925,7 @@ impl CrateInfo { dependency_formats: Lrc::clone(tcx.dependency_formats(())), windows_subsystem, natvis_debugger_visualizers: Default::default(), + lint_levels: CodegenLintLevels::from_tcx(tcx), }; info.native_libraries.reserve(n_crates); @@ -1052,14 +1051,11 @@ pub(crate) fn provide(providers: &mut Providers) { config::OptLevel::SizeMin => config::OptLevel::Default, }; - let (defids, _) = tcx.collect_and_partition_mono_items(cratenum); + let defids = tcx.collect_and_partition_mono_items(cratenum).all_mono_items; let any_for_speed = defids.items().any(|id| { let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id); - match optimize { - attr::OptimizeAttr::None | attr::OptimizeAttr::Size => false, - attr::OptimizeAttr::Speed => true, - } + matches!(optimize, attr::OptimizeAttr::Speed) }); if any_for_speed { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 1daa17fbaf3..a0bc2d4ea48 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -575,7 +575,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.inline = InlineAttr::Never; } - codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| { + codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| { if !attr.has_name(sym::optimize) { return ia; } @@ -587,17 +587,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { inline_span = Some(attr.span); if items.len() != 1 { err(attr.span, "expected one argument"); - OptimizeAttr::None + OptimizeAttr::Default } else if list_contains_name(items, sym::size) { OptimizeAttr::Size } else if list_contains_name(items, sym::speed) { OptimizeAttr::Speed + } else if list_contains_name(items, sym::none) { + OptimizeAttr::DoNotOptimize } else { err(items[0].span(), "invalid argument"); - OptimizeAttr::None + OptimizeAttr::Default } } else { - OptimizeAttr::None + OptimizeAttr::Default } }); diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index c7213bbc801..5e684632fb2 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -351,6 +351,7 @@ pub(crate) struct LinkingFailed<'a> { pub command: Command, pub escaped_output: String, pub verbose: bool, + pub sysroot_dir: PathBuf, } impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> { @@ -364,6 +365,8 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> { if self.verbose { diag.note(format!("{:?}", self.command)); } else { + self.command.env_clear(); + enum ArgGroup { Regular(OsString), Objects(usize), @@ -398,26 +401,55 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> { args.push(ArgGroup::Regular(arg)); } } - self.command.args(args.into_iter().map(|arg_group| match arg_group { - ArgGroup::Regular(arg) => arg, - ArgGroup::Objects(n) => OsString::from(format!("<{n} object files omitted>")), - ArgGroup::Rlibs(dir, rlibs) => { - let mut arg = dir.into_os_string(); - arg.push("/{"); - let mut first = true; - for rlib in rlibs { - if !first { - arg.push(","); + let crate_hash = regex::bytes::Regex::new(r"-[0-9a-f]+\.rlib$").unwrap(); + self.command.args(args.into_iter().map(|arg_group| { + match arg_group { + // SAFETY: we are only matching on ASCII, not any surrogate pairs, so any replacements we do will still be valid. + ArgGroup::Regular(arg) => unsafe { + use bstr::ByteSlice; + OsString::from_encoded_bytes_unchecked( + arg.as_encoded_bytes().replace( + self.sysroot_dir.as_os_str().as_encoded_bytes(), + b"<sysroot>", + ), + ) + }, + ArgGroup::Objects(n) => OsString::from(format!("<{n} object files omitted>")), + ArgGroup::Rlibs(mut dir, rlibs) => { + let is_sysroot_dir = match dir.strip_prefix(&self.sysroot_dir) { + Ok(short) => { + dir = Path::new("<sysroot>").join(short); + true + } + Err(_) => false, + }; + let mut arg = dir.into_os_string(); + arg.push("/{"); + let mut first = true; + for mut rlib in rlibs { + if !first { + arg.push(","); + } + first = false; + if is_sysroot_dir { + // SAFETY: Regex works one byte at a type, and our regex will not match surrogate pairs (because it only matches ascii). + rlib = unsafe { + OsString::from_encoded_bytes_unchecked( + crate_hash + .replace(rlib.as_encoded_bytes(), b"-*") + .into_owned(), + ) + }; + } + arg.push(rlib); } - first = false; - arg.push(rlib); + arg.push("}.rlib"); + arg } - arg.push("}"); - arg } })); - diag.note(format!("{:?}", self.command)); + diag.note(format!("{:?}", self.command).trim_start_matches("env -i").to_owned()); diag.note("some arguments are omitted. use `--verbose` to show all linker arguments"); } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 65c6067c740..40299ae2630 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -29,18 +29,23 @@ use rustc_ast as ast; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::unord::UnordMap; +use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::CrateNum; use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_middle::dep_graph::WorkProduct; +use rustc_middle::lint::LintLevelSource; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::middle::exported_symbols::SymbolExportKind; +use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_serialize::opaque::{FileEncoder, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_session::Session; use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT}; use rustc_session::cstore::{self, CrateSource}; +use rustc_session::lint::Level; +use rustc_session::lint::builtin::LINKER_MESSAGES; use rustc_session::utils::NativeLibKind; use rustc_span::Symbol; @@ -200,6 +205,7 @@ pub struct CrateInfo { pub dependency_formats: Lrc<Dependencies>, pub windows_subsystem: Option<String>, pub natvis_debugger_visualizers: BTreeSet<DebuggerVisualizerFile>, + pub lint_levels: CodegenLintLevels, } #[derive(Encodable, Decodable)] @@ -302,3 +308,19 @@ impl CodegenResults { Ok((codegen_results, outputs)) } } + +/// A list of lint levels used in codegen. +/// +/// When using `-Z link-only`, we don't have access to the tcx and must work +/// solely from the `.rlink` file. `Lint`s are defined too early to be encodeable. +/// Instead, encode exactly the information we need. +#[derive(Copy, Clone, Debug, Encodable, Decodable)] +pub struct CodegenLintLevels { + linker_messages: (Level, LintLevelSource), +} + +impl CodegenLintLevels { + pub fn from_tcx(tcx: TyCtxt<'_>) -> Self { + Self { linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID) } + } +} diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 843a996d2bf..5924c8991ad 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -19,9 +19,7 @@ use crate::traits::*; pub struct FunctionDebugContext<'tcx, S, L> { /// Maps from source code to the corresponding debug info scope. - /// May be None if the backend is not capable of representing the scope for - /// some reason. - pub scopes: IndexVec<mir::SourceScope, Option<DebugScope<S, L>>>, + pub scopes: IndexVec<mir::SourceScope, DebugScope<S, L>>, /// Maps from an inlined function to its debug info declaration. pub inlined_function_scopes: FxHashMap<Instance<'tcx>, S>, @@ -232,7 +230,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &self, source_info: mir::SourceInfo, ) -> Option<(Bx::DIScope, Option<Bx::DILocation>, Span)> { - let scope = &self.debug_context.as_ref()?.scopes[source_info.scope]?; + let scope = &self.debug_context.as_ref()?.scopes[source_info.scope]; let span = hygiene::walk_chain_collapsed(source_info.span, self.mir.span); Some((scope.adjust_dbg_scope_for_span(self.cx, span), scope.inlined_at, span)) } diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 8df270abc81..dc406809874 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,10 +1,14 @@ +use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind}; use rustc_attr_parsing::InstructionSetAttr; +use rustc_hir::def_id::DefId; use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility}; use rustc_middle::mir::{Body, InlineAsmOperand}; -use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf}; -use rustc_middle::ty::{Instance, TyCtxt}; -use rustc_middle::{bug, ty}; +use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; +use rustc_middle::ty::{Instance, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug, ty}; use rustc_span::sym; +use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; +use rustc_target::spec::WasmCAbi; use crate::common; use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods}; @@ -32,7 +36,8 @@ pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap(); let name = cx.mangled_name(instance); - let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data); + let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); + let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data, fn_abi); let mut template_vec = Vec::new(); template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin.into())); @@ -103,6 +108,7 @@ enum AsmBinaryFormat { Elf, Macho, Coff, + Wasm, } impl AsmBinaryFormat { @@ -111,6 +117,8 @@ impl AsmBinaryFormat { Self::Coff } else if target.is_like_osx { Self::Macho + } else if target.is_like_wasm { + Self::Wasm } else { Self::Elf } @@ -122,6 +130,7 @@ fn prefix_and_suffix<'tcx>( instance: Instance<'tcx>, asm_name: &str, item_data: &MonoItemData, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, ) -> (String, String) { use std::fmt::Write; @@ -169,7 +178,7 @@ fn prefix_and_suffix<'tcx>( } Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR => { match asm_binary_format { - AsmBinaryFormat::Elf | AsmBinaryFormat::Coff => { + AsmBinaryFormat::Elf | AsmBinaryFormat::Coff | AsmBinaryFormat::Wasm => { writeln!(w, ".weak {asm_name}")?; } AsmBinaryFormat::Macho => { @@ -264,7 +273,161 @@ fn prefix_and_suffix<'tcx>( writeln!(end, "{}", arch_suffix).unwrap(); } } + AsmBinaryFormat::Wasm => { + let section = link_section.unwrap_or(format!(".text.{asm_name}")); + + writeln!(begin, ".section {section},\"\",@").unwrap(); + // wasm functions cannot be aligned, so skip + write_linkage(&mut begin).unwrap(); + if let Visibility::Hidden = item_data.visibility { + writeln!(begin, ".hidden {asm_name}").unwrap(); + } + writeln!(begin, ".type {asm_name}, @function").unwrap(); + if !arch_prefix.is_empty() { + writeln!(begin, "{}", arch_prefix).unwrap(); + } + writeln!(begin, "{asm_name}:").unwrap(); + writeln!( + begin, + ".functype {asm_name} {}", + wasm_functype(tcx, fn_abi, instance.def_id()) + ) + .unwrap(); + + writeln!(end).unwrap(); + // .size is ignored for function symbols, so we can skip it + writeln!(end, "end_function").unwrap(); + } } (begin, end) } + +/// The webassembly type signature for the given function. +/// +/// Used by the `.functype` directive on wasm targets. +fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, def_id: DefId) -> String { + let mut signature = String::with_capacity(64); + + let ptr_type = match tcx.data_layout.pointer_size.bits() { + 32 => "i32", + 64 => "i64", + other => bug!("wasm pointer size cannot be {other} bits"), + }; + + // FIXME: remove this once the wasm32-unknown-unknown ABI is fixed + // please also add `wasm32-unknown-unknown` back in `tests/assembly/wasm32-naked-fn.rs` + // basically the commit introducing this comment should be reverted + if let PassMode::Pair { .. } = fn_abi.ret.mode { + let _ = WasmCAbi::Legacy; + span_bug!( + tcx.def_span(def_id), + "cannot return a pair (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666" + ); + } + + let hidden_return = matches!(fn_abi.ret.mode, PassMode::Indirect { .. }); + + signature.push('('); + + if hidden_return { + signature.push_str(ptr_type); + if !fn_abi.args.is_empty() { + signature.push_str(", "); + } + } + + let mut it = fn_abi.args.iter().peekable(); + while let Some(arg_abi) = it.next() { + wasm_type(tcx, &mut signature, arg_abi, ptr_type, def_id); + if it.peek().is_some() { + signature.push_str(", "); + } + } + + signature.push_str(") -> ("); + + if !hidden_return { + wasm_type(tcx, &mut signature, &fn_abi.ret, ptr_type, def_id); + } + + signature.push(')'); + + signature +} + +fn wasm_type<'tcx>( + tcx: TyCtxt<'tcx>, + signature: &mut String, + arg_abi: &ArgAbi<'_, Ty<'tcx>>, + ptr_type: &'static str, + def_id: DefId, +) { + match arg_abi.mode { + PassMode::Ignore => { /* do nothing */ } + PassMode::Direct(_) => { + let direct_type = match arg_abi.layout.backend_repr { + BackendRepr::Scalar(scalar) => wasm_primitive(scalar.primitive(), ptr_type), + BackendRepr::Vector { .. } => "v128", + BackendRepr::Memory { .. } => { + // FIXME: remove this branch once the wasm32-unknown-unknown ABI is fixed + let _ = WasmCAbi::Legacy; + span_bug!( + tcx.def_span(def_id), + "cannot use memory args (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666" + ); + } + other => unreachable!("unexpected BackendRepr: {:?}", other), + }; + + signature.push_str(direct_type); + } + PassMode::Pair(_, _) => match arg_abi.layout.backend_repr { + BackendRepr::ScalarPair(a, b) => { + signature.push_str(wasm_primitive(a.primitive(), ptr_type)); + signature.push_str(", "); + signature.push_str(wasm_primitive(b.primitive(), ptr_type)); + } + other => unreachable!("{other:?}"), + }, + PassMode::Cast { pad_i32, ref cast } => { + // For wasm, Cast is used for single-field primitive wrappers like `struct Wrapper(i64);` + assert!(!pad_i32, "not currently used by wasm calling convention"); + assert!(cast.prefix[0].is_none(), "no prefix"); + assert_eq!(cast.rest.total, arg_abi.layout.size, "single item"); + + let wrapped_wasm_type = match cast.rest.unit.kind { + RegKind::Integer => match cast.rest.unit.size.bytes() { + ..=4 => "i32", + ..=8 => "i64", + _ => ptr_type, + }, + RegKind::Float => match cast.rest.unit.size.bytes() { + ..=4 => "f32", + ..=8 => "f64", + _ => ptr_type, + }, + RegKind::Vector => "v128", + }; + + signature.push_str(wrapped_wasm_type); + } + PassMode::Indirect { .. } => signature.push_str(ptr_type), + } +} + +fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str { + match primitive { + Primitive::Int(integer, _) => match integer { + Integer::I8 | Integer::I16 | Integer::I32 => "i32", + Integer::I64 => "i64", + Integer::I128 => "i64, i64", + }, + Primitive::Float(float) => match float { + Float::F16 | Float::F32 => "f32", + Float::F64 => "f64", + Float::F128 => "i64, i64", + }, + Primitive::Pointer(_) => ptr_type, + } +} diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 19101ec2d1b..9ca7d4f8f00 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -204,14 +204,30 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let alloc_align = alloc.inner().align; assert!(alloc_align >= layout.align.abi); + // Returns `None` when the value is partially undefined or any byte of it has provenance. + // Otherwise returns the value or (if the entire value is undef) returns an undef. let read_scalar = |start, size, s: abi::Scalar, ty| { + let range = alloc_range(start, size); match alloc.0.read_scalar( bx, - alloc_range(start, size), + range, /*read_provenance*/ matches!(s.primitive(), abi::Primitive::Pointer(_)), ) { - Ok(val) => bx.scalar_to_backend(val, s, ty), - Err(_) => bx.const_poison(ty), + Ok(val) => Some(bx.scalar_to_backend(val, s, ty)), + Err(_) => { + // We may have failed due to partial provenance or unexpected provenance, + // continue down the normal code path if so. + if alloc.0.provenance().range_empty(range, &bx.tcx()) + // Since `read_scalar` failed, but there were no relocations involved, the + // bytes must be partially or fully uninitialized. Thus we can now unwrap the + // information about the range of uninit bytes and check if it's the full range. + && alloc.0.init_mask().is_range_initialized(range).unwrap_err() == range + { + Some(bx.const_undef(ty)) + } else { + None + } + } } }; @@ -222,16 +238,14 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { // check that walks over the type of `mplace` to make sure it is truly correct to treat this // like a `Scalar` (or `ScalarPair`). match layout.backend_repr { - BackendRepr::Scalar(s @ abi::Scalar::Initialized { .. }) => { + BackendRepr::Scalar(s) => { let size = s.size(bx); assert_eq!(size, layout.size, "abi::Scalar size does not match layout size"); - let val = read_scalar(offset, size, s, bx.immediate_backend_type(layout)); - OperandRef { val: OperandValue::Immediate(val), layout } + if let Some(val) = read_scalar(offset, size, s, bx.immediate_backend_type(layout)) { + return OperandRef { val: OperandValue::Immediate(val), layout }; + } } - BackendRepr::ScalarPair( - a @ abi::Scalar::Initialized { .. }, - b @ abi::Scalar::Initialized { .. }, - ) => { + BackendRepr::ScalarPair(a, b) => { let (a_size, b_size) = (a.size(bx), b.size(bx)); let b_offset = (offset + a_size).align_to(b.align(bx).abi); assert!(b_offset.bytes() > 0); @@ -247,20 +261,21 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { b, bx.scalar_pair_element_backend_type(layout, 1, true), ); - OperandRef { val: OperandValue::Pair(a_val, b_val), layout } - } - _ if layout.is_zst() => OperandRef::zero_sized(layout), - _ => { - // Neither a scalar nor scalar pair. Load from a place - // FIXME: should we cache `const_data_from_alloc` to avoid repeating this for the - // same `ConstAllocation`? - let init = bx.const_data_from_alloc(alloc); - let base_addr = bx.static_addr_of(init, alloc_align, None); - - let llval = bx.const_ptr_byte_offset(base_addr, offset); - bx.load_operand(PlaceRef::new_sized(llval, layout)) + if let (Some(a_val), Some(b_val)) = (a_val, b_val) { + return OperandRef { val: OperandValue::Pair(a_val, b_val), layout }; + } } + _ if layout.is_zst() => return OperandRef::zero_sized(layout), + _ => {} } + // Neither a scalar nor scalar pair. Load from a place + // FIXME: should we cache `const_data_from_alloc` to avoid repeating this for the + // same `ConstAllocation`? + let init = bx.const_data_from_alloc(alloc); + let base_addr = bx.static_addr_of(init, alloc_align, None); + + let llval = bx.const_ptr_byte_offset(base_addr, offset); + bx.load_operand(PlaceRef::new_sized(llval, layout)) } /// Asserts that this operand refers to a scalar and returns diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index eb4ef599b82..e775d219c7b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_middle::{bug, mir, span_bug}; use rustc_session::config::OptLevel; use rustc_span::{DUMMY_SP, Span}; -use tracing::{debug, instrument}; +use tracing::{debug, instrument, trace}; use super::operand::{OperandRef, OperandValue}; use super::place::PlaceRef; @@ -93,6 +93,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } + // If `v` is an integer constant whose value is just a single byte repeated N times, + // emit a `memset` filling the entire `dest` with that byte. let try_init_all_same = |bx: &mut Bx, v| { let start = dest.val.llval; let size = bx.const_usize(dest.layout.size.bytes()); @@ -117,13 +119,33 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { false }; + trace!(?cg_elem.val); match cg_elem.val { OperandValue::Immediate(v) => { if try_init_all_same(bx, v) { return; } } - _ => (), + OperandValue::Pair(a, b) => { + let a_is_undef = bx.cx().is_undef(a); + match (a_is_undef, bx.cx().is_undef(b)) { + // Can happen for uninit unions + (true, true) => { + // FIXME: can we produce better output here? + } + (false, true) | (true, false) => { + let val = if a_is_undef { b } else { a }; + if try_init_all_same(bx, val) { + return; + } + } + (false, false) => { + // FIXME: if both are the same value, use try_init_all_same + } + } + } + OperandValue::ZeroSized => unreachable!("checked above"), + OperandValue::Ref(..) => {} } let count = self @@ -365,10 +387,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { use abi::Primitive::*; imm = bx.from_immediate(imm); - // When scalars are passed by value, there's no metadata recording their - // valid ranges. For example, `char`s are passed as just `i32`, with no - // way for LLVM to know that they're 0x10FFFF at most. Thus we assume - // the range of the input value too, not just the output range. + // If we have a scalar, we must already know its range. Either + // + // 1) It's a parameter with `range` parameter metadata, + // 2) It's something we `load`ed with `!range` metadata, or + // 3) After a transmute we `assume`d the range (see below). + // + // That said, last time we tried removing this, it didn't actually help + // the rustc-perf results, so might as well keep doing it + // <https://github.com/rust-lang/rust/pull/135610#issuecomment-2599275182> self.assume_scalar_range(bx, imm, from_scalar, from_backend_ty); imm = match (from_scalar.primitive(), to_scalar.primitive()) { @@ -389,7 +416,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.bitcast(int_imm, to_backend_ty) } }; + + // This `assume` remains important for cases like (a conceptual) + // transmute::<u32, NonZeroU32>(x) == 0 + // since it's never passed to something with parameter metadata (especially + // after MIR inlining) so the only way to tell the backend about the + // constraint that the `transmute` introduced is to `assume` it. self.assume_scalar_range(bx, imm, to_scalar, to_backend_ty); + imm = bx.to_immediate_scalar(imm, to_scalar); imm } @@ -411,31 +445,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } - let abi::WrappingRange { start, end } = scalar.valid_range(self.cx); - - if start <= end { - if start > 0 { - let low = bx.const_uint_big(backend_ty, start); - let cmp = bx.icmp(IntPredicate::IntUGE, imm, low); - bx.assume(cmp); - } - - let type_max = scalar.size(self.cx).unsigned_int_max(); - if end < type_max { - let high = bx.const_uint_big(backend_ty, end); - let cmp = bx.icmp(IntPredicate::IntULE, imm, high); - bx.assume(cmp); - } - } else { - let low = bx.const_uint_big(backend_ty, start); - let cmp_low = bx.icmp(IntPredicate::IntUGE, imm, low); - - let high = bx.const_uint_big(backend_ty, end); - let cmp_high = bx.icmp(IntPredicate::IntULE, imm, high); - - let or = bx.or(cmp_low, cmp_high); - bx.assume(or); - } + let range = scalar.valid_range(self.cx); + bx.assume_integer_range(imm, backend_ty, range); } pub(crate) fn codegen_rvalue_unsized( diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 3ee13b19f66..bbf87a59942 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -217,6 +217,27 @@ pub trait BuilderMethods<'a, 'tcx>: dest: PlaceRef<'tcx, Self::Value>, ); + /// Emits an `assume` that the integer value `imm` of type `ty` is contained in `range`. + /// + /// This *always* emits the assumption, so you probably want to check the + /// optimization level and `Scalar::is_always_valid` before calling it. + fn assume_integer_range(&mut self, imm: Self::Value, ty: Self::Type, range: WrappingRange) { + let WrappingRange { start, end } = range; + + // Perhaps one day we'll be able to use assume operand bundles for this, + // but for now this encoding with a single icmp+assume is best per + // <https://github.com/llvm/llvm-project/issues/123278#issuecomment-2597440158> + let shifted = if start == 0 { + imm + } else { + let low = self.const_uint_big(ty, start); + self.sub(imm, low) + }; + let width = self.const_uint_big(ty, u128::wrapping_sub(end, start)); + let cmp = self.icmp(IntPredicate::IntULE, shifted, width); + self.assume(cmp); + } + fn range_metadata(&mut self, load: Self::Value, range: WrappingRange); fn nonnull_metadata(&mut self, load: Self::Value); diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index 9af463a691a..d0de7ff0b5f 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -9,6 +9,7 @@ pub trait ConstCodegenMethods<'tcx>: BackendTypes { /// Generate an uninitialized value (matching uninitialized memory in MIR). /// Whether memory is initialized or not is tracked byte-for-byte. fn const_undef(&self, t: Self::Type) -> Self::Value; + fn is_undef(&self, v: Self::Value) -> bool; /// Generate a fake value. Poison always affects the entire value, even if just a single byte is /// poison. This can only be used in codepaths that are already UB, i.e., UB-free Rust code /// (including code that e.g. copies uninit memory with `MaybeUninit`) can never encounter a diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 51e2255efe1..97fe614aa10 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -1,7 +1,6 @@ use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_middle::dep_graph::WorkProduct; -use rustc_middle::ty::TyCtxt; use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig}; @@ -65,7 +64,6 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer); fn autodiff( cgcx: &CodegenContext<Self>, - tcx: TyCtxt<'_>, module: &ModuleCodegen<Self::Module>, diff_fncs: Vec<AutoDiffItem>, config: &ModuleConfig, diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index d4bfb781320..485c8696342 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -416,7 +416,7 @@ const_eval_unsized_local = unsized locals are not supported const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn const_eval_unstable_in_stable_exposed = const function that might be (indirectly) exposed to stable cannot use `#[feature({$gate})]` - .is_function_call = mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unsafe features + .is_function_call = mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features .unstable_sugg = if the {$is_function_call2 -> [true] caller *[false] function diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index fee7287951d..cc21a18af3a 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -140,7 +140,7 @@ where // Don't emit a new diagnostic for these errors, they are already reported elsewhere or // should remain silent. err_inval!(AlreadyReported(info)) => ErrorHandled::Reported(info, span), - err_inval!(Layout(LayoutError::Unknown(_))) | err_inval!(TooGeneric) => { + err_inval!(Layout(LayoutError::TooGeneric(_))) | err_inval!(TooGeneric) => { ErrorHandled::TooGeneric(span) } err_inval!(Layout(LayoutError::ReferencesError(guar))) => { diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index c8ecddb046c..889a8299c18 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -32,7 +32,7 @@ tracing = "0.1" version = "0.12" [target.'cfg(windows)'.dependencies.windows] -version = "0.57.0" +version = "0.59.0" features = [ "Win32_Foundation", "Win32_Storage_FileSystem", diff --git a/compiler/rustc_data_structures/src/flock/windows.rs b/compiler/rustc_data_structures/src/flock/windows.rs index 9739e501272..e761faee67b 100644 --- a/compiler/rustc_data_structures/src/flock/windows.rs +++ b/compiler/rustc_data_structures/src/flock/windows.rs @@ -60,9 +60,9 @@ impl Lock { unsafe { LockFileEx( - HANDLE(file.as_raw_handle() as isize), + HANDLE(file.as_raw_handle()), flags, - 0, + None, u32::MAX, u32::MAX, &mut overlapped, diff --git a/compiler/rustc_data_structures/src/graph/mod.rs b/compiler/rustc_data_structures/src/graph/mod.rs index 92035e8bc48..4a1e5db6768 100644 --- a/compiler/rustc_data_structures/src/graph/mod.rs +++ b/compiler/rustc_data_structures/src/graph/mod.rs @@ -14,7 +14,23 @@ mod tests; pub trait DirectedGraph { type Node: Idx; + /// Returns the total number of nodes in this graph. + /// + /// Several graph algorithm implementations assume that every node ID is + /// strictly less than the number of nodes, i.e. nodes are densely numbered. + /// That assumption allows them to use `num_nodes` to allocate per-node + /// data structures, indexed by node. fn num_nodes(&self) -> usize; + + /// Iterates over all nodes of a graph in ascending numeric order. + /// + /// Assumes that nodes are densely numbered, i.e. every index in + /// `0..num_nodes` is a valid node. + fn iter_nodes( + &self, + ) -> impl Iterator<Item = Self::Node> + DoubleEndedIterator + ExactSizeIterator { + (0..self.num_nodes()).map(<Self::Node as Idx>::new) + } } pub trait NumEdges: DirectedGraph { diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 06fedef00fc..93f6192b10b 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -333,8 +333,8 @@ where to_annotation, }; - let scc_indices = (0..num_nodes) - .map(G::Node::new) + let scc_indices = graph + .iter_nodes() .map(|node| match this.start_walk_from(node) { WalkReturn::Complete { scc_index, .. } => scc_index, WalkReturn::Cycle { min_depth, .. } => { diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index 2f0fe64b096..07b88e59723 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -59,7 +59,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.57.0" +version = "0.59.0" features = [ "Win32_System_Diagnostics_Debug", ] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index f7e7aa64614..20be2144609 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -28,8 +28,8 @@ use std::io::{self, IsTerminal, Read, Write}; use std::panic::{self, PanicHookInfo, catch_unwind}; use std::path::{Path, PathBuf}; use std::process::{self, Command, Stdio}; +use std::sync::OnceLock; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, OnceLock}; use std::time::{Instant, SystemTime}; use std::{env, str}; @@ -53,14 +53,13 @@ use rustc_middle::ty::TyCtxt; use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal}; use rustc_session::config::{ CG_OPTIONS, ErrorOutputType, Input, OptionDesc, OutFileName, OutputType, UnstableOptions, - Z_OPTIONS, nightly_options, + Z_OPTIONS, nightly_options, parse_target_triple, }; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; use rustc_session::output::collect_crate_types; use rustc_session::{EarlyDiagCtxt, Session, config, filesearch}; use rustc_span::FileName; -use rustc_span::source_map::FileLoader; use rustc_target::json::ToJson; use rustc_target::spec::{Target, TargetTuple}; use time::OffsetDateTime; @@ -208,84 +207,7 @@ pub fn diagnostics_registry() -> Registry { } /// This is the primary entry point for rustc. -pub struct RunCompiler<'a> { - at_args: &'a [String], - callbacks: &'a mut (dyn Callbacks + Send), - file_loader: Option<Box<dyn FileLoader + Send + Sync>>, - make_codegen_backend: - Option<Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>>, - using_internal_features: Arc<std::sync::atomic::AtomicBool>, -} - -impl<'a> RunCompiler<'a> { - pub fn new(at_args: &'a [String], callbacks: &'a mut (dyn Callbacks + Send)) -> Self { - Self { - at_args, - callbacks, - file_loader: None, - make_codegen_backend: None, - using_internal_features: Arc::default(), - } - } - - /// Set a custom codegen backend. - /// - /// Has no uses within this repository, but is used by bjorn3 for "the - /// hotswapping branch of cg_clif" for "setting the codegen backend from a - /// custom driver where the custom codegen backend has arbitrary data." - /// (See #102759.) - pub fn set_make_codegen_backend( - &mut self, - make_codegen_backend: Option< - Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>, - >, - ) -> &mut Self { - self.make_codegen_backend = make_codegen_backend; - self - } - - /// Load files from sources other than the file system. - /// - /// Has no uses within this repository, but may be used in the future by - /// bjorn3 for "hooking rust-analyzer's VFS into rustc at some point for - /// running rustc without having to save". (See #102759.) - pub fn set_file_loader( - &mut self, - file_loader: Option<Box<dyn FileLoader + Send + Sync>>, - ) -> &mut Self { - self.file_loader = file_loader; - self - } - - /// Set the session-global flag that checks whether internal features have been used, - /// suppressing the message about submitting an issue in ICEs when enabled. - #[must_use] - pub fn set_using_internal_features(mut self, using_internal_features: Arc<AtomicBool>) -> Self { - self.using_internal_features = using_internal_features; - self - } - - /// Parse args and run the compiler. - pub fn run(self) { - run_compiler( - self.at_args, - self.callbacks, - self.file_loader, - self.make_codegen_backend, - self.using_internal_features, - ); - } -} - -fn run_compiler( - at_args: &[String], - callbacks: &mut (dyn Callbacks + Send), - file_loader: Option<Box<dyn FileLoader + Send + Sync>>, - make_codegen_backend: Option< - Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>, - >, - using_internal_features: Arc<std::sync::atomic::AtomicBool>, -) { +pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) { let mut default_early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default()); // Throw away the first argument, the name of the binary. @@ -322,16 +244,16 @@ fn run_compiler( output_file: ofile, output_dir: odir, ice_file, - file_loader, + file_loader: None, locale_resources: DEFAULT_LOCALE_RESOURCES.to_vec(), lint_caps: Default::default(), psess_created: None, hash_untracked_state: None, register_lints: None, override_queries: None, - make_codegen_backend, + make_codegen_backend: None, registry: diagnostics_registry(), - using_internal_features, + using_internal_features: &USING_INTERNAL_FEATURES, expanded_args: args, }; @@ -916,13 +838,7 @@ pub fn version_at_macro_invocation( safe_println!("host: {}", config::host_tuple()); safe_println!("release: {release}"); - let debug_flags = matches.opt_strs("Z"); - let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); - let opts = config::Options::default(); - let sysroot = filesearch::materialize_sysroot(opts.maybe_sysroot.clone()); - let target = config::build_target_config(early_dcx, &opts, &sysroot); - - get_codegen_backend(early_dcx, &sysroot, backend_name, &target).print_version(); + get_backend_from_raw_matches(early_dcx, matches).print_version(); } } @@ -1125,19 +1041,32 @@ pub fn describe_flag_categories(early_dcx: &EarlyDiagCtxt, matches: &Matches) -> } if cg_flags.iter().any(|x| *x == "passes=list") { - let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); - - let opts = config::Options::default(); - let sysroot = filesearch::materialize_sysroot(opts.maybe_sysroot.clone()); - let target = config::build_target_config(early_dcx, &opts, &sysroot); - - get_codegen_backend(early_dcx, &sysroot, backend_name, &target).print_passes(); + get_backend_from_raw_matches(early_dcx, matches).print_passes(); return true; } false } +/// Get the codegen backend based on the raw [`Matches`]. +/// +/// `rustc -vV` and `rustc -Cpasses=list` need to get the codegen backend before we have parsed all +/// arguments and created a [`Session`]. This function reads `-Zcodegen-backend`, `--target` and +/// `--sysroot` without validating any other arguments and loads the codegen backend based on these +/// arguments. +fn get_backend_from_raw_matches( + early_dcx: &EarlyDiagCtxt, + matches: &Matches, +) -> Box<dyn CodegenBackend> { + let debug_flags = matches.opt_strs("Z"); + let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend=")); + let target = parse_target_triple(early_dcx, matches); + let sysroot = filesearch::materialize_sysroot(matches.opt_str("sysroot").map(PathBuf::from)); + let target = config::build_target_config(early_dcx, &target, &sysroot); + + get_codegen_backend(early_dcx, &sysroot, backend_name, &target) +} + fn describe_debug_flags() { safe_println!("\nAvailable options:\n"); print_flag_list("-Z", config::Z_OPTIONS); @@ -1343,6 +1272,8 @@ fn ice_path_with_config(config: Option<&UnstableOptions>) -> &'static Option<Pat }) } +pub static USING_INTERNAL_FEATURES: AtomicBool = AtomicBool::new(false); + /// Installs a panic hook that will print the ICE message on unexpected panics. /// /// The hook is intended to be useable even by external tools. You can pass a custom @@ -1353,15 +1284,8 @@ fn ice_path_with_config(config: Option<&UnstableOptions>) -> &'static Option<Pat /// If you have no extra info to report, pass the empty closure `|_| ()` as the argument to /// extra_info. /// -/// Returns a flag that can be set to disable the note for submitting a bug. This can be passed to -/// [`RunCompiler::set_using_internal_features`] to let macro expansion set it when encountering -/// internal features. -/// /// A custom rustc driver can skip calling this to set up a custom ICE hook. -pub fn install_ice_hook( - bug_report_url: &'static str, - extra_info: fn(&DiagCtxt), -) -> Arc<AtomicBool> { +pub fn install_ice_hook(bug_report_url: &'static str, extra_info: fn(&DiagCtxt)) { // If the user has not explicitly overridden "RUST_BACKTRACE", then produce // full backtraces. When a compiler ICE happens, we want to gather // as much information as possible to present in the issue opened @@ -1378,8 +1302,6 @@ pub fn install_ice_hook( } } - let using_internal_features = Arc::new(std::sync::atomic::AtomicBool::default()); - let using_internal_features_hook = Arc::clone(&using_internal_features); panic::update_hook(Box::new( move |default_hook: &(dyn Fn(&PanicHookInfo<'_>) + Send + Sync + 'static), info: &PanicHookInfo<'_>| { @@ -1431,11 +1353,9 @@ pub fn install_ice_hook( } // Print the ICE message - report_ice(info, bug_report_url, extra_info, &using_internal_features_hook); + report_ice(info, bug_report_url, extra_info, &USING_INTERNAL_FEATURES); }, )); - - using_internal_features } /// Prints the ICE message, including query stack, but without backtrace. @@ -1521,9 +1441,9 @@ fn report_ice( // If backtraces are enabled, also print the query stack let backtrace = env::var_os("RUST_BACKTRACE").is_some_and(|x| &x != "0"); - let num_frames = if backtrace { None } else { Some(2) }; + let limit_frames = if backtrace { None } else { Some(2) }; - interface::try_print_query_stack(dcx, num_frames, file); + interface::try_print_query_stack(dcx, limit_frames, file); // We don't trust this callback not to panic itself, so run it at the end after we're sure we've // printed all the relevant info. @@ -1576,13 +1496,11 @@ pub fn main() -> ! { init_rustc_env_logger(&early_dcx); signal_handler::install(); let mut callbacks = TimePassesCallbacks::default(); - let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()); + install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()); install_ctrlc_handler(); let exit_code = catch_with_exit_code(|| { - RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks) - .set_using_internal_features(using_internal_features) - .run(); + run_compiler(&args::raw_args(&early_dcx)?, &mut callbacks); Ok(()) }); diff --git a/compiler/rustc_error_codes/src/error_codes/E0038.md b/compiler/rustc_error_codes/src/error_codes/E0038.md index 014d8c4f761..4b06395897a 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0038.md +++ b/compiler/rustc_error_codes/src/error_codes/E0038.md @@ -264,15 +264,15 @@ trait Foo { ### Trait contains associated constants Just like static functions, associated constants aren't stored on the method -table. If the trait or any subtrait contain an associated constant, they cannot -be made into an object. +table. If the trait or any subtrait contain an associated constant, they are not +dyn compatible. ```compile_fail,E0038 trait Foo { const X: i32; } -impl Foo {} +impl dyn Foo {} ``` A simple workaround is to use a helper method instead: diff --git a/compiler/rustc_error_codes/src/error_codes/E0132.md b/compiler/rustc_error_codes/src/error_codes/E0132.md index 51258739b89..cbb14510ed7 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0132.md +++ b/compiler/rustc_error_codes/src/error_codes/E0132.md @@ -1,32 +1,3 @@ -A function with the `start` attribute was declared with type parameters. - -Erroneous code example: - -```compile_fail,E0132 -#![feature(start)] - -#[start] -fn f<T>() {} -``` - -It is not possible to declare type parameters on a function that has the `start` -attribute. Such a function must have the following type signature (for more -information, view [the unstable book][1]): +#### Note: this error code is no longer emitted by the compiler. -[1]: https://doc.rust-lang.org/unstable-book/language-features/start.html - -``` -# let _: -fn(isize, *const *const u8) -> isize; -``` - -Example: - -``` -#![feature(start)] - -#[start] -fn my_start(argc: isize, argv: *const *const u8) -> isize { - 0 -} -``` +A function with the `start` attribute was declared with type parameters. diff --git a/compiler/rustc_error_codes/src/error_codes/E0138.md b/compiler/rustc_error_codes/src/error_codes/E0138.md index 3f5eaea9f98..2e6ba546a16 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0138.md +++ b/compiler/rustc_error_codes/src/error_codes/E0138.md @@ -1,25 +1,3 @@ -More than one function was declared with the `#[start]` attribute. - -Erroneous code example: - -```compile_fail,E0138 -#![feature(start)] - -#[start] -fn foo(argc: isize, argv: *const *const u8) -> isize {} +#### Note: this error code is no longer emitted by the compiler. -#[start] -fn f(argc: isize, argv: *const *const u8) -> isize {} -// error: multiple 'start' functions -``` - -This error indicates that the compiler found multiple functions with the -`#[start]` attribute. This is an error because there must be a unique entry -point into a Rust program. Example: - -``` -#![feature(start)] - -#[start] -fn foo(argc: isize, argv: *const *const u8) -> isize { 0 } // ok! -``` +More than one function was declared with the `#[start]` attribute. diff --git a/compiler/rustc_error_codes/src/error_codes/E0393.md b/compiler/rustc_error_codes/src/error_codes/E0393.md index 50225b25163..c7260815905 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0393.md +++ b/compiler/rustc_error_codes/src/error_codes/E0393.md @@ -3,12 +3,10 @@ A type parameter which references `Self` in its default value was not specified. Erroneous code example: ```compile_fail,E0393 -trait A<T=Self> {} +trait A<T = Self> {} -fn together_we_will_rule_the_galaxy(son: &A) {} -// error: the type parameter `T` must be explicitly specified in an -// object type because its default value `Self` references the -// type `Self` +fn together_we_will_rule_the_galaxy(son: &dyn A) {} +// error: the type parameter `T` must be explicitly specified ``` A trait object is defined over a single, fully-defined trait. With a regular @@ -23,7 +21,7 @@ disallowed. Making the trait concrete by explicitly specifying the value of the defaulted parameter will fix this issue. Fixed example: ``` -trait A<T=Self> {} +trait A<T = Self> {} -fn together_we_will_rule_the_galaxy(son: &A<i32>) {} // Ok! +fn together_we_will_rule_the_galaxy(son: &dyn A<i32>) {} // Ok! ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0433.md b/compiler/rustc_error_codes/src/error_codes/E0433.md index 5a64c13c9af..36e4b66e8dc 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0433.md +++ b/compiler/rustc_error_codes/src/error_codes/E0433.md @@ -19,7 +19,7 @@ If you've expected to use a crate name: ```compile_fail use ferris_wheel::BigO; -// error: failed to resolve: use of undeclared crate or module `ferris_wheel` +// error: failed to resolve: use of undeclared module or unlinked crate ``` Make sure the crate has been added as a dependency in `Cargo.toml`. diff --git a/compiler/rustc_error_codes/src/error_codes/E0647.md b/compiler/rustc_error_codes/src/error_codes/E0647.md index 59bb47ba62a..e2f14b81aa6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0647.md +++ b/compiler/rustc_error_codes/src/error_codes/E0647.md @@ -1,13 +1,3 @@ -The `start` function was defined with a where clause. - -Erroneous code example: +#### Note: this error code is no longer emitted by the compiler. -```compile_fail,E0647 -#![feature(start)] - -#[start] -fn start(_: isize, _: *const *const u8) -> isize where (): Copy { - //^ error: `#[start]` function is not allowed to have a where clause - 0 -} -``` +The `start` function was defined with a where clause. diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 29f3277d399..0a30bdb48a0 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -24,6 +24,10 @@ // // Both columns are necessary because it's not possible in Rust to create a new identifier such as // `E0123` from an integer literal such as `0123`, unfortunately. +// +// Do *not* remove entries from this list. Instead, just add a note th the corresponding markdown +// file saying that this error is not emitted by the compiler any more (see E0001.md for an +// example), and remove all code examples that do not build any more. #[macro_export] macro_rules! error_codes { ($macro:path) => ( diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 66b9adbead0..fbb6a1cc475 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -31,7 +31,7 @@ tracing = "0.1" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.57.0" +version = "0.59.0" features = [ "Win32_Foundation", "Win32_Security", diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index f938352820d..991dfa1821a 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -3146,7 +3146,7 @@ impl FileWithAnnotatedLines { add_annotation_to_file(&mut output, Lrc::clone(&file), line, ann.as_line()); } let line_end = ann.line_end - 1; - let end_is_empty = file.get_line(line_end - 1).map_or(false, |s| !filter(&s)); + let end_is_empty = file.get_line(line_end - 1).is_some_and(|s| !filter(&s)); if middle < line_end && !end_is_empty { add_annotation_to_file(&mut output, Lrc::clone(&file), line_end, ann.as_line()); } diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 97df7f9265a..95c81fc5f44 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -21,7 +21,7 @@ use rustc_error_messages::FluentArgs; use rustc_lint_defs::Applicability; use rustc_span::Span; use rustc_span::hygiene::ExpnData; -use rustc_span::source_map::SourceMap; +use rustc_span::source_map::{FilePathMapping, SourceMap}; use serde::Serialize; use termcolor::{ColorSpec, WriteColor}; @@ -45,7 +45,7 @@ pub struct JsonEmitter { #[setters(skip)] dst: IntoDynSyncSend<Box<dyn Write + Send>>, #[setters(skip)] - sm: Lrc<SourceMap>, + sm: Option<Lrc<SourceMap>>, fluent_bundle: Option<Lrc<FluentBundle>>, #[setters(skip)] fallback_bundle: LazyFallbackBundle, @@ -65,7 +65,7 @@ pub struct JsonEmitter { impl JsonEmitter { pub fn new( dst: Box<dyn Write + Send>, - sm: Lrc<SourceMap>, + sm: Option<Lrc<SourceMap>>, fallback_bundle: LazyFallbackBundle, pretty: bool, json_rendered: HumanReadableErrorType, @@ -171,7 +171,7 @@ impl Emitter for JsonEmitter { } fn source_map(&self) -> Option<&SourceMap> { - Some(&self.sm) + self.sm.as_deref() } fn should_show_explain(&self) -> bool { @@ -371,7 +371,7 @@ impl Diagnostic { } HumanEmitter::new(dst, Lrc::clone(&je.fallback_bundle)) .short_message(short) - .sm(Some(Lrc::clone(&je.sm))) + .sm(je.sm.clone()) .fluent_bundle(je.fluent_bundle.clone()) .diagnostic_width(je.diagnostic_width) .macro_backtrace(je.macro_backtrace) @@ -458,22 +458,34 @@ impl DiagnosticSpan { mut backtrace: impl Iterator<Item = ExpnData>, je: &JsonEmitter, ) -> DiagnosticSpan { - let start = je.sm.lookup_char_pos(span.lo()); + let empty_source_map; + let sm = match &je.sm { + Some(s) => s, + None => { + span = rustc_span::DUMMY_SP; + empty_source_map = Arc::new(SourceMap::new(FilePathMapping::empty())); + empty_source_map + .new_source_file(std::path::PathBuf::from("empty.rs").into(), String::new()); + &empty_source_map + } + }; + let start = sm.lookup_char_pos(span.lo()); // If this goes from the start of a line to the end and the replacement // is an empty string, increase the length to include the newline so we don't // leave an empty line if start.col.0 == 0 - && suggestion.map_or(false, |(s, _)| s.is_empty()) - && let Ok(after) = je.sm.span_to_next_source(span) + && let Some((suggestion, _)) = suggestion + && suggestion.is_empty() + && let Ok(after) = sm.span_to_next_source(span) && after.starts_with('\n') { span = span.with_hi(span.hi() + rustc_span::BytePos(1)); } - let end = je.sm.lookup_char_pos(span.hi()); + let end = sm.lookup_char_pos(span.hi()); let backtrace_step = backtrace.next().map(|bt| { let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je); let def_site_span = Self::from_span_full( - je.sm.guess_head_span(bt.def_site), + sm.guess_head_span(bt.def_site), false, None, None, @@ -488,7 +500,7 @@ impl DiagnosticSpan { }); DiagnosticSpan { - file_name: je.sm.filename_for_diagnostics(&start.file.name).to_string(), + file_name: sm.filename_for_diagnostics(&start.file.name).to_string(), byte_start: start.file.original_relative_byte_pos(span.lo()).0, byte_end: start.file.original_relative_byte_pos(span.hi()).0, line_start: start.line, @@ -558,19 +570,20 @@ impl DiagnosticSpanLine { /// `span` within the line. fn from_span(span: Span, je: &JsonEmitter) -> Vec<DiagnosticSpanLine> { je.sm - .span_to_lines(span) - .map(|lines| { + .as_ref() + .and_then(|sm| { + let lines = sm.span_to_lines(span).ok()?; // We can't get any lines if the source is unavailable. if !should_show_source_code( &je.ignored_directories_in_source_blocks, - &je.sm, + &sm, &lines.file, ) { - return vec![]; + return None; } let sf = &*lines.file; - lines + let span_lines = lines .lines .iter() .map(|line| { @@ -581,8 +594,9 @@ impl DiagnosticSpanLine { line.end_col.0 + 1, ) }) - .collect() + .collect(); + Some(span_lines) }) - .unwrap_or_else(|_| vec![]) + .unwrap_or_default() } } diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 0de555b83d3..cebaf7c1cfe 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -47,7 +47,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { let output = Arc::new(Mutex::new(Vec::new())); let je = JsonEmitter::new( Box::new(Shared { data: output.clone() }), - sm, + Some(sm), fallback_bundle, true, // pretty HumanReadableErrorType::Short, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index cc5eb9c335e..549729548f5 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -35,6 +35,7 @@ use std::backtrace::{Backtrace, BacktraceStatus}; use std::borrow::Cow; use std::cell::Cell; use std::error::Report; +use std::ffi::OsStr; use std::hash::Hash; use std::io::Write; use std::num::NonZero; @@ -1717,7 +1718,7 @@ impl DiagCtxtInner { let bugs: Vec<_> = std::mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect(); - let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0"); + let backtrace = std::env::var_os("RUST_BACKTRACE").as_deref() != Some(OsStr::new("0")); let decorate = backtrace || self.ice_file.is_none(); let mut out = self .ice_file diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 3e3f35332e0..83255b82017 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -391,7 +391,7 @@ impl<'a> StripUnconfigured<'a> { validate_attr::deny_builtin_meta_unsafety(&self.sess.psess, &meta_item); ( - parse_cfg(&meta_item, self.sess).map_or(true, |meta_item| { + parse_cfg(&meta_item, self.sess).is_none_or(|meta_item| { attr::cfg_matches(meta_item, &self.sess, self.lint_node_id, self.features) }), Some(meta_item), diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 9f48835e15d..e60a9e83184 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -159,7 +159,7 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match if self .best_failure .as_ref() - .map_or(true, |failure| failure.is_better_position(*approx_position)) + .is_none_or(|failure| failure.is_better_position(*approx_position)) { self.best_failure = Some(BestFailure { token: token.clone(), diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index ef209c2bce1..21688521ade 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -282,11 +282,13 @@ pub(super) fn transcribe<'a>( } MatchedSingle(ParseNtResult::Ident(ident, is_raw)) => { marker.visit_span(&mut sp); + with_metavar_spans(|mspans| mspans.insert(ident.span, sp)); let kind = token::NtIdent(*ident, *is_raw); TokenTree::token_alone(kind, sp) } MatchedSingle(ParseNtResult::Lifetime(ident, is_raw)) => { marker.visit_span(&mut sp); + with_metavar_spans(|mspans| mspans.insert(ident.span, sp)); let kind = token::NtLifetime(*ident, *is_raw); TokenTree::token_alone(kind, sp) } @@ -295,6 +297,8 @@ pub(super) fn transcribe<'a>( // `Delimiter::Invisible` to maintain parsing priorities. // `Interpolated` is currently used for such groups in rustc parser. marker.visit_span(&mut sp); + let use_span = nt.use_span(); + with_metavar_spans(|mspans| mspans.insert(use_span, sp)); TokenTree::token_alone(token::Interpolated(Lrc::clone(nt)), sp) } MatchedSeq(..) => { @@ -410,19 +414,15 @@ fn maybe_use_metavar_location( return orig_tt.clone(); } - let insert = |mspans: &mut FxHashMap<_, _>, s, ms| match mspans.try_insert(s, ms) { - Ok(_) => true, - Err(err) => *err.entry.get() == ms, // Tried to insert the same span, still success - }; marker.visit_span(&mut metavar_span); let no_collision = match orig_tt { TokenTree::Token(token, ..) => { - with_metavar_spans(|mspans| insert(mspans, token.span, metavar_span)) + with_metavar_spans(|mspans| mspans.insert(token.span, metavar_span)) } TokenTree::Delimited(dspan, ..) => with_metavar_spans(|mspans| { - insert(mspans, dspan.open, metavar_span) - && insert(mspans, dspan.close, metavar_span) - && insert(mspans, dspan.entire(), metavar_span) + mspans.insert(dspan.open, metavar_span) + && mspans.insert(dspan.close, metavar_span) + && mspans.insert(dspan.entire(), metavar_span) }), }; if no_collision || psess.source_map().is_imported(metavar_span) { @@ -434,14 +434,14 @@ fn maybe_use_metavar_location( match orig_tt { TokenTree::Token(Token { kind, span }, spacing) => { let span = metavar_span.with_ctxt(span.ctxt()); - with_metavar_spans(|mspans| insert(mspans, span, metavar_span)); + with_metavar_spans(|mspans| mspans.insert(span, metavar_span)); TokenTree::Token(Token { kind: kind.clone(), span }, *spacing) } TokenTree::Delimited(dspan, dspacing, delimiter, tts) => { let open = metavar_span.with_ctxt(dspan.open.ctxt()); let close = metavar_span.with_ctxt(dspan.close.ctxt()); with_metavar_spans(|mspans| { - insert(mspans, open, metavar_span) && insert(mspans, close, metavar_span) + mspans.insert(open, metavar_span) && mspans.insert(close, metavar_span) }); let dspan = DelimSpan::from_pair(open, close); TokenTree::Delimited(dspan, *dspacing, *delimiter, tts.clone()) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 5510e7e09e5..17433eed9e7 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -408,11 +408,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ crate_type, CrateLevel, template!(NameValueStr: "bin|lib|..."), DuplicatesOk, EncodeCrossCrate::No, ), - // crate_id is deprecated - ungated!( - crate_id, CrateLevel, template!(NameValueStr: "ignored"), FutureWarnFollowing, - EncodeCrossCrate::No, - ), // ABI, linking, symbols, and FFI ungated!( @@ -448,8 +443,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // Entry point: - ungated!(start, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), - ungated!(no_start, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No), ungated!(no_main, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No), // Modules, prelude, and resolution: @@ -533,7 +526,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), // RFC 2412 gated!( - optimize, Normal, template!(List: "size|speed"), ErrorPreceding, + optimize, Normal, template!(List: "none|size|speed"), ErrorPreceding, EncodeCrossCrate::No, optimize_attribute, experimental!(optimize) ), diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 9aa59375706..081638715df 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -163,7 +163,7 @@ declare_features! ( /// then removed. But there was no utility storing it separately, so now /// it's in this list. (removed, no_stack_check, "1.0.0", None, None), - /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn-compatible (object safe). + /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn compatible (object safe). /// Renamed to `dyn_compatible_for_dispatch`. (removed, object_safe_for_dispatch, "1.83.0", Some(43561), Some("renamed to `dyn_compatible_for_dispatch`")), @@ -220,8 +220,9 @@ declare_features! ( (removed, rustc_diagnostic_macros, "1.38.0", None, None), /// Allows identifying crates that contain sanitizer runtimes. (removed, sanitizer_runtime, "1.17.0", None, None), - (removed, simd, "1.0.0", Some(27731), - Some("removed in favor of `#[repr(simd)]`")), + (removed, simd, "1.0.0", Some(27731), Some("removed in favor of `#[repr(simd)]`")), + /// Allows using `#[start]` on a function indicating that it is the program entrypoint. + (removed, start, "1.0.0", Some(29633), Some("not portable enough and never RFC'd")), /// Allows `#[link(kind = "static-nobundle", ...)]`. (removed, static_nobundle, "1.16.0", Some(37403), Some(r#"subsumed by `#[link(kind = "static", modifiers = "-bundle", ...)]`"#)), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 4ab0bc47305..1a216ebf117 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -272,7 +272,7 @@ declare_features! ( (unstable, doc_notable_trait, "1.52.0", Some(45040)), /// Allows using the `may_dangle` attribute (RFC 1327). (unstable, dropck_eyepatch, "1.10.0", Some(34761)), - /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn-compatible[^1]. + /// Allows making `dyn Trait` well-formed even if `Trait` is not dyn compatible[^1]. /// In that case, `dyn Trait: Trait` does not hold. Moreover, coercions and /// casts in safe Rust to `dyn Trait` for such a `Trait` is also forbidden. /// @@ -300,8 +300,6 @@ declare_features! ( (internal, rustdoc_internals, "1.58.0", Some(90418)), /// Allows using the `rustdoc::missing_doc_code_examples` lint (unstable, rustdoc_missing_doc_code_examples, "1.31.0", Some(101730)), - /// Allows using `#[start]` on a function indicating that it is the program entrypoint. - (unstable, start, "1.0.0", Some(29633)), /// Allows using `#[structural_match]` which indicates that a type is structurally matchable. /// FIXME: Subsumed by trait `StructuralPartialEq`, cannot move to removed until a library /// feature with the same name exists. diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 705de389e07..7e3a8561da0 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -794,7 +794,7 @@ impl<Id> Res<Id> { /// Always returns `true` if `self` is `Res::Err` pub fn matches_ns(&self, ns: Namespace) -> bool { - self.ns().map_or(true, |actual_ns| actual_ns == ns) + self.ns().is_none_or(|actual_ns| actual_ns == ns) } /// Returns whether such a resolved path can occur in a tuple struct/variant pattern diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 5339feb5d27..a396d705cbb 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -15,6 +15,7 @@ pub use rustc_ast::{ }; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; +use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::LocalDefId; @@ -30,7 +31,7 @@ use crate::LangItem; use crate::def::{CtorKind, DefKind, Res}; use crate::def_id::{DefId, LocalDefIdMap}; pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId}; -use crate::intravisit::FnKind; +use crate::intravisit::{FnKind, VisitorExt}; #[derive(Debug, Copy, Clone, HashStable_Generic)] pub struct Lifetime { @@ -263,14 +264,58 @@ impl<'hir> PathSegment<'hir> { /// So, `ConstArg` (specifically, [`ConstArgKind`]) distinguishes between const args /// that are [just paths](ConstArgKind::Path) (currently just bare const params) /// versus const args that are literals or have arbitrary computations (e.g., `{ 1 + 3 }`). +/// +/// The `Unambig` generic parameter represents whether the position this const is from is +/// unambiguously a const or ambiguous as to whether it is a type or a const. When in an +/// ambiguous context the parameter is instantiated with an uninhabited type making the +/// [`ConstArgKind::Infer`] variant unusable and [`GenericArg::Infer`] is used instead. #[derive(Clone, Copy, Debug, HashStable_Generic)] -pub struct ConstArg<'hir> { +#[repr(C)] +pub struct ConstArg<'hir, Unambig = ()> { #[stable_hasher(ignore)] pub hir_id: HirId, - pub kind: ConstArgKind<'hir>, + pub kind: ConstArgKind<'hir, Unambig>, +} + +impl<'hir> ConstArg<'hir, AmbigArg> { + /// Converts a `ConstArg` in an ambiguous position to one in an unambiguous position. + /// + /// Functions accepting an unambiguous consts may expect the [`ConstArgKind::Infer`] variant + /// to be used. Care should be taken to separately handle infer consts when calling this + /// function as it cannot be handled by downstream code making use of the returned const. + /// + /// In practice this may mean overriding the [`Visitor::visit_infer`][visit_infer] method on hir visitors, or + /// specifically matching on [`GenericArg::Infer`] when handling generic arguments. + /// + /// [visit_infer]: [rustc_hir::intravisit::Visitor::visit_infer] + pub fn as_unambig_ct(&self) -> &ConstArg<'hir> { + // SAFETY: `ConstArg` is `repr(C)` and `ConstArgKind` is marked `repr(u8)` so that the + // layout is the same across different ZST type arguments. + let ptr = self as *const ConstArg<'hir, AmbigArg> as *const ConstArg<'hir, ()>; + unsafe { &*ptr } + } } impl<'hir> ConstArg<'hir> { + /// Converts a `ConstArg` in an unambigous position to one in an ambiguous position. This is + /// fallible as the [`ConstArgKind::Infer`] variant is not present in ambiguous positions. + /// + /// Functions accepting ambiguous consts will not handle the [`ConstArgKind::Infer`] variant, if + /// infer consts are relevant to you then care should be taken to handle them separately. + pub fn try_as_ambig_ct(&self) -> Option<&ConstArg<'hir, AmbigArg>> { + if let ConstArgKind::Infer(_, ()) = self.kind { + return None; + } + + // SAFETY: `ConstArg` is `repr(C)` and `ConstArgKind` is marked `repr(u8)` so that the layout is + // the same across different ZST type arguments. We also asserted that the `self` is + // not a `ConstArgKind::Infer` so there is no risk of transmuting a `()` to `AmbigArg`. + let ptr = self as *const ConstArg<'hir> as *const ConstArg<'hir, AmbigArg>; + Some(unsafe { &*ptr }) + } +} + +impl<'hir, Unambig> ConstArg<'hir, Unambig> { pub fn anon_const_hir_id(&self) -> Option<HirId> { match self.kind { ConstArgKind::Anon(ac) => Some(ac.hir_id), @@ -282,14 +327,15 @@ impl<'hir> ConstArg<'hir> { match self.kind { ConstArgKind::Path(path) => path.span(), ConstArgKind::Anon(anon) => anon.span, - ConstArgKind::Infer(span) => span, + ConstArgKind::Infer(span, _) => span, } } } /// See [`ConstArg`]. #[derive(Clone, Copy, Debug, HashStable_Generic)] -pub enum ConstArgKind<'hir> { +#[repr(u8, C)] +pub enum ConstArgKind<'hir, Unambig = ()> { /// **Note:** Currently this is only used for bare const params /// (`N` where `fn foo<const N: usize>(...)`), /// not paths to any const (`N` where `const N: usize = ...`). @@ -297,11 +343,9 @@ pub enum ConstArgKind<'hir> { /// However, in the future, we'll be using it for all of those. Path(QPath<'hir>), Anon(&'hir AnonConst), - /// **Note:** Not all inferred consts are represented as - /// `ConstArgKind::Infer`. In cases where it is ambiguous whether - /// a generic arg is a type or a const, inference variables are - /// represented as `GenericArg::Infer` instead. - Infer(Span), + /// This variant is not always used to represent inference consts, sometimes + /// [`GenericArg::Infer`] is used instead. + Infer(Span, Unambig), } #[derive(Clone, Copy, Debug, HashStable_Generic)] @@ -313,19 +357,24 @@ pub struct InferArg { impl InferArg { pub fn to_ty(&self) -> Ty<'static> { - Ty { kind: TyKind::Infer, span: self.span, hir_id: self.hir_id } + Ty { kind: TyKind::Infer(()), span: self.span, hir_id: self.hir_id } } } #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum GenericArg<'hir> { Lifetime(&'hir Lifetime), - Type(&'hir Ty<'hir>), - Const(&'hir ConstArg<'hir>), - /// **Note:** Inference variables are only represented as - /// `GenericArg::Infer` in cases where it is ambiguous whether - /// a generic arg is a type or a const. Otherwise, inference variables - /// are represented as `TyKind::Infer` or `ConstArgKind::Infer`. + Type(&'hir Ty<'hir, AmbigArg>), + Const(&'hir ConstArg<'hir, AmbigArg>), + /// Inference variables in [`GenericArg`] are always represnted by + /// `GenericArg::Infer` instead of the `Infer` variants on [`TyKind`] and + /// [`ConstArgKind`] as it is not clear until hir ty lowering whether a + /// `_` argument is a type or const argument. + /// + /// However, some builtin types' generic arguments are represented by [`TyKind`] + /// without a [`GenericArg`], instead directly storing a [`Ty`] or [`ConstArg`]. In + /// such cases they *are* represented by the `Infer` variants on [`TyKind`] and + /// [`ConstArgKind`] as it is not ambiguous whether the argument is a type or const. Infer(InferArg), } @@ -353,7 +402,7 @@ impl GenericArg<'_> { GenericArg::Lifetime(_) => "lifetime", GenericArg::Type(_) => "type", GenericArg::Const(_) => "constant", - GenericArg::Infer(_) => "inferred", + GenericArg::Infer(_) => "placeholder", } } @@ -764,11 +813,8 @@ impl<'hir> Generics<'hir> { && let [.., segment] = trait_ref.path.segments && let Some(ret_ty) = segment.args().paren_sugar_output() && let ret_ty = ret_ty.peel_refs() - && let TyKind::TraitObject( - _, - _, - TraitObjectSyntax::Dyn | TraitObjectSyntax::DynStar, - ) = ret_ty.kind + && let TyKind::TraitObject(_, tagged_ptr) = ret_ty.kind + && let TraitObjectSyntax::Dyn | TraitObjectSyntax::DynStar = tagged_ptr.tag() && ret_ty.span.can_be_used_for_suggestions() { Some(ret_ty.span) @@ -1239,13 +1285,13 @@ impl fmt::Debug for OwnerNodes<'_> { .field("node", &self.nodes[ItemLocalId::ZERO]) .field( "parents", - &self - .nodes - .iter_enumerated() - .map(|(id, parented_node)| { - debug_fn(move |f| write!(f, "({id:?}, {:?})", parented_node.parent)) - }) - .collect::<Vec<_>>(), + &fmt::from_fn(|f| { + f.debug_list() + .entries(self.nodes.iter_enumerated().map(|(id, parented_node)| { + fmt::from_fn(move |f| write!(f, "({id:?}, {:?})", parented_node.parent)) + })) + .finish() + }), ) .field("bodies", &self.bodies) .field("opt_hash_including_bodies", &self.opt_hash_including_bodies) @@ -2917,15 +2963,84 @@ impl<'hir> AssocItemConstraintKind<'hir> { } } +/// An uninhabited enum used to make `Infer` variants on [`Ty`] and [`ConstArg`] be +/// unreachable. Zero-Variant enums are guaranteed to have the same layout as the never +/// type. #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub struct Ty<'hir> { +pub enum AmbigArg {} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] +#[repr(C)] +/// Represents a type in the `HIR`. +/// +/// The `Unambig` generic parameter represents whether the position this type is from is +/// unambiguously a type or ambiguous as to whether it is a type or a const. When in an +/// ambiguous context the parameter is instantiated with an uninhabited type making the +/// [`TyKind::Infer`] variant unusable and [`GenericArg::Infer`] is used instead. +pub struct Ty<'hir, Unambig = ()> { #[stable_hasher(ignore)] pub hir_id: HirId, - pub kind: TyKind<'hir>, pub span: Span, + pub kind: TyKind<'hir, Unambig>, +} + +impl<'hir> Ty<'hir, AmbigArg> { + /// Converts a `Ty` in an ambiguous position to one in an unambiguous position. + /// + /// Functions accepting an unambiguous types may expect the [`TyKind::Infer`] variant + /// to be used. Care should be taken to separately handle infer types when calling this + /// function as it cannot be handled by downstream code making use of the returned ty. + /// + /// In practice this may mean overriding the [`Visitor::visit_infer`][visit_infer] method on hir visitors, or + /// specifically matching on [`GenericArg::Infer`] when handling generic arguments. + /// + /// [visit_infer]: [rustc_hir::intravisit::Visitor::visit_infer] + pub fn as_unambig_ty(&self) -> &Ty<'hir> { + // SAFETY: `Ty` is `repr(C)` and `TyKind` is marked `repr(u8)` so that the layout is + // the same across different ZST type arguments. + let ptr = self as *const Ty<'hir, AmbigArg> as *const Ty<'hir, ()>; + unsafe { &*ptr } + } +} + +impl<'hir> Ty<'hir> { + /// Converts a `Ty` in an unambigous position to one in an ambiguous position. This is + /// fallible as the [`TyKind::Infer`] variant is not present in ambiguous positions. + /// + /// Functions accepting ambiguous types will not handle the [`TyKind::Infer`] variant, if + /// infer types are relevant to you then care should be taken to handle them separately. + pub fn try_as_ambig_ty(&self) -> Option<&Ty<'hir, AmbigArg>> { + if let TyKind::Infer(()) = self.kind { + return None; + } + + // SAFETY: `Ty` is `repr(C)` and `TyKind` is marked `repr(u8)` so that the layout is + // the same across different ZST type arguments. We also asserted that the `self` is + // not a `TyKind::Infer` so there is no risk of transmuting a `()` to `AmbigArg`. + let ptr = self as *const Ty<'hir> as *const Ty<'hir, AmbigArg>; + Some(unsafe { &*ptr }) + } +} + +impl<'hir> Ty<'hir, AmbigArg> { + pub fn peel_refs(&self) -> &Ty<'hir> { + let mut final_ty = self.as_unambig_ty(); + while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind { + final_ty = ty; + } + final_ty + } } impl<'hir> Ty<'hir> { + pub fn peel_refs(&self) -> &Self { + let mut final_ty = self; + while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind { + final_ty = ty; + } + final_ty + } + /// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate. pub fn as_generic_param(&self) -> Option<(DefId, Ident)> { let TyKind::Path(QPath::Resolved(None, path)) = self.kind else { @@ -2942,19 +3057,11 @@ impl<'hir> Ty<'hir> { } } - pub fn peel_refs(&self) -> &Self { - let mut final_ty = self; - while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind { - final_ty = ty; - } - final_ty - } - pub fn find_self_aliases(&self) -> Vec<Span> { use crate::intravisit::Visitor; struct MyVisitor(Vec<Span>); impl<'v> Visitor<'v> for MyVisitor { - fn visit_ty(&mut self, t: &'v Ty<'v>) { + fn visit_ty(&mut self, t: &'v Ty<'v, AmbigArg>) { if matches!( &t.kind, TyKind::Path(QPath::Resolved(_, Path { @@ -2970,7 +3077,7 @@ impl<'hir> Ty<'hir> { } let mut my_visitor = MyVisitor(vec![]); - my_visitor.visit_ty(self); + my_visitor.visit_ty_unambig(self); my_visitor.0 } @@ -2979,14 +3086,14 @@ impl<'hir> Ty<'hir> { pub fn is_suggestable_infer_ty(&self) -> bool { fn are_suggestable_generic_args(generic_args: &[GenericArg<'_>]) -> bool { generic_args.iter().any(|arg| match arg { - GenericArg::Type(ty) => ty.is_suggestable_infer_ty(), + GenericArg::Type(ty) => ty.as_unambig_ty().is_suggestable_infer_ty(), GenericArg::Infer(_) => true, _ => false, }) } debug!(?self); match &self.kind { - TyKind::Infer => true, + TyKind::Infer(()) => true, TyKind::Slice(ty) => ty.is_suggestable_infer_ty(), TyKind::Array(ty, length) => { ty.is_suggestable_infer_ty() || matches!(length.kind, ConstArgKind::Infer(..)) @@ -3200,7 +3307,9 @@ pub enum InferDelegationKind { /// The various kinds of types recognized by the compiler. #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub enum TyKind<'hir> { +// SAFETY: `repr(u8)` is required so that `TyKind<()>` and `TyKind<!>` are layout compatible +#[repr(u8, C)] +pub enum TyKind<'hir, Unambig = ()> { /// Actual type should be inherited from `DefId` signature InferDelegation(DefId, InferDelegationKind), /// A variable length slice (i.e., `[T]`). @@ -3230,21 +3339,22 @@ pub enum TyKind<'hir> { TraitAscription(GenericBounds<'hir>), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax), + /// + /// We use pointer tagging to represent a `&'hir Lifetime` and `TraitObjectSyntax` pair + /// as otherwise this type being `repr(C)` would result in `TyKind` increasing in size. + TraitObject(&'hir [PolyTraitRef<'hir>], TaggedRef<'hir, Lifetime, TraitObjectSyntax>), /// Unused for now. Typeof(&'hir AnonConst), - /// `TyKind::Infer` means the type should be inferred instead of it having been - /// specified. This can appear anywhere in a type. - /// - /// **Note:** Not all inferred types are represented as - /// `TyKind::Infer`. In cases where it is ambiguous whether - /// a generic arg is a type or a const, inference variables are - /// represented as `GenericArg::Infer` instead. - Infer, /// Placeholder for a type that has failed to be defined. Err(rustc_span::ErrorGuaranteed), /// Pattern types (`pattern_type!(u32 is 1..)`) Pat(&'hir Ty<'hir>, &'hir Pat<'hir>), + /// `TyKind::Infer` means the type should be inferred instead of it having been + /// specified. This can appear anywhere in a type. + /// + /// This variant is not always used to represent inference types, sometimes + /// [`GenericArg::Infer`] is used instead. + Infer(Unambig), } #[derive(Debug, Clone, Copy, HashStable_Generic)] @@ -4528,12 +4638,5 @@ mod size_asserts { // tidy-alphabetical-end } -fn debug_fn(f: impl Fn(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Debug { - struct DebugFn<F>(F); - impl<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Debug for DebugFn<F> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - (self.0)(fmt) - } - } - DebugFn(f) -} +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_hir/src/hir/tests.rs b/compiler/rustc_hir/src/hir/tests.rs new file mode 100644 index 00000000000..300d4435530 --- /dev/null +++ b/compiler/rustc_hir/src/hir/tests.rs @@ -0,0 +1,83 @@ +use rustc_span::def_id::DefIndex; + +use super::*; + +macro_rules! define_tests { + ($($name:ident $kind:ident $variant:ident {$($init:tt)*})*) => {$( + #[test] + fn $name() { + let unambig = $kind::$variant::<'_, ()> { $($init)* }; + let unambig_to_ambig = unsafe { std::mem::transmute::<_, $kind<'_, AmbigArg>>(unambig) }; + + assert!(matches!(&unambig_to_ambig, $kind::$variant { $($init)* })); + + let ambig_to_unambig = unsafe { std::mem::transmute::<_, $kind<'_, ()>>(unambig_to_ambig) }; + + assert!(matches!(&ambig_to_unambig, $kind::$variant { $($init)* })); + } + )*}; +} + +define_tests! { + cast_never TyKind Never {} + cast_tup TyKind Tup { 0: &[Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }] } + cast_ptr TyKind Ptr { 0: MutTy { ty: &Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }, mutbl: Mutability::Not }} + cast_array TyKind Array { + 0: &Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }, + 1: &ConstArg { hir_id: HirId::INVALID, kind: ConstArgKind::Anon(&AnonConst { + hir_id: HirId::INVALID, + def_id: LocalDefId { local_def_index: DefIndex::ZERO }, + body: BodyId { hir_id: HirId::INVALID }, + span: DUMMY_SP, + })} + } + + cast_anon ConstArgKind Anon { + 0: &AnonConst { + hir_id: HirId::INVALID, + def_id: LocalDefId { local_def_index: DefIndex::ZERO }, + body: BodyId { hir_id: HirId::INVALID }, + span: DUMMY_SP, + } + } +} + +#[test] +fn trait_object_roundtrips() { + trait_object_roundtrips_impl(TraitObjectSyntax::Dyn); + trait_object_roundtrips_impl(TraitObjectSyntax::DynStar); + trait_object_roundtrips_impl(TraitObjectSyntax::None); +} + +fn trait_object_roundtrips_impl(syntax: TraitObjectSyntax) { + let unambig = TyKind::TraitObject::<'_, ()>( + &[], + TaggedRef::new( + &const { + Lifetime { + hir_id: HirId::INVALID, + ident: Ident::new(sym::name, DUMMY_SP), + res: LifetimeName::Static, + } + }, + syntax, + ), + ); + let unambig_to_ambig = unsafe { std::mem::transmute::<_, TyKind<'_, AmbigArg>>(unambig) }; + + match unambig_to_ambig { + TyKind::TraitObject(_, tagged_ref) => { + assert!(tagged_ref.tag() == syntax) + } + _ => panic!("`TyKind::TraitObject` did not roundtrip"), + }; + + let ambig_to_unambig = unsafe { std::mem::transmute::<_, TyKind<'_, ()>>(unambig_to_ambig) }; + + match ambig_to_unambig { + TyKind::TraitObject(_, tagged_ref) => { + assert!(tagged_ref.tag() == syntax) + } + _ => panic!("`TyKind::TraitObject` did not roundtrip"), + }; +} diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index ef863aca090..b0d80d0f809 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -351,18 +351,48 @@ pub trait Visitor<'v>: Sized { fn visit_inline_const(&mut self, c: &'v ConstBlock) -> Self::Result { walk_inline_const(self, c) } - fn visit_const_arg(&mut self, c: &'v ConstArg<'v>) -> Self::Result { - walk_const_arg(self, c) + + fn visit_generic_arg(&mut self, generic_arg: &'v GenericArg<'v>) -> Self::Result { + walk_generic_arg(self, generic_arg) + } + + /// All types are treated as ambiguous types for the purposes of hir visiting in + /// order to ensure that visitors can handle infer vars without it being too error-prone. + /// + /// See the doc comments on [`Ty`] for an explanation of what it means for a type to be + /// ambiguous. + /// + /// The [`Visitor::visit_infer`] method should be overriden in order to handle infer vars. + fn visit_ty(&mut self, t: &'v Ty<'v, AmbigArg>) -> Self::Result { + walk_ty(self, t) } + + /// All consts are treated as ambiguous consts for the purposes of hir visiting in + /// order to ensure that visitors can handle infer vars without it being too error-prone. + /// + /// See the doc comments on [`ConstArg`] for an explanation of what it means for a const to be + /// ambiguous. + /// + /// The [`Visitor::visit_infer`] method should be overriden in order to handle infer vars. + fn visit_const_arg(&mut self, c: &'v ConstArg<'v, AmbigArg>) -> Self::Result { + walk_ambig_const_arg(self, c) + } + + #[allow(unused_variables)] + fn visit_infer(&mut self, inf_id: HirId, inf_span: Span, kind: InferKind<'v>) -> Self::Result { + self.visit_id(inf_id) + } + + fn visit_lifetime(&mut self, lifetime: &'v Lifetime) -> Self::Result { + walk_lifetime(self, lifetime) + } + fn visit_expr(&mut self, ex: &'v Expr<'v>) -> Self::Result { walk_expr(self, ex) } fn visit_expr_field(&mut self, field: &'v ExprField<'v>) -> Self::Result { walk_expr_field(self, field) } - fn visit_ty(&mut self, t: &'v Ty<'v>) -> Self::Result { - walk_ty(self, t) - } fn visit_pattern_type_pattern(&mut self, _p: &'v Pat<'v>) { // Do nothing. Only a few visitors need to know the details of the pattern type, // and they opt into it. All other visitors will just choke on our fake patterns @@ -444,15 +474,6 @@ pub trait Visitor<'v>: Sized { fn visit_label(&mut self, label: &'v Label) -> Self::Result { walk_label(self, label) } - fn visit_infer(&mut self, inf: &'v InferArg) -> Self::Result { - walk_inf(self, inf) - } - fn visit_generic_arg(&mut self, generic_arg: &'v GenericArg<'v>) -> Self::Result { - walk_generic_arg(self, generic_arg) - } - fn visit_lifetime(&mut self, lifetime: &'v Lifetime) -> Self::Result { - walk_lifetime(self, lifetime) - } // The span is that of the surrounding type/pattern/expr/whatever. fn visit_qpath(&mut self, qpath: &'v QPath<'v>, id: HirId, _span: Span) -> Self::Result { walk_qpath(self, qpath, id) @@ -486,6 +507,26 @@ pub trait Visitor<'v>: Sized { } } +pub trait VisitorExt<'v>: Visitor<'v> { + /// Extension trait method to visit types in unambiguous positions, this is not + /// directly on the [`Visitor`] trait as this method should never be overridden. + /// + /// Named `visit_ty_unambig` instead of `visit_unambig_ty` to aid in discovery + /// by IDes when `v.visit_ty` is written. + fn visit_ty_unambig(&mut self, t: &'v Ty<'v>) -> Self::Result { + walk_unambig_ty(self, t) + } + /// Extension trait method to visit consts in unambiguous positions, this is not + /// directly on the [`Visitor`] trait as this method should never be overridden. + /// + /// Named `visit_const_arg_unambig` instead of `visit_unambig_const_arg` to aid in + /// discovery by IDes when `v.visit_const_arg` is written. + fn visit_const_arg_unambig(&mut self, c: &'v ConstArg<'v>) -> Self::Result { + walk_const_arg(self, c) + } +} +impl<'v, V: Visitor<'v>> VisitorExt<'v> for V {} + pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) -> V::Result { try_visit!(visitor.visit_id(param.hir_id)); visitor.visit_pat(param.pat) @@ -503,12 +544,12 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: } ItemKind::Static(ref typ, _, body) => { try_visit!(visitor.visit_id(item.hir_id())); - try_visit!(visitor.visit_ty(typ)); + try_visit!(visitor.visit_ty_unambig(typ)); try_visit!(visitor.visit_nested_body(body)); } ItemKind::Const(ref typ, ref generics, body) => { try_visit!(visitor.visit_id(item.hir_id())); - try_visit!(visitor.visit_ty(typ)); + try_visit!(visitor.visit_ty_unambig(typ)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_nested_body(body)); } @@ -539,7 +580,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: } ItemKind::TyAlias(ref ty, ref generics) => { try_visit!(visitor.visit_id(item.hir_id())); - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); try_visit!(visitor.visit_generics(generics)); } ItemKind::Enum(ref enum_definition, ref generics) => { @@ -561,7 +602,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: try_visit!(visitor.visit_id(item.hir_id())); try_visit!(visitor.visit_generics(generics)); visit_opt!(visitor, visit_trait_ref, of_trait); - try_visit!(visitor.visit_ty(self_ty)); + try_visit!(visitor.visit_ty_unambig(self_ty)); walk_list!(visitor, visit_impl_item_ref, *items); } ItemKind::Struct(ref struct_definition, ref generics) @@ -618,7 +659,7 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>( walk_list!(visitor, visit_ident, param_names.iter().copied()); } ForeignItemKind::Static(ref typ, _, _) => { - try_visit!(visitor.visit_ty(typ)); + try_visit!(visitor.visit_ty_unambig(typ)); } ForeignItemKind::Type => (), } @@ -632,7 +673,7 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v LetStmt<'v>) - try_visit!(visitor.visit_id(local.hir_id)); try_visit!(visitor.visit_pat(local.pat)); visit_opt!(visitor, visit_block, local.els); - visit_opt!(visitor, visit_ty, local.ty); + visit_opt!(visitor, visit_ty_unambig, local.ty); V::Result::output() } @@ -735,18 +776,6 @@ pub fn walk_inline_const<'v, V: Visitor<'v>>( visitor.visit_nested_body(constant.body) } -pub fn walk_const_arg<'v, V: Visitor<'v>>( - visitor: &mut V, - const_arg: &'v ConstArg<'v>, -) -> V::Result { - try_visit!(visitor.visit_id(const_arg.hir_id)); - match &const_arg.kind { - ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, const_arg.hir_id, qpath.span()), - ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon), - ConstArgKind::Infer(..) => V::Result::output(), - } -} - pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) -> V::Result { try_visit!(visitor.visit_id(expression.hir_id)); match expression.kind { @@ -758,7 +787,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::Repeat(ref element, ref count) => { try_visit!(visitor.visit_expr(element)); - try_visit!(visitor.visit_const_arg(count)); + try_visit!(visitor.visit_const_arg_unambig(count)); } ExprKind::Struct(ref qpath, fields, ref optional_base) => { try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span)); @@ -789,7 +818,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::Cast(ref subexpression, ref typ) | ExprKind::Type(ref subexpression, ref typ) => { try_visit!(visitor.visit_expr(subexpression)); - try_visit!(visitor.visit_ty(typ)); + try_visit!(visitor.visit_ty_unambig(typ)); } ExprKind::DropTemps(ref subexpression) => { try_visit!(visitor.visit_expr(subexpression)); @@ -798,7 +827,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) // match the visit order in walk_local try_visit!(visitor.visit_expr(init)); try_visit!(visitor.visit_pat(pat)); - visit_opt!(visitor, visit_ty, ty); + visit_opt!(visitor, visit_ty_unambig, ty); } ExprKind::If(ref cond, ref then, ref else_opt) => { try_visit!(visitor.visit_expr(cond)); @@ -866,7 +895,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) try_visit!(visitor.visit_inline_asm(asm, expression.hir_id)); } ExprKind::OffsetOf(ref container, ref fields) => { - try_visit!(visitor.visit_ty(container)); + try_visit!(visitor.visit_ty_unambig(container)); walk_list!(visitor, visit_ident, fields.iter().copied()); } ExprKind::Yield(ref subexpression, _) => { @@ -874,7 +903,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::UnsafeBinderCast(_kind, expr, ty) => { try_visit!(visitor.visit_expr(expr)); - visit_opt!(visitor, visit_ty, ty); + visit_opt!(visitor, visit_ty_unambig, ty); } ExprKind::Lit(_) | ExprKind::Err(_) => {} } @@ -886,20 +915,49 @@ pub fn walk_expr_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v ExprField try_visit!(visitor.visit_ident(field.ident)); visitor.visit_expr(field.expr) } +/// We track whether an infer var is from a [`Ty`], [`ConstArg`], or [`GenericArg`] so that +/// HIR visitors overriding [`Visitor::visit_infer`] can determine what kind of infer is being visited +pub enum InferKind<'hir> { + Ty(&'hir Ty<'hir>), + Const(&'hir ConstArg<'hir>), + Ambig(&'hir InferArg), +} -pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Result { +pub fn walk_generic_arg<'v, V: Visitor<'v>>( + visitor: &mut V, + generic_arg: &'v GenericArg<'v>, +) -> V::Result { + match generic_arg { + GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt), + GenericArg::Type(ty) => visitor.visit_ty(ty), + GenericArg::Const(ct) => visitor.visit_const_arg(ct), + GenericArg::Infer(inf) => visitor.visit_infer(inf.hir_id, inf.span, InferKind::Ambig(inf)), + } +} + +pub fn walk_unambig_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Result { + match typ.try_as_ambig_ty() { + Some(ambig_ty) => visitor.visit_ty(ambig_ty), + None => { + try_visit!(visitor.visit_id(typ.hir_id)); + visitor.visit_infer(typ.hir_id, typ.span, InferKind::Ty(typ)) + } + } +} + +pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) -> V::Result { try_visit!(visitor.visit_id(typ.hir_id)); match typ.kind { - TyKind::Slice(ref ty) => try_visit!(visitor.visit_ty(ty)), - TyKind::Ptr(ref mutable_type) => try_visit!(visitor.visit_ty(mutable_type.ty)), + TyKind::Slice(ref ty) => try_visit!(visitor.visit_ty_unambig(ty)), + TyKind::Ptr(ref mutable_type) => try_visit!(visitor.visit_ty_unambig(mutable_type.ty)), TyKind::Ref(ref lifetime, ref mutable_type) => { try_visit!(visitor.visit_lifetime(lifetime)); - try_visit!(visitor.visit_ty(mutable_type.ty)); + try_visit!(visitor.visit_ty_unambig(mutable_type.ty)); } TyKind::Never => {} TyKind::Tup(tuple_element_types) => { - walk_list!(visitor, visit_ty, tuple_element_types); + walk_list!(visitor, visit_ty_unambig, tuple_element_types); } TyKind::BareFn(ref function_declaration) => { walk_list!(visitor, visit_generic_param, function_declaration.generic_params); @@ -907,7 +965,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul } TyKind::UnsafeBinder(ref unsafe_binder) => { walk_list!(visitor, visit_generic_param, unsafe_binder.generic_params); - try_visit!(visitor.visit_ty(unsafe_binder.inner_ty)); + try_visit!(visitor.visit_ty_unambig(unsafe_binder.inner_ty)); } TyKind::Path(ref qpath) => { try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span)); @@ -919,25 +977,49 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul walk_list!(visitor, visit_param_bound, bounds); } TyKind::Array(ref ty, ref length) => { - try_visit!(visitor.visit_ty(ty)); - try_visit!(visitor.visit_const_arg(length)); + try_visit!(visitor.visit_ty_unambig(ty)); + try_visit!(visitor.visit_const_arg_unambig(length)); } - TyKind::TraitObject(bounds, ref lifetime, _syntax) => { + TyKind::TraitObject(bounds, ref lifetime) => { for bound in bounds { try_visit!(visitor.visit_poly_trait_ref(bound)); } try_visit!(visitor.visit_lifetime(lifetime)); } TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)), - TyKind::Infer | TyKind::InferDelegation(..) | TyKind::Err(_) => {} + TyKind::InferDelegation(..) | TyKind::Err(_) => {} TyKind::Pat(ty, pat) => { - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); try_visit!(visitor.visit_pattern_type_pattern(pat)); } } V::Result::output() } +pub fn walk_const_arg<'v, V: Visitor<'v>>( + visitor: &mut V, + const_arg: &'v ConstArg<'v>, +) -> V::Result { + match const_arg.try_as_ambig_ct() { + Some(ambig_ct) => visitor.visit_const_arg(ambig_ct), + None => { + try_visit!(visitor.visit_id(const_arg.hir_id)); + visitor.visit_infer(const_arg.hir_id, const_arg.span(), InferKind::Const(const_arg)) + } + } +} + +pub fn walk_ambig_const_arg<'v, V: Visitor<'v>>( + visitor: &mut V, + const_arg: &'v ConstArg<'v, AmbigArg>, +) -> V::Result { + try_visit!(visitor.visit_id(const_arg.hir_id)); + match &const_arg.kind { + ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, const_arg.hir_id, qpath.span()), + ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon), + } +} + pub fn walk_generic_param<'v, V: Visitor<'v>>( visitor: &mut V, param: &'v GenericParam<'v>, @@ -949,9 +1031,11 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>( } match param.kind { GenericParamKind::Lifetime { .. } => {} - GenericParamKind::Type { ref default, .. } => visit_opt!(visitor, visit_ty, default), + GenericParamKind::Type { ref default, .. } => { + visit_opt!(visitor, visit_ty_unambig, default) + } GenericParamKind::Const { ref ty, ref default, synthetic: _ } => { - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); if let Some(ref default) = default { try_visit!(visitor.visit_const_param_default(param.hir_id, default)); } @@ -964,7 +1048,7 @@ pub fn walk_const_param_default<'v, V: Visitor<'v>>( visitor: &mut V, ct: &'v ConstArg<'v>, ) -> V::Result { - visitor.visit_const_arg(ct) + visitor.visit_const_arg_unambig(ct) } pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics<'v>) -> V::Result { @@ -986,7 +1070,7 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( bound_generic_params, origin: _, }) => { - try_visit!(visitor.visit_ty(bounded_ty)); + try_visit!(visitor.visit_ty_unambig(bounded_ty)); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_generic_param, bound_generic_params); } @@ -999,8 +1083,8 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( walk_list!(visitor, visit_param_bound, bounds); } WherePredicateKind::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty }) => { - try_visit!(visitor.visit_ty(lhs_ty)); - try_visit!(visitor.visit_ty(rhs_ty)); + try_visit!(visitor.visit_ty_unambig(lhs_ty)); + try_visit!(visitor.visit_ty_unambig(rhs_ty)); } } V::Result::output() @@ -1010,13 +1094,13 @@ pub fn walk_fn_decl<'v, V: Visitor<'v>>( visitor: &mut V, function_declaration: &'v FnDecl<'v>, ) -> V::Result { - walk_list!(visitor, visit_ty, function_declaration.inputs); + walk_list!(visitor, visit_ty_unambig, function_declaration.inputs); visitor.visit_fn_ret_ty(&function_declaration.output) } pub fn walk_fn_ret_ty<'v, V: Visitor<'v>>(visitor: &mut V, ret_ty: &'v FnRetTy<'v>) -> V::Result { if let FnRetTy::Return(output_ty) = *ret_ty { - try_visit!(visitor.visit_ty(output_ty)); + try_visit!(visitor.visit_ty_unambig(output_ty)); } V::Result::output() } @@ -1069,7 +1153,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_id(hir_id)); match *kind { TraitItemKind::Const(ref ty, default) => { - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); visit_opt!(visitor, visit_nested_body, default); } TraitItemKind::Fn(ref sig, TraitFn::Required(param_names)) => { @@ -1087,7 +1171,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>( } TraitItemKind::Type(bounds, ref default) => { walk_list!(visitor, visit_param_bound, bounds); - visit_opt!(visitor, visit_ty, default); + visit_opt!(visitor, visit_ty_unambig, default); } } V::Result::output() @@ -1125,7 +1209,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_id(impl_item.hir_id())); match *kind { ImplItemKind::Const(ref ty, body) => { - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); visitor.visit_nested_body(body) } ImplItemKind::Fn(ref sig, body_id) => visitor.visit_fn( @@ -1135,7 +1219,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>( impl_item.span, impl_item.owner_id.def_id, ), - ImplItemKind::Type(ref ty) => visitor.visit_ty(ty), + ImplItemKind::Type(ref ty) => visitor.visit_ty_unambig(ty), } } @@ -1223,7 +1307,7 @@ pub fn walk_field_def<'v, V: Visitor<'v>>( try_visit!(visitor.visit_id(*hir_id)); try_visit!(visitor.visit_ident(*ident)); visit_opt!(visitor, visit_anon_const, default); - visitor.visit_ty(*ty) + visitor.visit_ty_unambig(*ty) } pub fn walk_enum_def<'v, V: Visitor<'v>>( @@ -1252,18 +1336,6 @@ pub fn walk_inf<'v, V: Visitor<'v>>(visitor: &mut V, inf: &'v InferArg) -> V::Re visitor.visit_id(inf.hir_id) } -pub fn walk_generic_arg<'v, V: Visitor<'v>>( - visitor: &mut V, - generic_arg: &'v GenericArg<'v>, -) -> V::Result { - match generic_arg { - GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt), - GenericArg::Type(ty) => visitor.visit_ty(ty), - GenericArg::Const(ct) => visitor.visit_const_arg(ct), - GenericArg::Infer(inf) => visitor.visit_infer(inf), - } -} - pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) -> V::Result { try_visit!(visitor.visit_id(lifetime.hir_id)); visitor.visit_ident(lifetime.ident) @@ -1276,11 +1348,11 @@ pub fn walk_qpath<'v, V: Visitor<'v>>( ) -> V::Result { match *qpath { QPath::Resolved(ref maybe_qself, ref path) => { - visit_opt!(visitor, visit_ty, maybe_qself); + visit_opt!(visitor, visit_ty_unambig, maybe_qself); visitor.visit_path(path, id) } QPath::TypeRelative(ref qself, ref segment) => { - try_visit!(visitor.visit_ty(qself)); + try_visit!(visitor.visit_ty_unambig(qself)); visitor.visit_path_segment(segment) } QPath::LangItem(..) => V::Result::output(), @@ -1320,8 +1392,8 @@ pub fn walk_assoc_item_constraint<'v, V: Visitor<'v>>( try_visit!(visitor.visit_generic_args(constraint.gen_args)); match constraint.kind { AssocItemConstraintKind::Equality { ref term } => match term { - Term::Ty(ref ty) => try_visit!(visitor.visit_ty(ty)), - Term::Const(ref c) => try_visit!(visitor.visit_const_arg(c)), + Term::Ty(ref ty) => try_visit!(visitor.visit_ty_unambig(ty)), + Term::Const(ref c) => try_visit!(visitor.visit_const_arg_unambig(c)), }, AssocItemConstraintKind::Bound { bounds } => { walk_list!(visitor, visit_param_bound, bounds) diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index fae3b778d7b..02bc069fc5f 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -332,6 +332,10 @@ language_item_table! { FallbackSurfaceDrop, sym::fallback_surface_drop, fallback_surface_drop_fn, Target::Fn, GenericRequirement::None; AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; + /// For all binary crates without `#![no_main]`, Rust will generate a "main" function. + /// The exact name and signature are target-dependent. The "main" function will invoke + /// this lang item, passing it the `argc` and `argv` (or null, if those don't exist + /// on the current target) as well as the user-defined `fn main` from the binary crate. Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1); EhPersonality, sym::eh_personality, eh_personality, Target::Fn, GenericRequirement::None; diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 381062b1429..705c167e258 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -6,6 +6,8 @@ #![allow(internal_features)] #![feature(associated_type_defaults)] #![feature(closure_track_caller)] +#![feature(debug_closure_helpers)] +#![feature(exhaustive_patterns)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index d7ab6eca84b..be4004f5904 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -353,7 +353,13 @@ hir_analysis_missing_type_params = [one] reference *[other] references } to {$parameters} - .note = because of the default `Self` reference, type parameters must be specified on object types + .note = because the parameter {$parameterCount -> + [one] default references + *[other] defaults reference + } `Self`, the {$parameterCount -> + [one] parameter + *[other] parameters + } must be specified on the object type hir_analysis_multiple_relaxed_default_bounds = type parameter has more than one relaxed default bound, only one is supported @@ -489,21 +495,6 @@ hir_analysis_simd_ffi_highly_experimental = use of SIMD type{$snip} in FFI is hi hir_analysis_specialization_trait = implementing `rustc_specialization_trait` traits is unstable .help = add `#![feature(min_specialization)]` to the crate attributes to enable -hir_analysis_start_function_parameters = `#[start]` function is not allowed to have type parameters - .label = `#[start]` function cannot have type parameters - -hir_analysis_start_function_where = `#[start]` function is not allowed to have a `where` clause - .label = `#[start]` function cannot have a `where` clause - -hir_analysis_start_not_async = `#[start]` function is not allowed to be `async` - .label = `#[start]` is not allowed to be `async` - -hir_analysis_start_not_target_feature = `#[start]` function is not allowed to have `#[target_feature]` - .label = `#[start]` function is not allowed to have `#[target_feature]` - -hir_analysis_start_not_track_caller = `#[start]` function is not allowed to be `#[track_caller]` - .label = `#[start]` function is not allowed to be `#[track_caller]` - hir_analysis_static_specialize = cannot specialize on `'static` lifetime hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index d8e9227a87c..b3eade8c8ae 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -86,7 +86,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { if self.infcx.next_trait_solver() && let ty::Alias(..) = ty.kind() { - let (normalized_ty, obligations) = self.structurally_normalize(ty)?; + let (normalized_ty, obligations) = self.structurally_normalize_ty(ty)?; self.state.obligations.extend(obligations); (AutoderefKind::Builtin, normalized_ty) } else { @@ -166,7 +166,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { } let (normalized_ty, obligations) = - self.structurally_normalize(Ty::new_projection(tcx, trait_target_def_id, [ty]))?; + self.structurally_normalize_ty(Ty::new_projection(tcx, trait_target_def_id, [ty]))?; debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); self.state.obligations.extend(obligations); @@ -174,12 +174,12 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { } #[instrument(level = "debug", skip(self), ret)] - pub fn structurally_normalize( + pub fn structurally_normalize_ty( &self, ty: Ty<'tcx>, ) -> Option<(Ty<'tcx>, PredicateObligations<'tcx>)> { let ocx = ObligationCtxt::new(self.infcx); - let Ok(normalized_ty) = ocx.structurally_normalize( + let Ok(normalized_ty) = ocx.structurally_normalize_ty( &traits::ObligationCause::misc(self.span, self.body_id), self.param_env, ty, 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 4a957d5da24..d2ab98bae89 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -6,9 +6,9 @@ use hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{GenericParamKind, ImplItemKind, intravisit}; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisit}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; @@ -1610,7 +1610,7 @@ fn compare_synthetic_generics<'tcx>( struct Visitor(hir::def_id::LocalDefId); impl<'v> intravisit::Visitor<'v> for Visitor { type Result = ControlFlow<Span>; - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) -> Self::Result { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) -> Self::Result { if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind && let Res::Def(DefKind::TyParam, def_id) = path.res && def_id == self.0.to_def_id() @@ -1622,9 +1622,9 @@ fn compare_synthetic_generics<'tcx>( } } - let span = input_tys.iter().find_map(|ty| { - intravisit::Visitor::visit_ty(&mut Visitor(impl_def_id), ty).break_value() - })?; + let span = input_tys + .iter() + .find_map(|ty| Visitor(impl_def_id).visit_ty_unambig(ty).break_value())?; let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds; let bounds = bounds.first()?.span().to(bounds.last()?.span()); @@ -2362,7 +2362,7 @@ fn try_report_async_mismatch<'tcx>( // the right span is a bit difficult. return Err(tcx.sess.dcx().emit_err(MethodShouldReturnFuture { span: tcx.def_span(impl_m.def_id), - method_name: trait_m.name, + method_name: tcx.item_ident(impl_m.def_id), trait_item_span: tcx.hir().span_if_local(trait_m.def_id), })); } diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index 332ac2fa0c0..25c2f8554b7 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -5,7 +5,7 @@ use rustc_hir as hir; use rustc_hir::Node; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::span_bug; -use rustc_middle::ty::{self, Ty, TyCtxt, TypingMode}; +use rustc_middle::ty::{self, TyCtxt, TypingMode}; use rustc_session::config::EntryFnType; use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_span::{Span, sym}; @@ -18,7 +18,6 @@ use crate::errors; pub(crate) fn check_for_entry_fn(tcx: TyCtxt<'_>) { match tcx.entry_fn(()) { Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id), - Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id), _ => {} } } @@ -192,83 +191,3 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { }); } } - -fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { - let start_def_id = start_def_id.expect_local(); - let start_id = tcx.local_def_id_to_hir_id(start_def_id); - let start_span = tcx.def_span(start_def_id); - let start_t = tcx.type_of(start_def_id).instantiate_identity(); - match start_t.kind() { - ty::FnDef(..) => { - if let Node::Item(it) = tcx.hir_node(start_id) { - if let hir::ItemKind::Fn { sig, generics, .. } = &it.kind { - let mut error = false; - if !generics.params.is_empty() { - tcx.dcx().emit_err(errors::StartFunctionParameters { span: generics.span }); - error = true; - } - if generics.has_where_clause_predicates { - tcx.dcx().emit_err(errors::StartFunctionWhere { - span: generics.where_clause_span, - }); - error = true; - } - if sig.header.asyncness.is_async() { - let span = tcx.def_span(it.owner_id); - tcx.dcx().emit_err(errors::StartAsync { span }); - error = true; - } - - let attrs = tcx.hir().attrs(start_id); - for attr in attrs { - if attr.has_name(sym::track_caller) { - tcx.dcx().emit_err(errors::StartTrackCaller { - span: attr.span, - start: start_span, - }); - error = true; - } - if attr.has_name(sym::target_feature) - // Calling functions with `#[target_feature]` is - // not unsafe on WASM, see #84988 - && !tcx.sess.target.is_like_wasm - && !tcx.sess.opts.actually_rustdoc - { - tcx.dcx().emit_err(errors::StartTargetFeature { - span: attr.span, - start: start_span, - }); - error = true; - } - } - - if error { - return; - } - } - } - - let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig( - [tcx.types.isize, Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8))], - tcx.types.isize, - false, - hir::Safety::Safe, - ExternAbi::Rust, - )); - - let _ = check_function_signature( - tcx, - ObligationCause::new( - start_span, - start_def_id, - ObligationCauseCode::StartFunctionType, - ), - start_def_id.into(), - expected_sig, - ); - } - _ => { - span_bug!(start_span, "start has a non-function type: found `{}`", start_t); - } - } -} diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 92b18c80fd8..69b4aa47eba 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -197,7 +197,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { fn report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId) { let span = tcx.def_span(impl_item); - let ident = tcx.item_name(impl_item); + let ident = tcx.item_ident(impl_item); let err = match tcx.span_of_impl(parent_impl) { Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp }, @@ -297,7 +297,7 @@ fn default_body_is_unstable( reason: Option<Symbol>, issue: Option<NonZero<u32>>, ) { - let missing_item_name = tcx.associated_item(item_did).name; + let missing_item_name = tcx.item_ident(item_did); let (mut some_note, mut none_note, mut reason_str) = (false, false, String::new()); match reason { Some(r) => { diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index dd6adb17c5e..cd19993f937 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -6,10 +6,10 @@ use rustc_abi::ExternAbi; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; -use rustc_hir::ItemKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::lang_items::LangItem; +use rustc_hir::{AmbigArg, ItemKind}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_macros::LintDiagnostic; @@ -2196,7 +2196,7 @@ impl<'tcx> Visitor<'tcx> for CollectUsageSpans<'_> { // Skip the generics. We only care about fields, not where clause/param bounds. } - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { if let hir::TyKind::Path(hir::QPath::Resolved(None, qpath)) = t.kind { if let Res::Def(DefKind::TyParam, def_id) = qpath.res && def_id == self.param_def_id diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index b43a808ccdc..27a7c2ea530 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -292,7 +292,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST { span, - name: field.name, + name: field.ident(tcx), ty: ty_a, })); diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index 1be4aa2f63a..4e5f0a3186a 100644 --- a/compiler/rustc_hir_analysis/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs @@ -13,6 +13,7 @@ use rustc_middle::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_session::parse::feature_err; use rustc_span::{ErrorGuaranteed, sym}; +use rustc_type_ir::elaborate; use tracing::debug; use crate::errors; @@ -184,7 +185,7 @@ fn check_object_overlap<'tcx>( // check for overlap with the automatic `impl Trait for dyn Trait` if let ty::Dynamic(data, ..) = trait_ref.self_ty().kind() { // This is something like `impl Trait1 for Trait2`. Illegal if - // Trait1 is a supertrait of Trait2 or Trait2 is not dyn-compatible. + // Trait1 is a supertrait of Trait2 or Trait2 is not dyn compatible. let component_def_ids = data.iter().flat_map(|predicate| { match predicate.skip_binder() { @@ -205,7 +206,7 @@ fn check_object_overlap<'tcx>( // With the feature enabled, the trait is not implemented automatically, // so this is valid. } else { - let mut supertrait_def_ids = tcx.supertrait_def_ids(component_def_id); + let mut supertrait_def_ids = elaborate::supertrait_def_ids(tcx, component_def_id); if supertrait_def_ids .any(|d| d == trait_def_id && tcx.trait_def(d).implement_via_object) { diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 7d651155781..dbf7a7378f5 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -320,7 +320,7 @@ fn orphan_check<'tcx>( } let ty = if infcx.next_trait_solver() { - ocx.structurally_normalize( + ocx.structurally_normalize_ty( &cause, ty::ParamEnv::empty(), infcx.resolve_vars_if_possible(ty), @@ -465,8 +465,8 @@ fn emit_orphan_check_error<'tcx>( traits::OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => { let mut reported = None; for param_def_id in uncovered { - let span = tcx.def_ident_span(param_def_id).unwrap(); - let name = tcx.item_name(param_def_id); + let name = tcx.item_ident(param_def_id); + let span = name.span; reported.get_or_insert(match local_ty { Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal { @@ -492,7 +492,7 @@ fn lint_uncovered_ty_params<'tcx>( for param_def_id in uncovered { let span = tcx.def_ident_span(param_def_id).unwrap(); - let name = tcx.item_name(param_def_id); + let name = tcx.item_ident(param_def_id); match local_ty { Some(local_type) => tcx.emit_node_span_lint( diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 86c6532c97d..447050ea7d2 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -28,7 +28,7 @@ use rustc_errors::{ }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, Visitor, walk_generics}; +use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt, walk_generics}; use rustc_hir::{self as hir, GenericParamKind, HirId, Node}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; @@ -139,29 +139,12 @@ pub(crate) struct HirPlaceholderCollector { } impl<'v> Visitor<'v> for HirPlaceholderCollector { - fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { - if let hir::TyKind::Infer = t.kind { - self.spans.push(t.span); - } - intravisit::walk_ty(self, t) - } - fn visit_generic_arg(&mut self, generic_arg: &'v hir::GenericArg<'v>) { - match generic_arg { - hir::GenericArg::Infer(inf) => { - self.spans.push(inf.span); - self.may_contain_const_infer = true; - intravisit::walk_inf(self, inf); - } - hir::GenericArg::Type(t) => self.visit_ty(t), - _ => {} - } - } - fn visit_const_arg(&mut self, const_arg: &'v hir::ConstArg<'v>) { - if let hir::ConstArgKind::Infer(span) = const_arg.kind { + fn visit_infer(&mut self, _inf_id: HirId, inf_span: Span, kind: InferKind<'v>) -> Self::Result { + self.spans.push(inf_span); + + if let InferKind::Const(_) | InferKind::Ambig(_) = kind { self.may_contain_const_infer = true; - self.spans.push(span); } - intravisit::walk_const_arg(self, const_arg) } } @@ -583,7 +566,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { .iter() .enumerate() .map(|(i, a)| { - if let hir::TyKind::Infer = a.kind { + if let hir::TyKind::Infer(()) = a.kind { if let Some(suggested_ty) = self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i)) { @@ -593,21 +576,21 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { } // Only visit the type looking for `_` if we didn't fix the type above - visitor.visit_ty(a); + visitor.visit_ty_unambig(a); self.lowerer().lower_arg_ty(a, None) }) .collect(); let output_ty = match decl.output { hir::FnRetTy::Return(output) => { - if let hir::TyKind::Infer = output.kind + if let hir::TyKind::Infer(()) = output.kind && let Some(suggested_ty) = self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None) { infer_replacements.push((output.span, suggested_ty.to_string())); Ty::new_error_with_message(tcx, output.span, suggested_ty.to_string()) } else { - visitor.visit_ty(output); + visitor.visit_ty_unambig(output); self.lower_ty(output) } } @@ -945,7 +928,7 @@ fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { tcx.dcx().emit_err(errors::EnumDiscriminantOverflowed { span, discr: prev_discr.unwrap().to_string(), - item_name: tcx.item_name(variant.def_id), + item_name: tcx.item_ident(variant.def_id), wrapped_discr: wrapped_discr.to_string(), }); None @@ -1007,11 +990,10 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { } /// Check if a given field `ident` declared at `field_decl` has been declared elsewhere before. - fn check_field_decl(&mut self, ident: Ident, field_decl: FieldDeclSpan) { + fn check_field_decl(&mut self, field_name: Ident, field_decl: FieldDeclSpan) { use FieldDeclSpan::*; - let field_name = ident.name; - let ident = ident.normalize_to_macros_2_0(); - match (field_decl, self.seen_fields.get(&ident).copied()) { + let field_name = field_name.normalize_to_macros_2_0(); + match (field_decl, self.seen_fields.get(&field_name).copied()) { (NotNested(span), Some(NotNested(prev_span))) => { self.tcx.dcx().emit_err(errors::FieldAlreadyDeclared::NotNested { field_name, @@ -1052,7 +1034,7 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { }); } (field_decl, None) => { - self.seen_fields.insert(ident, field_decl); + self.seen_fields.insert(field_name, field_decl); } } } @@ -1453,7 +1435,7 @@ fn recover_infer_ret_ty<'tcx>( }); let mut visitor = HirPlaceholderCollector::default(); - visitor.visit_ty(infer_ret_ty); + visitor.visit_ty_unambig(infer_ret_ty); let mut diag = bad_placeholder(icx.lowerer(), visitor.spans, "return type"); let ret_ty = fn_sig.output(); diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 075615de522..2ac8acbd3a4 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -3,9 +3,10 @@ use std::ops::ControlFlow; use hir::intravisit::{self, Visitor}; use hir::{GenericParamKind, HirId, Node}; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint; use rustc_span::{Span, Symbol, kw}; @@ -461,7 +462,7 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S impl<'tcx> Visitor<'tcx> for LateBoundRegionsDetector<'tcx> { type Result = ControlFlow<Span>; - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) -> ControlFlow<Span> { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) -> ControlFlow<Span> { match ty.kind { hir::TyKind::BareFn(..) => { self.outer_index.shift_in(1); @@ -539,7 +540,7 @@ impl<'v> Visitor<'v> for AnonConstInParamTyDetector { if let GenericParamKind::Const { ty, default: _, synthetic: _ } = p.kind { let prev = self.in_param_ty; self.in_param_ty = true; - let res = self.visit_ty(ty); + let res = self.visit_ty_unambig(ty); self.in_param_ty = prev; res } else { diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index e8706d1adfb..72baf5c4b58 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -14,11 +14,11 @@ use rustc_ast::visit::walk_list; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt}; use rustc_hir::{ - GenericArg, GenericParam, GenericParamKind, HirId, ItemLocalMap, LifetimeName, Node, + self as hir, AmbigArg, GenericArg, GenericParam, GenericParamKind, HirId, ItemLocalMap, + LifetimeName, Node, }; use rustc_macros::extension; use rustc_middle::hir::nested_filter; @@ -184,29 +184,11 @@ enum Scope<'a> { }, } -#[derive(Copy, Clone, Debug)] -enum BinderScopeType { - /// Any non-concatenating binder scopes. - Normal, - /// Within a syntactic trait ref, there may be multiple poly trait refs that - /// are nested (under the `associated_type_bounds` feature). The binders of - /// the inner poly trait refs are extended from the outer poly trait refs - /// and don't increase the late bound depth. If you had - /// `T: for<'a> Foo<Bar: for<'b> Baz<'a, 'b>>`, then the `for<'b>` scope - /// would be `Concatenating`. This also used in trait refs in where clauses - /// where we have two binders `for<> T: for<> Foo` (I've intentionally left - /// out any lifetimes because they aren't needed to show the two scopes). - /// The inner `for<>` has a scope of `Concatenating`. - Concatenating, -} - -// A helper struct for debugging scopes without printing parent scopes -struct TruncatedScopeDebug<'a>(&'a Scope<'a>); - -impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 { - Scope::Binder { bound_vars, scope_type, hir_id, where_bound_origin, s: _ } => f +impl<'a> Scope<'a> { + // A helper for debugging scopes without printing parent scopes + fn debug_truncated(&'a self) -> impl fmt::Debug + 'a { + fmt::from_fn(move |f| match self { + Self::Binder { bound_vars, scope_type, hir_id, where_bound_origin, s: _ } => f .debug_struct("Binder") .field("bound_vars", bound_vars) .field("scope_type", scope_type) @@ -214,38 +196,54 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> { .field("where_bound_origin", where_bound_origin) .field("s", &"..") .finish(), - Scope::Opaque { captures, def_id, s: _ } => f + Self::Opaque { captures, def_id, s: _ } => f .debug_struct("Opaque") .field("def_id", def_id) .field("captures", &captures.borrow()) .field("s", &"..") .finish(), - Scope::Body { id, s: _ } => { + Self::Body { id, s: _ } => { f.debug_struct("Body").field("id", id).field("s", &"..").finish() } - Scope::ObjectLifetimeDefault { lifetime, s: _ } => f + Self::ObjectLifetimeDefault { lifetime, s: _ } => f .debug_struct("ObjectLifetimeDefault") .field("lifetime", lifetime) .field("s", &"..") .finish(), - Scope::Supertrait { bound_vars, s: _ } => f + Self::Supertrait { bound_vars, s: _ } => f .debug_struct("Supertrait") .field("bound_vars", bound_vars) .field("s", &"..") .finish(), - Scope::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(), - Scope::LateBoundary { s: _, what, deny_late_regions } => f + Self::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(), + Self::LateBoundary { s: _, what, deny_late_regions } => f .debug_struct("LateBoundary") .field("what", what) .field("deny_late_regions", deny_late_regions) .finish(), - Scope::Root { opt_parent_item } => { + Self::Root { opt_parent_item } => { f.debug_struct("Root").field("opt_parent_item", &opt_parent_item).finish() } - } + }) } } +#[derive(Copy, Clone, Debug)] +enum BinderScopeType { + /// Any non-concatenating binder scopes. + Normal, + /// Within a syntactic trait ref, there may be multiple poly trait refs that + /// are nested (under the `associated_type_bounds` feature). The binders of + /// the inner poly trait refs are extended from the outer poly trait refs + /// and don't increase the late bound depth. If you had + /// `T: for<'a> Foo<Bar: for<'b> Baz<'a, 'b>>`, then the `for<'b>` scope + /// would be `Concatenating`. This also used in trait refs in where clauses + /// where we have two binders `for<> T: for<> Foo` (I've intentionally left + /// out any lifetimes because they aren't needed to show the two scopes). + /// The inner `for<>` has a scope of `Concatenating`. + Concatenating, +} + type ScopeRef<'a> = &'a Scope<'a>; pub(crate) fn provide(providers: &mut Providers) { @@ -489,15 +487,17 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { struct FindInferInClosureWithBinder; impl<'v> Visitor<'v> for FindInferInClosureWithBinder { type Result = ControlFlow<Span>; - fn visit_ty(&mut self, t: &'v hir::Ty<'v>) -> Self::Result { - if matches!(t.kind, hir::TyKind::Infer) { - ControlFlow::Break(t.span) - } else { - intravisit::walk_ty(self, t) - } + + fn visit_infer( + &mut self, + _inf_id: HirId, + inf_span: Span, + _kind: InferKind<'v>, + ) -> Self::Result { + ControlFlow::Break(inf_span) } } - FindInferInClosureWithBinder.visit_ty(ty).break_value() + FindInferInClosureWithBinder.visit_ty_unambig(ty).break_value() } let infer_in_rt_sp = match fn_decl.output { @@ -749,7 +749,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { match ty.kind { hir::TyKind::BareFn(c) => { let (mut bound_vars, binders): (FxIndexMap<LocalDefId, ResolvedArg>, Vec<_>) = c @@ -810,7 +810,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { intravisit::walk_ty(this, ty); }); } - hir::TyKind::TraitObject(bounds, lifetime, _) => { + hir::TyKind::TraitObject(bounds, lifetime) => { + let lifetime = lifetime.pointer(); + debug!(?bounds, ?lifetime, "TraitObject"); let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { @@ -827,7 +829,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // use the object lifetime defaulting // rules. So e.g., `Box<dyn Debug>` becomes // `Box<dyn Debug + 'static>`. - self.resolve_object_lifetime_default(lifetime) + self.resolve_object_lifetime_default(&*lifetime) } LifetimeName::Infer => { // If the user writes `'_`, we use the *ordinary* elision @@ -838,7 +840,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } LifetimeName::Param(..) | LifetimeName::Static => { // If the user wrote an explicit name, use that. - self.visit_lifetime(lifetime); + self.visit_lifetime(&*lifetime); } LifetimeName::Error => {} } @@ -849,7 +851,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { lifetime: self.map.defs.get(&lifetime_ref.hir_id.local_id).cloned(), s: self.scope, }; - self.with(scope, |this| this.visit_ty(mt.ty)); + self.with(scope, |this| this.visit_ty_unambig(mt.ty)); } hir::TyKind::TraitAscription(bounds) => { let scope = Scope::TraitRefBoundary { s: self.scope }; @@ -891,7 +893,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { this.visit_param_bound(bound); } if let Some(ty) = ty { - this.visit_ty(ty); + this.visit_ty_unambig(ty); } }) } @@ -910,7 +912,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }), Type(ty) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| { this.visit_generics(impl_item.generics); - this.visit_ty(ty); + this.visit_ty_unambig(ty); }), Const(_, _) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| { intravisit::walk_impl_item(this, impl_item) @@ -1019,7 +1021,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }; self.with(scope, |this| { walk_list!(this, visit_generic_param, bound_generic_params); - this.visit_ty(bounded_ty); + this.visit_ty_unambig(bounded_ty); walk_list!(this, visit_param_bound, bounds); }) } @@ -1034,8 +1036,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { &hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { - self.visit_ty(lhs_ty); - self.visit_ty(rhs_ty); + self.visit_ty_unambig(lhs_ty); + self.visit_ty_unambig(rhs_ty); } } } @@ -1068,13 +1070,13 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { default, .. } => { if let Some(ty) = default { - self.visit_ty(ty); + self.visit_ty_unambig(ty); } } GenericParamKind::Const { ty, default, .. } => { - self.visit_ty(ty); + self.visit_ty_unambig(ty); if let Some(default) = default { - self.visit_const_arg(default); + self.visit_const_arg_unambig(default); } } } @@ -1140,7 +1142,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { { let BoundVarContext { tcx, map, .. } = self; let mut this = BoundVarContext { tcx: *tcx, map, scope: &wrap_scope }; - let span = debug_span!("scope", scope = ?TruncatedScopeDebug(this.scope)); + let span = debug_span!("scope", scope = ?this.scope.debug_truncated()); { let _enter = span.enter(); f(&mut this); @@ -1983,15 +1985,15 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { }, |this| { for input in inputs { - this.visit_ty(input); + this.visit_ty_unambig(input); } if !in_closure && let Some(output) = output { - this.visit_ty(output); + this.visit_ty_unambig(output); } }, ); if in_closure && let Some(output) = output { - self.visit_ty(output); + self.visit_ty_unambig(output); } } @@ -2309,7 +2311,7 @@ fn is_late_bound_map( let mut constrained_by_input = ConstrainedCollector { regions: Default::default(), tcx }; for arg_ty in sig.decl.inputs { - constrained_by_input.visit_ty(arg_ty); + constrained_by_input.visit_ty_unambig(arg_ty); } let mut appears_in_output = @@ -2417,7 +2419,7 @@ fn is_late_bound_map( } impl<'v> Visitor<'v> for ConstrainedCollector<'_> { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { match ty.kind { hir::TyKind::Path( hir::QPath::Resolved(Some(_), _) | hir::QPath::TypeRelative(..), diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 51e15767b8c..348d4d708b5 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -1,10 +1,9 @@ use core::ops::ControlFlow; use rustc_errors::{Applicability, StashKey, Suggestions}; -use rustc_hir as hir; -use rustc_hir::HirId; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{self as hir, AmbigArg, HirId}; use rustc_middle::query::plumbing::CyclePlaceholder; use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::print::with_forced_trimmed_paths; @@ -451,7 +450,7 @@ fn infer_placeholder_type<'tcx>( let mut visitor = HirPlaceholderCollector::default(); let node = tcx.hir_node_by_def_id(def_id); if let Some(ty) = node.ty() { - visitor.visit_ty(ty); + visitor.visit_ty_unambig(ty); } // If we have just one span, let's try to steal a const `_` feature error. let try_steal_span = if !tcx.features().generic_arg_infer() && visitor.spans.len() == 1 @@ -525,7 +524,7 @@ pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> struct HasTait; impl<'tcx> Visitor<'tcx> for HasTait { type Result = ControlFlow<()>; - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { if let hir::TyKind::OpaqueDef(..) = t.kind { ControlFlow::Break(()) } else { @@ -533,5 +532,5 @@ pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> } } } - HasTait.visit_ty(tcx.hir().expect_item(def_id).expect_ty_alias().0).is_break() + HasTait.visit_ty_unambig(tcx.hir().expect_item(def_id).expect_ty_alias().0).is_break() } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index d1a1e36c1d5..e2b9fe0f9f7 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -55,7 +55,7 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( } else { let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType { span: tcx.def_span(def_id), - name: tcx.item_name(parent_def_id.to_def_id()), + name: tcx.item_ident(parent_def_id.to_def_id()), what: "impl", }); Ty::new_error(tcx, reported) @@ -136,7 +136,7 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local } let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType { span: tcx.def_span(def_id), - name: tcx.item_name(parent_def_id.to_def_id()), + name: tcx.item_ident(parent_def_id.to_def_id()), what: match tcx.hir_node(scope) { _ if scope == hir::CRATE_HIR_ID => "module", Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module", diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 00ba1741ed7..1dcea5d0335 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -217,7 +217,7 @@ pub(crate) struct DropImplOnWrongItem { pub(crate) enum FieldAlreadyDeclared { #[diag(hir_analysis_field_already_declared, code = E0124)] NotNested { - field_name: Symbol, + field_name: Ident, #[primary_span] #[label] span: Span, @@ -226,7 +226,7 @@ pub(crate) enum FieldAlreadyDeclared { }, #[diag(hir_analysis_field_already_declared_current_nested)] CurrentNested { - field_name: Symbol, + field_name: Ident, #[primary_span] #[label] span: Span, @@ -239,7 +239,7 @@ pub(crate) enum FieldAlreadyDeclared { }, #[diag(hir_analysis_field_already_declared_previous_nested)] PreviousNested { - field_name: Symbol, + field_name: Ident, #[primary_span] #[label] span: Span, @@ -252,7 +252,7 @@ pub(crate) enum FieldAlreadyDeclared { }, #[diag(hir_analysis_field_already_declared_both_nested)] BothNested { - field_name: Symbol, + field_name: Ident, #[primary_span] #[label] span: Span, @@ -418,7 +418,7 @@ pub(crate) struct ValueOfAssociatedStructAlreadySpecified { pub(crate) struct UnconstrainedOpaqueType { #[primary_span] pub span: Span, - pub name: Symbol, + pub name: Ident, pub what: &'static str, } @@ -620,48 +620,6 @@ pub(crate) struct TargetFeatureOnMain { } #[derive(Diagnostic)] -#[diag(hir_analysis_start_not_track_caller)] -pub(crate) struct StartTrackCaller { - #[primary_span] - pub span: Span, - #[label] - pub start: Span, -} - -#[derive(Diagnostic)] -#[diag(hir_analysis_start_not_target_feature)] -pub(crate) struct StartTargetFeature { - #[primary_span] - pub span: Span, - #[label] - pub start: Span, -} - -#[derive(Diagnostic)] -#[diag(hir_analysis_start_not_async, code = E0752)] -pub(crate) struct StartAsync { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(hir_analysis_start_function_where, code = E0647)] -pub(crate) struct StartFunctionWhere { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(hir_analysis_start_function_parameters, code = E0132)] -pub(crate) struct StartFunctionParameters { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(Diagnostic)] #[diag(hir_analysis_main_function_return_type_generic, code = E0131)] pub(crate) struct MainFunctionReturnTypeGeneric { #[primary_span] @@ -844,7 +802,7 @@ pub(crate) struct EnumDiscriminantOverflowed { #[label] pub span: Span, pub discr: String, - pub item_name: Symbol, + pub item_name: Ident, pub wrapped_discr: String, } @@ -935,7 +893,7 @@ pub(crate) enum ImplNotMarkedDefault { span: Span, #[label(hir_analysis_ok_label)] ok_label: Span, - ident: Symbol, + ident: Ident, }, #[diag(hir_analysis_impl_not_marked_default_err, code = E0520)] #[note] @@ -943,7 +901,7 @@ pub(crate) enum ImplNotMarkedDefault { #[primary_span] span: Span, cname: Symbol, - ident: Symbol, + ident: Ident, }, } @@ -1019,7 +977,7 @@ pub(crate) struct MissingTraitItemUnstable { pub some_note: bool, #[note(hir_analysis_none_note)] pub none_note: bool, - pub missing_item_name: Symbol, + pub missing_item_name: Ident, pub feature: Symbol, pub reason: String, } @@ -1291,7 +1249,7 @@ pub(crate) struct InherentNominal { pub(crate) struct DispatchFromDynZST<'a> { #[primary_span] pub span: Span, - pub name: Symbol, + pub name: Ident, pub ty: Ty<'a>, } @@ -1431,7 +1389,7 @@ pub(crate) struct TyParamFirstLocal<'tcx> { pub span: Span, #[note(hir_analysis_case_note)] pub note: (), - pub param: Symbol, + pub param: Ident, pub local_type: Ty<'tcx>, } @@ -1443,7 +1401,7 @@ pub(crate) struct TyParamFirstLocalLint<'tcx> { pub span: Span, #[note(hir_analysis_case_note)] pub note: (), - pub param: Symbol, + pub param: Ident, pub local_type: Ty<'tcx>, } @@ -1456,7 +1414,7 @@ pub(crate) struct TyParamSome { pub span: Span, #[note(hir_analysis_only_note)] pub note: (), - pub param: Symbol, + pub param: Ident, } #[derive(LintDiagnostic)] @@ -1467,7 +1425,7 @@ pub(crate) struct TyParamSomeLint { pub span: Span, #[note(hir_analysis_only_note)] pub note: (), - pub param: Symbol, + pub param: Ident, } #[derive(Diagnostic)] @@ -1575,7 +1533,7 @@ pub(crate) struct UnsupportedDelegation<'a> { pub(crate) struct MethodShouldReturnFuture { #[primary_span] pub span: Span, - pub method_name: Symbol, + pub method_name: Ident, #[note] pub trait_item_span: Option<Span>, } @@ -1627,7 +1585,7 @@ pub(crate) struct UnconstrainedGenericParameter { #[primary_span] #[label] pub span: Span, - pub param_name: Symbol, + pub param_name: Ident, pub param_def_kind: &'static str, #[note(hir_analysis_const_param_note)] pub const_param_note: bool, diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 5ae7944f6d5..67407349729 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -495,7 +495,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { .iter() .any(|constraint| constraint.ident.name == item.name) }) - .map(|item| item.name.to_ident_string()) + .map(|item| self.tcx.item_ident(item.def_id).to_string()) .collect() } else { Vec::default() diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs index 39471931461..4c8f2735b97 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs @@ -201,7 +201,7 @@ fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError use LayoutError::*; match layout_err { - Unknown(ty) => { + TooGeneric(ty) => { match abi { ExternAbi::CCmseNonSecureCall => { // prevent double reporting of this error @@ -211,7 +211,11 @@ fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError _ => bug!("invalid ABI: {abi}"), } } - SizeOverflow(..) | NormalizationFailure(..) | ReferencesError(..) | Cycle(..) => { + Unknown(..) + | SizeOverflow(..) + | NormalizationFailure(..) + | ReferencesError(..) + | Cycle(..) => { false // not our job to report these } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs index 71a5727ed6c..e59ff02642c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs @@ -4,13 +4,12 @@ use rustc_errors::struct_span_code_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; -use rustc_middle::span_bug; use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::{ self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast, }; -use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; +use rustc_span::{ErrorGuaranteed, Span}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; use rustc_trait_selection::traits::{self, hir_ty_lowering_dyn_compatibility_violations}; use rustc_type_ir::elaborate::ClauseWithSupertraitSpan; @@ -30,16 +29,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, span: Span, hir_id: hir::HirId, - hir_trait_bounds: &[hir::PolyTraitRef<'tcx>], + hir_bounds: &[hir::PolyTraitRef<'tcx>], lifetime: &hir::Lifetime, representation: DynKind, ) -> Ty<'tcx> { let tcx = self.tcx(); + let dummy_self = tcx.types.trait_object_dummy_self; - let mut bounds = Bounds::default(); + let mut user_written_bounds = Bounds::default(); let mut potential_assoc_types = Vec::new(); - let dummy_self = self.tcx().types.trait_object_dummy_self; - for trait_bound in hir_trait_bounds.iter().rev() { + for trait_bound in hir_bounds.iter() { if let hir::BoundPolarity::Maybe(_) = trait_bound.modifiers.polarity { continue; } @@ -53,92 +52,67 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir::BoundConstness::Never, hir::BoundPolarity::Positive, dummy_self, - &mut bounds, + &mut user_written_bounds, PredicateFilter::SelfOnly, ) { potential_assoc_types.extend(cur_potential_assoc_types); } } - let mut trait_bounds = vec![]; - let mut projection_bounds = vec![]; - for (pred, span) in bounds.clauses() { - let bound_pred = pred.kind(); - match bound_pred.skip_binder() { - ty::ClauseKind::Trait(trait_pred) => { - assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive); - trait_bounds.push((bound_pred.rebind(trait_pred.trait_ref), span)); - } - ty::ClauseKind::Projection(proj) => { - projection_bounds.push((bound_pred.rebind(proj), span)); - } - ty::ClauseKind::TypeOutlives(_) => { - // Do nothing, we deal with regions separately - } - ty::ClauseKind::RegionOutlives(_) - | ty::ClauseKind::ConstArgHasType(..) - | ty::ClauseKind::WellFormed(_) - | ty::ClauseKind::ConstEvaluatable(_) - | ty::ClauseKind::HostEffect(..) => { - span_bug!(span, "did not expect {pred} clause in object bounds"); - } - } - } - - // 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, trait_bounds.iter().map(|&(a, b)| (a, b))); - - let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = - expanded_traits.partition(|i| tcx.trait_is_auto(i.trait_ref().def_id())); + let (trait_bounds, mut projection_bounds) = + traits::expand_trait_aliases(tcx, user_written_bounds.clauses()); + let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = trait_bounds + .into_iter() + .partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())); + // We don't support empty trait objects. + if regular_traits.is_empty() && auto_traits.is_empty() { + let guar = + self.report_trait_object_with_no_traits_error(span, user_written_bounds.clauses()); + return Ty::new_error(tcx, guar); + } // We don't support >1 principal if regular_traits.len() > 1 { let guar = self.report_trait_object_addition_traits_error(®ular_traits); return Ty::new_error(tcx, guar); } - // We don't support empty trait objects. - if regular_traits.is_empty() && auto_traits.is_empty() { - let guar = self.report_trait_object_with_no_traits_error(span, &trait_bounds); - return Ty::new_error(tcx, guar); - } // Don't create a dyn trait if we have errors in the principal. - if let Err(guar) = trait_bounds.error_reported() { + if let Err(guar) = regular_traits.error_reported() { return Ty::new_error(tcx, guar); } // Check that there are no gross dyn-compatibility violations; // most importantly, that the supertraits don't contain `Self`, // to avoid ICEs. - for item in ®ular_traits { - let violations = - hir_ty_lowering_dyn_compatibility_violations(tcx, item.trait_ref().def_id()); - if !violations.is_empty() { - let reported = report_dyn_incompatibility( - tcx, - span, - Some(hir_id), - item.trait_ref().def_id(), - &violations, - ) - .emit(); - return Ty::new_error(tcx, reported); + for (clause, span) in user_written_bounds.clauses() { + if let Some(trait_pred) = clause.as_trait_clause() { + let violations = + hir_ty_lowering_dyn_compatibility_violations(tcx, trait_pred.def_id()); + if !violations.is_empty() { + let reported = report_dyn_incompatibility( + tcx, + span, + Some(hir_id), + trait_pred.def_id(), + &violations, + ) + .emit(); + return Ty::new_error(tcx, reported); + } } } - let mut needed_associated_types = FxIndexSet::default(); - - let principal_span = regular_traits.first().map_or(DUMMY_SP, |info| info.bottom().1); - let regular_traits_refs_spans = trait_bounds - .into_iter() - .filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id())); + let principal_trait = regular_traits.into_iter().next(); - for (base_trait_ref, original_span) in regular_traits_refs_spans { - let base_pred: ty::Predicate<'tcx> = base_trait_ref.upcast(tcx); + let mut needed_associated_types = FxIndexSet::default(); + if let Some((principal_trait, spans)) = &principal_trait { + let pred: ty::Predicate<'tcx> = (*principal_trait).upcast(tcx); for ClauseWithSupertraitSpan { pred, supertrait_span } in - traits::elaborate(tcx, [ClauseWithSupertraitSpan::new(base_pred, original_span)]) - .filter_only_self() + traits::elaborate(tcx, [ClauseWithSupertraitSpan::new( + pred, + *spans.last().unwrap(), + )]) + .filter_only_self() { debug!("observing object predicate `{pred:?}`"); @@ -179,7 +153,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // } // ``` // - // Here, the user could theoretically write `dyn MyTrait<Output = X>`, + // Here, the user could theoretically write `dyn MyTrait<MyOutput = X>`, // but actually supporting that would "expand" to an infinitely-long type // `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`. // @@ -188,12 +162,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // the discussion in #56288 for alternatives. if !references_self { // Include projections defined on supertraits. - projection_bounds.push((pred, original_span)); + projection_bounds.push((pred, supertrait_span)); } self.check_elaborated_projection_mentions_input_lifetimes( pred, - original_span, + *spans.first().unwrap(), supertrait_span, ); } @@ -202,11 +176,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - // `dyn Trait<Assoc = Foo>` desugars to (not Rust syntax) `dyn Trait where <Self as Trait>::Assoc = Foo`. - // So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated - // types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a - // corresponding `Projection` clause - for (projection_bound, span) in &projection_bounds { + // `dyn Trait<Assoc = Foo>` desugars to (not Rust syntax) `dyn Trait where + // <Self as Trait>::Assoc = Foo`. So every `Projection` clause is an + // `Assoc = Foo` bound. `needed_associated_types` contains all associated + // types that we expect to be provided by the user, so the following loop + // removes all the associated types that have a corresponding `Projection` + // clause, either from expanding trait aliases or written by the user. + for &(projection_bound, span) in &projection_bounds { let def_id = projection_bound.item_def_id(); let trait_ref = tcx.anonymize_bound_vars( projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)), @@ -216,17 +192,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.emit_node_span_lint( UNUSED_ASSOCIATED_TYPE_BOUNDS, hir_id, - *span, - crate::errors::UnusedAssociatedTypeBounds { span: *span }, + span, + crate::errors::UnusedAssociatedTypeBounds { span }, ); } } if let Err(guar) = self.check_for_required_assoc_tys( - principal_span, + principal_trait.as_ref().map_or(smallvec![], |(_, spans)| spans.clone()), needed_associated_types, potential_assoc_types, - hir_trait_bounds, + hir_bounds, ) { return Ty::new_error(tcx, guar); } @@ -236,45 +212,42 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering // the bounds let mut duplicates = FxHashSet::default(); - auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id())); - debug!(?regular_traits); + auto_traits.retain(|(trait_pred, _)| duplicates.insert(trait_pred.def_id())); + + debug!(?principal_trait); debug!(?auto_traits); // Erase the `dummy_self` (`trait_object_dummy_self`) used above. - let existential_trait_refs = regular_traits.iter().map(|i| { - i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| { + let principal_trait_ref = principal_trait.map(|(trait_pred, spans)| { + trait_pred.map_bound(|trait_pred| { + let trait_ref = trait_pred.trait_ref; + assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive); assert_eq!(trait_ref.self_ty(), dummy_self); + let span = *spans.first().unwrap(); + // Verify that `dummy_self` did not leak inside default type parameters. This // could not be done at path creation, since we need to see through trait aliases. let mut missing_type_params = vec![]; - let mut references_self = false; let generics = tcx.generics_of(trait_ref.def_id); let args: Vec<_> = trait_ref .args .iter() .enumerate() - .skip(1) // Remove `Self` for `ExistentialPredicate`. + // Skip `Self` + .skip(1) .map(|(index, arg)| { - if arg == dummy_self.into() { + if arg.walk().any(|arg| arg == dummy_self.into()) { let param = &generics.own_params[index]; missing_type_params.push(param.name); Ty::new_misc_error(tcx).into() - } else if arg.walk().any(|arg| arg == dummy_self.into()) { - references_self = true; - let guar = self.dcx().span_delayed_bug( - span, - "trait object trait bounds reference `Self`", - ); - replace_dummy_self_with_error(tcx, arg, guar) } else { arg } }) .collect(); - let span = i.bottom().1; - let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| { + let empty_generic_args = hir_bounds.iter().any(|hir_bound| { hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id) && hir_bound.span.contains(span) }); @@ -285,26 +258,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { empty_generic_args, ); - if references_self { - let def_id = i.bottom().0.def_id(); - struct_span_code_err!( - self.dcx(), - i.bottom().1, - E0038, - "the {} `{}` cannot be made into an object", - tcx.def_descr(def_id), - tcx.item_name(def_id), - ) - .with_note( - rustc_middle::traits::DynCompatibilityViolation::SupertraitSelf( - smallvec![], - ) - .error_msg(), - ) - .emit(); - } - - ty::ExistentialTraitRef::new(tcx, trait_ref.def_id, args) + ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new( + tcx, + trait_ref.def_id, + args, + )) }) }); @@ -327,21 +285,24 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { b.projection_term = replace_dummy_self_with_error(tcx, b.projection_term, guar); } - ty::ExistentialProjection::erase_self_ty(tcx, b) + ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty( + tcx, b, + )) }) }); - let regular_trait_predicates = existential_trait_refs - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)); - let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| { - ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id())) + let auto_trait_predicates = auto_traits.into_iter().map(|(trait_pred, _)| { + assert_eq!(trait_pred.polarity(), ty::PredicatePolarity::Positive); + assert_eq!(trait_pred.self_ty().skip_binder(), dummy_self); + + ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_pred.def_id())) }); + // N.b. principal, projections, auto traits // FIXME: This is actually wrong with multiple principals in regards to symbol mangling - let mut v = regular_trait_predicates - .chain( - existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)), - ) + let mut v = principal_trait_ref + .into_iter() + .chain(existential_projections) .chain(auto_trait_predicates) .collect::<SmallVec<[_; 8]>>(); v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 7a3d921f00e..36b214b6ae7 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -9,9 +9,10 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_middle::bug; +use rustc_middle::ty::fast_reject::{TreatParams, simplify_type}; use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; use rustc_middle::ty::{ - self, AdtDef, Binder, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeVisitableExt, + self, AdtDef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, suggest_constraining_type_param, }; use rustc_session::parse::feature_err; @@ -19,8 +20,9 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, kw, sym}; use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility; use rustc_trait_selection::traits::{ - FulfillmentError, TraitAliasExpansionInfo, dyn_compatibility_violations_for_assoc_item, + FulfillmentError, dyn_compatibility_violations_for_assoc_item, }; +use smallvec::SmallVec; use crate::errors::{ self, AssocItemConstraintsNotAllowedHere, ManualImplementation, MissingTypeParams, @@ -720,7 +722,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// emit a generic note suggesting using a `where` clause to constraint instead. pub(crate) fn check_for_required_assoc_tys( &self, - principal_span: Span, + spans: SmallVec<[Span; 1]>, missing_assoc_types: FxIndexSet<(DefId, ty::PolyTraitRef<'tcx>)>, potential_assoc_types: Vec<usize>, trait_bounds: &[hir::PolyTraitRef<'_>], @@ -729,6 +731,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return Ok(()); } + let principal_span = *spans.first().unwrap(); + let tcx = self.tcx(); // FIXME: This logic needs some more care w.r.t handling of conflicts let missing_assoc_types: Vec<_> = missing_assoc_types @@ -995,12 +999,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { )), .. }) = node - && let Some(adt_def) = qself_ty.ty_adt_def() - && let [inherent_impl] = tcx.inherent_impls(adt_def.did()) - && let name = format!("{ident2}_{ident3}") - && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx - .associated_items(inherent_impl) - .filter_by_name_unhygienic(Symbol::intern(&name)) + && let Some(inherent_impls) = qself_ty + .ty_adt_def() + .map(|adt_def| tcx.inherent_impls(adt_def.did())) + .or_else(|| { + simplify_type(tcx, qself_ty, TreatParams::InstantiateWithInfer) + .map(|simple_ty| tcx.incoherent_impls(simple_ty)) + }) + && let name = Symbol::intern(&format!("{ident2}_{ident3}")) + && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = inherent_impls + .iter() + .flat_map(|inherent_impl| { + tcx.associated_items(inherent_impl).filter_by_name_unhygienic(name) + }) .next() { Err(struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type") @@ -1124,29 +1135,36 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub fn report_trait_object_addition_traits_error( &self, - regular_traits: &Vec<TraitAliasExpansionInfo<'_>>, + regular_traits: &Vec<(ty::PolyTraitPredicate<'tcx>, SmallVec<[Span; 1]>)>, ) -> ErrorGuaranteed { - let first_trait = ®ular_traits[0]; - let additional_trait = ®ular_traits[1]; + // we use the last span to point at the traits themselves, + // and all other preceding spans are trait alias expansions. + let (&first_span, first_alias_spans) = regular_traits[0].1.split_last().unwrap(); + let (&second_span, second_alias_spans) = regular_traits[1].1.split_last().unwrap(); let mut err = struct_span_code_err!( self.dcx(), - additional_trait.bottom().1, + *regular_traits[1].1.first().unwrap(), E0225, "only auto traits can be used as additional traits in a trait object" ); - additional_trait.label_with_exp_info( - &mut err, - "additional non-auto trait", - "additional use", - ); - first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use"); + err.span_label(first_span, "first non-auto trait"); + for &alias_span in first_alias_spans { + err.span_label(alias_span, "first non-auto trait comes from this alias"); + } + err.span_label(second_span, "additional non-auto trait"); + for &alias_span in second_alias_spans { + err.span_label(alias_span, "second non-auto trait comes from this alias"); + } err.help(format!( "consider creating a new trait with all of these as supertraits and using that \ trait here instead: `trait NewTrait: {} {{}}`", regular_traits .iter() // FIXME: This should `print_sugared`, but also needs to integrate projection bounds... - .map(|t| t.trait_ref().print_only_trait_path().to_string()) + .map(|(pred, _)| pred + .map_bound(|pred| pred.trait_ref) + .print_only_trait_path() + .to_string()) .collect::<Vec<_>>() .join(" + "), )); @@ -1161,14 +1179,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub fn report_trait_object_with_no_traits_error( &self, span: Span, - trait_bounds: &Vec<(Binder<'tcx, TraitRef<'tcx>>, Span)>, + user_written_clauses: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>, ) -> ErrorGuaranteed { let tcx = self.tcx(); - let trait_alias_span = trait_bounds - .iter() - .map(|&(trait_ref, _)| trait_ref.def_id()) - .find(|&trait_ref| tcx.is_trait_alias(trait_ref)) - .map(|trait_ref| tcx.def_span(trait_ref)); + let trait_alias_span = user_written_clauses + .into_iter() + .filter_map(|(clause, _)| clause.as_trait_clause()) + .find(|trait_ref| tcx.is_trait_alias(trait_ref.def_id())) + .map(|trait_ref| tcx.def_span(trait_ref.def_id())); self.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span }) } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index df00948dd21..fe3dcb35639 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -1,10 +1,9 @@ use rustc_ast::ast::ParamKindOrd; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, struct_span_code_err}; -use rustc_hir as hir; -use rustc_hir::GenericArg; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, GenericArg}; use rustc_middle::ty::{ self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, }; @@ -41,17 +40,6 @@ fn generic_arg_mismatch_err( 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 `_`"); - tcx.disabled_nightly_features( - &mut err, - param.def_id.as_local().map(|local| tcx.local_def_id_to_hir_id(local)), - [(String::new(), sym::generic_arg_infer)], - ); - } - } - let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diag<'_>| { let suggestions = vec![ (arg.span().shrink_to_lo(), String::from("{ ")), @@ -270,6 +258,21 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( GenericParamDefKind::Const { .. }, _, ) => { + if let GenericParamDefKind::Const { .. } = param.kind + && let GenericArg::Infer(inf) = arg + && !tcx.features().generic_arg_infer() + { + rustc_session::parse::feature_err( + tcx.sess, + sym::generic_arg_infer, + inf.span, + "const arguments cannot yet be inferred with `_`", + ) + .emit(); + } + + // We lower to an infer even when the feature gate is not enabled + // as it is useful for diagnostics to be able to see a `ConstKind::Infer` args.push(ctx.provided_kind(&args, param, arg)); args_iter.next(); params.next(); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index a1f2b8c7594..539c5f6a20a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -23,9 +23,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) -> Option<ErrorGuaranteed> { let tcx = self.tcx(); - let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = + let poly_trait_ref = if let hir::TyKind::TraitObject([poly_trait_ref, ..], tagged_ptr) = self_ty.kind - else { + && let TraitObjectSyntax::None = tagged_ptr.tag() + { + poly_trait_ref + } else { return None; }; @@ -294,7 +297,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let (dyn_str, paren_dyn_str) = if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; - let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind { + let sugg = if let hir::TyKind::TraitObject([_, _, ..], _) = self_ty.kind { // There are more than one trait bound, we need surrounding parentheses. vec![ (self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()), diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index cb90fff782f..61d5869c19f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -517,14 +517,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lowerer.lower_lifetime(lt, RegionInferReason::Param(param)).into() } (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => { - handle_ty_args(has_default, ty) + // We handle the other parts of `Ty` in the match arm below + handle_ty_args(has_default, ty.as_unambig_ty()) } (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Infer(inf)) => { handle_ty_args(has_default, &inf.to_ty()) } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.lowerer.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into() - } + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self + .lowerer + // Ambig portions of `ConstArg` are handled in the match arm below + .lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id)) + .into(), (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { self.lowerer.ct_infer(Some(param), inf.span).into() } @@ -2115,7 +2118,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { format!("Const::lower_const_arg: invalid qpath {qpath:?}"), ), hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon), - hir::ConstArgKind::Infer(span) => self.ct_infer(None, span), + hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span), } } @@ -2311,7 +2314,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.late_bound_vars(hir_ty.hir_id), ), ), - hir::TyKind::TraitObject(bounds, lifetime, repr) => { + hir::TyKind::TraitObject(bounds, tagged_ptr) => { + let lifetime = tagged_ptr.pointer(); + let repr = tagged_ptr.tag(); + if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { // Don't continue with type analysis if the `dyn` keyword is missing // It generates confusing errors, especially if the user meant to use another @@ -2420,7 +2426,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ty::new_array_with_const_len(tcx, self.lower_ty(ty), length) } hir::TyKind::Typeof(e) => tcx.type_of(e.def_id).instantiate_identity(), - hir::TyKind::Infer => { + hir::TyKind::Infer(()) => { // Infer also appears as the type of arguments or return // values in an ExprKind::Closure, or as // the type of local variables. Both of these cases are @@ -2553,7 +2559,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub fn lower_arg_ty(&self, ty: &hir::Ty<'tcx>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> { match ty.kind { - hir::TyKind::Infer if let Some(expected_ty) = expected_ty => { + hir::TyKind::Infer(()) if let Some(expected_ty) = expected_ty => { self.record_ty(ty.hir_id, expected_ty, ty.span); expected_ty } diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index c0fb94d2cb2..f5abcd23440 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -1,6 +1,5 @@ -use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ForeignItem, ForeignItemKind}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; +use rustc_hir::{self as hir, AmbigArg, ForeignItem, ForeignItemKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ObligationCause, WellFormedLoc}; use rustc_middle::bug; @@ -68,11 +67,13 @@ fn diagnostic_hir_wf_check<'tcx>( } impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let tcx_ty = self.icx.lower_ty(ty); + // We don't handle infer vars but we wouldn't handle them anyway as we're creating a + // fresh `InferCtxt` in this function. + let tcx_ty = self.icx.lower_ty(ty.as_unambig_ty()); // This visitor can walk into binders, resulting in the `tcx_ty` to // potentially reference escaping bound variables. We simply erase // those here. @@ -149,7 +150,11 @@ fn diagnostic_hir_wf_check<'tcx>( .iter() .flat_map(|seg| seg.args().args) .filter_map(|arg| { - if let hir::GenericArg::Type(ty) = arg { Some(*ty) } else { None } + if let hir::GenericArg::Type(ty) = arg { + Some(ty.as_unambig_ty()) + } else { + None + } }) .chain([impl_.self_ty]) .collect(), @@ -196,7 +201,7 @@ fn diagnostic_hir_wf_check<'tcx>( } }; for ty in tys { - visitor.visit_ty(ty); + visitor.visit_ty_unambig(ty); } visitor.cause } diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 42034736ad6..fd5a7089b4c 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -152,7 +152,7 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained( { let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter { span: tcx.def_span(param.def_id), - param_name: param.name, + param_name: tcx.item_ident(param.def_id), param_def_kind: tcx.def_descr(param.def_id), const_param_note: false, const_param_note2: false, @@ -223,7 +223,7 @@ pub(crate) fn enforce_impl_non_lifetime_params_are_constrained( let const_param_note = matches!(param.kind, ty::GenericParamDefKind::Const { .. }); let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter { span: tcx.def_span(param.def_id), - param_name: param.name, + param_name: tcx.item_ident(param.def_id), param_def_kind: tcx.def_descr(param.def_id), const_param_note, const_param_note2: const_param_note, diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index a42a168234f..bc7d4365eee 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -63,6 +63,7 @@ This API is completely unstable and subject to change. #![doc(rust_logo)] #![feature(assert_matches)] #![feature(coroutines)] +#![feature(debug_closure_helpers)] #![feature(if_let_guard)] #![feature(iter_from_coroutine)] #![feature(iter_intersperse)] diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 3ff6acd79fc..afc0f627f69 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -402,7 +402,8 @@ impl<'a> State<'a> { self.print_bounds("impl", bounds); } hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false), - hir::TyKind::TraitObject(bounds, lifetime, syntax) => { + hir::TyKind::TraitObject(bounds, lifetime) => { + let syntax = lifetime.tag(); match syntax { ast::TraitObjectSyntax::Dyn => self.word_nbsp("dyn"), ast::TraitObjectSyntax::DynStar => self.word_nbsp("dyn*"), @@ -421,7 +422,7 @@ impl<'a> State<'a> { if !lifetime.is_elided() { self.nbsp(); self.word_space("+"); - self.print_lifetime(lifetime); + self.print_lifetime(lifetime.pointer()); } } hir::TyKind::Array(ty, ref length) => { @@ -441,7 +442,7 @@ impl<'a> State<'a> { self.word("/*ERROR*/"); self.pclose(); } - hir::TyKind::Infer | hir::TyKind::InferDelegation(..) => { + hir::TyKind::Infer(()) | hir::TyKind::InferDelegation(..) => { self.word("_"); } hir::TyKind::Pat(ty, pat) => { @@ -1799,8 +1800,8 @@ impl<'a> State<'a> { match generic_arg { GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt), GenericArg::Lifetime(_) => {} - GenericArg::Type(ty) => s.print_type(ty), - GenericArg::Const(ct) => s.print_const_arg(ct), + GenericArg::Type(ty) => s.print_type(ty.as_unambig_ty()), + GenericArg::Const(ct) => s.print_const_arg(ct.as_unambig_ct()), GenericArg::Infer(_inf) => s.word("_"), } }); @@ -2150,7 +2151,7 @@ impl<'a> State<'a> { s.ann.nested(s, Nested::BodyParamPat(body_id, i)); i += 1; - if let hir::TyKind::Infer = ty.kind { + if let hir::TyKind::Infer(()) = ty.kind { // Print nothing. } else { s.word(":"); diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 7b07e0ee939..5d00ecbe918 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -45,6 +45,7 @@ use rustc_session::lint; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::{DUMMY_SP, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_type_ir::elaborate; use tracing::{debug, instrument}; use super::FnCtxt; @@ -923,7 +924,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { let src_auto: FxHashSet<_> = src_tty .auto_traits() .chain( - tcx.supertrait_def_ids(src_principal.def_id()) + elaborate::supertrait_def_ids(tcx, src_principal.def_id()) .filter(|def_id| tcx.trait_is_auto(*def_id)), ) .collect(); diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 6945dbc3216..47abba1cc29 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -461,9 +461,17 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // to the target type), since that should be the least // confusing. let Some(InferOk { value: ty, mut obligations }) = found else { - let err = first_error.expect("coerce_borrowed_pointer had no error"); - debug!("coerce_borrowed_pointer: failed with err = {:?}", err); - return Err(err); + if let Some(first_error) = first_error { + debug!("coerce_borrowed_pointer: failed with err = {:?}", first_error); + return Err(first_error); + } else { + // This may happen in the new trait solver since autoderef requires + // the pointee to be structurally normalizable, or else it'll just bail. + // So when we have a type like `&<not well formed>`, then we get no + // autoderef steps (even though there should be at least one). That means + // we get no type mismatches, since the loop above just exits early. + return Err(TypeError::Mismatch); + } }; if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 { @@ -1124,7 +1132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.next_trait_solver() && let ty::Alias(..) = ty.kind() { - ocx.structurally_normalize(&cause, self.param_env, ty) + ocx.structurally_normalize_ty(&cause, self.param_env, ty) } else { Ok(ty) } diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 367e7c6de95..bc076670585 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -851,32 +851,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = pat.kind // Look for the type corresponding to the argument pattern we have in the argument list. - && let Some(ty_sugg) = fn_decl + && let Some(ty_ref) = fn_decl .inputs .iter() - .filter_map(|ty| { - if ty.span == *ty_span - && let hir::TyKind::Ref(lt, x) = ty.kind - { - // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty` - Some(( - x.ty.span.shrink_to_lo(), - format!( - "{}mut ", - if lt.ident.span.lo() == lt.ident.span.hi() { "" } else { " " } - ), - )) - } else { - None - } + .filter_map(|ty| match ty.kind { + hir::TyKind::Ref(lt, mut_ty) if ty.span == *ty_span => Some((lt, mut_ty)), + _ => None, }) .next() { - let sugg = vec![ - ty_sugg, + let mut sugg = if ty_ref.1.mutbl.is_mut() { + // Leave `&'name mut Ty` and `&mut Ty` as they are (#136028). + vec![] + } else { + // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty` + vec![( + ty_ref.1.ty.span.shrink_to_lo(), + format!( + "{}mut ", + if ty_ref.0.ident.span.lo() == ty_ref.0.ident.span.hi() { "" } else { " " }, + ), + )] + }; + sugg.extend([ (pat.span.until(ident.span), String::new()), (lhs.span.shrink_to_lo(), "*".to_string()), - ]; + ]); // We suggest changing the argument from `mut ident: &Ty` to `ident: &'_ mut Ty` and the // assignment from `ident = val;` to `*ident = val;`. err.multipart_suggestion_verbose( diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 01fed72d5a2..1c828591bcb 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2349,6 +2349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.report_missing_fields( adt_ty, path_span, + expr.span, remaining_fields, variant, hir_fields, @@ -2386,6 +2387,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, adt_ty: Ty<'tcx>, span: Span, + full_span: Span, remaining_fields: UnordMap<Ident, (FieldIdx, &ty::FieldDef)>, variant: &'tcx ty::VariantDef, hir_fields: &'tcx [hir::ExprField<'tcx>], @@ -2425,6 +2427,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}")); + if remaining_fields.items().all(|(_, (_, field))| field.value.is_some()) + && self.tcx.sess.is_nightly_build() + { + let msg = format!( + "all remaining fields have default values, {you_can} use those values with `..`", + you_can = if self.tcx.features().default_field_values() { + "you can" + } else { + "if you added `#![feature(default_field_values)]` to your crate you could" + }, + ); + if let Some(hir_field) = hir_fields.last() { + err.span_suggestion_verbose( + hir_field.span.shrink_to_hi(), + msg, + ", ..".to_string(), + Applicability::MachineApplicable, + ); + } else if hir_fields.is_empty() { + err.span_suggestion_verbose( + span.shrink_to_hi().with_hi(full_span.hi()), + msg, + " { .. }".to_string(), + Applicability::MachineApplicable, + ); + } + } + if let Some(hir_field) = hir_fields.last() { self.suggest_fru_from_range_and_emit(hir_field, variant, args, err); } else { @@ -3307,10 +3337,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .map(|mut field_path| { field_path.pop(); - field_path - .iter() - .map(|id| format!("{}.", id.name.to_ident_string())) - .collect::<String>() + field_path.iter().map(|id| format!("{}.", id)).collect::<String>() }) .collect::<Vec<_>>(); candidate_fields.sort(); diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index c128485d93e..1f3f03b9929 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -10,7 +10,7 @@ use rustc_hir as hir; use rustc_hir::HirId; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{InferKind, Visitor}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; @@ -641,16 +641,21 @@ impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> { impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> { type Result = ControlFlow<errors::SuggestAnnotation>; - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_infer( + &mut self, + inf_id: HirId, + inf_span: Span, + _kind: InferKind<'tcx>, + ) -> Self::Result { // Try to replace `_` with `()`. - if let hir::TyKind::Infer = hir_ty.kind - && let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(hir_ty.hir_id) + if let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(inf_id) && let Some(vid) = self.fcx.root_vid(ty) && self.reachable_vids.contains(&vid) { - return ControlFlow::Break(errors::SuggestAnnotation::Unit(hir_ty.span)); + return ControlFlow::Break(errors::SuggestAnnotation::Unit(inf_span)); } - hir::intravisit::walk_ty(self, hir_ty) + + ControlFlow::Continue(()) } fn visit_qpath( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index be6d9570e35..8e647ad3c6a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -6,9 +6,9 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; use rustc_hir::lang_items::LangItem; -use rustc_hir::{self as hir, ExprKind, GenericArg, HirId, Node, QPath, intravisit}; +use rustc_hir::{self as hir, AmbigArg, ExprKind, GenericArg, HirId, Node, QPath, intravisit}; use rustc_hir_analysis::hir_ty_lowering::errors::GenericsArgsErrExtend; use rustc_hir_analysis::hir_ty_lowering::generics::{ check_generic_arg_count_for_call, lower_generic_args, @@ -470,7 +470,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } impl<'tcx> intravisit::Visitor<'tcx> for CollectClauses<'_, 'tcx> { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { if let Some(clauses) = self.fcx.trait_ascriptions.borrow().get(&ty.hir_id.local_id) { self.clauses.extend(clauses.iter().cloned()); @@ -480,7 +480,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let mut clauses = CollectClauses { clauses: vec![], fcx: self }; - clauses.visit_ty(hir_ty); + clauses.visit_ty_unambig(hir_ty); self.tcx.mk_clauses(&clauses.clauses) } @@ -1272,14 +1272,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lower_lifetime(lt, RegionInferReason::Param(param)) .into(), (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.fcx.lower_ty(ty).raw.into() - } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.fcx.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into() + // We handle the ambig portions of `Ty` in match arm below + self.fcx.lower_ty(ty.as_unambig_ty()).raw.into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { - self.fcx.ty_infer(Some(param), inf.span).into() + self.fcx.lower_ty(&inf.to_ty()).raw.into() } + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self + .fcx + // Ambiguous parts of `ConstArg` are handled in the match arms below + .lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id)) + .into(), (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { self.fcx.ct_infer(Some(param), inf.span).into() } @@ -1433,7 +1436,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // in a reentrant borrow, causing an ICE. let result = self .at(&self.misc(sp), self.param_env) - .structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut()); + .structurally_normalize_ty(ty, &mut **self.fulfillment_cx.borrow_mut()); match result { Ok(normalized_ty) => normalized_ty, Err(errors) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 46eed2db236..8e78fb3e219 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1210,7 +1210,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // - foo((), "current", 42u32, "next") // + foo((), 42u32) { - prev_extra_idx.map_or(true, |prev_extra_idx| { + prev_extra_idx.is_none_or(|prev_extra_idx| { prev_extra_idx + 1 == arg_idx.index() }) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 53e055fdeef..928010c03c2 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -185,6 +185,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty: Ty<'tcx>, can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool, ) -> bool { + if lhs_expr.span.in_derive_expansion() || rhs_expr.span.in_derive_expansion() { + return false; + } let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_ty) else { return false; }; @@ -1143,7 +1146,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.may_coerce(found, ty) } hir::FnRetTy::DefaultReturn(_) if in_closure => { - self.ret_coercion.as_ref().map_or(false, |ret| { + self.ret_coercion.as_ref().is_some_and(|ret| { let ret_ty = ret.borrow().expected_ty(); self.may_coerce(found, ret_ty) }) @@ -1784,14 +1787,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let results = self.typeck_results.borrow(); // First, look for a `Clone::clone` call if segment.ident.name == sym::clone - && results.type_dependent_def_id(expr.hir_id).map_or( - false, - |did| { + && results.type_dependent_def_id(expr.hir_id).is_some_and(|did| { let assoc_item = self.tcx.associated_item(did); assoc_item.container == ty::AssocItemContainer::Trait && assoc_item.container_id(self.tcx) == clone_trait_did - }, - ) + }) // If that clone call hasn't already dereferenced the self type (i.e. don't give this // diagnostic in cases where we have `(&&T).clone()` and we expect `T`). && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..))) diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs index f4929aae599..54e9e699353 100644 --- a/compiler/rustc_hir_typeck/src/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs @@ -122,7 +122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("generic size {size}") } } - Err(LayoutError::Unknown(bad)) => { + Err(LayoutError::TooGeneric(bad)) => { if *bad == ty { "this type does not have a fixed size".to_owned() } else { diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 9cd9ca040ce..07e013e4afa 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -251,7 +251,8 @@ fn typeck_with_inspect<'tcx>( fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Option<Ty<'tcx>> { let tcx = fcx.tcx; let def_id = fcx.body_id; - let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = node.ty() { + let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer(()), span, .. }) = node.ty() + { if let Some(item) = tcx.opt_associated_item(def_id.into()) && let ty::AssocKind::Const = item.kind && let ty::AssocItemContainer::Impl = item.container @@ -452,7 +453,7 @@ fn report_unexpected_variant_res( ); let fields = fields .iter() - .map(|field| format!("{}: _", field.name.to_ident_string())) + .map(|field| format!("{}: _", field.ident(tcx))) .collect::<Vec<_>>() .join(", "); let sugg = format!(" {{ {} }}", fields); diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index f549ced9dc3..880ee83c80a 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -425,14 +425,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { .lower_lifetime(lt, RegionInferReason::Param(param)) .into(), (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.cfcx.lower_ty(ty).raw.into() - } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.cfcx.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into() + // We handle the ambig portions of `Ty` in the match arms below + self.cfcx.lower_ty(ty.as_unambig_ty()).raw.into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { - self.cfcx.ty_infer(Some(param), inf.span).into() + self.cfcx.lower_ty(&inf.to_ty()).raw.into() } + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self + .cfcx + // We handle the ambig portions of `ConstArg` in the match arms below + .lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id)) + .into(), (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { self.cfcx.ct_infer(Some(param), inf.span).into() } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 116765325a9..b9d1f93bfb8 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -1,6 +1,5 @@ use std::cell::{Cell, RefCell}; use std::cmp::max; -use std::iter; use std::ops::Deref; use rustc_data_structures::fx::FxHashSet; @@ -1009,11 +1008,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { if self.tcx.is_trait_alias(trait_def_id) { // For trait aliases, recursively assume all explicitly named traits are relevant - for expansion in traits::expand_trait_aliases( - self.tcx, - iter::once((ty::Binder::dummy(trait_ref), self.span)), - ) { - let bound_trait_ref = expansion.trait_ref(); + for (bound_trait_pred, _) in + traits::expand_trait_aliases(self.tcx, [(trait_ref.upcast(self.tcx), self.span)]).0 + { + assert_eq!(bound_trait_pred.polarity(), ty::PredicatePolarity::Positive); + let bound_trait_ref = bound_trait_pred.map_bound(|pred| pred.trait_ref); for item in self.impl_or_trait_item(bound_trait_ref.def_id()) { if !self.has_applicable_self(&item) { self.record_static_candidate(CandidateSource::Trait( diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index b3d87ef4ad2..5d4e67d1a0e 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2714,7 +2714,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|field_path| { field_path .iter() - .map(|id| id.name.to_ident_string()) + .map(|id| id.to_string()) .collect::<Vec<String>>() .join(".") }) @@ -3761,18 +3761,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::TraitFn::Required([ident, ..]) => { ident.name == kw::SelfLower } - hir::TraitFn::Provided(body_id) => { - self.tcx.hir().body(*body_id).params.first().map_or( - false, - |param| { - matches!( - param.pat.kind, - hir::PatKind::Binding(_, _, ident, _) - if ident.name == kw::SelfLower - ) - }, - ) - } + hir::TraitFn::Provided(body_id) => self + .tcx + .hir() + .body(*body_id) + .params + .first() + .is_some_and(|param| { + matches!( + param.pat.kind, + hir::PatKind::Binding(_, _, ident, _) + if ident.name == kw::SelfLower + ) + }), _ => false, }; diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 683cacdff7d..1bf5b19d68d 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -6,9 +6,8 @@ use std::mem; use rustc_data_structures::unord::ExtendUnord; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; -use rustc_hir::HirId; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, InferKind, Visitor}; +use rustc_hir::{self as hir, AmbigArg, HirId}; use rustc_middle::span_bug; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; @@ -354,7 +353,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { self.write_ty_to_typeck_results(l.hir_id, var_ty); } - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx, AmbigArg>) { intravisit::walk_ty(self, hir_ty); // If there are type checking errors, Type privacy pass will stop, // so we may not get the type from hid_id, see #104513 @@ -364,12 +363,20 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { } } - fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { - intravisit::walk_inf(self, inf); - // Ignore cases where the inference is a const. - if let Some(ty) = self.fcx.node_ty_opt(inf.hir_id) { - let ty = self.resolve(ty, &inf.span); - self.write_ty_to_typeck_results(inf.hir_id, ty); + fn visit_infer( + &mut self, + inf_id: HirId, + inf_span: Span, + _kind: InferKind<'cx>, + ) -> Self::Result { + self.visit_id(inf_id); + + // We don't currently write inference results of const infer vars to + // the typeck results as there is not yet any part of the compiler that + // needs this information. + if let Some(ty) = self.fcx.node_ty_opt(inf_id) { + let ty = self.resolve(ty, &inf_span); + self.write_ty_to_typeck_results(inf_id, ty); } } } diff --git a/compiler/rustc_index/src/interval.rs b/compiler/rustc_index/src/interval.rs index 34f541a8cc6..a3d87f59567 100644 --- a/compiler/rustc_index/src/interval.rs +++ b/compiler/rustc_index/src/interval.rs @@ -258,7 +258,7 @@ impl<I: Idx> IntervalSet<I> { } current = Some(*end); } - current.map_or(true, |x| x < self.domain as u32) + current.is_none_or(|x| x < self.domain as u32) } } diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 5fc9b679c8a..69ab0e69e21 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -114,7 +114,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { ) } - fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>> + Copy, U>( + fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>>, U>( &self, value: ty::Binder<'tcx, T>, f: impl FnOnce(T) -> U, diff --git a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs index 7908733e734..061f7e6c22a 100644 --- a/compiler/rustc_infer/src/infer/relate/higher_ranked.rs +++ b/compiler/rustc_infer/src/infer/relate/higher_ranked.rs @@ -24,9 +24,9 @@ impl<'tcx> InferCtxt<'tcx> { #[instrument(level = "debug", skip(self), ret)] pub fn enter_forall_and_leak_universe<T>(&self, binder: ty::Binder<'tcx, T>) -> T where - T: TypeFoldable<TyCtxt<'tcx>> + Copy, + T: TypeFoldable<TyCtxt<'tcx>>, { - if let Some(inner) = binder.no_bound_vars() { + if let Some(inner) = binder.clone().no_bound_vars() { return inner; } @@ -71,7 +71,7 @@ impl<'tcx> InferCtxt<'tcx> { #[instrument(level = "debug", skip(self, f))] pub fn enter_forall<T, U>(&self, forall: ty::Binder<'tcx, T>, f: impl FnOnce(T) -> U) -> U where - T: TypeFoldable<TyCtxt<'tcx>> + Copy, + T: TypeFoldable<TyCtxt<'tcx>>, { // FIXME: currently we do nothing to prevent placeholders with the new universe being // used after exiting `f`. For example region subtyping can result in outlives constraints diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 1456255ea14..2113345eda3 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -1,6 +1,5 @@ use std::path::PathBuf; use std::result; -use std::sync::Arc; use rustc_ast::{LitKind, MetaItemKind, token}; use rustc_codegen_ssa::traits::CodegenBackend; @@ -309,6 +308,11 @@ pub struct Config { pub output_dir: Option<PathBuf>, pub output_file: Option<OutFileName>, pub ice_file: Option<PathBuf>, + /// Load files from sources other than the file system. + /// + /// Has no uses within this repository, but may be used in the future by + /// bjorn3 for "hooking rust-analyzer's VFS into rustc at some point for + /// running rustc without having to save". (See #102759.) pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>, /// The list of fluent resources, used for lints declared with /// [`Diagnostic`](rustc_errors::Diagnostic) and [`LintDiagnostic`](rustc_errors::LintDiagnostic). @@ -337,6 +341,11 @@ pub struct Config { pub override_queries: Option<fn(&Session, &mut Providers)>, /// This is a callback from the driver that is called to create a codegen backend. + /// + /// Has no uses within this repository, but is used by bjorn3 for "the + /// hotswapping branch of cg_clif" for "setting the codegen backend from a + /// custom driver where the custom codegen backend has arbitrary data." + /// (See #102759.) pub make_codegen_backend: Option<Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>>, @@ -346,8 +355,7 @@ pub struct Config { /// The inner atomic value is set to true when a feature marked as `internal` is /// enabled. Makes it so that "please report a bug" is hidden, as ICEs with /// internal features are wontfix, and they are usually the cause of the ICEs. - /// None signifies that this is not tracked. - pub using_internal_features: Arc<std::sync::atomic::AtomicBool>, + pub using_internal_features: &'static std::sync::atomic::AtomicBool, /// All commandline args used to invoke the compiler, with @file args fully expanded. /// This will only be used within debug info, e.g. in the pdb file on windows @@ -383,7 +391,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se crate::callbacks::setup_callbacks(); let sysroot = filesearch::materialize_sysroot(config.opts.maybe_sysroot.clone()); - let target = config::build_target_config(&early_dcx, &config.opts, &sysroot); + let target = config::build_target_config(&early_dcx, &config.opts.target_triple, &sysroot); let file_loader = config.file_loader.unwrap_or_else(|| Box::new(RealFileLoader)); let path_mapping = config.opts.file_path_mapping(); let hash_kind = config.opts.unstable_opts.src_hash_algorithm(&target); @@ -533,7 +541,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se pub fn try_print_query_stack( dcx: DiagCtxtHandle<'_>, - num_frames: Option<usize>, + limit_frames: Option<usize>, file: Option<std::fs::File>, ) { eprintln!("query stack during panic:"); @@ -541,13 +549,13 @@ pub fn try_print_query_stack( // Be careful relying on global state here: this code is called from // a panic hook, which means that the global `DiagCtxt` may be in a weird // state if it was responsible for triggering the panic. - let i = ty::tls::with_context_opt(|icx| { + let all_frames = ty::tls::with_context_opt(|icx| { if let Some(icx) = icx { ty::print::with_no_queries!(print_query_stack( QueryCtxt::new(icx.tcx), icx.query, dcx, - num_frames, + limit_frames, file, )) } else { @@ -555,9 +563,14 @@ pub fn try_print_query_stack( } }); - if num_frames == None || num_frames >= Some(i) { - eprintln!("end of query stack"); + if let Some(limit_frames) = limit_frames + && all_frames > limit_frames + { + eprintln!( + "... and {} other queries... use `env RUST_BACKTRACE=1` to see the full query stack", + all_frames - limit_frames + ); } else { - eprintln!("we're just showing a limited slice of the query stack"); + eprintln!("end of query stack"); } } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 07c4b898721..74d02ac2227 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -2,7 +2,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::num::NonZero; use std::path::{Path, PathBuf}; -use std::sync::Arc; +use std::sync::atomic::AtomicBool; use rustc_data_structures::profiling::TimePassesFormat; use rustc_errors::emitter::HumanReadableErrorType; @@ -42,7 +42,8 @@ where let matches = optgroups().parse(args).unwrap(); let sessopts = build_session_options(&mut early_dcx, &matches); let sysroot = filesearch::materialize_sysroot(sessopts.maybe_sysroot.clone()); - let target = rustc_session::config::build_target_config(&early_dcx, &sessopts, &sysroot); + let target = + rustc_session::config::build_target_config(&early_dcx, &sessopts.target_triple, &sysroot); let hash_kind = sessopts.unstable_opts.src_hash_algorithm(&target); let checksum_hash_kind = sessopts.unstable_opts.checksum_hash_algorithm(); let sm_inputs = Some(SourceMapInputs { @@ -61,6 +62,8 @@ where temps_dir, }; + static USING_INTERNAL_FEATURES: AtomicBool = AtomicBool::new(false); + let sess = build_session( early_dcx, sessopts, @@ -73,7 +76,7 @@ where sysroot, "", None, - Arc::default(), + &USING_INTERNAL_FEATURES, Default::default(), ); let cfg = parse_cfg(sess.dcx(), matches.opt_strs("cfg")); diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 7522e21d0ef..55c6a122d35 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -69,15 +69,18 @@ lint_builtin_const_no_mangle = const items should never be `#[no_mangle]` lint_builtin_decl_unsafe_fn = declaration of an `unsafe` function lint_builtin_decl_unsafe_method = declaration of an `unsafe` method -lint_builtin_deprecated_attr_default_suggestion = remove this attribute 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_deref_nullptr = dereferencing a null pointer .label = this code causes undefined behavior when executed +lint_builtin_double_negations = use of a double negation + .note = the prefix `--` could be misinterpreted as a decrement operator which exists in other languages + .note_decrement = use `-= 1` if you meant to decrement the value + .add_parens_suggestion = add parentheses for clarity + lint_builtin_ellipsis_inclusive_range_patterns = `...` range patterns are deprecated .suggestion = use `..=` for an inclusive range @@ -209,7 +212,9 @@ lint_dangling_pointers_from_temporaries = a dangling pointer will be produced be .label_ptr = this pointer will immediately be invalid .label_temporary = this `{$ty}` is deallocated at the end of the statement, bind it to a variable to extend its lifetime .note = pointers do not have a lifetime; when calling `{$callee}` the `{$ty}` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned - .help = for more information, see <https://doc.rust-lang.org/reference/destructors.html> + .help_bind = you must make sure that the variable you bind the `{$ty}` to lives at least as long as the pointer returned by the call to `{$callee}` + .help_returned = in particular, if this pointer is returned from the current function, binding the `{$ty}` inside the function will not suffice + .help_visit = for more information, see <https://doc.rust-lang.org/reference/destructors.html> lint_default_hash_types = prefer `{$preferred}` over `{$used}`, it has better performance .note = a `use rustc_data_structures::fx::{$preferred}` may be necessary @@ -970,8 +975,7 @@ lint_unused_result = unused result of type `{$ty}` lint_use_let_underscore_ignore_suggestion = use `let _ = ...` to ignore the expression or result +lint_uses_power_alignment = repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + lint_variant_size_differences = enum variant is more than three times larger ({$largest} bytes) than the next largest - -lint_wasm_c_abi = - older versions of the `wasm-bindgen` crate will be incompatible with future versions of Rust; please update to `wasm-bindgen` v0.2.88 diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index fb3cf5afad0..c03de687a33 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -20,7 +20,7 @@ use rustc_abi::BackendRepr; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{self as ast, *}; -use rustc_ast_pretty::pprust::{self, expr_to_string}; +use rustc_ast_pretty::pprust::expr_to_string; use rustc_errors::{Applicability, LintDiagnostic}; use rustc_feature::{AttributeGate, BuiltinAttribute, GateIssue, Stability, deprecated_attributes}; use rustc_hir as hir; @@ -49,16 +49,16 @@ use rustc_trait_selection::traits::{self}; use crate::errors::BuiltinEllipsisInclusiveRangePatterns; use crate::lints::{ BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink, - BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr, - BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives, - BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures, - BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents, - BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes, - BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, - BuiltinTrivialBounds, BuiltinTypeAliasBounds, BuiltinUngatedAsyncFnTrackCaller, - BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, - BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, - BuiltinWhileTrue, InvalidAsmLabel, + BuiltinDeprecatedAttrLinkSuggestion, BuiltinDerefNullptr, BuiltinDoubleNegations, + BuiltinDoubleNegationsAddParens, BuiltinEllipsisInclusiveRangePatternsLint, + BuiltinExplicitOutlives, BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, + BuiltinIncompleteFeatures, BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, + BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, + BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, + BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds, + BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, + BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, + BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, }; use crate::nonstandard_style::{MethodLateContext, method_context}; use crate::{ @@ -90,19 +90,11 @@ declare_lint! { declare_lint_pass!(WhileTrue => [WHILE_TRUE]); -/// Traverse through any amount of parenthesis and return the first non-parens expression. -fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr { - while let ast::ExprKind::Paren(sub) = &expr.kind { - expr = sub; - } - expr -} - impl EarlyLintPass for WhileTrue { #[inline] fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { if let ast::ExprKind::While(cond, _, label) = &e.kind - && let ast::ExprKind::Lit(token_lit) = pierce_parens(cond).kind + && let ast::ExprKind::Lit(token_lit) = cond.peel_parens().kind && let token::Lit { kind: token::Bool, symbol: kw::True, .. } = token_lit && !cond.span.from_expansion() { @@ -848,12 +840,6 @@ impl EarlyLintPass for DeprecatedAttr { return; } } - if attr.has_name(sym::no_start) || attr.has_name(sym::crate_id) { - cx.emit_span_lint(DEPRECATED, attr.span, BuiltinDeprecatedAttrUsed { - name: pprust::path_to_string(&attr.get_normal_item().path), - suggestion: attr.span, - }); - } } } @@ -1582,6 +1568,58 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { } } +declare_lint! { + /// The `double_negations` lint detects expressions of the form `--x`. + /// + /// ### Example + /// + /// ```rust + /// fn main() { + /// let x = 1; + /// let _b = --x; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Negating something twice is usually the same as not negating it at all. + /// However, a double negation in Rust can easily be confused with the + /// prefix decrement operator that exists in many languages derived from C. + /// Use `-(-x)` if you really wanted to negate the value twice. + /// + /// To decrement a value, use `x -= 1` instead. + pub DOUBLE_NEGATIONS, + Warn, + "detects expressions of the form `--x`" +} + +declare_lint_pass!( + /// Lint for expressions of the form `--x` that can be confused with C's + /// prefix decrement operator. + DoubleNegations => [DOUBLE_NEGATIONS] +); + +impl EarlyLintPass for DoubleNegations { + #[inline] + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { + // only lint on the innermost `--` in a chain of `-` operators, + // even if there are 3 or more negations + if let ExprKind::Unary(UnOp::Neg, ref inner) = expr.kind + && let ExprKind::Unary(UnOp::Neg, ref inner2) = inner.kind + && !matches!(inner2.kind, ExprKind::Unary(UnOp::Neg, _)) + { + cx.emit_span_lint(DOUBLE_NEGATIONS, expr.span, BuiltinDoubleNegations { + add_parens: BuiltinDoubleNegationsAddParens { + start_span: inner.span.shrink_to_lo(), + end_span: inner.span.shrink_to_hi(), + }, + }); + } + } +} + declare_lint_pass!( /// Does nothing as a lint pass, but registers some `Lint`s /// which are used by other parts of the compiler. @@ -1600,7 +1638,8 @@ declare_lint_pass!( UNSTABLE_FEATURES, UNREACHABLE_PUB, TYPE_ALIAS_BOUNDS, - TRIVIAL_BOUNDS + TRIVIAL_BOUNDS, + DOUBLE_NEGATIONS ] ); @@ -2657,7 +2696,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { } declare_lint! { - /// The `deref_nullptr` lint detects when an null pointer is dereferenced, + /// The `deref_nullptr` lint detects when a null pointer is dereferenced, /// which causes [undefined behavior]. /// /// ### Example diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index 98b717a3070..fd6b3e90ada 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -141,7 +141,7 @@ fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.hir_id, method.ident.span, DanglingPointersFromTemporaries { - callee: method.ident.name, + callee: method.ident, ty, ptr_span: method.ident.span, temporary_span: receiver.span, diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 6d73715562b..92dbc76a7b5 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -430,7 +430,6 @@ pub(super) fn decorate_lint( BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => { lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag) } - BuiltinLintDiag::WasmCAbi => lints::WasmCAbi.decorate_lint(diag), BuiltinLintDiag::IllFormedAttributeInput { suggestions } => { lints::IllFormedAttributeInput { num_suggestions: suggestions.len(), diff --git a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs index eebb131599a..da6dd8161ee 100644 --- a/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs +++ b/compiler/rustc_lint/src/early/diagnostics/check_cfg.rs @@ -10,19 +10,35 @@ use crate::lints; const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; +enum FilterWellKnownNames { + Yes, + No, +} + fn sort_and_truncate_possibilities( sess: &Session, mut possibilities: Vec<Symbol>, + filter_well_known_names: FilterWellKnownNames, ) -> (Vec<Symbol>, usize) { + let possibilities_len = possibilities.len(); + let n_possibilities = if sess.opts.unstable_opts.check_cfg_all_expected { possibilities.len() } else { + match filter_well_known_names { + FilterWellKnownNames::Yes => { + possibilities.retain(|cfg_name| { + !sess.psess.check_config.well_known_names.contains(cfg_name) + }); + } + FilterWellKnownNames::No => {} + }; std::cmp::min(possibilities.len(), MAX_CHECK_CFG_NAMES_OR_VALUES) }; possibilities.sort_by(|s1, s2| s1.as_str().cmp(s2.as_str())); - let and_more = possibilities.len().saturating_sub(n_possibilities); + let and_more = possibilities_len.saturating_sub(n_possibilities); possibilities.truncate(n_possibilities); (possibilities, and_more) } @@ -198,8 +214,10 @@ pub(super) fn unexpected_cfg_name( } else { vec![] }; + + let (possibilities, and_more) = + sort_and_truncate_possibilities(sess, possibilities, FilterWellKnownNames::Yes); let expected_names = if !possibilities.is_empty() { - let (possibilities, and_more) = sort_and_truncate_possibilities(sess, possibilities); let possibilities: Vec<_> = possibilities.into_iter().map(|s| Ident::new(s, name_span)).collect(); Some(lints::unexpected_cfg_name::ExpectedNames { @@ -269,8 +287,11 @@ pub(super) fn unexpected_cfg_value( // for names as the possibilities could be very long let code_sugg = if !possibilities.is_empty() { let expected_values = { - let (possibilities, and_more) = - sort_and_truncate_possibilities(sess, possibilities.clone()); + let (possibilities, and_more) = sort_and_truncate_possibilities( + sess, + possibilities.clone(), + FilterWellKnownNames::No, + ); lints::unexpected_cfg_value::ExpectedValues { name, have_none_possibility, diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 289e2c9b722..ef79f1301e5 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -57,7 +57,7 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) { let expect_id = canonicalize_id(expect_id); if !fulfilled_expectations.contains(&expect_id) - && tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter)) + && tool_filter.is_none_or(|filter| expectation.lint_tool == Some(filter)) { let rationale = expectation.reason.map(|rationale| ExpectationNote { rationale }); let note = expectation.is_unfulfilled_lint_expectations; diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 259ea908fc6..b5a6159bd0a 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -9,7 +9,7 @@ use rustc_errors::{ use rustc_hir::{self as hir, HirIdSet}; use rustc_macros::LintDiagnostic; use rustc_middle::ty::TyCtxt; -use rustc_session::lint::{FutureIncompatibilityReason, Level}; +use rustc_session::lint::{FutureIncompatibilityReason, LintId}; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::Span; use rustc_span::edition::Edition; @@ -245,12 +245,12 @@ impl_lint_pass!( impl<'tcx> LateLintPass<'tcx> for IfLetRescope { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - if expr.span.edition().at_least_rust_2024() { - return; - } - if let (Level::Allow, _) = cx.tcx.lint_level_at_node(IF_LET_RESCOPE, expr.hir_id) { + if expr.span.edition().at_least_rust_2024() + || cx.tcx.lints_that_dont_need_to_run(()).contains(&LintId::of(IF_LET_RESCOPE)) + { return; } + if let hir::ExprKind::Loop(block, _label, hir::LoopSource::While, _span) = expr.kind && let Some(value) = block.expr && let hir::ExprKind::If(cond, _conseq, _alt) = value.kind @@ -290,7 +290,6 @@ struct IfLetRescopeLint { rewrite: Option<IfLetRescopeRewrite>, } -// #[derive(Subdiagnostic)] struct IfLetRescopeRewrite { match_heads: Vec<SingleArmMatchBegin>, consequent_heads: Vec<ConsequentRewrite>, diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index b31a4c74787..166ff60f7e1 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -5,8 +5,8 @@ use rustc_ast as ast; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{ - BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, PatKind, - Path, PathSegment, QPath, Ty, TyKind, + AmbigArg, BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, + PatKind, Path, PathSegment, QPath, Ty, TyKind, }; use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -159,7 +159,7 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { } } - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx, AmbigArg>) { match &ty.kind { TyKind::Path(QPath::Resolved(_, path)) => { if lint_ty_kind_usage(cx, &path.res) { diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index a4d50c73104..c6d7b839e19 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -8,9 +8,8 @@ use std::cell::Cell; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::join; -use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; -use rustc_hir::{HirId, intravisit as hir_visit}; +use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::Session; @@ -214,15 +213,11 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas }) } - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) { lint_callback!(self, check_ty, t); hir_visit::walk_ty(self, t); } - fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { - hir_visit::walk_inf(self, inf); - } - fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _: Span, n: HirId) { if !self.context.only_module { self.process_mod(m, n); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 1465c2cff7b..83a168c3f44 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -181,6 +181,7 @@ early_lint_methods!( UnusedDocComment: UnusedDocComment, Expr2024: Expr2024, Precedence: Precedence, + DoubleNegations: DoubleNegations, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index ac995b59caf..09b0e1ed8bd 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -9,6 +9,7 @@ use rustc_errors::{ }; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::VisitorExt; use rustc_hir::{self as hir, MissingLifetimeKind}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::inhabitedness::InhabitedPredicate; @@ -177,19 +178,6 @@ pub(crate) enum BuiltinDeprecatedAttrLinkSuggestion<'a> { } #[derive(LintDiagnostic)] -#[diag(lint_builtin_deprecated_attr_used)] -pub(crate) 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(crate) struct BuiltinUnusedDocComment<'a> { pub kind: &'a str, @@ -293,7 +281,7 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> { // avoid doing throwaway work in case the lint ends up getting suppressed. let mut collector = ShorthandAssocTyCollector { qselves: Vec::new() }; if let Some(ty) = self.ty { - hir::intravisit::Visitor::visit_ty(&mut collector, ty); + collector.visit_ty_unambig(ty); } let affect_object_lifetime_defaults = self @@ -344,6 +332,24 @@ pub(crate) struct BuiltinTrivialBounds<'a> { } #[derive(LintDiagnostic)] +#[diag(lint_builtin_double_negations)] +#[note(lint_note)] +#[note(lint_note_decrement)] +pub(crate) struct BuiltinDoubleNegations { + #[subdiagnostic] + pub add_parens: BuiltinDoubleNegationsAddParens, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_add_parens_suggestion, applicability = "maybe-incorrect")] +pub(crate) struct BuiltinDoubleNegationsAddParens { + #[suggestion_part(code = "(")] + pub start_span: Span, + #[suggestion_part(code = ")")] + pub end_span: Span, +} + +#[derive(LintDiagnostic)] pub(crate) enum BuiltinEllipsisInclusiveRangePatternsLint { #[diag(lint_builtin_ellipsis_inclusive_range_patterns)] Parenthesise { @@ -1139,10 +1145,12 @@ pub(crate) struct IgnoredUnlessCrateSpecified<'a> { #[derive(LintDiagnostic)] #[diag(lint_dangling_pointers_from_temporaries)] #[note] -#[help] +#[help(lint_help_bind)] +#[help(lint_help_returned)] +#[help(lint_help_visit)] // FIXME: put #[primary_span] on `ptr_span` once it does not cause conflicts pub(crate) struct DanglingPointersFromTemporaries<'tcx> { - pub callee: Symbol, + pub callee: Ident, pub ty: Ty<'tcx>, #[label(lint_label_ptr)] pub ptr_span: Span, @@ -1343,7 +1351,7 @@ pub(crate) enum NonUpperCaseGlobalSub { #[diag(lint_noop_method_call)] #[note] pub(crate) struct NoopMethodCallDiag<'a> { - pub method: Symbol, + pub method: Ident, pub orig_ty: Ty<'a>, pub trait_: Symbol, #[suggestion(code = "", applicability = "machine-applicable")] @@ -1693,6 +1701,10 @@ pub(crate) struct OverflowingLiteral<'a> { } #[derive(LintDiagnostic)] +#[diag(lint_uses_power_alignment)] +pub(crate) struct UsesPowerAlignment; + +#[derive(LintDiagnostic)] #[diag(lint_unused_comparisons)] pub(crate) struct UnusedComparisons; @@ -2560,10 +2572,6 @@ pub(crate) struct UnusedCrateDependency { } #[derive(LintDiagnostic)] -#[diag(lint_wasm_c_abi)] -pub(crate) struct WasmCAbi; - -#[derive(LintDiagnostic)] #[diag(lint_ill_formed_attribute_input)] pub(crate) struct IllFormedAttributeInput { pub num_suggestions: usize, diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index a1cc3a85109..4e9d793be5b 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,6 +1,6 @@ use rustc_errors::MultiSpan; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind}; use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint, impl_lint_pass}; @@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { // 1. We collect all the `hir::Path` from the `Self` type and `Trait` ref // of the `impl` definition let mut collector = PathCollector { paths: Vec::new() }; - collector.visit_ty(&impl_.self_ty); + collector.visit_ty_unambig(&impl_.self_ty); if let Some(of_trait) = &impl_.of_trait { collector.visit_trait_ref(of_trait); } @@ -343,5 +343,5 @@ fn path_span_without_args(path: &Path<'_>) -> Span { /// Return a "error message-able" ident for the last segment of the `Path` fn path_name_to_string(path: &Path<'_>) -> String { - path.segments.last().unwrap().ident.name.to_ident_string() + path.segments.last().unwrap().ident.to_string() } diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index fa519281be5..790ef910b04 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -129,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { _ => None, }; cx.emit_span_lint(NOOP_METHOD_CALL, span, NoopMethodCallDiag { - method: call.ident.name, + method: call.ident, orig_ty, trait_, label: span, diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 53a411e2b87..d347a8c1bc7 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -1,4 +1,4 @@ -use rustc_hir as hir; +use rustc_hir::{self as hir, AmbigArg}; use rustc_infer::infer::TyCtxtInferExt; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::fold::BottomUpFolder; @@ -67,7 +67,7 @@ declare_lint! { declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]); impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { - fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { let hir::TyKind::OpaqueDef(opaque) = &ty.kind else { return; }; diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index 3f264859d48..a1d66047058 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -1,6 +1,5 @@ -use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::{GenericArg, PathSegment, QPath, TyKind}; +use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -22,7 +21,7 @@ declare_tool_lint! { declare_lint_pass!(PassByValue => [PASS_BY_VALUE]); impl<'tcx> LateLintPass<'tcx> for PassByValue { - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { match &ty.kind { TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => { if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) { @@ -46,7 +45,7 @@ fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option<Stri if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind { match path.res { Res::Def(_, def_id) if cx.tcx.has_attr(def_id, sym::rustc_pass_by_value) => { - let name = cx.tcx.item_name(def_id).to_ident_string(); + let name = cx.tcx.item_ident(def_id); let path_segment = path.segments.last().unwrap(); return Some(format!("{}{}", name, gen_args(cx, path_segment))); } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 3a323298bee..8cc8f911d3a 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -25,7 +25,7 @@ macro_rules! late_lint_methods { fn check_pat(a: &'tcx rustc_hir::Pat<'tcx>); fn check_expr(a: &'tcx rustc_hir::Expr<'tcx>); fn check_expr_post(a: &'tcx rustc_hir::Expr<'tcx>); - fn check_ty(a: &'tcx rustc_hir::Ty<'tcx>); + fn check_ty(a: &'tcx rustc_hir::Ty<'tcx, rustc_hir::AmbigArg>); fn check_generic_param(a: &'tcx rustc_hir::GenericParam<'tcx>); fn check_generics(a: &'tcx rustc_hir::Generics<'tcx>); fn check_poly_trait_ref(a: &'tcx rustc_hir::PolyTraitRef<'tcx>); diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index a9797c3b32a..e0937e43c9a 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -1,4 +1,4 @@ -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, AmbigArg, LangItem}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; @@ -110,8 +110,10 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { } } - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { - let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return }; + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { + let hir::TyKind::TraitObject(bounds, _lifetime_and_syntax_pointer) = &ty.kind else { + return; + }; for bound in &bounds[..] { let def_id = bound.trait_ref.trait_def_id(); if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop)) { diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 3bd27a224e7..0060f33888e 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,14 +1,16 @@ use std::iter; use std::ops::ControlFlow; -use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, Variants, WrappingRange}; +use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, VariantIdx, Variants, WrappingRange}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; -use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{AmbigArg, Expr, ExprKind, LangItem}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; use rustc_middle::ty::{ - self, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + self, Adt, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, }; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::def_id::LocalDefId; @@ -23,7 +25,7 @@ use crate::lints::{ AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons, - UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, + UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, UsesPowerAlignment, VariantSizeDifferencesDiag, }; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -727,7 +729,60 @@ declare_lint! { "proper use of libc types in foreign item definitions" } -declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS]); +declare_lint! { + /// The `uses_power_alignment` lint detects specific `repr(C)` + /// aggregates on AIX. + /// In its platform C ABI, AIX uses the "power" (as in PowerPC) alignment + /// rule (detailed in https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=data-using-alignment-modes#alignment), + /// which can also be set for XLC by `#pragma align(power)` or + /// `-qalign=power`. Aggregates with a floating-point type as the + /// recursively first field (as in "at offset 0") modify the layout of + /// *subsequent* fields of the associated structs to use an alignment value + /// where the floating-point type is aligned on a 4-byte boundary. + /// + /// The power alignment rule for structs needed for C compatibility is + /// unimplementable within `repr(C)` in the compiler without building in + /// handling of references to packed fields and infectious nested layouts, + /// so a warning is produced in these situations. + /// + /// ### Example + /// + /// ```rust,ignore (fails on non-powerpc64-ibm-aix) + /// #[repr(C)] + /// pub struct Floats { + /// a: f64, + /// b: u8, + /// c: f64, + /// } + /// ``` + /// + /// This will produce: + /// + /// ```text + /// warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type + /// --> <source>:5:3 + /// | + /// 5 | c: f64, + /// | ^^^^^^ + /// | + /// = note: `#[warn(uses_power_alignment)]` on by default + /// ``` + /// + /// ### Explanation + /// + /// The power alignment rule specifies that the above struct has the + /// following alignment: + /// - offset_of!(Floats, a) == 0 + /// - offset_of!(Floats, b) == 8 + /// - offset_of!(Floats, c) == 12 + /// However, rust currently aligns `c` at offset_of!(Floats, c) == 16. + /// Thus, a warning should be produced for the above struct in this case. + USES_POWER_ALIGNMENT, + Warn, + "Structs do not follow the power alignment rule under repr(C)" +} + +declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS, USES_POWER_ALIGNMENT]); #[derive(Clone, Copy)] pub(crate) enum CItemKind { @@ -1472,7 +1527,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } impl<'a, 'b, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'a, 'b, 'tcx> { - fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) { + fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { debug!(?ty); if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind && !self.visitor.is_internal_abi(*abi) @@ -1500,7 +1555,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let mut visitor = FnPtrFinder { visitor: self, spans: Vec::new(), tys: Vec::new() }; ty.visit_with(&mut visitor); - hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty); + visitor.visit_ty_unambig(hir_ty); iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect() } @@ -1539,6 +1594,71 @@ impl ImproperCTypesDefinitions { vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false); } } + + fn check_arg_for_power_alignment<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + ) -> bool { + // Structs (under repr(C)) follow the power alignment rule if: + // - the first field of the struct is a floating-point type that + // is greater than 4-bytes, or + // - the first field of the struct is an aggregate whose + // recursively first field is a floating-point type greater than + // 4 bytes. + if cx.tcx.sess.target.os != "aix" { + return false; + } + if ty.is_floating_point() && ty.primitive_size(cx.tcx).bytes() > 4 { + return true; + } else if let Adt(adt_def, _) = ty.kind() + && adt_def.is_struct() + { + let struct_variant = adt_def.variant(VariantIdx::ZERO); + // Within a nested struct, all fields are examined to correctly + // report if any fields after the nested struct within the + // original struct are misaligned. + for struct_field in &struct_variant.fields { + let field_ty = cx.tcx.type_of(struct_field.did).instantiate_identity(); + if self.check_arg_for_power_alignment(cx, field_ty) { + return true; + } + } + } + return false; + } + + fn check_struct_for_power_alignment<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + item: &'tcx hir::Item<'tcx>, + ) { + let adt_def = cx.tcx.adt_def(item.owner_id.to_def_id()); + if adt_def.repr().c() + && !adt_def.repr().packed() + && cx.tcx.sess.target.os == "aix" + && !adt_def.all_fields().next().is_none() + { + let struct_variant_data = item.expect_struct().0; + for (index, ..) in struct_variant_data.fields().iter().enumerate() { + // Struct fields (after the first field) are checked for the + // power alignment rule, as fields after the first are likely + // to be the fields that are misaligned. + if index != 0 { + let first_field_def = struct_variant_data.fields()[index]; + let def_id = first_field_def.def_id; + let ty = cx.tcx.type_of(def_id).instantiate_identity(); + if self.check_arg_for_power_alignment(cx, ty) { + cx.emit_span_lint( + USES_POWER_ALIGNMENT, + first_field_def.span, + UsesPowerAlignment, + ); + } + } + } + } + } } /// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in @@ -1562,8 +1682,13 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions { } // See `check_fn`.. hir::ItemKind::Fn { .. } => {} + // Structs are checked based on if they follow the power alignment + // rule (under repr(C)). + hir::ItemKind::Struct(..) => { + self.check_struct_for_power_alignment(cx, item); + } // See `check_field_def`.. - hir::ItemKind::Union(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) => {} + hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {} // Doesn't define something that can contain a external type to be checked. hir::ItemKind::Impl(..) | hir::ItemKind::TraitAlias(..) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9fc527a6a3a..5b4a1f174cb 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -60,6 +60,7 @@ declare_lint_pass! { LARGE_ASSIGNMENTS, LATE_BOUND_LIFETIME_ARGUMENTS, LEGACY_DERIVE_HELPERS, + LINKER_MESSAGES, LONG_RUNNING_CONST_EVAL, LOSSY_PROVENANCE_CASTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, @@ -143,7 +144,6 @@ declare_lint_pass! { UNUSED_VARIABLES, USELESS_DEPRECATED, WARNINGS, - WASM_C_ABI, // tidy-alphabetical-end ] } @@ -4086,6 +4086,47 @@ declare_lint! { } declare_lint! { + /// The `linker_messages` lint forwards warnings from the linker. + /// + /// ### Example + /// + /// ```rust,ignore (needs CLI args, platform-specific) + /// #[warn(linker_messages)] + /// extern "C" { + /// fn foo(); + /// } + /// fn main () { unsafe { foo(); } } + /// ``` + /// + /// On Linux, using `gcc -Wl,--warn-unresolved-symbols` as a linker, this will produce + /// + /// ```text + /// warning: linker stderr: rust-lld: undefined symbol: foo + /// >>> referenced by rust_out.69edbd30df4ae57d-cgu.0 + /// >>> rust_out.rust_out.69edbd30df4ae57d-cgu.0.rcgu.o:(rust_out::main::h3a90094b06757803) + /// | + /// note: the lint level is defined here + /// --> warn.rs:1:9 + /// | + /// 1 | #![warn(linker_messages)] + /// | ^^^^^^^^^^^^^^^ + /// warning: 1 warning emitted + /// ``` + /// + /// ### Explanation + /// + /// Linkers emit platform-specific and program-specific warnings that cannot be predicted in + /// advance by the Rust compiler. Such messages are ignored by default for now. While linker + /// warnings could be very useful they have been ignored for many years by essentially all + /// users, so we need to do a bit more work than just surfacing their text to produce a clear + /// and actionable warning of similar quality to our other diagnostics. See this tracking + /// issue for more details: <https://github.com/rust-lang/rust/issues/136096>. + pub LINKER_MESSAGES, + Allow, + "warnings emitted at runtime by the target-specific linker program" +} + +declare_lint! { /// The `named_arguments_used_positionally` lint detects cases where named arguments are only /// used positionally in format strings. This usage is valid but potentially very confusing. /// @@ -4648,44 +4689,6 @@ declare_lint! { } declare_lint! { - /// The `wasm_c_abi` lint detects crate dependencies that are incompatible - /// with future versions of Rust that will emit spec-compliant C ABI. - /// - /// ### Example - /// - /// ```rust,ignore (needs extern crate) - /// #![deny(wasm_c_abi)] - /// ``` - /// - /// This will produce: - /// - /// ```text - /// error: the following packages contain code that will be rejected by a future version of Rust: wasm-bindgen v0.2.87 - /// | - /// note: the lint level is defined here - /// --> src/lib.rs:1:9 - /// | - /// 1 | #![deny(wasm_c_abi)] - /// | ^^^^^^^^^^ - /// ``` - /// - /// ### Explanation - /// - /// Rust has historically emitted non-spec-compliant C ABI. This has caused - /// incompatibilities between other compilers and Wasm targets. In a future - /// version of Rust this will be fixed and therefore dependencies relying - /// on the non-spec-compliant C ABI will stop functioning. - pub WASM_C_ABI, - Deny, - "detects dependencies that are incompatible with the Wasm C ABI", - @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, - reference: "issue #71871 <https://github.com/rust-lang/rust/issues/71871>", - }; - crate_level_only -} - -declare_lint! { /// The `uncovered_param_in_projection` lint detects a violation of one of Rust's orphan rules for /// foreign trait implementations that concerns the use of type parameters inside trait associated /// type paths ("projections") whose output may not be a local type that is mistakenly considered diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 7786d3eb59a..3f1e98556ef 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -161,7 +161,19 @@ impl<HCX: rustc_hir::HashStableContext> ToStableHashKey<HCX> for LintExpectation /// Setting for how to handle a lint. /// /// See: <https://doc.rust-lang.org/rustc/lints/levels.html> -#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, HashStable_Generic)] +#[derive( + Clone, + Copy, + PartialEq, + PartialOrd, + Eq, + Ord, + Debug, + Hash, + Encodable, + Decodable, + HashStable_Generic +)] pub enum Level { /// The `allow` level will not issue any message. Allow, @@ -783,7 +795,6 @@ pub enum BuiltinLintDiag { extern_crate: Symbol, local_crate: Symbol, }, - WasmCAbi, IllFormedAttributeInput { suggestions: Vec<String>, }, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index dd72ea2497f..35186778671 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -2,6 +2,7 @@ #include "llvm-c/Analysis.h" #include "llvm-c/Core.h" +#include "llvm-c/DebugInfo.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" @@ -676,120 +677,73 @@ template <typename DIT> DIT *unwrapDIPtr(LLVMMetadataRef Ref) { #define DIArray DINodeArray #define unwrapDI unwrapDIPtr -// These values **must** match debuginfo::DIFlags! They also *happen* -// to match LLVM, but that isn't required as we do giant sets of -// matching below. The value shouldn't be directly passed to LLVM. -enum class LLVMRustDIFlags : uint32_t { - FlagZero = 0, - FlagPrivate = 1, - FlagProtected = 2, - FlagPublic = 3, - FlagFwdDecl = (1 << 2), - FlagAppleBlock = (1 << 3), - FlagBlockByrefStruct = (1 << 4), - FlagVirtual = (1 << 5), - FlagArtificial = (1 << 6), - FlagExplicit = (1 << 7), - FlagPrototyped = (1 << 8), - FlagObjcClassComplete = (1 << 9), - FlagObjectPointer = (1 << 10), - FlagVector = (1 << 11), - FlagStaticMember = (1 << 12), - FlagLValueReference = (1 << 13), - FlagRValueReference = (1 << 14), - FlagExternalTypeRef = (1 << 15), - FlagIntroducedVirtual = (1 << 18), - FlagBitField = (1 << 19), - FlagNoReturn = (1 << 20), - // Do not add values that are not supported by the minimum LLVM - // version we support! see llvm/include/llvm/IR/DebugInfoFlags.def -}; - -inline LLVMRustDIFlags operator&(LLVMRustDIFlags A, LLVMRustDIFlags B) { - return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(A) & - static_cast<uint32_t>(B)); -} - -inline LLVMRustDIFlags operator|(LLVMRustDIFlags A, LLVMRustDIFlags B) { - return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(A) | - static_cast<uint32_t>(B)); -} - -inline LLVMRustDIFlags &operator|=(LLVMRustDIFlags &A, LLVMRustDIFlags B) { - return A = A | B; -} - -inline bool isSet(LLVMRustDIFlags F) { return F != LLVMRustDIFlags::FlagZero; } - -inline LLVMRustDIFlags visibility(LLVMRustDIFlags F) { - return static_cast<LLVMRustDIFlags>(static_cast<uint32_t>(F) & 0x3); -} - -static DINode::DIFlags fromRust(LLVMRustDIFlags Flags) { - DINode::DIFlags Result = DINode::DIFlags::FlagZero; - - switch (visibility(Flags)) { - case LLVMRustDIFlags::FlagPrivate: - Result |= DINode::DIFlags::FlagPrivate; - break; - case LLVMRustDIFlags::FlagProtected: - Result |= DINode::DIFlags::FlagProtected; - break; - case LLVMRustDIFlags::FlagPublic: - Result |= DINode::DIFlags::FlagPublic; - break; - default: - // The rest are handled below - break; - } - - if (isSet(Flags & LLVMRustDIFlags::FlagFwdDecl)) { - Result |= DINode::DIFlags::FlagFwdDecl; - } - if (isSet(Flags & LLVMRustDIFlags::FlagAppleBlock)) { - Result |= DINode::DIFlags::FlagAppleBlock; - } - if (isSet(Flags & LLVMRustDIFlags::FlagVirtual)) { - Result |= DINode::DIFlags::FlagVirtual; - } - if (isSet(Flags & LLVMRustDIFlags::FlagArtificial)) { - Result |= DINode::DIFlags::FlagArtificial; - } - if (isSet(Flags & LLVMRustDIFlags::FlagExplicit)) { - Result |= DINode::DIFlags::FlagExplicit; - } - if (isSet(Flags & LLVMRustDIFlags::FlagPrototyped)) { - Result |= DINode::DIFlags::FlagPrototyped; - } - if (isSet(Flags & LLVMRustDIFlags::FlagObjcClassComplete)) { - Result |= DINode::DIFlags::FlagObjcClassComplete; - } - if (isSet(Flags & LLVMRustDIFlags::FlagObjectPointer)) { - Result |= DINode::DIFlags::FlagObjectPointer; - } - if (isSet(Flags & LLVMRustDIFlags::FlagVector)) { - Result |= DINode::DIFlags::FlagVector; - } - if (isSet(Flags & LLVMRustDIFlags::FlagStaticMember)) { - Result |= DINode::DIFlags::FlagStaticMember; - } - if (isSet(Flags & LLVMRustDIFlags::FlagLValueReference)) { - Result |= DINode::DIFlags::FlagLValueReference; - } - if (isSet(Flags & LLVMRustDIFlags::FlagRValueReference)) { - Result |= DINode::DIFlags::FlagRValueReference; - } - if (isSet(Flags & LLVMRustDIFlags::FlagIntroducedVirtual)) { - Result |= DINode::DIFlags::FlagIntroducedVirtual; - } - if (isSet(Flags & LLVMRustDIFlags::FlagBitField)) { - Result |= DINode::DIFlags::FlagBitField; - } - if (isSet(Flags & LLVMRustDIFlags::FlagNoReturn)) { - Result |= DINode::DIFlags::FlagNoReturn; - } - - return Result; +// FIXME(Zalathar): This is a temporary typedef to avoid churning dozens of +// bindings that are going to be deleted and replaced with their LLVM-C +// equivalents, as part of #134009. After that happens, the remaining bindings +// can be adjusted to use `LLVMDIFlags` instead of relying on this typedef. +typedef LLVMDIFlags LLVMRustDIFlags; + +// Statically assert that `LLVMDIFlags` (C) and `DIFlags` (C++) have the same +// layout, at least for the flags we know about. This isn't guaranteed, but is +// likely to remain true, and as long as it is true it makes conversions easy. +#define ASSERT_DIFLAG_VALUE(FLAG, VALUE) \ + static_assert((LLVMDI##FLAG == (VALUE)) && (DINode::DIFlags::FLAG == (VALUE))) +ASSERT_DIFLAG_VALUE(FlagZero, 0); +ASSERT_DIFLAG_VALUE(FlagPrivate, 1); +ASSERT_DIFLAG_VALUE(FlagProtected, 2); +ASSERT_DIFLAG_VALUE(FlagPublic, 3); +// Bit (1 << 1) is part of the private/protected/public values above. +ASSERT_DIFLAG_VALUE(FlagFwdDecl, 1 << 2); +ASSERT_DIFLAG_VALUE(FlagAppleBlock, 1 << 3); +ASSERT_DIFLAG_VALUE(FlagReservedBit4, 1 << 4); +ASSERT_DIFLAG_VALUE(FlagVirtual, 1 << 5); +ASSERT_DIFLAG_VALUE(FlagArtificial, 1 << 6); +ASSERT_DIFLAG_VALUE(FlagExplicit, 1 << 7); +ASSERT_DIFLAG_VALUE(FlagPrototyped, 1 << 8); +ASSERT_DIFLAG_VALUE(FlagObjcClassComplete, 1 << 9); +ASSERT_DIFLAG_VALUE(FlagObjectPointer, 1 << 10); +ASSERT_DIFLAG_VALUE(FlagVector, 1 << 11); +ASSERT_DIFLAG_VALUE(FlagStaticMember, 1 << 12); +ASSERT_DIFLAG_VALUE(FlagLValueReference, 1 << 13); +ASSERT_DIFLAG_VALUE(FlagRValueReference, 1 << 14); +// Bit (1 << 15) has been recycled, but the C API value hasn't been renamed. +static_assert((LLVMDIFlagReserved == (1 << 15)) && + (DINode::DIFlags::FlagExportSymbols == (1 << 15))); +ASSERT_DIFLAG_VALUE(FlagSingleInheritance, 1 << 16); +ASSERT_DIFLAG_VALUE(FlagMultipleInheritance, 2 << 16); +ASSERT_DIFLAG_VALUE(FlagVirtualInheritance, 3 << 16); +// Bit (1 << 17) is part of the inheritance values above. +ASSERT_DIFLAG_VALUE(FlagIntroducedVirtual, 1 << 18); +ASSERT_DIFLAG_VALUE(FlagBitField, 1 << 19); +ASSERT_DIFLAG_VALUE(FlagNoReturn, 1 << 20); +// Bit (1 << 21) is unused, but was `LLVMDIFlagMainSubprogram`. +ASSERT_DIFLAG_VALUE(FlagTypePassByValue, 1 << 22); +ASSERT_DIFLAG_VALUE(FlagTypePassByReference, 1 << 23); +ASSERT_DIFLAG_VALUE(FlagEnumClass, 1 << 24); +ASSERT_DIFLAG_VALUE(FlagThunk, 1 << 25); +ASSERT_DIFLAG_VALUE(FlagNonTrivial, 1 << 26); +ASSERT_DIFLAG_VALUE(FlagBigEndian, 1 << 27); +ASSERT_DIFLAG_VALUE(FlagLittleEndian, 1 << 28); +ASSERT_DIFLAG_VALUE(FlagIndirectVirtualBase, (1 << 2) | (1 << 5)); +#undef ASSERT_DIFLAG_VALUE + +// There are two potential ways to convert `LLVMDIFlags` to `DIFlags`: +// - Check and copy every individual bit/subvalue from input to output. +// - Statically assert that both have the same layout, and cast. +// As long as the static assertions succeed, a cast is easier and faster. +// In the (hopefully) unlikely event that the assertions do fail someday, and +// LLVM doesn't expose its own conversion function, we'll have to switch over +// to copying each bit/subvalue. +static DINode::DIFlags fromRust(LLVMDIFlags Flags) { + // Check that all set bits are covered by the static assertions above. + const unsigned UNKNOWN_BITS = (1 << 31) | (1 << 30) | (1 << 29) | (1 << 21); + if (Flags & UNKNOWN_BITS) { + report_fatal_error("bad LLVMDIFlags"); + } + + // As long as the static assertions are satisfied and no unknown bits are + // present, we can convert from `LLVMDIFlags` to `DIFlags` with a cast. + return static_cast<DINode::DIFlags>(Flags); } // These values **must** match debuginfo::DISPFlags! They also *happen* diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl index 6d7d88fa8d7..d2b5ae53185 100644 --- a/compiler/rustc_metadata/messages.ftl +++ b/compiler/rustc_metadata/messages.ftl @@ -290,6 +290,9 @@ metadata_unsupported_abi = metadata_unsupported_abi_i686 = ABI not supported by `#[link(kind = "raw-dylib")]` on i686 +metadata_wasm_c_abi = + older versions of the `wasm-bindgen` crate are incompatible with current versions of Rust; please update to `wasm-bindgen` v0.2.88 + metadata_wasm_import_form = wasm import module must be of the form `wasm_import_module = "string"` diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 6512176cc4a..a6cd0ecafd0 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -417,7 +417,8 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // Any descendants of `std` should be private. These crates are usually not marked // private in metadata, so we ignore that field. if extern_private.is_none() - && dep_root.map_or(false, |d| STDLIB_STABLE_CRATES.contains(&d.name)) + && let Some(dep) = dep_root + && STDLIB_STABLE_CRATES.contains(&dep.name) { return true; } @@ -1075,12 +1076,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // Make a point span rather than covering the whole file let span = krate.spans.inner_span.shrink_to_lo(); - self.sess.psess.buffer_lint( - lint::builtin::WASM_C_ABI, - span, - ast::CRATE_NODE_ID, - BuiltinLintDiag::WasmCAbi, - ); + self.sess.dcx().emit_err(errors::WasmCAbi { span }); } } diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index b6c5619ec18..cefc6498f68 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -732,3 +732,10 @@ pub struct ImportNameTypeRaw { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(metadata_wasm_c_abi)] +pub(crate) struct WasmCAbi { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 5dd85978f00..8dc529b4d7a 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -100,6 +100,8 @@ middle_strict_coherence_needs_negative_coherence = to use `strict_coherence` on this trait, the `with_negative_coherence` feature must be enabled .label = due to this attribute +middle_too_generic = `{$ty}` does not have a fixed size + middle_type_length_limit = reached the type-length limit while instantiating `{$shrunk}` middle_unknown_layout = @@ -107,4 +109,5 @@ middle_unknown_layout = middle_values_too_big = values of the type `{$ty}` are too big for the target architecture + middle_written_to_path = the full type name has been written to '{$path}' diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 6300d856393..b0187a1848c 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -129,6 +129,9 @@ pub enum LayoutError<'tcx> { #[diag(middle_unknown_layout)] Unknown { ty: Ty<'tcx> }, + #[diag(middle_too_generic)] + TooGeneric { ty: Ty<'tcx> }, + #[diag(middle_values_too_big)] Overflow { ty: Ty<'tcx> }, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 5d78bed5cf8..926760b84aa 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -12,7 +12,7 @@ use rustc_hir::*; use rustc_hir_pretty as pprust_hir; use rustc_middle::hir::nested_filter; use rustc_span::def_id::StableCrateId; -use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; +use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym, with_metavar_spans}; use crate::hir::ModuleItems; use crate::middle::debugger_visualizer::DebuggerVisualizerFile; @@ -1117,6 +1117,9 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { // the fly in the resolver, storing only their accumulated hash in `ResolverGlobalCtxt`, // and combining it with other hashes here. resolutions.visibilities_for_hashing.hash_stable(&mut hcx, &mut stable_hasher); + with_metavar_spans(|mspans| { + mspans.freeze_and_get_read_spans().hash_stable(&mut hcx, &mut stable_hasher); + }); stable_hasher.finish() }); diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 04a06ba7464..bbe23d8abe8 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -40,6 +40,7 @@ #![feature(const_type_name)] #![feature(core_intrinsics)] #![feature(coroutines)] +#![feature(debug_closure_helpers)] #![feature(decl_macro)] #![feature(discriminant_kind)] #![feature(extern_types)] diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 620d9f1c357..cae980cde61 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; use rustc_errors::{Diag, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; -use rustc_macros::HashStable; +use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_session::Session; use rustc_session::lint::builtin::{self, FORBIDDEN_LINT_GROUPS}; use rustc_session::lint::{FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId}; @@ -15,7 +15,7 @@ use tracing::instrument; use crate::ty::TyCtxt; /// How a lint level was set. -#[derive(Clone, Copy, PartialEq, Eq, HashStable, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, HashStable, Debug)] pub enum LintLevelSource { /// Lint is at the default level as declared in rustc. Default, @@ -173,7 +173,7 @@ impl TyCtxt<'_> { /// This struct represents a lint expectation and holds all required information /// to emit the `unfulfilled_lint_expectations` lint if it is unfulfilled after /// the `LateLintPass` has completed. -#[derive(Clone, Debug, HashStable)] +#[derive(Clone, Debug, Encodable, Decodable, HashStable)] pub struct LintExpectation { /// The reason for this expectation that can optionally be added as part of /// the attribute. It will be displayed as part of the lint message. diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index e05f42af6fd..cc980f6e62a 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -147,7 +147,7 @@ impl CodegenFnAttrs { CodegenFnAttrs { flags: CodegenFnAttrFlags::empty(), inline: InlineAttr::None, - optimize: OptimizeAttr::None, + optimize: OptimizeAttr::Default, export_name: None, link_name: None, link_ordinal: None, diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 60e1ff1d049..66d97fda433 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -460,17 +460,6 @@ impl<'tcx> Const<'tcx> { Self::Val(val, ty) } - pub fn from_ty_const(c: ty::Const<'tcx>, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Self { - match c.kind() { - ty::ConstKind::Value(ty, valtree) => { - // Make sure that if `c` is normalized, then the return value is normalized. - let const_val = tcx.valtree_to_const_val((ty, valtree)); - Self::Val(const_val, ty) - } - _ => Self::Ty(ty, c), - } - } - /// Return true if any evaluation of this constant always returns the same value, /// taking into account even pointer identity tests. pub fn is_deterministic(&self) -> bool { diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 65f51ae9d39..46534697e1d 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Formatter}; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; -use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::Span; rustc_index::newtype_index! { @@ -72,7 +72,7 @@ impl ConditionId { /// Enum that can hold a constant zero value, the ID of an physical coverage /// counter, or the ID of a coverage-counter expression. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub enum CovTerm { Zero, Counter(CounterId), @@ -89,7 +89,7 @@ impl Debug for CovTerm { } } -#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] pub enum CoverageKind { /// Marks a span that might otherwise not be represented in MIR, so that /// coverage instrumentation can associate it with its enclosing block/BCB. @@ -151,7 +151,7 @@ impl Debug for CoverageKind { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] -#[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable)] pub enum Op { Subtract, Add, @@ -168,7 +168,7 @@ impl Op { } #[derive(Clone, Debug, PartialEq, Eq)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct Expression { pub lhs: CovTerm, pub op: Op, @@ -176,7 +176,7 @@ pub struct Expression { } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub enum MappingKind { /// Associates a normal region of code with a counter/expression/zero. Code(CovTerm), @@ -208,7 +208,7 @@ impl MappingKind { } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct Mapping { pub kind: MappingKind, pub span: Span, @@ -218,7 +218,7 @@ pub struct Mapping { /// to be used in conjunction with the individual coverage statements injected /// into the function's basic blocks. #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct FunctionCoverageInfo { pub function_source_hash: u64, pub body_span: Span, @@ -238,7 +238,7 @@ pub struct FunctionCoverageInfo { /// ("Hi" indicates that this is "high-level" information collected at the /// THIR/MIR boundary, before the MIR-based coverage instrumentation pass.) #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct CoverageInfoHi { /// 1 more than the highest-numbered [`CoverageKind::BlockMarker`] that was /// injected into the MIR body. This makes it possible to allocate per-ID @@ -252,7 +252,7 @@ pub struct CoverageInfoHi { } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct BranchSpan { pub span: Span, pub true_marker: BlockMarkerId, @@ -260,7 +260,7 @@ pub struct BranchSpan { } #[derive(Copy, Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct ConditionInfo { pub condition_id: ConditionId, pub true_next_id: Option<ConditionId>, @@ -268,7 +268,7 @@ pub struct ConditionInfo { } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct MCDCBranchSpan { pub span: Span, pub condition_info: ConditionInfo, @@ -277,14 +277,14 @@ pub struct MCDCBranchSpan { } #[derive(Copy, Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct DecisionInfo { pub bitmap_idx: u32, pub num_conditions: u16, } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct MCDCDecisionSpan { pub span: Span, pub end_markers: Vec<BlockMarkerId>, diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index d6f8fed755f..1b07846e0cf 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -222,7 +222,7 @@ impl AllocError { } /// The information that makes up a memory access: offset and size. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq)] pub struct AllocRange { pub start: Size, pub size: Size, diff --git a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs index 3a83b184d83..78196b05361 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs @@ -97,7 +97,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { debug_assert!(prov.len() <= 1); if let Some(entry) = prov.first() { // If it overlaps with this byte, it is on this byte. - debug_assert!(self.bytes.as_ref().map_or(true, |b| b.get(&offset).is_none())); + debug_assert!(self.bytes.as_ref().is_none_or(|b| !b.contains_key(&offset))); Some(entry.1) } else { // Look up per-byte provenance. @@ -301,7 +301,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> { // For really small copies, make sure we don't start before `src` does. let entry_start = cmp::max(entry.0, src.start); for offset in entry_start..src.end() { - if bytes.last().map_or(true, |bytes_entry| bytes_entry.0 < offset) { + if bytes.last().is_none_or(|bytes_entry| bytes_entry.0 < offset) { // The last entry, if it exists, has a lower offset than us. bytes.push((offset, entry.1)); } else { diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index bbb8bdce4a0..0f3fca434ee 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -358,6 +358,8 @@ pub struct Body<'tcx> { /// /// Only present if coverage is enabled and this function is eligible. /// Boxed to limit space overhead in non-coverage builds. + #[type_foldable(identity)] + #[type_visitable(ignore)] pub coverage_info_hi: Option<Box<coverage::CoverageInfoHi>>, /// Per-function coverage information added by the `InstrumentCoverage` @@ -366,6 +368,8 @@ pub struct Body<'tcx> { /// /// If `-Cinstrument-coverage` is not active, or if an individual function /// is not eligible for coverage, then this should always be `None`. + #[type_foldable(identity)] + #[type_visitable(ignore)] pub function_coverage_info: Option<Box<coverage::FunctionCoverageInfo>>, } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 111c3b6956a..3eccf56d8c4 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher, ToStableHashKey}; use rustc_data_structures::unord::UnordMap; use rustc_hir::ItemId; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, LOCAL_CRATE}; use rustc_index::Idx; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; @@ -247,6 +247,12 @@ impl ToStableHashKey<StableHashingContext<'_>> for MonoItem<'_> { } } +#[derive(Debug, HashStable, Copy, Clone)] +pub struct MonoItemPartitions<'tcx> { + pub codegen_units: &'tcx [CodegenUnit<'tcx>], + pub all_mono_items: &'tcx DefIdSet, +} + #[derive(Debug, HashStable)] pub struct CodegenUnit<'tcx> { /// A name for this CGU. Incremental compilation requires that diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index ea35323ccc7..3b4fba97e60 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1,8 +1,7 @@ use std::collections::BTreeSet; use std::fmt::{Display, Write as _}; -use std::fs; -use std::io::{self, Write as _}; use std::path::{Path, PathBuf}; +use std::{fs, io}; use rustc_abi::Size; use rustc_ast::InlineAsmTemplatePiece; @@ -149,37 +148,59 @@ pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool { // `def_path_str()` would otherwise trigger `type_of`, and this can // run while we are already attempting to evaluate `type_of`. +/// Most use-cases of dumping MIR should use the [dump_mir] entrypoint instead, which will also +/// check if dumping MIR is enabled, and if this body matches the filters passed on the CLI. +/// +/// That being said, if the above requirements have been validated already, this function is where +/// most of the MIR dumping occurs, if one needs to export it to a file they have created with +/// [create_dump_file], rather than to a new file created as part of [dump_mir], or to stdout/stderr +/// for debugging purposes. +pub fn dump_mir_to_writer<'tcx, F>( + tcx: TyCtxt<'tcx>, + pass_name: &str, + disambiguator: &dyn Display, + body: &Body<'tcx>, + w: &mut dyn io::Write, + mut extra_data: F, + options: PrettyPrintMirOptions, +) -> io::Result<()> +where + F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, +{ + // see notes on #41697 above + let def_path = + ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id())); + // ignore-tidy-odd-backticks the literal below is fine + write!(w, "// MIR for `{def_path}")?; + match body.source.promoted { + None => write!(w, "`")?, + Some(promoted) => write!(w, "::{promoted:?}`")?, + } + writeln!(w, " {disambiguator} {pass_name}")?; + if let Some(ref layout) = body.coroutine_layout_raw() { + writeln!(w, "/* coroutine_layout = {layout:#?} */")?; + } + writeln!(w)?; + extra_data(PassWhere::BeforeCFG, w)?; + write_user_type_annotations(tcx, body, w)?; + write_mir_fn(tcx, body, &mut extra_data, w, options)?; + extra_data(PassWhere::AfterCFG, w) +} + fn dump_matched_mir_node<'tcx, F>( tcx: TyCtxt<'tcx>, pass_num: bool, pass_name: &str, disambiguator: &dyn Display, body: &Body<'tcx>, - mut extra_data: F, + extra_data: F, options: PrettyPrintMirOptions, ) where F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>, { let _: io::Result<()> = try { let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body)?; - // see notes on #41697 above - let def_path = - ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id())); - // ignore-tidy-odd-backticks the literal below is fine - write!(file, "// MIR for `{def_path}")?; - match body.source.promoted { - None => write!(file, "`")?, - Some(promoted) => write!(file, "::{promoted:?}`")?, - } - writeln!(file, " {disambiguator} {pass_name}")?; - if let Some(ref layout) = body.coroutine_layout_raw() { - writeln!(file, "/* coroutine_layout = {layout:#?} */")?; - } - writeln!(file)?; - extra_data(PassWhere::BeforeCFG, &mut file)?; - write_user_type_annotations(tcx, body, &mut file)?; - write_mir_fn(tcx, body, &mut extra_data, &mut file, options)?; - extra_data(PassWhere::AfterCFG, &mut file)?; + dump_mir_to_writer(tcx, pass_name, disambiguator, body, &mut file, extra_data, options)?; }; if tcx.sess.opts.unstable_opts.dump_mir_graphviz { diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs index db5da941f1e..50494355e3e 100644 --- a/compiler/rustc_middle/src/mir/query.rs +++ b/compiler/rustc_middle/src/mir/query.rs @@ -1,6 +1,5 @@ //! Values computed by queries that use MIR. -use std::cell::Cell; use std::fmt::{self, Debug}; use rustc_abi::{FieldIdx, VariantIdx}; @@ -62,55 +61,26 @@ pub struct CoroutineLayout<'tcx> { impl Debug for CoroutineLayout<'_> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - /// Prints an iterator of (key, value) tuples as a map. - struct MapPrinter<'a, K, V>(Cell<Option<Box<dyn Iterator<Item = (K, V)> + 'a>>>); - impl<'a, K, V> MapPrinter<'a, K, V> { - fn new(iter: impl Iterator<Item = (K, V)> + 'a) -> Self { - Self(Cell::new(Some(Box::new(iter)))) - } - } - impl<'a, K: Debug, V: Debug> Debug for MapPrinter<'a, K, V> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.debug_map().entries(self.0.take().unwrap()).finish() - } - } - - /// Prints the coroutine variant name. - struct GenVariantPrinter(VariantIdx); - impl From<VariantIdx> for GenVariantPrinter { - fn from(idx: VariantIdx) -> Self { - GenVariantPrinter(idx) - } - } - impl Debug for GenVariantPrinter { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let variant_name = ty::CoroutineArgs::variant_name(self.0); - if fmt.alternate() { - write!(fmt, "{:9}({:?})", variant_name, self.0) - } else { - write!(fmt, "{variant_name}") - } - } - } - - /// Forces its contents to print in regular mode instead of alternate mode. - struct OneLinePrinter<T>(T); - impl<T: Debug> Debug for OneLinePrinter<T> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "{:?}", self.0) - } - } - fmt.debug_struct("CoroutineLayout") - .field("field_tys", &MapPrinter::new(self.field_tys.iter_enumerated())) - .field( - "variant_fields", - &MapPrinter::new( - self.variant_fields - .iter_enumerated() - .map(|(k, v)| (GenVariantPrinter(k), OneLinePrinter(v))), - ), - ) + .field_with("field_tys", |fmt| { + fmt.debug_map().entries(self.field_tys.iter_enumerated()).finish() + }) + .field_with("variant_fields", |fmt| { + let mut map = fmt.debug_map(); + for (idx, fields) in self.variant_fields.iter_enumerated() { + map.key_with(|fmt| { + let variant_name = ty::CoroutineArgs::variant_name(idx); + if fmt.alternate() { + write!(fmt, "{variant_name:9}({idx:?})") + } else { + write!(fmt, "{variant_name}") + } + }); + // Force variant fields to print in regular mode instead of alternate mode. + map.value_with(|fmt| write!(fmt, "{fields:?}")); + } + map.finish() + }) .field("storage_conflicts", &self.storage_conflicts) .finish() } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 0c17a2e0fe5..29ae2e1bd6b 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -417,7 +417,14 @@ pub enum StatementKind<'tcx> { /// /// Interpreters and codegen backends that don't support coverage instrumentation /// can usually treat this as a no-op. - Coverage(CoverageKind), + Coverage( + // Coverage statements are unlikely to ever contain type information in + // the foreseeable future, so excluding them from TypeFoldable/TypeVisitable + // avoids some unhelpful derive boilerplate. + #[type_foldable(identity)] + #[type_visitable(ignore)] + CoverageKind, + ), /// Denotes a call to an intrinsic that does not require an unwind path and always returns. /// This avoids adding a new block and a terminator for simple intrinsics. diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 473b817aed0..c04a8251fbc 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -581,9 +581,11 @@ impl<'tcx> TerminatorKind<'tcx> { pub enum TerminatorEdges<'mir, 'tcx> { /// For terminators that have no successor, like `return`. None, - /// For terminators that a single successor, like `goto`, and `assert` without cleanup block. + /// For terminators that have a single successor, like `goto`, and `assert` without a cleanup + /// block. Single(BasicBlock), - /// For terminators that two successors, `assert` with cleanup block and `falseEdge`. + /// For terminators that have two successors, like `assert` with a cleanup block, and + /// `falseEdge`. Double(BasicBlock, BasicBlock), /// Special action for `Yield`, `Call` and `InlineAsm` terminators. AssignOnReturn { diff --git a/compiler/rustc_middle/src/query/arena_cached.rs b/compiler/rustc_middle/src/query/arena_cached.rs new file mode 100644 index 00000000000..ec6e466ff68 --- /dev/null +++ b/compiler/rustc_middle/src/query/arena_cached.rs @@ -0,0 +1,47 @@ +/// Helper trait that allows `arena_cache` queries to return `Option<&T>` +/// instead of `&Option<T>`, and avoid allocating `None` in the arena. +/// +/// An arena-cached query must be declared to return a type that implements +/// this trait, i.e. either `&'tcx T` or `Option<&'tcx T>`. This trait then +/// determines the types returned by the provider and stored in the arena, +/// and provides a function to bridge between the three types. +pub trait ArenaCached<'tcx>: Sized { + /// Type that is returned by the query provider. + type Provided; + /// Type that is stored in the arena. + type Allocated: 'tcx; + + /// Takes a provided value, and allocates it in the arena (if appropriate) + /// with the help of the given `arena_alloc` closure. + fn alloc_in_arena( + arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated, + value: Self::Provided, + ) -> Self; +} + +impl<'tcx, T> ArenaCached<'tcx> for &'tcx T { + type Provided = T; + type Allocated = T; + + fn alloc_in_arena( + arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated, + value: Self::Provided, + ) -> Self { + // Just allocate in the arena normally. + arena_alloc(value) + } +} + +impl<'tcx, T> ArenaCached<'tcx> for Option<&'tcx T> { + type Provided = Option<T>; + /// The provide value is `Option<T>`, but we only store `T` in the arena. + type Allocated = T; + + fn alloc_in_arena( + arena_alloc: impl Fn(Self::Allocated) -> &'tcx Self::Allocated, + value: Self::Provided, + ) -> Self { + // Don't store None in the arena, and wrap the allocated reference in Some. + value.map(arena_alloc) + } +} diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 1676afb4b6e..14f871cbbdc 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -349,6 +349,7 @@ tcx_lifetime! { rustc_middle::mir::interpret::GlobalId, rustc_middle::mir::interpret::LitToConstInput, rustc_middle::mir::interpret::EvalStaticInitializerRawResult, + rustc_middle::mir::mono::MonoItemPartitions, rustc_middle::traits::query::MethodAutoderefStepsResult, rustc_middle::traits::query::type_op::AscribeUserType, rustc_middle::traits::query::type_op::Eq, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 65e93c3a1cc..e27a9823639 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -7,7 +7,6 @@ #![allow(unused_parens)] use std::mem; -use std::ops::Deref; use std::path::PathBuf; use std::sync::Arc; @@ -24,7 +23,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, DocLinkResMap}; use rustc_hir::def_id::{ - CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, + CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, }; use rustc_hir::lang_items::{LangItem, LanguageItems}; use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, TraitCandidate}; @@ -59,7 +58,7 @@ use crate::mir::interpret::{ EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, GlobalId, LitToConstInput, }; -use crate::mir::mono::{CodegenUnit, CollectionMode, MonoItem}; +use crate::mir::mono::{CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions}; use crate::query::erase::{Erase, erase, restore}; use crate::query::plumbing::{ CyclePlaceholder, DynamicQuery, query_ensure, query_ensure_error_guaranteed, query_get_at, @@ -85,6 +84,7 @@ use crate::ty::{ }; use crate::{dep_graph, mir, thir}; +mod arena_cached; pub mod erase; mod keys; pub use keys::{AsLocalKey, Key, LocalCrate}; @@ -586,7 +586,7 @@ rustc_queries! { separate_provide_extern } - query mir_coroutine_witnesses(key: DefId) -> &'tcx Option<mir::CoroutineLayout<'tcx>> { + query mir_coroutine_witnesses(key: DefId) -> Option<&'tcx mir::CoroutineLayout<'tcx>> { arena_cache desc { |tcx| "coroutine witness types for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } @@ -618,7 +618,9 @@ rustc_queries! { /// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass /// (for compiler option `-Cinstrument-coverage`), after MIR optimizations /// have had a chance to potentially remove some of them. - query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> &'tcx mir::coverage::CoverageIdsInfo { + /// + /// Returns `None` for functions that were not instrumented. + query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> Option<&'tcx mir::coverage::CoverageIdsInfo> { desc { |tcx| "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) } arena_cache } @@ -2164,7 +2166,7 @@ rustc_queries! { separate_provide_extern } - query collect_and_partition_mono_items(_: ()) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { + query collect_and_partition_mono_items(_: ()) -> MonoItemPartitions<'tcx> { eval_always desc { "collect_and_partition_mono_items" } } @@ -2415,7 +2417,7 @@ rustc_queries! { /// because the `ty::Ty`-based wfcheck is always run. query diagnostic_hir_wf_check( key: (ty::Predicate<'tcx>, WellFormedLoc) - ) -> &'tcx Option<ObligationCause<'tcx>> { + ) -> Option<&'tcx ObligationCause<'tcx>> { arena_cache eval_always no_hash diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 2cb6f6d8c6e..1c157f33a81 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -289,10 +289,10 @@ macro_rules! define_callbacks { /// This type alias specifies the type returned from query providers and the type /// used for decoding. For regular queries this is the declared returned type `V`, - /// but `arena_cache` will use `<V as Deref>::Target` instead. + /// but `arena_cache` will use `<V as ArenaCached>::Provided` instead. pub type ProvidedValue<'tcx> = query_if_arena!( [$($modifiers)*] - (<$V as Deref>::Target) + (<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided) ($V) ); @@ -307,10 +307,18 @@ macro_rules! define_callbacks { ) -> Erase<Value<'tcx>> { erase(query_if_arena!([$($modifiers)*] { - if mem::needs_drop::<ProvidedValue<'tcx>>() { - &*_tcx.query_system.arenas.$name.alloc(value) + use $crate::query::arena_cached::ArenaCached; + + if mem::needs_drop::<<$V as ArenaCached<'tcx>>::Allocated>() { + <$V as ArenaCached>::alloc_in_arena( + |v| _tcx.query_system.arenas.$name.alloc(v), + value, + ) } else { - &*_tcx.arena.dropless.alloc(value) + <$V as ArenaCached>::alloc_in_arena( + |v| _tcx.arena.dropless.alloc(v), + value, + ) } } (value) @@ -354,7 +362,7 @@ macro_rules! define_callbacks { pub struct QueryArenas<'tcx> { $($(#[$attr])* pub $name: query_if_arena!([$($modifiers)*] - (TypedArena<<$V as Deref>::Target>) + (TypedArena<<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Allocated>) () ),)* } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index db2bb8a7248..55d78e083e0 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -345,9 +345,6 @@ pub enum ObligationCauseCode<'tcx> { /// `main` has wrong type MainFunctionType, - /// `start` has wrong type - StartFunctionType, - /// language function has wrong type LangFunctionType(Symbol), diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 33d39b137b6..c4d5367e2f0 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -433,7 +433,7 @@ pub fn analyze_coroutine_closure_captures<'a, 'tcx: 'a, T>( // A parent matches a child if they share the same prefix of projections. // The child may have more, if it is capturing sub-fields out of // something that is captured by-move in the parent closure. - while child_captures.peek().map_or(false, |(_, child_capture)| { + while child_captures.peek().is_some_and(|(_, child_capture)| { child_prefix_matches_parent_projections(parent_capture, child_capture) }) { let (child_field_idx, child_capture) = child_captures.next().unwrap(); diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 7035e641f39..1f0710e2415 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -33,7 +33,7 @@ use rustc_errors::{ use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::Definitions; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; use rustc_hir::lang_items::LangItem; use rustc_hir::{self as hir, Attribute, HirId, Node, TraitCandidate}; use rustc_index::IndexVec; @@ -52,7 +52,9 @@ use rustc_type_ir::TyKind::*; use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::lang_items::TraitSolverLangItem; pub use rustc_type_ir::lift::Lift; -use rustc_type_ir::{CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo, search_graph}; +use rustc_type_ir::{ + CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo, elaborate, search_graph, +}; use tracing::{debug, instrument}; use crate::arena::Arena; @@ -1347,6 +1349,33 @@ pub struct GlobalCtxt<'tcx> { /// Stores memory for globals (statics/consts). pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>, + + current_gcx: CurrentGcx, +} + +impl<'tcx> GlobalCtxt<'tcx> { + /// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of + /// `f`. + pub fn enter<F, R>(&'tcx self, f: F) -> R + where + F: FnOnce(TyCtxt<'tcx>) -> R, + { + let icx = tls::ImplicitCtxt::new(self); + + // Reset `current_gcx` to `None` when we exit. + let _on_drop = defer(move || { + *self.current_gcx.value.write() = None; + }); + + // Set this `GlobalCtxt` as the current one. + { + let mut guard = self.current_gcx.value.write(); + assert!(guard.is_none(), "no `GlobalCtxt` is currently set"); + *guard = Some(self as *const _ as *const ()); + } + + tls::enter_context(&icx, || f(icx.tcx)) + } } /// This is used to get a reference to a `GlobalCtxt` if one is available. @@ -1537,23 +1566,11 @@ impl<'tcx> TyCtxt<'tcx> { canonical_param_env_cache: Default::default(), data_layout, alloc_map: Lock::new(interpret::AllocMap::new()), + current_gcx, }); - let icx = tls::ImplicitCtxt::new(&gcx); - - // Reset `current_gcx` to `None` when we exit. - let _on_drop = defer(|| { - *current_gcx.value.write() = None; - }); - - // Set this `GlobalCtxt` as the current one. - { - let mut guard = current_gcx.value.write(); - assert!(guard.is_none(), "no `GlobalCtxt` is currently set"); - *guard = Some(&gcx as *const _ as *const ()); - } - - tls::enter_context(&icx, || f(icx.tcx)) + // This is a separate function to work around a crash with parallel rustc (#135870) + gcx.enter(f) } /// Obtain all lang items of this crate and all dependencies (recursively) @@ -2026,7 +2043,7 @@ impl<'tcx> TyCtxt<'tcx> { }; let mut v = TraitObjectVisitor(vec![], self.hir()); - v.visit_ty(hir_output); + v.visit_ty_unambig(hir_output); v.0 } @@ -2048,7 +2065,7 @@ impl<'tcx> TyCtxt<'tcx> { && let Some(alias_ty) = self.hir_node_by_def_id(local_id).alias_ty() // it is type alias && let Some(alias_generics) = self.hir_node_by_def_id(local_id).generics() { - v.visit_ty(alias_ty); + v.visit_ty_unambig(alias_ty); if !v.0.is_empty() { return Some(( v.0, @@ -2323,51 +2340,41 @@ macro_rules! sty_debug_print { } impl<'tcx> TyCtxt<'tcx> { - pub fn debug_stats(self) -> impl std::fmt::Debug + 'tcx { - struct DebugStats<'tcx>(TyCtxt<'tcx>); - - impl<'tcx> std::fmt::Debug for DebugStats<'tcx> { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - sty_debug_print!( - fmt, - self.0, - Adt, - Array, - Slice, - RawPtr, - Ref, - FnDef, - FnPtr, - UnsafeBinder, - Placeholder, - Coroutine, - CoroutineWitness, - Dynamic, - Closure, - CoroutineClosure, - Tuple, - Bound, - Param, - Infer, - Alias, - Pat, - Foreign - )?; - - writeln!(fmt, "GenericArgs interner: #{}", self.0.interners.args.len())?; - writeln!(fmt, "Region interner: #{}", self.0.interners.region.len())?; - writeln!( - fmt, - "Const Allocation interner: #{}", - self.0.interners.const_allocation.len() - )?; - writeln!(fmt, "Layout interner: #{}", self.0.interners.layout.len())?; - - Ok(()) - } - } - - DebugStats(self) + pub fn debug_stats(self) -> impl fmt::Debug + 'tcx { + fmt::from_fn(move |fmt| { + sty_debug_print!( + fmt, + self, + Adt, + Array, + Slice, + RawPtr, + Ref, + FnDef, + FnPtr, + UnsafeBinder, + Placeholder, + Coroutine, + CoroutineWitness, + Dynamic, + Closure, + CoroutineClosure, + Tuple, + Bound, + Param, + Infer, + Alias, + Pat, + Foreign + )?; + + writeln!(fmt, "GenericArgs interner: #{}", self.interners.args.len())?; + writeln!(fmt, "Region interner: #{}", self.interners.region.len())?; + writeln!(fmt, "Const Allocation interner: #{}", self.interners.const_allocation.len())?; + writeln!(fmt, "Layout interner: #{}", self.interners.layout.len())?; + + Ok(()) + }) } } @@ -2558,7 +2565,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Given the def_id of a Trait `trait_def_id` and the name of an associated item `assoc_name` /// returns true if the `trait_def_id` defines an associated item of name `assoc_name`. pub fn trait_may_define_assoc_item(self, trait_def_id: DefId, assoc_name: Ident) -> bool { - self.supertrait_def_ids(trait_def_id).any(|trait_did| { + elaborate::supertrait_def_ids(self, trait_def_id).any(|trait_did| { self.associated_items(trait_did) .filter_by_name_unhygienic(assoc_name.name) .any(|item| self.hygienic_eq(assoc_name, item.ident(self), trait_did)) @@ -2579,14 +2586,6 @@ impl<'tcx> TyCtxt<'tcx> { }) } - /// Computes the def-ids of the transitive supertraits of `trait_def_id`. This (intentionally) - /// does not compute the full elaborated super-predicates but just the set of def-ids. It is used - /// to identify which traits may define a given associated type to help avoid cycle errors, - /// and to make size estimates for vtable layout computation. - pub fn supertrait_def_ids(self, trait_def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx { - rustc_type_ir::elaborate::supertrait_def_ids(self, trait_def_id) - } - /// Given a closure signature, returns an equivalent fn signature. Detuples /// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then /// you would get a `fn(u32, i32)`. diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 77745599afb..b4b66a8133a 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -9,7 +9,7 @@ use rustc_errors::{ }; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicateKind}; +use rustc_hir::{self as hir, AmbigArg, LangItem, PredicateOrigin, WherePredicateKind}; use rustc_span::{BytePos, Span}; use rustc_type_ir::TyKind::*; @@ -336,7 +336,7 @@ pub fn suggest_constraining_type_params<'a>( .collect(); constraints - .retain(|(_, def_id, _)| def_id.map_or(true, |def| !bound_trait_defs.contains(&def))); + .retain(|(_, def_id, _)| def_id.is_none_or(|def| !bound_trait_defs.contains(&def))); if constraints.is_empty() { continue; @@ -570,18 +570,18 @@ pub fn suggest_constraining_type_params<'a>( pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>); impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { match ty.kind { - hir::TyKind::TraitObject( - _, - hir::Lifetime { + hir::TyKind::TraitObject(_, tagged_ptr) + if let hir::Lifetime { res: hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static, .. - }, - _, - ) - | hir::TyKind::OpaqueDef(..) => self.0.push(ty), + } = tagged_ptr.pointer() => + { + self.0.push(ty.as_unambig_ty()) + } + hir::TyKind::OpaqueDef(..) => self.0.push(ty.as_unambig_ty()), _ => {} } hir::intravisit::walk_ty(self, ty); diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index e4ded2c30f5..b7a648aae3f 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -328,7 +328,7 @@ impl<'tcx> InstanceKind<'tcx> { // We include enums without destructors to allow, say, optimizing // drops of `Option::None` before LTO. We also respect the intent of // `#[inline]` on `Drop::drop` implementations. - return ty.ty_adt_def().map_or(true, |adt_def| { + return ty.ty_adt_def().is_none_or(|adt_def| { match *self { ty::InstanceKind::DropGlue(..) => adt_def.destructor(tcx).map(|dtor| dtor.did), ty::InstanceKind::AsyncDropGlueCtorShim(..) => { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 1e67cdfc32a..0d41a1d7dbf 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -231,6 +231,7 @@ impl fmt::Display for ValidityRequirement { pub enum LayoutError<'tcx> { Unknown(Ty<'tcx>), SizeOverflow(Ty<'tcx>), + TooGeneric(Ty<'tcx>), NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>), ReferencesError(ErrorGuaranteed), Cycle(ErrorGuaranteed), @@ -244,6 +245,7 @@ impl<'tcx> LayoutError<'tcx> { match self { Unknown(_) => middle_unknown_layout, SizeOverflow(_) => middle_values_too_big, + TooGeneric(_) => middle_too_generic, NormalizationFailure(_, _) => middle_cannot_be_normalized, Cycle(_) => middle_cycle, ReferencesError(_) => middle_layout_references_error, @@ -257,6 +259,7 @@ impl<'tcx> LayoutError<'tcx> { match self { Unknown(ty) => E::Unknown { ty }, SizeOverflow(ty) => E::Overflow { ty }, + TooGeneric(ty) => E::TooGeneric { ty }, NormalizationFailure(ty, e) => { E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() } } @@ -272,6 +275,9 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { LayoutError::Unknown(ty) => write!(f, "the type `{ty}` has an unknown layout"), + LayoutError::TooGeneric(ty) => { + write!(f, "`{ty}` does not have a fixed size") + } LayoutError::SizeOverflow(ty) => { write!(f, "values of the type `{ty}` are too big for the target architecture") } @@ -350,10 +356,11 @@ impl<'tcx> SizeSkeleton<'tcx> { return Err(tcx.arena.alloc(LayoutError::Unknown(ty))); } } - Err(err @ LayoutError::Unknown(_)) => err, + Err(err @ LayoutError::TooGeneric(_)) => err, // We can't extract SizeSkeleton info from other layout errors Err( e @ LayoutError::Cycle(_) + | e @ LayoutError::Unknown(_) | e @ LayoutError::SizeOverflow(_) | e @ LayoutError::NormalizationFailure(..) | e @ LayoutError::ReferencesError(_), @@ -413,10 +420,9 @@ impl<'tcx> SizeSkeleton<'tcx> { // Alignment is unchanged by arrays. return Ok(SizeSkeleton::Known(Size::from_bytes(size), a)); } - Err(tcx.arena.alloc(LayoutError::Unknown(ty))) + Err(err) } - SizeSkeleton::Pointer { .. } => Err(err), - SizeSkeleton::Generic(_) => Err(tcx.arena.alloc(LayoutError::Unknown(ty))), + SizeSkeleton::Pointer { .. } | SizeSkeleton::Generic(_) => Err(err), } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ca70ae794c5..8cd632790a8 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1596,6 +1596,15 @@ impl<'tcx> TyCtxt<'tcx> { Some(Ident::new(def, span)) } + /// Look up the name and span of a definition. + /// + /// See [`item_name`][Self::item_name] for more information. + pub fn item_ident(self, def_id: DefId) -> Ident { + self.opt_item_ident(def_id).unwrap_or_else(|| { + bug!("item_ident: no name for {:?}", self.def_path(def_id)); + }) + } + pub fn opt_associated_item(self, def_id: DefId) -> Option<AssocItem> { if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = self.def_kind(def_id) { Some(self.associated_item(def_id)) diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index bf37ae05c82..a9a47c87a38 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1894,11 +1894,11 @@ impl<'tcx> Ty<'tcx> { ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false, - ty::Tuple(tys) => tys.last().map_or(true, |ty| ty.is_trivially_sized(tcx)), + ty::Tuple(tys) => tys.last().is_none_or(|ty| ty.is_trivially_sized(tcx)), ty::Adt(def, args) => def .sized_constraint(tcx) - .map_or(true, |ty| ty.instantiate(tcx, args).is_trivially_sized(tcx)), + .is_none_or(|ty| ty.instantiate(tcx, args).is_trivially_sized(tcx)), ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) | ty::Bound(..) => false, diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 75893da0e58..7d5e5c2e823 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -20,6 +20,7 @@ use tracing::{debug, instrument}; use super::TypingEnv; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use crate::mir; use crate::query::Providers; use crate::ty::fold::fold_regions; use crate::ty::layout::{FloatExt, IntegerExt}; @@ -1183,18 +1184,18 @@ impl<'tcx> Ty<'tcx> { /// Returns the maximum value for the given numeric type (including `char`s) /// or returns `None` if the type is not numeric. - pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option<ty::Const<'tcx>> { + pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option<mir::Const<'tcx>> { let typing_env = TypingEnv::fully_monomorphized(); self.numeric_min_and_max_as_bits(tcx) - .map(|(_, max)| ty::Const::from_bits(tcx, max, typing_env, self)) + .map(|(_, max)| mir::Const::from_bits(tcx, max, typing_env, self)) } /// Returns the minimum value for the given numeric type (including `char`s) /// or returns `None` if the type is not numeric. - pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option<ty::Const<'tcx>> { + pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option<mir::Const<'tcx>> { let typing_env = TypingEnv::fully_monomorphized(); self.numeric_min_and_max_as_bits(tcx) - .map(|(min, _)| ty::Const::from_bits(tcx, min, typing_env, self)) + .map(|(min, _)| mir::Const::from_bits(tcx, min, typing_env, self)) } /// Checks whether values of this type `T` have a size known at diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 09a05104e49..23e2e8ad3d3 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -2,6 +2,7 @@ use std::fmt; use rustc_ast::Mutability; use rustc_macros::HashStable; +use rustc_type_ir::elaborate; use crate::mir::interpret::{AllocId, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range}; use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt}; @@ -64,7 +65,7 @@ pub(crate) fn vtable_min_entries<'tcx>( }; // This includes self in supertraits. - for def_id in tcx.supertrait_def_ids(trait_ref.def_id()) { + for def_id in elaborate::supertrait_def_ids(tcx, trait_ref.def_id()) { count += tcx.own_existential_vtable_entries(def_id).len(); } diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 390909bb0ab..867f8f63969 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -360,7 +360,7 @@ fn find_item_ty_spans( if let Res::Def(kind, def_id) = path.res && matches!(kind, DefKind::Enum | DefKind::Struct | DefKind::Union) { - let check_params = def_id.as_local().map_or(true, |def_id| { + let check_params = def_id.as_local().is_none_or(|def_id| { if def_id == needle { spans.push(ty.span); } @@ -374,7 +374,13 @@ fn find_item_ty_spans( if let hir::GenericArg::Type(ty) = arg && params_in_repr.contains(i as u32) { - find_item_ty_spans(tcx, ty, needle, spans, seen_representable); + find_item_ty_spans( + tcx, + ty.as_unambig_ty(), + needle, + spans, + seen_representable, + ); } } } diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index ffdb721fb18..053775b4937 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -25,6 +25,8 @@ mir_build_borrow_of_moved_value = borrow of moved value .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 + .full_type_name = the full type name has been written to '{$path}' + .consider_verbose = consider using `--verbose` to print the full type name to the console mir_build_call_to_deprecated_safe_fn_requires_unsafe = call to deprecated safe function `{$function}` is unsafe and requires unsafe block diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index b944d13fb0d..b21ec8f3083 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -1986,6 +1986,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { return; } + let false_edge_start_block = candidate.subcandidates[0].false_edge_start_block; candidate.subcandidates.retain_mut(|candidate| { if candidate.extra_data.is_never { candidate.visit_leaves(|subcandidate| { @@ -2000,8 +2001,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } }); if candidate.subcandidates.is_empty() { - // If `candidate` has become a leaf candidate, ensure it has a `pre_binding_block`. - candidate.pre_binding_block = Some(self.cfg.start_new_block()); + // If `candidate` has become a leaf candidate, ensure it has a `pre_binding_block` and `otherwise_block`. + let next_block = self.cfg.start_new_block(); + candidate.pre_binding_block = Some(next_block); + candidate.otherwise_block = Some(next_block); + // In addition, if `candidate` doesn't have `false_edge_start_block`, it should be assigned here. + if candidate.false_edge_start_block.is_none() { + candidate.false_edge_start_block = false_edge_start_block; + } } } diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 20441530a47..945f02821d9 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -785,6 +785,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let local = place.as_local().unwrap_or_else(|| bug!("projection in tail call args")); + if !self.local_decls[local].ty.needs_drop(self.tcx, self.typing_env()) { + return None; + } + Some(DropData { source_info, local, kind: DropKind::Value }) } Operand::Constant(_) => None, @@ -795,6 +799,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.scopes.scopes.iter().rev().nth(1).unwrap().region_scope, DUMMY_SP, ); + let typing_env = self.typing_env(); let unwind_drops = &mut self.scopes.unwind_drops; // the innermost scope contains only the destructors for the tail call arguments @@ -805,6 +810,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = drop_data.source_info; let local = drop_data.local; + if !self.local_decls[local].ty.needs_drop(self.tcx, typing_env) { + continue; + } + match drop_data.kind { DropKind::Value => { // `unwind_to` should drop the value that we're about to diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 5eed9ef798d..995bc311b7c 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -266,7 +266,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for LayoutConstrainedPlaceVisitor<'a, 'tcx> { // place, i.e. the expression is a place expression and not a dereference // (since dereferencing something leads us to a different place). ExprKind::Deref { .. } => {} - ref kind if ExprCategory::of(kind).map_or(true, |cat| cat == ExprCategory::Place) => { + ref kind if ExprCategory::of(kind).is_none_or(|cat| cat == ExprCategory::Place) => { visit::walk_expr(self, expr); } diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 83aec9ccdef..1f87bf0dbbb 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -7,7 +7,7 @@ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; use rustc_pattern_analysis::errors::Uncovered; use rustc_pattern_analysis::rustc::RustcPatCtxt; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; @@ -753,7 +753,7 @@ pub(crate) struct BindingsWithVariantName { #[suggestion(code = "{ty_path}::{name}", applicability = "machine-applicable")] pub(crate) suggestion: Option<Span>, pub(crate) ty_path: String, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(LintDiagnostic)] @@ -790,17 +790,21 @@ pub(crate) struct IrrefutableLetPatternsWhileLet { #[derive(Diagnostic)] #[diag(mir_build_borrow_of_moved_value)] -pub(crate) struct BorrowOfMovedValue<'tcx> { +pub(crate) struct BorrowOfMovedValue { #[primary_span] #[label] #[label(mir_build_occurs_because_label)] pub(crate) binding_span: Span, #[label(mir_build_value_borrowed_label)] pub(crate) conflicts_ref: Vec<Span>, - pub(crate) name: Symbol, - pub(crate) ty: Ty<'tcx>, + pub(crate) name: Ident, + pub(crate) ty: String, #[suggestion(code = "ref ", applicability = "machine-applicable")] pub(crate) suggest_borrowing: Option<Span>, + #[note(mir_build_full_type_name)] + #[note(mir_build_consider_verbose)] + pub(crate) has_path: bool, + pub(crate) path: String, } #[derive(Diagnostic)] 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 66310867200..e0a1117f905 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -25,7 +25,7 @@ use rustc_session::lint::builtin::{ }; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; -use rustc_span::{Span, sym}; +use rustc_span::{Ident, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::instrument; @@ -795,12 +795,16 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: } }); if !conflicts_ref.is_empty() { + let mut path = None; + let ty = cx.tcx.short_ty_string(ty, &mut path); sess.dcx().emit_err(BorrowOfMovedValue { binding_span: pat.span, conflicts_ref, - name, + name: Ident::new(name, pat.span), ty, suggest_borrowing: Some(pat.span.shrink_to_lo()), + has_path: path.is_some(), + path: path.map(|p| p.display().to_string()).unwrap_or_default(), }); } return; @@ -904,7 +908,7 @@ fn check_for_bindings_named_same_as_variants( None }, ty_path, - name, + name: Ident::new(name, pat.span), }, ) } @@ -1086,6 +1090,12 @@ fn find_fallback_pattern_typo<'tcx>( let vis = cx.tcx.visibility(item.owner_id); if vis.is_accessible_from(parent, cx.tcx) { accessible.push(item_name); + // FIXME: the line below from PR #135310 is a workaround for the ICE in issue + // #135289, where a macro in a dependency can create unreachable patterns in the + // current crate. Path trimming expects diagnostics for a typoed const, but no + // diagnostics are emitted and we ICE. See + // `tests/ui/resolve/const-with-typo-in-pattern-binding-ice-135289.rs` for a + // test that reproduces the ICE if we don't use `with_no_trimmed_paths!`. let path = with_no_trimmed_paths!(cx.tcx.def_path_str(item.owner_id)); accessible_path.push(path); } else if name == item_name { diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index e457b514936..9ccabb46c63 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -608,7 +608,7 @@ where let before = diffs_before.as_mut().map(next_in_dataflow_order); assert!(diffs_after.is_empty()); - assert!(diffs_before.as_ref().map_or(true, ExactSizeIterator::is_empty)); + assert!(diffs_before.as_ref().is_none_or(ExactSizeIterator::is_empty)); let terminator = self.cursor.body()[block].terminator(); let mut terminator_str = String::new(); diff --git a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs index 03f11885d58..5bd6fdcf485 100644 --- a/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs +++ b/compiler/rustc_mir_transform/src/abort_unwinding_calls.rs @@ -116,4 +116,8 @@ impl<'tcx> crate::MirPass<'tcx> for AbortUnwindingCalls { // We may have invalidated some `cleanup` blocks so clean those up now. super::simplify::remove_dead_blocks(body); } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs index 24c955c0c78..55694cacd92 100644 --- a/compiler/rustc_mir_transform/src/add_call_guards.rs +++ b/compiler/rustc_mir_transform/src/add_call_guards.rs @@ -75,4 +75,8 @@ impl<'tcx> crate::MirPass<'tcx> for AddCallGuards { body.basic_blocks_mut().extend(new_blocks); } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs index 12a2fe23b14..87ae2b71654 100644 --- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs +++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs @@ -68,6 +68,10 @@ impl<'tcx> crate::MirPass<'tcx> for AddMovesForPackedDrops { patch.apply(body); } + + fn is_required(&self) -> bool { + true + } } fn add_move_for_packed_drop<'tcx>( diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index 53176eec9bc..1fc788a2dad 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -176,4 +176,8 @@ impl<'tcx> crate::MirPass<'tcx> for AddRetag { } } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs index e585e338613..e41f47db545 100644 --- a/compiler/rustc_mir_transform/src/add_subtyping_projections.rs +++ b/compiler/rustc_mir_transform/src/add_subtyping_projections.rs @@ -61,4 +61,8 @@ impl<'tcx> crate::MirPass<'tcx> for Subtyper { } checker.patcher.apply(body); } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/check_alignment.rs b/compiler/rustc_mir_transform/src/check_alignment.rs index 1b7c89fd251..d7e22c12394 100644 --- a/compiler/rustc_mir_transform/src/check_alignment.rs +++ b/compiler/rustc_mir_transform/src/check_alignment.rs @@ -60,6 +60,10 @@ impl<'tcx> crate::MirPass<'tcx> for CheckAlignment { } } } + + fn is_required(&self) -> bool { + true + } } struct PointerFinder<'a, 'tcx> { diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index 6a22a58470c..cb844019857 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -72,4 +72,8 @@ impl<'tcx> crate::MirPass<'tcx> for CleanupPostBorrowck { decl.user_ty = None; } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/copy_prop.rs b/compiler/rustc_mir_transform/src/copy_prop.rs index f149bf97cde..27af5818982 100644 --- a/compiler/rustc_mir_transform/src/copy_prop.rs +++ b/compiler/rustc_mir_transform/src/copy_prop.rs @@ -56,6 +56,10 @@ impl<'tcx> crate::MirPass<'tcx> for CopyProp { crate::simplify::remove_unused_definitions(body); } } + + fn is_required(&self) -> bool { + false + } } /// `SsaLocals` computed equivalence classes between locals considering copy/move assignments. diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index a3715b5d485..3b75be58e43 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1688,6 +1688,10 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { // Run derefer to fix Derefs that are not in the first place deref_finder(tcx, body); } + + fn is_required(&self) -> bool { + true + } } /// Looks for any assignments between locals (e.g., `_4 = _5`) that will both be converted to fields diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 8d397f63cc7..adb99a75a9e 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -10,12 +10,12 @@ use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op}; use crate::coverage::counters::balanced_flow::BalancedFlowGraph; -use crate::coverage::counters::iter_nodes::IterNodes; -use crate::coverage::counters::node_flow::{CounterTerm, MergedNodeFlowGraph, NodeCounters}; +use crate::coverage::counters::node_flow::{ + CounterTerm, NodeCounters, make_node_counters, node_flow_data_for_balanced_graph, +}; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; mod balanced_flow; -mod iter_nodes; mod node_flow; mod union_find; @@ -27,12 +27,12 @@ pub(super) fn make_bcb_counters( ) -> CoverageCounters { // Create the derived graphs that are necessary for subsequent steps. let balanced_graph = BalancedFlowGraph::for_graph(graph, |n| !graph[n].is_out_summable); - let merged_graph = MergedNodeFlowGraph::for_balanced_graph(&balanced_graph); + let node_flow_data = node_flow_data_for_balanced_graph(&balanced_graph); // Use those graphs to determine which nodes get physical counters, and how // to compute the execution counts of other nodes from those counters. - let nodes = make_node_counter_priority_list(graph, balanced_graph); - let node_counters = merged_graph.make_node_counters(&nodes); + let priority_list = make_node_flow_priority_list(graph, balanced_graph); + let node_counters = make_node_counters(&node_flow_data, &priority_list); // Convert the counters into a form suitable for embedding into MIR. transcribe_counters(&node_counters, bcb_needs_counter) @@ -40,7 +40,7 @@ pub(super) fn make_bcb_counters( /// Arranges the nodes in `balanced_graph` into a list, such that earlier nodes /// take priority in being given a counter expression instead of a physical counter. -fn make_node_counter_priority_list( +fn make_node_flow_priority_list( graph: &CoverageGraph, balanced_graph: BalancedFlowGraph<&CoverageGraph>, ) -> Vec<BasicCoverageBlock> { @@ -81,11 +81,11 @@ fn transcribe_counters( let mut new = CoverageCounters::with_num_bcbs(bcb_needs_counter.domain_size()); for bcb in bcb_needs_counter.iter() { - // Our counter-creation algorithm doesn't guarantee that a counter - // expression starts or ends with a positive term, so partition the + // Our counter-creation algorithm doesn't guarantee that a node's list + // of terms starts or ends with a positive term, so partition the // counters into "positive" and "negative" lists for easier handling. let (mut pos, mut neg): (Vec<_>, Vec<_>) = - old.counter_expr(bcb).iter().partition_map(|&CounterTerm { node, op }| match op { + old.counter_terms[bcb].iter().partition_map(|&CounterTerm { node, op }| match op { Op::Add => Either::Left(node), Op::Subtract => Either::Right(node), }); diff --git a/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs b/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs index c108f96a564..4c20722a043 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs @@ -20,8 +20,6 @@ use rustc_data_structures::graph::reversed::ReversedGraph; use rustc_index::Idx; use rustc_index::bit_set::DenseBitSet; -use crate::coverage::counters::iter_nodes::IterNodes; - /// A view of an underlying graph that has been augmented to have “balanced flow”. /// This means that the flow (execution count) of each node is equal to the /// sum of its in-edge flows, and also equal to the sum of its out-edge flows. diff --git a/compiler/rustc_mir_transform/src/coverage/counters/iter_nodes.rs b/compiler/rustc_mir_transform/src/coverage/counters/iter_nodes.rs deleted file mode 100644 index 9d87f7af1b0..00000000000 --- a/compiler/rustc_mir_transform/src/coverage/counters/iter_nodes.rs +++ /dev/null @@ -1,16 +0,0 @@ -use rustc_data_structures::graph; -use rustc_index::Idx; - -pub(crate) trait IterNodes: graph::DirectedGraph { - /// Iterates over all nodes of a graph in ascending numeric order. - /// Assumes that nodes are densely numbered, i.e. every index in - /// `0..num_nodes` is a valid node. - /// - /// FIXME: Can this just be part of [`graph::DirectedGraph`]? - fn iter_nodes( - &self, - ) -> impl Iterator<Item = Self::Node> + DoubleEndedIterator + ExactSizeIterator { - (0..self.num_nodes()).map(<Self::Node as Idx>::new) - } -} -impl<G: graph::DirectedGraph> IterNodes for G {} diff --git a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs index 610498c6c0e..9d80b3af42d 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs @@ -8,18 +8,16 @@ use rustc_data_structures::graph; use rustc_index::bit_set::DenseBitSet; -use rustc_index::{Idx, IndexVec}; +use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_middle::mir::coverage::Op; -use smallvec::SmallVec; -use crate::coverage::counters::iter_nodes::IterNodes; -use crate::coverage::counters::union_find::{FrozenUnionFind, UnionFind}; +use crate::coverage::counters::union_find::UnionFind; #[cfg(test)] mod tests; -/// View of some underlying graph, in which each node's successors have been -/// merged into a single "supernode". +/// Data representing a view of some underlying graph, in which each node's +/// successors have been merged into a single "supernode". /// /// The resulting supernodes have no obvious meaning on their own. /// However, merging successor nodes means that a node's out-edges can all @@ -30,10 +28,10 @@ mod tests; /// in the merged graph, it becomes possible to analyze the original node flows /// using techniques for analyzing edge flows. #[derive(Debug)] -pub(crate) struct MergedNodeFlowGraph<Node: Idx> { +pub(crate) struct NodeFlowData<Node: Idx> { /// Maps each node to the supernode that contains it, indicated by some /// arbitrary "root" node that is part of that supernode. - supernodes: FrozenUnionFind<Node>, + supernodes: IndexVec<Node, Node>, /// For each node, stores the single supernode that all of its successors /// have been merged into. /// @@ -42,84 +40,71 @@ pub(crate) struct MergedNodeFlowGraph<Node: Idx> { succ_supernodes: IndexVec<Node, Node>, } -impl<Node: Idx> MergedNodeFlowGraph<Node> { - /// Creates a "merged" view of an underlying graph. - /// - /// The given graph is assumed to have [“balanced flow”](balanced-flow), - /// though it does not necessarily have to be a `BalancedFlowGraph`. - /// - /// [balanced-flow]: `crate::coverage::counters::balanced_flow::BalancedFlowGraph`. - pub(crate) fn for_balanced_graph<G>(graph: G) -> Self - where - G: graph::DirectedGraph<Node = Node> + graph::Successors, - { - let mut supernodes = UnionFind::<G::Node>::new(graph.num_nodes()); - - // For each node, merge its successors into a single supernode, and - // arbitrarily choose one of those successors to represent all of them. - let successors = graph - .iter_nodes() - .map(|node| { - graph - .successors(node) - .reduce(|a, b| supernodes.unify(a, b)) - .expect("each node in a balanced graph must have at least one out-edge") - }) - .collect::<IndexVec<G::Node, G::Node>>(); - - // Now that unification is complete, freeze the supernode forest, - // and resolve each arbitrarily-chosen successor to its canonical root. - // (This avoids having to explicitly resolve them later.) - let supernodes = supernodes.freeze(); - let succ_supernodes = successors.into_iter().map(|succ| supernodes.find(succ)).collect(); - - Self { supernodes, succ_supernodes } - } - - fn num_nodes(&self) -> usize { - self.succ_supernodes.len() - } +/// Creates a "merged" view of an underlying graph. +/// +/// The given graph is assumed to have [“balanced flow”](balanced-flow), +/// though it does not necessarily have to be a `BalancedFlowGraph`. +/// +/// [balanced-flow]: `crate::coverage::counters::balanced_flow::BalancedFlowGraph`. +pub(crate) fn node_flow_data_for_balanced_graph<G>(graph: G) -> NodeFlowData<G::Node> +where + G: graph::Successors, +{ + let mut supernodes = UnionFind::<G::Node>::new(graph.num_nodes()); + + // For each node, merge its successors into a single supernode, and + // arbitrarily choose one of those successors to represent all of them. + let successors = graph + .iter_nodes() + .map(|node| { + graph + .successors(node) + .reduce(|a, b| supernodes.unify(a, b)) + .expect("each node in a balanced graph must have at least one out-edge") + }) + .collect::<IndexVec<G::Node, G::Node>>(); + + // Now that unification is complete, take a snapshot of the supernode forest, + // and resolve each arbitrarily-chosen successor to its canonical root. + // (This avoids having to explicitly resolve them later.) + let supernodes = supernodes.snapshot(); + let succ_supernodes = successors.into_iter().map(|succ| supernodes[succ]).collect(); + + NodeFlowData { supernodes, succ_supernodes } +} - fn is_supernode(&self, node: Node) -> bool { - self.supernodes.find(node) == node +/// Uses the graph information in `node_flow_data`, together with a given +/// permutation of all nodes in the graph, to create physical counters and +/// counter expressions for each node in the underlying graph. +/// +/// The given list must contain exactly one copy of each node in the +/// underlying balanced-flow graph. The order of nodes is used as a hint to +/// influence counter allocation: +/// - Earlier nodes are more likely to receive counter expressions. +/// - Later nodes are more likely to receive physical counters. +pub(crate) fn make_node_counters<Node: Idx>( + node_flow_data: &NodeFlowData<Node>, + priority_list: &[Node], +) -> NodeCounters<Node> { + let mut builder = SpantreeBuilder::new(node_flow_data); + + for &node in priority_list { + builder.visit_node(node); } - /// Using the information in this merged graph, together with a given - /// permutation of all nodes in the graph, to create physical counters and - /// counter expressions for each node in the underlying graph. - /// - /// The given list must contain exactly one copy of each node in the - /// underlying balanced-flow graph. The order of nodes is used as a hint to - /// influence counter allocation: - /// - Earlier nodes are more likely to receive counter expressions. - /// - Later nodes are more likely to receive physical counters. - pub(crate) fn make_node_counters(&self, all_nodes_permutation: &[Node]) -> NodeCounters<Node> { - let mut builder = SpantreeBuilder::new(self); - - for &node in all_nodes_permutation { - builder.visit_node(node); - } - - NodeCounters { counter_exprs: builder.finish() } - } + NodeCounters { counter_terms: builder.finish() } } /// End result of allocating physical counters and counter expressions for the /// nodes of a graph. #[derive(Debug)] pub(crate) struct NodeCounters<Node: Idx> { - counter_exprs: IndexVec<Node, CounterExprVec<Node>>, -} - -impl<Node: Idx> NodeCounters<Node> { /// For the given node, returns the finished list of terms that represent /// its physical counter or counter expression. Always non-empty. /// - /// If a node was given a physical counter, its "expression" will contain + /// If a node was given a physical counter, the term list will contain /// that counter as its sole element. - pub(crate) fn counter_expr(&self, this: Node) -> &[CounterTerm<Node>] { - self.counter_exprs[this].as_slice() - } + pub(crate) counter_terms: IndexVec<Node, Vec<CounterTerm<Node>>>, } #[derive(Debug)] @@ -146,12 +131,11 @@ pub(crate) struct CounterTerm<Node> { pub(crate) node: Node, } -/// Stores the list of counter terms that make up a node's counter expression. -type CounterExprVec<Node> = SmallVec<[CounterTerm<Node>; 2]>; - #[derive(Debug)] struct SpantreeBuilder<'a, Node: Idx> { - graph: &'a MergedNodeFlowGraph<Node>, + supernodes: &'a IndexSlice<Node, Node>, + succ_supernodes: &'a IndexSlice<Node, Node>, + is_unvisited: DenseBitSet<Node>, /// Links supernodes to each other, gradually forming a spanning tree of /// the merged-flow graph. @@ -163,26 +147,32 @@ struct SpantreeBuilder<'a, Node: Idx> { yank_buffer: Vec<Node>, /// An in-progress counter expression for each node. Each expression is /// initially empty, and will be filled in as relevant nodes are visited. - counter_exprs: IndexVec<Node, CounterExprVec<Node>>, + counter_terms: IndexVec<Node, Vec<CounterTerm<Node>>>, } impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { - fn new(graph: &'a MergedNodeFlowGraph<Node>) -> Self { - let num_nodes = graph.num_nodes(); + fn new(node_flow_data: &'a NodeFlowData<Node>) -> Self { + let NodeFlowData { supernodes, succ_supernodes } = node_flow_data; + let num_nodes = supernodes.len(); Self { - graph, + supernodes, + succ_supernodes, is_unvisited: DenseBitSet::new_filled(num_nodes), span_edges: IndexVec::from_fn_n(|_| None, num_nodes), yank_buffer: vec![], - counter_exprs: IndexVec::from_fn_n(|_| SmallVec::new(), num_nodes), + counter_terms: IndexVec::from_fn_n(|_| vec![], num_nodes), } } + fn is_supernode(&self, node: Node) -> bool { + self.supernodes[node] == node + } + /// Given a supernode, finds the supernode that is the "root" of its /// spantree component. Two nodes that have the same spantree root are /// connected in the spantree. fn spantree_root(&self, this: Node) -> Node { - debug_assert!(self.graph.is_supernode(this)); + debug_assert!(self.is_supernode(this)); match self.span_edges[this] { None => this, @@ -193,7 +183,7 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { /// Rotates edges in the spantree so that `this` is the root of its /// spantree component. fn yank_to_spantree_root(&mut self, this: Node) { - debug_assert!(self.graph.is_supernode(this)); + debug_assert!(self.is_supernode(this)); // The rotation is done iteratively, by first traversing from `this` to // its root and storing the path in a buffer, and then traversing the @@ -235,12 +225,12 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { // Get the supernode containing `this`, and make it the root of its // component of the spantree. - let this_supernode = self.graph.supernodes.find(this); + let this_supernode = self.supernodes[this]; self.yank_to_spantree_root(this_supernode); // Get the supernode containing all of this's successors. - let succ_supernode = self.graph.succ_supernodes[this]; - debug_assert!(self.graph.is_supernode(succ_supernode)); + let succ_supernode = self.succ_supernodes[this]; + debug_assert!(self.is_supernode(succ_supernode)); // If two supernodes are already connected in the spantree, they will // have the same spantree root. (Each supernode is connected to itself.) @@ -268,8 +258,8 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { // `this_supernode`. // Instead of setting `this.measure = true` as in the original paper, - // we just add the node's ID to its own "expression". - self.counter_exprs[this].push(CounterTerm { node: this, op: Op::Add }); + // we just add the node's ID to its own list of terms. + self.counter_terms[this].push(CounterTerm { node: this, op: Op::Add }); // Walk the spantree from `this.successor` back to `this`. For each // spantree edge along the way, add this node's physical counter to @@ -279,7 +269,7 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { let &SpantreeEdge { is_reversed, claiming_node, span_parent } = self.span_edges[curr].as_ref().unwrap(); let op = if is_reversed { Op::Subtract } else { Op::Add }; - self.counter_exprs[claiming_node].push(CounterTerm { node: this, op }); + self.counter_terms[claiming_node].push(CounterTerm { node: this, op }); curr = span_parent; } @@ -288,19 +278,20 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { /// Asserts that all nodes have been visited, and returns the computed /// counter expressions (made up of physical counters) for each node. - fn finish(self) -> IndexVec<Node, CounterExprVec<Node>> { - let Self { graph, is_unvisited, span_edges, yank_buffer: _, counter_exprs } = self; + fn finish(self) -> IndexVec<Node, Vec<CounterTerm<Node>>> { + let Self { ref span_edges, ref is_unvisited, ref counter_terms, .. } = self; assert!(is_unvisited.is_empty(), "some nodes were never visited: {is_unvisited:?}"); debug_assert!( span_edges .iter_enumerated() - .all(|(node, span_edge)| { span_edge.is_some() <= graph.is_supernode(node) }), + .all(|(node, span_edge)| { span_edge.is_some() <= self.is_supernode(node) }), "only supernodes can have a span edge", ); debug_assert!( - counter_exprs.iter().all(|expr| !expr.is_empty()), - "after visiting all nodes, every node should have a non-empty expression", + counter_terms.iter().all(|terms| !terms.is_empty()), + "after visiting all nodes, every node should have at least one term", ); - counter_exprs + + self.counter_terms } } diff --git a/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs b/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs index 9e7f754523d..b509a14514b 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs @@ -4,10 +4,12 @@ use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_index::Idx; use rustc_middle::mir::coverage::Op; -use super::{CounterTerm, MergedNodeFlowGraph, NodeCounters}; +use crate::coverage::counters::node_flow::{ + CounterTerm, NodeCounters, NodeFlowData, make_node_counters, node_flow_data_for_balanced_graph, +}; -fn merged_node_flow_graph<G: graph::Successors>(graph: G) -> MergedNodeFlowGraph<G::Node> { - MergedNodeFlowGraph::for_balanced_graph(graph) +fn node_flow_data<G: graph::Successors>(graph: G) -> NodeFlowData<G::Node> { + node_flow_data_for_balanced_graph(graph) } fn make_graph<Node: Idx + Ord>(num_nodes: usize, edge_pairs: Vec<(Node, Node)>) -> VecGraph<Node> { @@ -30,8 +32,8 @@ fn example_driver() { (4, 0), ]); - let merged = merged_node_flow_graph(&graph); - let counters = merged.make_node_counters(&[3, 1, 2, 0, 4]); + let node_flow_data = node_flow_data(&graph); + let counters = make_node_counters(&node_flow_data, &[3, 1, 2, 0, 4]); assert_eq!(format_counter_expressions(&counters), &[ // (comment to force vertical formatting for clarity) @@ -53,12 +55,12 @@ fn format_counter_expressions<Node: Idx>(counters: &NodeCounters<Node>) -> Vec<S }; counters - .counter_exprs + .counter_terms .indices() .map(|node| { - let mut expr = counters.counter_expr(node).iter().collect::<Vec<_>>(); - expr.sort_by_key(|item| item.node.index()); - format!("[{node:?}]: {}", expr.into_iter().map(format_item).join(" ")) + let mut terms = counters.counter_terms[node].iter().collect::<Vec<_>>(); + terms.sort_by_key(|item| item.node.index()); + format!("[{node:?}]: {}", terms.into_iter().map(format_item).join(" ")) }) .collect() } diff --git a/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs b/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs index 2da4f5f5fce..a826a953fa6 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs @@ -88,29 +88,9 @@ impl<Key: Idx> UnionFind<Key> { a } - /// Creates a snapshot of this disjoint-set forest that can no longer be - /// mutated, but can be queried without mutation. - pub(crate) fn freeze(&mut self) -> FrozenUnionFind<Key> { - // Just resolve each key to its actual root. - let roots = self.table.indices().map(|key| self.find(key)).collect(); - FrozenUnionFind { roots } - } -} - -/// Snapshot of a disjoint-set forest that can no longer be mutated, but can be -/// queried in O(1) time without mutation. -/// -/// This is really just a wrapper around a direct mapping from keys to roots, -/// but with a [`Self::find`] method that resembles [`UnionFind::find`]. -#[derive(Debug)] -pub(crate) struct FrozenUnionFind<Key: Idx> { - roots: IndexVec<Key, Key>, -} - -impl<Key: Idx> FrozenUnionFind<Key> { - /// Returns the "root" key of the disjoint-set containing the given key. - /// If two keys have the same root, they belong to the same set. - pub(crate) fn find(&self, key: Key) -> Key { - self.roots[key] + /// Takes a "snapshot" of the current state of this disjoint-set forest, in + /// the form of a vector that directly maps each key to its current root. + pub(crate) fn snapshot(&mut self) -> IndexVec<Key, Key> { + self.table.indices().map(|key| self.find(key)).collect() } } diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 25dc7f31227..392b54c8d81 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -135,7 +135,7 @@ impl CoverageGraph { bb_to_bcb[bb] = Some(bcb); } - let is_out_summable = basic_blocks.last().map_or(false, |&bb| { + let is_out_summable = basic_blocks.last().is_some_and(|&bb| { bcb_filtered_successors(mir_body[bb].terminator()).is_out_summable() }); let bcb_data = BasicCoverageBlockData { basic_blocks, is_out_summable }; diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 19568735df7..15487d05a30 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -61,6 +61,10 @@ impl<'tcx> crate::MirPass<'tcx> for InstrumentCoverage { instrument_function_for_coverage(tcx, mir_body); } + + fn is_required(&self) -> bool { + false + } } fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { @@ -180,7 +184,12 @@ fn create_mappings( )); for (decision, branches) in mcdc_mappings { - let num_conditions = branches.len() as u16; + // FIXME(#134497): Previously it was possible for some of these branch + // conversions to fail, in which case the remaining branches in the + // decision would be degraded to plain `MappingKind::Branch`. + // The changes in #134497 made that failure impossible, because the + // fallible step was deferred to codegen. But the corresponding code + // in codegen wasn't updated to detect the need for a degrade step. let conditions = branches .into_iter() .map( @@ -206,24 +215,13 @@ fn create_mappings( ) .collect::<Vec<_>>(); - if conditions.len() == num_conditions as usize { - // LLVM requires end index for counter mapping regions. - let kind = MappingKind::MCDCDecision(DecisionInfo { - bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32, - num_conditions, - }); - let span = decision.span; - mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter())); - } else { - mappings.extend(conditions.into_iter().map(|mapping| { - let MappingKind::MCDCBranch { true_term, false_term, mcdc_params: _ } = - mapping.kind - else { - unreachable!("all mappings here are MCDCBranch as shown above"); - }; - Mapping { kind: MappingKind::Branch { true_term, false_term }, span: mapping.span } - })) - } + // LLVM requires end index for counter mapping regions. + let kind = MappingKind::MCDCDecision(DecisionInfo { + bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32, + num_conditions: u16::try_from(conditions.len()).unwrap(), + }); + let span = decision.span; + mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter())); } mappings diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 3e7cf8541c2..5e7b46182dc 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -87,15 +87,9 @@ fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { fn coverage_ids_info<'tcx>( tcx: TyCtxt<'tcx>, instance_def: ty::InstanceKind<'tcx>, -) -> CoverageIdsInfo { +) -> Option<CoverageIdsInfo> { let mir_body = tcx.instance_mir(instance_def); - - let Some(fn_cov_info) = mir_body.function_coverage_info.as_deref() else { - return CoverageIdsInfo { - counters_seen: DenseBitSet::new_empty(0), - zero_expressions: DenseBitSet::new_empty(0), - }; - }; + let fn_cov_info = mir_body.function_coverage_info.as_deref()?; let mut counters_seen = DenseBitSet::new_empty(fn_cov_info.num_counters); let mut expressions_seen = DenseBitSet::new_filled(fn_cov_info.expressions.len()); @@ -129,7 +123,7 @@ fn coverage_ids_info<'tcx>( let zero_expressions = identify_zero_expressions(fn_cov_info, &counters_seen, &expressions_seen); - CoverageIdsInfo { counters_seen, zero_expressions } + Some(CoverageIdsInfo { counters_seen, zero_expressions }) } fn all_coverage_in_mir_body<'a, 'tcx>( diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs index 8fce856687c..4f45d9588a8 100644 --- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs +++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs @@ -69,7 +69,7 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // Don't do any inference if codegen optimizations are disabled and also MIR inlining is not // enabled. This ensures that we do inference even if someone only passes -Zinline-mir, // which is less confusing than having to also enable -Copt-level=1. - let inliner_will_run = pm::should_run_pass(tcx, &inline::Inline) + let inliner_will_run = pm::should_run_pass(tcx, &inline::Inline, pm::Optimizations::Allowed) || inline::ForceInline::should_run_pass_for_callee(tcx, def_id.to_def_id()); if matches!(tcx.sess.opts.optimize, OptLevel::No) && !inliner_will_run { return false; diff --git a/compiler/rustc_mir_transform/src/ctfe_limit.rs b/compiler/rustc_mir_transform/src/ctfe_limit.rs index bd58b1b6689..d0b313e149a 100644 --- a/compiler/rustc_mir_transform/src/ctfe_limit.rs +++ b/compiler/rustc_mir_transform/src/ctfe_limit.rs @@ -36,6 +36,10 @@ impl<'tcx> crate::MirPass<'tcx> for CtfeLimit { ); } } + + fn is_required(&self) -> bool { + true + } } fn has_back_edge( diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index 51af77778af..8879e029346 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -71,6 +71,10 @@ impl<'tcx> crate::MirPass<'tcx> for DataflowConstProp { let mut patch = visitor.patch; debug_span!("patch").in_scope(|| patch.visit_body_preserves_cfg(body)); } + + fn is_required(&self) -> bool { + false + } } // Note: Currently, places that have their reference taken cannot be tracked. Although this would diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 434e921d439..eea2b0990d7 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -147,4 +147,8 @@ impl<'tcx> crate::MirPass<'tcx> for DeadStoreElimination { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { eliminate(tcx, body); } + + fn is_required(&self) -> bool { + false + } } diff --git a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs index 26b28c8c487..63257df66fb 100644 --- a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs +++ b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs @@ -31,6 +31,10 @@ impl<'tcx> crate::MirPass<'tcx> for DeduplicateBlocks { simplify_cfg(body); } } + + fn is_required(&self) -> bool { + false + } } struct OptApplier<'tcx> { diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs index ad7ccef4976..a54bdaa4075 100644 --- a/compiler/rustc_mir_transform/src/deref_separator.rs +++ b/compiler/rustc_mir_transform/src/deref_separator.rs @@ -81,4 +81,8 @@ impl<'tcx> crate::MirPass<'tcx> for Derefer { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { deref_finder(tcx, body); } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index b4f9f1f08ef..41de1b58b91 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -240,6 +240,10 @@ impl<'tcx> crate::MirPass<'tcx> for DestinationPropagation { trace!(round_count); } + + fn is_required(&self) -> bool { + false + } } #[derive(Debug, Default)] diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index 5dd84975b88..e4fcbaa483d 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs @@ -15,6 +15,10 @@ impl<'tcx> crate::MirPass<'tcx> for Marker { } fn run_pass(&self, _tcx: TyCtxt<'tcx>, _body: &mut Body<'tcx>) {} + + fn is_required(&self) -> bool { + false + } } pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> { diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 91e1395e764..eb335a1e4a7 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -227,6 +227,10 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch { simplify_cfg(body); } } + + fn is_required(&self) -> bool { + false + } } #[derive(Debug)] diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index d6ecadbfe29..6d3b5d9851b 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -150,4 +150,8 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs { } } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 988f1a25561..cb869160c80 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -88,6 +88,10 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateDrops { elaborate_patch.apply(body); deref_finder(tcx, body); } + + fn is_required(&self) -> bool { + true + } } /// Records unwind edges which are known to be unreachable, because they are in `drop` terminators diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index a2fd46043ca..29698b0c2e4 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -5,7 +5,7 @@ use rustc_middle::mir::AssertKind; use rustc_middle::ty::TyCtxt; use rustc_session::lint::{self, Lint}; use rustc_span::def_id::DefId; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; @@ -114,7 +114,7 @@ pub(crate) struct FnItemRef { #[suggestion(code = "{sugg}", applicability = "unspecified")] pub span: Span, pub sugg: String, - pub ident: String, + pub ident: Ident, } #[derive(Diagnostic)] diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index fb21bf9977f..7e88925b2e1 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -168,7 +168,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { s } }; - let ident = self.tcx.item_name(fn_id).to_ident_string(); + let ident = self.tcx.item_ident(fn_id); let ty_params = fn_args.types().map(|ty| format!("{ty}")); let const_params = fn_args.consts().map(|c| format!("{c}")); let params = ty_params.chain(const_params).join(", "); @@ -177,7 +177,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" }; let sugg = format!( "{} as {}{}fn({}{}){}", - if params.is_empty() { ident.clone() } else { format!("{ident}::<{params}>") }, + if params.is_empty() { ident.to_string() } else { format!("{ident}::<{params}>") }, unsafety, abi, vec!["_"; num_args].join(", "), diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index cb03b422d9e..1c2c82d4cd5 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -163,6 +163,10 @@ impl<'tcx> crate::MirPass<'tcx> for GVN { // statements. StorageRemover { tcx, reused_locals: state.reused_locals }.visit_body_preserves_cfg(body); } + + fn is_required(&self) -> bool { + false + } } newtype_index! { @@ -1174,11 +1178,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ) if let ty::Slice(..) = to.builtin_deref(true).unwrap().kind() && let ty::Array(_, len) = from.builtin_deref(true).unwrap().kind() => { - return self.insert_constant(Const::from_ty_const( - *len, - self.tcx.types.usize, - self.tcx, - )); + return self.insert_constant(Const::Ty(self.tcx.types.usize, *len)); } _ => Value::UnaryOp(op, arg_index), }; @@ -1488,11 +1488,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { // Trivial case: we are fetching a statically known length. let place_ty = place.ty(self.local_decls, self.tcx).ty; if let ty::Array(_, len) = place_ty.kind() { - return self.insert_constant(Const::from_ty_const( - *len, - self.tcx.types.usize, - self.tcx, - )); + return self.insert_constant(Const::Ty(self.tcx.types.usize, *len)); } let mut inner = self.simplify_place_value(place, location)?; @@ -1514,11 +1510,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { && let Some(to) = to.builtin_deref(true) && let ty::Slice(..) = to.kind() { - return self.insert_constant(Const::from_ty_const( - *len, - self.tcx.types.usize, - self.tcx, - )); + return self.insert_constant(Const::Ty(self.tcx.types.usize, *len)); } // Fallback: a symbolic `Len`. diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs index ba8389bbe2f..86e2bf6cb3c 100644 --- a/compiler/rustc_mir_transform/src/impossible_predicates.rs +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -53,4 +53,8 @@ impl<'tcx> MirPass<'tcx> for ImpossiblePredicates { body.local_decls.raw.truncate(body.arg_count + 1); } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 2052e28325c..95aeccfdda6 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -66,6 +66,10 @@ impl<'tcx> crate::MirPass<'tcx> for Inline { deref_finder(tcx, body); } } + + fn is_required(&self) -> bool { + false + } } pub struct ForceInline; @@ -85,6 +89,10 @@ impl<'tcx> crate::MirPass<'tcx> for ForceInline { false } + fn is_required(&self) -> bool { + true + } + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let span = trace_span!("force_inline", body = %tcx.def_path_str(body.source.def_id())); let _guard = span.enter(); diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 5a36519e6a3..4b9ebd40b85 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -62,6 +62,10 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { simplify_duplicate_switch_targets(block.terminator.as_mut().unwrap()); } } + + fn is_required(&self) -> bool { + false + } } struct InstSimplifyContext<'a, 'tcx> { @@ -167,7 +171,7 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { if let Rvalue::Len(ref place) = *rvalue { let place_ty = place.ty(self.local_decls, self.tcx).ty; if let ty::Array(_, len) = *place_ty.kind() { - let const_ = Const::from_ty_const(len, self.tcx.types.usize, self.tcx); + let const_ = Const::Ty(self.tcx.types.usize, len); let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None }; *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant))); } diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index c73a03489c5..17084eca6e3 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -105,6 +105,10 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading { } OpportunitySet::new(body, opportunities).apply(body); } + + fn is_required(&self) -> bool { + false + } } #[derive(Debug)] diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index 490e7dd8f7e..e201763468b 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -200,6 +200,10 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { }); } } + + fn is_required(&self) -> bool { + false + } } impl EnumSizeOpt { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index d1bacf1f598..2dc55e3614e 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -388,6 +388,7 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> { &Lint(sanity_check::SanityCheck), ], None, + pm::Optimizations::Allowed, ); tcx.alloc_steal_mir(body) } @@ -440,6 +441,7 @@ fn mir_promoted( &mut body, &[&promote_pass, &simplify::SimplifyCfg::PromoteConsts, &coverage::InstrumentCoverage], Some(MirPhase::Analysis(AnalysisPhase::Initial)), + pm::Optimizations::Allowed, ); lint_tail_expr_drop_order::run_lint(tcx, def, &body); @@ -473,7 +475,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: LocalDefId) -> Body<'_> { }; let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const); - pm::run_passes(tcx, &mut body, &[&ctfe_limit::CtfeLimit], None); + pm::run_passes(tcx, &mut body, &[&ctfe_limit::CtfeLimit], None, pm::Optimizations::Allowed); body } @@ -493,7 +495,7 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> & let is_fn_like = tcx.def_kind(def).is_fn_like(); if is_fn_like { // Do not compute the mir call graph without said call graph actually being used. - if pm::should_run_pass(tcx, &inline::Inline) + if pm::should_run_pass(tcx, &inline::Inline, pm::Optimizations::Allowed) || inline::ForceInline::should_run_pass_for_callee(tcx, def.to_def_id()) { tcx.ensure_with_value().mir_inliner_callees(ty::InstanceKind::Item(def.to_def_id())); @@ -533,6 +535,7 @@ pub fn run_analysis_to_runtime_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<' &Lint(post_drop_elaboration::CheckLiveDrops), ], None, + pm::Optimizations::Allowed, ); } @@ -557,7 +560,13 @@ fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &deref_separator::Derefer, ]; - pm::run_passes(tcx, body, passes, Some(MirPhase::Analysis(AnalysisPhase::PostCleanup))); + pm::run_passes( + tcx, + body, + passes, + Some(MirPhase::Analysis(AnalysisPhase::PostCleanup)), + pm::Optimizations::Allowed, + ); } /// Returns the sequence of passes that lowers analysis to runtime MIR. @@ -597,7 +606,13 @@ fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &simplify::SimplifyCfg::PreOptimizations, ]; - pm::run_passes(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::PostCleanup))); + pm::run_passes( + tcx, + body, + passes, + Some(MirPhase::Runtime(RuntimePhase::PostCleanup)), + pm::Optimizations::Allowed, + ); // Clear this by anticipation. Optimizations and runtime MIR have no reason to look // into this information, which is meant for borrowck diagnostics. @@ -611,6 +626,15 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { WithMinOptLevel(1, x) } + let def_id = body.source.def_id(); + let optimizations = if tcx.def_kind(def_id).has_codegen_attrs() + && tcx.codegen_fn_attrs(def_id).optimize.do_not_optimize() + { + pm::Optimizations::Suppressed + } else { + pm::Optimizations::Allowed + }; + // The main optimizations that we do on MIR. pm::run_passes( tcx, @@ -683,6 +707,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &dump_mir::Marker("PreCodegen"), ], Some(MirPhase::Runtime(RuntimePhase::Optimized)), + optimizations, ); } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 942c6144ea6..9a9f66ed4fd 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -325,4 +325,8 @@ impl<'tcx> crate::MirPass<'tcx> for LowerIntrinsics { } } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 420661f29c8..aca80e36e33 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -26,6 +26,10 @@ impl<'tcx> crate::MirPass<'tcx> for LowerSliceLenCalls { lower_slice_len_call(block, slice_len_fn_item_def_id); } } + + fn is_required(&self) -> bool { + false + } } fn lower_slice_len_call<'tcx>(block: &mut BasicBlockData<'tcx>, slice_len_fn_item_def_id: DefId) { diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 534ba991780..500116580d5 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -49,6 +49,10 @@ impl<'tcx> crate::MirPass<'tcx> for MatchBranchSimplification { simplify_cfg(body); } } + + fn is_required(&self) -> bool { + false + } } trait SimplifyMatch<'tcx> { diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index cf5c5f85a9f..f5c57418467 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -27,6 +27,10 @@ impl<'tcx> crate::MirPass<'tcx> for MentionedItems { visitor.visit_body(body); body.set_mentioned_items(visitor.mentioned_items); } + + fn is_required(&self) -> bool { + true + } } // This visitor is carefully in sync with the one in `rustc_monomorphize::collector`. We are diff --git a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs index 6dfa14d6b52..c63bfdcee85 100644 --- a/compiler/rustc_mir_transform/src/multiple_return_terminators.rs +++ b/compiler/rustc_mir_transform/src/multiple_return_terminators.rs @@ -36,4 +36,8 @@ impl<'tcx> crate::MirPass<'tcx> for MultipleReturnTerminators { simplify::remove_dead_blocks(body) } + + fn is_required(&self) -> bool { + false + } } diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs index 35872de3852..965002aae04 100644 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ b/compiler/rustc_mir_transform/src/nrvo.rs @@ -71,6 +71,10 @@ impl<'tcx> crate::MirPass<'tcx> for RenameReturnPlace { // The return place is always mutable. ret_decl.mutability = Mutability::Mut; } + + fn is_required(&self) -> bool { + false + } } /// MIR that is eligible for the NRVO must fulfill two conditions: diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index c3f0a989ce1..7a8d3ba1ff1 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -90,6 +90,11 @@ pub(super) trait MirPass<'tcx> { fn is_mir_dump_enabled(&self) -> bool { true } + + /// Returns `true` if this pass must be run (i.e. it is required for soundness). + /// For passes which are strictly optimizations, this should return `false`. + /// If this is `false`, `#[optimize(none)]` will disable the pass. + fn is_required(&self) -> bool; } /// Just like `MirPass`, except it cannot mutate `Body`, and MIR dumping is @@ -134,6 +139,10 @@ where fn is_mir_dump_enabled(&self) -> bool { false } + + fn is_required(&self) -> bool { + true + } } pub(super) struct WithMinOptLevel<T>(pub u32, pub T); @@ -153,6 +162,19 @@ where fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { self.1.run_pass(tcx, body) } + + fn is_required(&self) -> bool { + self.1.is_required() + } +} + +/// Whether to allow non-[required] optimizations +/// +/// [required]: MirPass::is_required +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum Optimizations { + Suppressed, + Allowed, } /// Run the sequence of passes without validating the MIR after each pass. The MIR is still @@ -163,7 +185,7 @@ pub(super) fn run_passes_no_validate<'tcx>( passes: &[&dyn MirPass<'tcx>], phase_change: Option<MirPhase>, ) { - run_passes_inner(tcx, body, passes, phase_change, false); + run_passes_inner(tcx, body, passes, phase_change, false, Optimizations::Allowed); } /// The optional `phase_change` is applied after executing all the passes, if present @@ -172,11 +194,16 @@ pub(super) fn run_passes<'tcx>( body: &mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>], phase_change: Option<MirPhase>, + optimizations: Optimizations, ) { - run_passes_inner(tcx, body, passes, phase_change, true); + run_passes_inner(tcx, body, passes, phase_change, true, optimizations); } -pub(super) fn should_run_pass<'tcx, P>(tcx: TyCtxt<'tcx>, pass: &P) -> bool +pub(super) fn should_run_pass<'tcx, P>( + tcx: TyCtxt<'tcx>, + pass: &P, + optimizations: Optimizations, +) -> bool where P: MirPass<'tcx> + ?Sized, { @@ -196,7 +223,8 @@ where ); *polarity }); - overridden.unwrap_or_else(|| pass.is_enabled(tcx.sess)) + let suppressed = !pass.is_required() && matches!(optimizations, Optimizations::Suppressed); + overridden.unwrap_or_else(|| !suppressed && pass.is_enabled(tcx.sess)) } fn run_passes_inner<'tcx>( @@ -205,6 +233,7 @@ fn run_passes_inner<'tcx>( passes: &[&dyn MirPass<'tcx>], phase_change: Option<MirPhase>, validate_each: bool, + optimizations: Optimizations, ) { let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes; trace!(?overridden_passes); @@ -243,7 +272,7 @@ fn run_passes_inner<'tcx>( for pass in passes { let name = pass.name(); - if !should_run_pass(tcx, *pass) { + if !should_run_pass(tcx, *pass, optimizations) { continue; }; diff --git a/compiler/rustc_mir_transform/src/post_analysis_normalize.rs b/compiler/rustc_mir_transform/src/post_analysis_normalize.rs index 3eecf79a7ea..76c2f082c0b 100644 --- a/compiler/rustc_mir_transform/src/post_analysis_normalize.rs +++ b/compiler/rustc_mir_transform/src/post_analysis_normalize.rs @@ -15,6 +15,10 @@ impl<'tcx> crate::MirPass<'tcx> for PostAnalysisNormalize { let typing_env = ty::TypingEnv::post_analysis(tcx, body.source.def_id()); PostAnalysisNormalizeVisitor { tcx, typing_env }.visit_body_preserves_cfg(body); } + + fn is_required(&self) -> bool { + true + } } struct PostAnalysisNormalizeVisitor<'tcx> { diff --git a/compiler/rustc_mir_transform/src/prettify.rs b/compiler/rustc_mir_transform/src/prettify.rs index 51abd4da86e..8ccfbe2f194 100644 --- a/compiler/rustc_mir_transform/src/prettify.rs +++ b/compiler/rustc_mir_transform/src/prettify.rs @@ -35,6 +35,10 @@ impl<'tcx> crate::MirPass<'tcx> for ReorderBasicBlocks { permute(body.basic_blocks.as_mut(), &updater.map); } + + fn is_required(&self) -> bool { + false + } } /// Rearranges the locals into *use* order. @@ -85,6 +89,10 @@ impl<'tcx> crate::MirPass<'tcx> for ReorderLocals { permute(&mut body.local_decls, &updater.map); } + + fn is_required(&self) -> bool { + false + } } fn permute<I: rustc_index::Idx + Ord, T>(data: &mut IndexVec<I, T>, map: &IndexSlice<I, I>) { diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 6be95b1f0f1..6aa3343bb6e 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -61,6 +61,10 @@ impl<'tcx> crate::MirPass<'tcx> for PromoteTemps<'tcx> { let promoted = promote_candidates(body, tcx, temps, promotable_candidates); self.promoted_fragments.set(promoted); } + + fn is_required(&self) -> bool { + true + } } /// State of a temporary during collection and promotion. diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index 95b05f94270..368d5340ac3 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -81,6 +81,10 @@ impl<'tcx> crate::MirPass<'tcx> for ReferencePropagation { debug!(def_id = ?body.source.def_id()); while propagate_ssa(tcx, body) {} } + + fn is_required(&self) -> bool { + false + } } fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool { diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 76a3edfe0be..9cdd52bec7b 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -74,6 +74,10 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { debug!("removed {:?} jumps and {:?} landing pads", jumps_folded, landing_pads_removed); } + + fn is_required(&self) -> bool { + true + } } impl RemoveNoopLandingPads { diff --git a/compiler/rustc_mir_transform/src/remove_place_mention.rs b/compiler/rustc_mir_transform/src/remove_place_mention.rs index 71399eb72f0..15fe77d5319 100644 --- a/compiler/rustc_mir_transform/src/remove_place_mention.rs +++ b/compiler/rustc_mir_transform/src/remove_place_mention.rs @@ -20,4 +20,8 @@ impl<'tcx> crate::MirPass<'tcx> for RemovePlaceMention { }) } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs index 3ecb4a8994f..1ae33c00968 100644 --- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs +++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs @@ -22,4 +22,8 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveStorageMarkers { }) } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs index e955d8277a4..9044a88295c 100644 --- a/compiler/rustc_mir_transform/src/remove_uninit_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_uninit_drops.rs @@ -62,6 +62,10 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUninitDrops { block.terminator_mut().kind = TerminatorKind::Goto { target: *target }; } } + + fn is_required(&self) -> bool { + true + } } fn is_needs_drop_and_init<'tcx>( diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index e335051d656..8a8cdafc690 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -38,4 +38,8 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveUnneededDrops { simplify_cfg(body); } } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 55e5701bd0a..78d94a03867 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -27,6 +27,10 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveZsts { replacer.visit_basic_block_data(bb, data); } } + + fn is_required(&self) -> bool { + true + } } struct Replacer<'a, 'tcx> { diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 4648ec33c93..f7ec0f740d3 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -119,6 +119,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< &add_call_guards::CriticalCallEdges, ], Some(MirPhase::Runtime(RuntimePhase::Optimized)), + pm::Optimizations::Allowed, ); return body; diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs index 4f312ed2aaa..67070f03ded 100644 --- a/compiler/rustc_mir_transform/src/simplify.rs +++ b/compiler/rustc_mir_transform/src/simplify.rs @@ -83,6 +83,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyCfg { debug!("SimplifyCfg({:?}) - simplifying {:?}", self.name(), body.source); simplify_cfg(body); } + + fn is_required(&self) -> bool { + false + } } struct CfgSimplifier<'a, 'tcx> { @@ -405,6 +409,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyLocals { body.local_decls.shrink_to_fit(); } } + + fn is_required(&self) -> bool { + false + } } pub(super) fn remove_unused_definitions<'tcx>(body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/simplify_branches.rs b/compiler/rustc_mir_transform/src/simplify_branches.rs index bea3d0d8557..12c3503879f 100644 --- a/compiler/rustc_mir_transform/src/simplify_branches.rs +++ b/compiler/rustc_mir_transform/src/simplify_branches.rs @@ -60,4 +60,8 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyConstCondition { }; } } + + fn is_required(&self) -> bool { + false + } } diff --git a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs index b6d80173086..c0f25c7ecfe 100644 --- a/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs +++ b/compiler/rustc_mir_transform/src/simplify_comparison_integral.rs @@ -140,6 +140,10 @@ impl<'tcx> crate::MirPass<'tcx> for SimplifyComparisonIntegral { body.basic_blocks_mut()[idx].statements.insert(0, stmt); } } + + fn is_required(&self) -> bool { + false + } } struct OptimizationFinder<'a, 'tcx> { diff --git a/compiler/rustc_mir_transform/src/single_use_consts.rs b/compiler/rustc_mir_transform/src/single_use_consts.rs index 10b3c0ae94f..c5e951eb8b2 100644 --- a/compiler/rustc_mir_transform/src/single_use_consts.rs +++ b/compiler/rustc_mir_transform/src/single_use_consts.rs @@ -81,6 +81,10 @@ impl<'tcx> crate::MirPass<'tcx> for SingleUseConsts { } } } + + fn is_required(&self) -> bool { + true + } } #[derive(Copy, Clone, Debug)] diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index d54ea3feab6..92e5c8a9ca4 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -48,6 +48,10 @@ impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates { } } } + + fn is_required(&self) -> bool { + false + } } /// Identify all locals that are not eligible for SROA. diff --git a/compiler/rustc_mir_transform/src/strip_debuginfo.rs b/compiler/rustc_mir_transform/src/strip_debuginfo.rs index 438c75726bb..9ede8aa79c4 100644 --- a/compiler/rustc_mir_transform/src/strip_debuginfo.rs +++ b/compiler/rustc_mir_transform/src/strip_debuginfo.rs @@ -31,4 +31,8 @@ impl<'tcx> crate::MirPass<'tcx> for StripDebugInfo { ) }); } + + fn is_required(&self) -> bool { + true + } } diff --git a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs index 55dcad0680a..1ff7043ed14 100644 --- a/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs +++ b/compiler/rustc_mir_transform/src/unreachable_enum_branching.rs @@ -208,4 +208,8 @@ impl<'tcx> crate::MirPass<'tcx> for UnreachableEnumBranching { patch.apply(body); } + + fn is_required(&self) -> bool { + false + } } diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs index 734703ec78b..1e5d9469c03 100644 --- a/compiler/rustc_mir_transform/src/unreachable_prop.rs +++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs @@ -52,6 +52,10 @@ impl crate::MirPass<'_> for UnreachablePropagation { body.basic_blocks_mut()[bb].statements.clear(); } } + + fn is_required(&self) -> bool { + false + } } /// Return whether the current terminator is fully unreachable. diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index b62e34ac08d..026923ad786 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -91,6 +91,10 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { } } } + + fn is_required(&self) -> bool { + true + } } struct CfgChecker<'a, 'tcx> { diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 7b179663430..e08c348a64d 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -110,7 +110,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; use rustc_middle::mir::mono::{ CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, MonoItemData, - Visibility, + MonoItemPartitions, Visibility, }; use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths}; use rustc_middle::ty::{self, InstanceKind, TyCtxt}; @@ -1114,7 +1114,7 @@ where } } -fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[CodegenUnit<'_>]) { +fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitions<'_> { let collection_strategy = match tcx.sess.opts.unstable_opts.print_mono_items { Some(ref s) => { let mode = s.to_lowercase(); @@ -1236,7 +1236,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co } } - (tcx.arena.alloc(mono_items), codegen_units) + MonoItemPartitions { all_mono_items: tcx.arena.alloc(mono_items), codegen_units } } /// Outputs stats about instantiation counts and estimated size, per `MonoItem`'s @@ -1319,14 +1319,13 @@ fn dump_mono_items_stats<'tcx>( pub(crate) fn provide(providers: &mut Providers) { providers.collect_and_partition_mono_items = collect_and_partition_mono_items; - providers.is_codegened_item = |tcx, def_id| { - let (all_mono_items, _) = tcx.collect_and_partition_mono_items(()); - all_mono_items.contains(&def_id) - }; + providers.is_codegened_item = + |tcx, def_id| tcx.collect_and_partition_mono_items(()).all_mono_items.contains(&def_id); providers.codegen_unit = |tcx, name| { - let (_, all) = tcx.collect_and_partition_mono_items(()); - all.iter() + tcx.collect_and_partition_mono_items(()) + .codegen_units + .iter() .find(|cgu| cgu.name() == name) .unwrap_or_else(|| panic!("failed to find cgu with name {name:?}")) }; diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml index f9168112216..451c215566b 100644 --- a/compiler/rustc_next_trait_solver/Cargo.toml +++ b/compiler/rustc_next_trait_solver/Cargo.toml @@ -13,7 +13,6 @@ rustc_macros = { path = "../rustc_macros", optional = true } rustc_serialize = { path = "../rustc_serialize", optional = true } rustc_type_ir = { path = "../rustc_type_ir", default-features = false } rustc_type_ir_macros = { path = "../rustc_type_ir_macros" } -smallvec = "1.8.1" tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 8a54a4ece98..62a7c84bc28 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -273,7 +273,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> { // // For this we set `next_orig_uv` to the next smallest, not yet compressed, // universe of the input. - if next_orig_uv.map_or(true, |curr_next_uv| uv.cannot_name(curr_next_uv)) { + if next_orig_uv.is_none_or(|curr_next_uv| uv.cannot_name(curr_next_uv)) { next_orig_uv = Some(uv); } } diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 37678bfd880..8d1194ee539 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -277,23 +277,7 @@ where param_env: I::ParamEnv, ty: I::Ty, ) -> Result<I::Ty, NoSolution> { - if let ty::Alias(..) = ty.kind() { - let normalized_ty = self.next_ty_infer(); - let alias_relate_goal = Goal::new( - self.cx(), - param_env, - ty::PredicateKind::AliasRelate( - ty.into(), - normalized_ty.into(), - ty::AliasRelationDirection::Equate, - ), - ); - self.add_goal(GoalSource::Misc, alias_relate_goal); - self.try_evaluate_added_goals()?; - Ok(self.resolve_vars_if_possible(normalized_ty)) - } else { - Ok(ty) - } + self.structurally_normalize_term(param_env, ty.into()).map(|term| term.expect_ty()) } /// Normalize a const for when it is structurally matched on, or more likely @@ -308,22 +292,34 @@ where param_env: I::ParamEnv, ct: I::Const, ) -> Result<I::Const, NoSolution> { - if let ty::ConstKind::Unevaluated(..) = ct.kind() { - let normalized_ct = self.next_const_infer(); + self.structurally_normalize_term(param_env, ct.into()).map(|term| term.expect_const()) + } + + /// Normalize a term for when it is structurally matched on. + /// + /// This function is necessary in nearly all cases before matching on a ty/const. + /// Not doing so is likely to be incomplete and therefore unsound during coherence. + fn structurally_normalize_term( + &mut self, + param_env: I::ParamEnv, + term: I::Term, + ) -> Result<I::Term, NoSolution> { + if let Some(_) = term.to_alias_term() { + let normalized_term = self.next_term_infer_of_kind(term); let alias_relate_goal = Goal::new( self.cx(), param_env, ty::PredicateKind::AliasRelate( - ct.into(), - normalized_ct.into(), + term, + normalized_term, ty::AliasRelationDirection::Equate, ), ); self.add_goal(GoalSource::Misc, alias_relate_goal); self.try_evaluate_added_goals()?; - Ok(self.resolve_vars_if_possible(normalized_ct)) + Ok(self.resolve_vars_if_possible(normalized_term)) } else { - Ok(ct) + Ok(term) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 4faa243c02a..513fc9355c8 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -8,7 +8,6 @@ use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::solve::CanonicalResponse; use rustc_type_ir::visit::TypeVisitableExt as _; use rustc_type_ir::{self as ty, Interner, TraitPredicate, TypingMode, Upcast as _, elaborate}; -use smallvec::SmallVec; use tracing::{instrument, trace}; use crate::delegate::SolverDelegate; @@ -1199,33 +1198,42 @@ where // nested requirements, over all others. This is a fix for #53123 and // prevents where-bounds from accidentally extending the lifetime of a // variable. - if candidates - .iter() - .any(|c| matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial))) - { - let trivial_builtin_impls: SmallVec<[_; 1]> = candidates - .iter() - .filter(|c| { - matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial)) - }) - .map(|c| c.result) - .collect(); + let mut trivial_builtin_impls = candidates.iter().filter(|c| { + matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial)) + }); + if let Some(candidate) = trivial_builtin_impls.next() { // There should only ever be a single trivial builtin candidate // as they would otherwise overlap. - assert_eq!(trivial_builtin_impls.len(), 1); - return if let Some(response) = self.try_merge_responses(&trivial_builtin_impls) { - Ok((response, Some(TraitGoalProvenVia::Misc))) - } else { - Ok((self.bail_with_ambiguity(&trivial_builtin_impls), None)) - }; + assert!(trivial_builtin_impls.next().is_none()); + return Ok((candidate.result, Some(TraitGoalProvenVia::Misc))); } // If there are non-global where-bounds, prefer where-bounds // (including global ones) over everything else. let has_non_global_where_bounds = candidates.iter().any(|c| match c.source { CandidateSource::ParamEnv(idx) => { - let where_bound = goal.param_env.caller_bounds().get(idx); - where_bound.has_bound_vars() || !where_bound.is_global() + let where_bound = goal.param_env.caller_bounds().get(idx).unwrap(); + let ty::ClauseKind::Trait(trait_pred) = where_bound.kind().skip_binder() else { + unreachable!("expected trait-bound: {where_bound:?}"); + }; + + if trait_pred.has_bound_vars() || !trait_pred.is_global() { + return true; + } + + // We don't consider a trait-bound global if it has a projection bound. + // + // See ui/traits/next-solver/normalization-shadowing/global-trait-with-project.rs + // for an example where this is necessary. + for p in goal.param_env.caller_bounds().iter() { + if let ty::ClauseKind::Projection(proj) = p.kind().skip_binder() { + if proj.projection_term.trait_ref(self.cx()) == trait_pred.trait_ref { + return true; + } + } + } + + false } _ => false, }); diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index f78d9dc2bfc..50287b706ce 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3233,7 +3233,7 @@ pub(crate) struct MalformedCfgAttr { pub(crate) struct UnknownBuiltinConstruct { #[primary_span] pub span: Span, - pub name: Symbol, + pub name: Ident, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs index f963a424a7f..e681987ff07 100644 --- a/compiler/rustc_parse/src/lib.rs +++ b/compiler/rustc_parse/src/lib.rs @@ -11,18 +11,21 @@ #![feature(if_let_guard)] #![feature(iter_intersperse)] #![feature(let_chains)] +#![feature(string_from_utf8_lossy_owned)] #![warn(unreachable_pub)] // tidy-alphabetical-end -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::str::Utf8Error; use rustc_ast as ast; use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AttrItem, Attribute, MetaItemInner, token}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; -use rustc_errors::{Diag, FatalError, PResult}; +use rustc_errors::{Diag, EmissionGuarantee, FatalError, PResult, pluralize}; use rustc_session::parse::ParseSess; +use rustc_span::source_map::SourceMap; use rustc_span::{FileName, SourceFile, Span}; pub use unicode_normalization::UNICODE_VERSION as UNICODE_NORMALIZATION_VERSION; @@ -73,9 +76,22 @@ pub fn new_parser_from_file<'a>( path: &Path, sp: Option<Span>, ) -> Result<Parser<'a>, Vec<Diag<'a>>> { - let source_file = psess.source_map().load_file(path).unwrap_or_else(|e| { - let msg = format!("couldn't read {}: {}", path.display(), e); + let sm = psess.source_map(); + let source_file = sm.load_file(path).unwrap_or_else(|e| { + let msg = format!("couldn't read `{}`: {}", path.display(), e); let mut err = psess.dcx().struct_fatal(msg); + if let Ok(contents) = std::fs::read(path) + && let Err(utf8err) = String::from_utf8(contents.clone()) + { + utf8_error( + sm, + &path.display().to_string(), + sp, + &mut err, + utf8err.utf8_error(), + &contents, + ); + } if let Some(sp) = sp { err.span(sp); } @@ -84,6 +100,49 @@ pub fn new_parser_from_file<'a>( new_parser_from_source_file(psess, source_file) } +pub fn utf8_error<E: EmissionGuarantee>( + sm: &SourceMap, + path: &str, + sp: Option<Span>, + err: &mut Diag<'_, E>, + utf8err: Utf8Error, + contents: &[u8], +) { + // The file exists, but it wasn't valid UTF-8. + let start = utf8err.valid_up_to(); + let note = format!("invalid utf-8 at byte `{start}`"); + let msg = if let Some(len) = utf8err.error_len() { + format!( + "byte{s} `{bytes}` {are} not valid utf-8", + bytes = if len == 1 { + format!("{:?}", contents[start]) + } else { + format!("{:?}", &contents[start..start + len]) + }, + s = pluralize!(len), + are = if len == 1 { "is" } else { "are" }, + ) + } else { + note.clone() + }; + let contents = String::from_utf8_lossy(contents).to_string(); + let source = sm.new_source_file(PathBuf::from(path).into(), contents); + let span = Span::with_root_ctxt( + source.normalized_byte_pos(start as u32), + source.normalized_byte_pos(start as u32), + ); + if span.is_dummy() { + err.note(note); + } else { + if sp.is_some() { + err.span_note(span, msg); + } else { + err.span(span); + err.span_label(span, msg); + } + } +} + /// Given a session and a `source_file`, return a parser. Returns any buffered errors from lexing /// the initial token stream. fn new_parser_from_source_file( diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 7533e75ffe2..a5b73ce4098 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1958,7 +1958,7 @@ impl<'a> Parser<'a> { } else { let err = self.dcx().create_err(errors::UnknownBuiltinConstruct { span: lo.to(ident.span), - name: ident.name, + name: ident, }); return Err(err); }; @@ -2095,7 +2095,7 @@ impl<'a> Parser<'a> { // point literal here, since there's no use of the exponent // syntax that also constitutes a valid integer, so we need // not check for that. - if suffix.map_or(true, |s| s == sym::f32 || s == sym::f64) + if suffix.is_none_or(|s| s == sym::f32 || s == sym::f64) && symbol.as_str().chars().all(|c| c.is_numeric() || c == '_') && self.token.span.hi() == next_token.span.lo() { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 10756be6afb..714a60cb179 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -189,8 +189,9 @@ pub struct Parser<'a> { } // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with -// nonterminals. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_pointer_width = "64", not(target_arch = "s390x")))] +// nonterminals. Make sure it doesn't unintentionally get bigger. We only check a few arches +// though, because `TokenTypeSet(u128)` alignment varies on others, changing the total size. +#[cfg(all(target_pointer_width = "64", any(target_arch = "aarch64", target_arch = "x86_64")))] rustc_data_structures::static_assert_size!(Parser<'_>, 288); /// Stores span information about a closure. @@ -1597,45 +1598,35 @@ impl<'a> Parser<'a> { // Only used when debugging. #[allow(unused)] pub(crate) fn debug_lookahead(&self, lookahead: usize) -> impl fmt::Debug + '_ { - struct DebugParser<'dbg> { - parser: &'dbg Parser<'dbg>, - lookahead: usize, - } - - impl fmt::Debug for DebugParser<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let Self { parser, lookahead } = self; - let mut dbg_fmt = f.debug_struct("Parser"); // or at least, one view of - - // we don't need N spans, but we want at least one, so print all of prev_token - dbg_fmt.field("prev_token", &parser.prev_token); - let mut tokens = vec![]; - for i in 0..*lookahead { - let tok = parser.look_ahead(i, |tok| tok.kind.clone()); - let is_eof = tok == TokenKind::Eof; - tokens.push(tok); - if is_eof { - // Don't look ahead past EOF. - break; - } - } - dbg_fmt.field_with("tokens", |field| field.debug_list().entries(tokens).finish()); - dbg_fmt.field("approx_token_stream_pos", &parser.num_bump_calls); - - // some fields are interesting for certain values, as they relate to macro parsing - if let Some(subparser) = parser.subparser_name { - dbg_fmt.field("subparser_name", &subparser); - } - if let Recovery::Forbidden = parser.recovery { - dbg_fmt.field("recovery", &parser.recovery); + fmt::from_fn(move |f| { + let mut dbg_fmt = f.debug_struct("Parser"); // or at least, one view of + + // we don't need N spans, but we want at least one, so print all of prev_token + dbg_fmt.field("prev_token", &self.prev_token); + let mut tokens = vec![]; + for i in 0..lookahead { + let tok = self.look_ahead(i, |tok| tok.kind.clone()); + let is_eof = tok == TokenKind::Eof; + tokens.push(tok); + if is_eof { + // Don't look ahead past EOF. + break; } + } + dbg_fmt.field_with("tokens", |field| field.debug_list().entries(tokens).finish()); + dbg_fmt.field("approx_token_stream_pos", &self.num_bump_calls); - // imply there's "more to know" than this view - dbg_fmt.finish_non_exhaustive() + // some fields are interesting for certain values, as they relate to macro parsing + if let Some(subparser) = self.subparser_name { + dbg_fmt.field("subparser_name", &subparser); + } + if let Recovery::Forbidden = self.recovery { + dbg_fmt.field("recovery", &self.recovery); } - } - DebugParser { parser: self, lookahead } + // imply there's "more to know" than this view + dbg_fmt.finish_non_exhaustive() + }) } pub fn clear_expected_token_types(&mut self) { diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 8b6b37c0f8f..86f673c100c 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -11,7 +11,7 @@ use rustc_session::errors::report_lit_error; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNSAFE_ATTR_OUTSIDE_UNSAFE}; use rustc_session::parse::ParseSess; -use rustc_span::{BytePos, Span, Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use crate::{errors, parse_in}; @@ -164,11 +164,7 @@ pub fn check_attribute_safety(psess: &ParseSess, safety: AttributeSafety, attr: // wrapping it in `unsafe(...)`. Otherwise, we suggest putting the // `unsafe(`, `)` right after and right before the opening and closing // square bracket respectively. - let diag_span = if attr_item.span().can_be_used_for_suggestions() { - attr_item.span() - } else { - attr.span.with_lo(attr.span.lo() + BytePos(2)).with_hi(attr.span.hi() - BytePos(1)) - }; + let diag_span = attr_item.span(); if attr.span.at_least_rust_2024() { psess.dcx().emit_err(errors::UnsafeAttrOutsideUnsafe { diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 9eb335cb34c..5418f054beb 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -514,13 +514,7 @@ impl<'a> Parser<'a> { /// Consumes all whitespace characters until the first non-whitespace character fn ws(&mut self) { - while let Some(&(_, c)) = self.cur.peek() { - if c.is_whitespace() { - self.cur.next(); - } else { - break; - } - } + while let Some(_) = self.cur.next_if(|&(_, c)| c.is_whitespace()) {} } /// Parses all of a string which is to be considered a "raw literal" in a @@ -545,7 +539,7 @@ impl<'a> Parser<'a> { } } } - &self.input[start..self.input.len()] + &self.input[start..] } /// Parses an `Argument` structure, or what's contained within braces inside the format string. diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 3ed600a717f..978cb7af242 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -502,11 +502,6 @@ passes_multiple_rustc_main = .first = first `#[rustc_main]` function .additional = additional `#[rustc_main]` function -passes_multiple_start_functions = - multiple `start` functions - .label = multiple `start` functions - .previous = previous `#[start]` function here - passes_must_not_suspend = `must_not_suspend` attribute should be applied to a struct, enum, union, or trait .label = is not a struct, enum, union, or trait @@ -817,6 +812,9 @@ passes_unused_duplicate = passes_unused_empty_lints_note = attribute `{$name}` with an empty list has no effect +passes_unused_linker_warnings_note = + the `linker_warnings` lint can only be controlled at the root of a crate that needs to be linked + passes_unused_multiple = multiple `{$name}` attributes .suggestion = remove this attribute diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 1b2b8ac5dd9..e19819a22b4 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -25,6 +25,7 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{self, TyCtxt, TypingMode}; use rustc_middle::{bug, span_bug}; +use rustc_session::config::CrateType; use rustc_session::lint::builtin::{ CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, @@ -275,7 +276,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::lang | sym::needs_allocator | sym::default_lib_allocator - | sym::start | sym::custom_mir, .. ] => {} @@ -2328,6 +2328,42 @@ impl<'tcx> CheckAttrVisitor<'tcx> { && item.path == sym::reason { errors::UnusedNote::NoLints { name: attr.name_or_empty() } + } else if matches!( + attr.name_or_empty(), + sym::allow | sym::warn | sym::deny | sym::forbid | sym::expect + ) && let Some(meta) = attr.meta_item_list() + && meta.iter().any(|meta| { + meta.meta_item().map_or(false, |item| item.path == sym::linker_messages) + }) + { + if hir_id != CRATE_HIR_ID { + match attr.style { + ast::AttrStyle::Outer => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::OuterCrateLevelAttr, + ), + ast::AttrStyle::Inner => self.tcx.emit_node_span_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::InnerCrateLevelAttr, + ), + }; + return; + } else { + let never_needs_link = self + .tcx + .crate_types() + .iter() + .all(|kind| matches!(kind, CrateType::Rlib | CrateType::Staticlib)); + if never_needs_link { + errors::UnusedNote::LinkerWarningsBinaryCrateOnly + } else { + return; + } + } } else if attr.name_or_empty() == sym::default_method_body_is_const { errors::UnusedNote::DefaultMethodBodyConst } else { @@ -2655,7 +2691,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) { sym::repr, sym::path, sym::automatically_derived, - sym::start, sym::rustc_main, sym::derive, sym::test, @@ -2810,7 +2845,7 @@ fn doc_fake_variadic_is_allowed_self_ty(self_ty: &hir::Ty<'_>) -> bool { && let Some(&[hir::GenericArg::Type(ty)]) = path.segments.last().map(|last| last.args().args) { - doc_fake_variadic_is_allowed_self_ty(ty) + doc_fake_variadic_is_allowed_self_ty(ty.as_unambig_ty()) } else { false }) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index c5dc8e68fe8..e5b63b9b4a6 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -460,7 +460,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } // mark self_ty live - intravisit::walk_ty(self, impl_ref.self_ty); + intravisit::walk_unambig_ty(self, impl_ref.self_ty); if let Some(&impl_item_id) = self.tcx.impl_item_implementor_ids(impl_id).get(&trait_item_id) { @@ -1231,7 +1231,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { continue; } - let is_positional = variant.fields.raw.first().map_or(false, |field| { + let is_positional = variant.fields.raw.first().is_some_and(|field| { field.name.as_str().starts_with(|c: char| c.is_ascii_digit()) }); let report_on = diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index 4949a4316a7..22291c9282d 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -10,9 +10,7 @@ use rustc_session::RemapFileNameExt; use rustc_session::config::{CrateType, EntryFnType, RemapPathScopeComponents, sigpipe}; use rustc_span::{Span, Symbol, sym}; -use crate::errors::{ - AttrOnlyInFunctions, ExternMain, MultipleRustcMain, MultipleStartFunctions, NoMainErr, -}; +use crate::errors::{AttrOnlyInFunctions, ExternMain, MultipleRustcMain, NoMainErr}; struct EntryContext<'tcx> { tcx: TyCtxt<'tcx>, @@ -20,9 +18,6 @@ struct EntryContext<'tcx> { /// The function has the `#[rustc_main]` attribute. rustc_main_fn: Option<(LocalDefId, Span)>, - /// The function that has the attribute `#[start]` on it. - start_fn: Option<(LocalDefId, Span)>, - /// The functions that one might think are `main` but aren't, e.g. /// main functions not defined at the top level. For diagnostics. non_main_fns: Vec<Span>, @@ -40,8 +35,7 @@ fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> { return None; } - let mut ctxt = - EntryContext { tcx, rustc_main_fn: None, start_fn: None, non_main_fns: Vec::new() }; + let mut ctxt = EntryContext { tcx, rustc_main_fn: None, non_main_fns: Vec::new() }; for id in tcx.hir().items() { check_and_search_item(id, &mut ctxt); @@ -57,7 +51,7 @@ fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Opti fn check_and_search_item(id: ItemId, ctxt: &mut EntryContext<'_>) { if !matches!(ctxt.tcx.def_kind(id.owner_id), DefKind::Fn) { - for attr in [sym::start, sym::rustc_main] { + for attr in [sym::rustc_main] { if let Some(span) = attr_span_by_symbol(ctxt, id, attr) { ctxt.tcx.dcx().emit_err(AttrOnlyInFunctions { span, attr }); } @@ -91,24 +85,11 @@ fn check_and_search_item(id: ItemId, ctxt: &mut EntryContext<'_>) { }); } } - EntryPointType::Start => { - if ctxt.start_fn.is_none() { - ctxt.start_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id))); - } else { - ctxt.tcx.dcx().emit_err(MultipleStartFunctions { - span: ctxt.tcx.def_span(id.owner_id), - labeled: ctxt.tcx.def_span(id.owner_id.to_def_id()), - previous: ctxt.start_fn.unwrap().1, - }); - } - } } } fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> { - if let Some((def_id, _)) = visitor.start_fn { - Some((def_id.to_def_id(), EntryFnType::Start)) - } else if let Some((local_def_id, _)) = visitor.rustc_main_fn { + if let Some((local_def_id, _)) = visitor.rustc_main_fn { let def_id = local_def_id.to_def_id(); Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx) })) } else { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index c3043ac60aa..0aa50ad19ff 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -802,6 +802,8 @@ pub(crate) enum UnusedNote { NoLints { name: Symbol }, #[note(passes_unused_default_method_body_const_note)] DefaultMethodBodyConst, + #[note(passes_unused_linker_warnings_note)] + LinkerWarningsBinaryCrateOnly, } #[derive(LintDiagnostic)] @@ -1314,17 +1316,6 @@ pub(crate) struct MultipleRustcMain { } #[derive(Diagnostic)] -#[diag(passes_multiple_start_functions, code = E0138)] -pub(crate) struct MultipleStartFunctions { - #[primary_span] - pub span: Span, - #[label] - pub labeled: Span, - #[label(passes_previous)] - pub previous: Span, -} - -#[derive(Diagnostic)] #[diag(passes_extern_main)] pub(crate) struct ExternMain { #[primary_span] @@ -1800,7 +1791,7 @@ pub(crate) struct UnusedAssign { pub(crate) struct UnusedAssignSuggestion { pub pre: &'static str, #[suggestion_part(code = "{pre}mut ")] - pub ty_span: Span, + pub ty_span: Option<Span>, #[suggestion_part(code = "")] pub ty_ref_span: Span, #[suggestion_part(code = "*")] diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 6617cf2f723..8b10543f6fd 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -5,8 +5,7 @@ use rustc_ast::visit::BoundKind; use rustc_ast::{self as ast, NodeId, visit as ast_visit}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir as hir; -use rustc_hir::{HirId, intravisit as hir_visit}; +use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit}; use rustc_middle::hir::map::Map; use rustc_middle::ty::TyCtxt; use rustc_middle::util::common::to_readable_str; @@ -363,7 +362,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_expr_field(self, f) } - fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, t: &'v hir::Ty<'v, AmbigArg>) { record_variants!((self, t, t.kind, Some(t.hir_id), hir, Ty, TyKind), [ InferDelegation, Slice, @@ -476,7 +475,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir::GenericArg::Lifetime(lt) => self.visit_lifetime(lt), hir::GenericArg::Type(ty) => self.visit_ty(ty), hir::GenericArg::Const(ct) => self.visit_const_arg(ct), - hir::GenericArg::Infer(inf) => self.visit_infer(inf), + hir::GenericArg::Infer(inf) => self.visit_id(inf.hir_id), } } diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 426899a4d5c..73da8855e10 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1620,24 +1620,28 @@ impl<'tcx> Liveness<'_, 'tcx> { && let item = self.ir.tcx.hir_owner_node(item_id) && let Some(fn_decl) = item.fn_decl() && let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = pat.kind - && let Some((ty_span, pre)) = fn_decl + && let Some((lt, mut_ty)) = fn_decl .inputs .iter() .filter_map(|ty| { if ty.span == *ty_span && let hir::TyKind::Ref(lt, mut_ty) = ty.kind { - // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty` - Some(( - mut_ty.ty.span.shrink_to_lo(), - if lt.ident.span.lo() == lt.ident.span.hi() { "" } else { " " }, - )) + Some((lt, mut_ty)) } else { None } }) .next() { + let ty_span = if mut_ty.mutbl.is_mut() { + // Leave `&'name mut Ty` and `&mut Ty` as they are (#136028). + None + } else { + // `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty` + Some(mut_ty.ty.span.shrink_to_lo()) + }; + let pre = if lt.ident.span.lo() == lt.ident.span.hi() { "" } else { " " }; Some(errors::UnusedAssignSuggestion { ty_span, pre, diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 60734122e63..a52f080038d 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -11,12 +11,11 @@ use rustc_attr_parsing::{ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; use rustc_feature::{ACCEPTED_LANG_FEATURES, EnabledLangFeature, EnabledLibFeature}; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::hir_id::CRATE_HIR_ID; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; +use rustc_hir::{self as hir, AmbigArg, FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; use rustc_middle::middle::privacy::EffectiveVisibilities; @@ -802,7 +801,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { )) = stab { let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; - c.visit_ty(self_ty); + c.visit_ty_unambig(self_ty); c.visit_trait_ref(t); // do not lint when the trait isn't resolved, since resolution error should @@ -1028,7 +1027,7 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { intravisit::walk_trait_ref(self, t) } - fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { + fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) { if let TyKind::Never = t.kind { self.fully_stable = false; } @@ -1042,12 +1041,12 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) { for ty in fd.inputs { - self.visit_ty(ty) + self.visit_ty_unambig(ty) } if let hir::FnRetTy::Return(output_ty) = fd.output { match output_ty.kind { TyKind::Never => {} // `-> !` is stable - _ => self.visit_ty(output_ty), + _ => self.visit_ty_unambig(output_ty), } } } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index ae991e3ce40..2694cf472f4 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -794,9 +794,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // fictitious values after `{u,i}size::MAX` (see [`IntRange::split`] for why we do // this). We show this to the user as `usize::MAX..` which is slightly incorrect but // probably clear enough. - let c = ty.numeric_max_val(cx.tcx).unwrap(); - let value = mir::Const::from_ty_const(c, ty.0, cx.tcx); - lo = PatRangeBoundary::Finite(value); + lo = PatRangeBoundary::Finite(ty.numeric_max_val(cx.tcx).unwrap()); } let hi = if let Some(hi) = range.hi.minus_one() { hi diff --git a/compiler/rustc_privacy/messages.ftl b/compiler/rustc_privacy/messages.ftl index 7785f1a7f81..43c34a109d7 100644 --- a/compiler/rustc_privacy/messages.ftl +++ b/compiler/rustc_privacy/messages.ftl @@ -1,5 +1,19 @@ -privacy_field_is_private = field `{$field_name}` of {$variant_descr} `{$def_path_str}` is private -privacy_field_is_private_is_update_syntax_label = field `{$field_name}` is private +privacy_field_is_private = + {$len -> + [1] field + *[other] fields + } {$field_names} of {$variant_descr} `{$def_path_str}` {$len -> + [1] is + *[other] are + } private + .label = in this type +privacy_field_is_private_is_update_syntax_label = {$rest_len -> + [1] field + *[other] fields + } {$rest_field_names} {$rest_len -> + [1] is + *[other] are + } private privacy_field_is_private_label = private field privacy_from_private_dep_in_public_interface = diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs index f5e641eb642..4d1d58c0852 100644 --- a/compiler/rustc_privacy/src/errors.rs +++ b/compiler/rustc_privacy/src/errors.rs @@ -1,5 +1,5 @@ -use rustc_errors::DiagArgFromDisplay; use rustc_errors::codes::*; +use rustc_errors::{DiagArgFromDisplay, MultiSpan}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; @@ -7,12 +7,15 @@ use rustc_span::{Span, Symbol}; #[diag(privacy_field_is_private, code = E0451)] pub(crate) struct FieldIsPrivate { #[primary_span] - pub span: Span, - pub field_name: Symbol, + pub span: MultiSpan, + #[label] + pub struct_span: Option<Span>, + pub field_names: String, pub variant_descr: &'static str, pub def_path_str: String, #[subdiagnostic] - pub label: FieldIsPrivateLabel, + pub labels: Vec<FieldIsPrivateLabel>, + pub len: usize, } #[derive(Subdiagnostic)] @@ -21,7 +24,8 @@ pub(crate) enum FieldIsPrivateLabel { IsUpdateSyntax { #[primary_span] span: Span, - field_name: Symbol, + rest_field_names: String, + rest_len: usize, }, #[label(privacy_field_is_private_label)] Other { diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index e484cfed06b..d19df08519d 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -24,10 +24,11 @@ use rustc_ast::MacroDef; use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::intern::Interned; +use rustc_errors::MultiSpan; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId, LocalModDefId}; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{AssocItemKind, ForeignItemKind, ItemId, ItemKind, PatKind}; +use rustc_hir::intravisit::{self, InferKind, Visitor}; +use rustc_hir::{AmbigArg, AssocItemKind, ForeignItemKind, ItemId, ItemKind, PatKind}; use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level}; use rustc_middle::query::Providers; use rustc_middle::ty::print::PrintTraitRefExt as _; @@ -38,7 +39,7 @@ use rustc_middle::ty::{ use rustc_middle::{bug, span_bug}; use rustc_session::lint; use rustc_span::hygiene::Transparency; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use tracing::debug; use {rustc_attr_parsing as attr, rustc_hir as hir}; @@ -921,31 +922,95 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { &mut self, hir_id: hir::HirId, // ID of the field use use_ctxt: Span, // syntax context of the field name at the use site - span: Span, // span of the field pattern, e.g., `x: 0` def: ty::AdtDef<'tcx>, // definition of the struct or enum field: &'tcx ty::FieldDef, - in_update_syntax: bool, - ) { + ) -> bool { if def.is_enum() { - return; + return true; } // definition of the field let ident = Ident::new(kw::Empty, use_ctxt); let def_id = self.tcx.adjust_ident_and_get_scope(ident, def.did(), hir_id).1; - if !field.vis.is_accessible_from(def_id, self.tcx) { - self.tcx.dcx().emit_err(FieldIsPrivate { - span, - field_name: field.name, - variant_descr: def.variant_descr(), - def_path_str: self.tcx.def_path_str(def.did()), - label: if in_update_syntax { - FieldIsPrivateLabel::IsUpdateSyntax { span, field_name: field.name } - } else { - FieldIsPrivateLabel::Other { span } - }, - }); + !field.vis.is_accessible_from(def_id, self.tcx) + } + + // Checks that a field in a struct constructor (expression or pattern) is accessible. + fn emit_unreachable_field_error( + &mut self, + fields: Vec<(Symbol, Span, bool /* field is present */)>, + def: ty::AdtDef<'tcx>, // definition of the struct or enum + update_syntax: Option<Span>, + struct_span: Span, + ) { + if def.is_enum() || fields.is_empty() { + return; } + + // error[E0451]: fields `beta` and `gamma` of struct `Alpha` are private + // --> $DIR/visibility.rs:18:13 + // | + // LL | let _x = Alpha { + // | ----- in this type # from `def` + // LL | beta: 0, + // | ^^^^^^^ private field # `fields.2` is `true` + // LL | .. + // | ^^ field `gamma` is private # `fields.2` is `false` + + // Get the list of all private fields for the main message. + let field_names: Vec<_> = fields.iter().map(|(name, _, _)| name).collect(); + let field_names = match &field_names[..] { + [] => return, + [name] => format!("`{name}`"), + [fields @ .., last] => format!( + "{} and `{last}`", + fields.iter().map(|f| format!("`{f}`")).collect::<Vec<_>>().join(", "), + ), + }; + let span: MultiSpan = fields.iter().map(|(_, span, _)| *span).collect::<Vec<Span>>().into(); + + // Get the list of all private fields when pointing at the `..rest`. + let rest_field_names: Vec<_> = + fields.iter().filter(|(_, _, is_present)| !is_present).map(|(n, _, _)| n).collect(); + let rest_len = rest_field_names.len(); + let rest_field_names = match &rest_field_names[..] { + [] => String::new(), + [name] => format!("`{name}`"), + [fields @ .., last] => format!( + "{} and `{last}`", + fields.iter().map(|f| format!("`{f}`")).collect::<Vec<_>>().join(", "), + ), + }; + // Get all the labels for each field or `..rest` in the primary MultiSpan. + let labels = fields + .iter() + .filter(|(_, _, is_present)| *is_present) + .map(|(_, span, _)| FieldIsPrivateLabel::Other { span: *span }) + .chain(update_syntax.iter().map(|span| FieldIsPrivateLabel::IsUpdateSyntax { + span: *span, + rest_field_names: rest_field_names.clone(), + rest_len, + })) + .collect(); + + self.tcx.dcx().emit_err(FieldIsPrivate { + span, + struct_span: if self + .tcx + .sess + .source_map() + .is_multiline(fields[0].1.between(struct_span)) + { + Some(struct_span) + } else { + None + }, + field_names: field_names.clone(), + variant_descr: def.variant_descr(), + def_path_str: self.tcx.def_path_str(def.did()), + labels, + len: fields.len(), + }); } fn check_expanded_fields( @@ -955,7 +1020,9 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { fields: &[hir::ExprField<'tcx>], hir_id: hir::HirId, span: Span, + struct_span: Span, ) { + let mut failed_fields = vec![]; for (vf_index, variant_field) in variant.fields.iter_enumerated() { let field = fields.iter().find(|f| self.typeck_results().field_index(f.hir_id) == vf_index); @@ -963,8 +1030,15 @@ impl<'tcx> NamePrivacyVisitor<'tcx> { Some(field) => (field.hir_id, field.ident.span, field.span), None => (hir_id, span, span), }; - self.check_field(hir_id, use_ctxt, span, adt, variant_field, true); + if self.check_field(hir_id, use_ctxt, adt, variant_field) { + let name = match field { + Some(field) => field.ident.name, + None => variant_field.name, + }; + failed_fields.push((name, span, field.is_some())); + } } + self.emit_unreachable_field_error(failed_fields, adt, Some(span), struct_span); } } @@ -990,24 +1064,35 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { // If the expression uses FRU we need to make sure all the unmentioned fields // are checked for privacy (RFC 736). Rather than computing the set of // unmentioned fields, just check them all. - self.check_expanded_fields(adt, variant, fields, base.hir_id, base.span); + self.check_expanded_fields( + adt, + variant, + fields, + base.hir_id, + base.span, + qpath.span(), + ); } hir::StructTailExpr::DefaultFields(span) => { - self.check_expanded_fields(adt, variant, fields, expr.hir_id, span); + self.check_expanded_fields( + adt, + variant, + fields, + expr.hir_id, + span, + qpath.span(), + ); } hir::StructTailExpr::None => { + let mut failed_fields = vec![]; for field in fields { - let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span); + let (hir_id, use_ctxt) = (field.hir_id, field.ident.span); let index = self.typeck_results().field_index(field.hir_id); - self.check_field( - hir_id, - use_ctxt, - span, - adt, - &variant.fields[index], - false, - ); + if self.check_field(hir_id, use_ctxt, adt, &variant.fields[index]) { + failed_fields.push((field.ident.name, field.ident.span, true)); + } } + self.emit_unreachable_field_error(failed_fields, adt, None, qpath.span()); } } } @@ -1020,11 +1105,15 @@ impl<'tcx> Visitor<'tcx> for NamePrivacyVisitor<'tcx> { let res = self.typeck_results().qpath_res(qpath, pat.hir_id); let adt = self.typeck_results().pat_ty(pat).ty_adt_def().unwrap(); let variant = adt.variant_of_res(res); + let mut failed_fields = vec![]; for field in fields { - let (hir_id, use_ctxt, span) = (field.hir_id, field.ident.span, field.span); + let (hir_id, use_ctxt) = (field.hir_id, field.ident.span); let index = self.typeck_results().field_index(field.hir_id); - self.check_field(hir_id, use_ctxt, span, adt, &variant.fields[index], false); + if self.check_field(hir_id, use_ctxt, adt, &variant.fields[index]) { + failed_fields.push((field.ident.name, field.ident.span, true)); + } } + self.emit_unreachable_field_error(failed_fields, adt, None, qpath.span()); } intravisit::walk_pat(self, pat); @@ -1090,7 +1179,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { self.maybe_typeck_results = old_maybe_typeck_results; } - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx, AmbigArg>) { self.span = hir_ty.span; if self .visit( @@ -1106,12 +1195,17 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { intravisit::walk_ty(self, hir_ty); } - fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { - self.span = inf.span; + fn visit_infer( + &mut self, + inf_id: rustc_hir::HirId, + inf_span: Span, + _kind: InferKind<'tcx>, + ) -> Self::Result { + self.span = inf_span; if let Some(ty) = self .maybe_typeck_results - .unwrap_or_else(|| span_bug!(inf.span, "`hir::InferArg` outside of a body")) - .node_type_opt(inf.hir_id) + .unwrap_or_else(|| span_bug!(inf_span, "Inference variable outside of a body")) + .node_type_opt(inf_id) { if self.visit(ty).is_break() { return; @@ -1119,7 +1213,8 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { } else { // FIXME: check types of const infers here. } - intravisit::walk_inf(self, inf); + + self.visit_id(inf_id) } // Check types of expressions diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 3e179c61f39..a8c2aa98cd0 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -567,7 +567,7 @@ pub fn print_query_stack<Qcx: QueryContext>( qcx: Qcx, mut current_query: Option<QueryJobId>, dcx: DiagCtxtHandle<'_>, - num_frames: Option<usize>, + limit_frames: Option<usize>, mut file: Option<std::fs::File>, ) -> usize { // Be careful relying on global state here: this code is called from @@ -584,7 +584,7 @@ pub fn print_query_stack<Qcx: QueryContext>( let Some(query_info) = query_map.get(&query) else { break; }; - if Some(count_printed) < num_frames || num_frames.is_none() { + if Some(count_printed) < limit_frames || limit_frames.is_none() { // Only print to stderr as many stack frames as `num_frames` when present. // FIXME: needs translation #[allow(rustc::diagnostic_outside_of_impl)] @@ -615,5 +615,5 @@ pub fn print_query_stack<Qcx: QueryContext>( if let Some(ref mut file) = file { let _ = writeln!(file, "end of query stack"); } - count_printed + count_total } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 3fe1eed243f..eec9e9a8515 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -645,10 +645,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let self_spans = items .iter() .filter_map(|(use_tree, _)| { - if let ast::UseTreeKind::Simple(..) = use_tree.kind { - if use_tree.ident().name == kw::SelfLower { - return Some(use_tree.span); - } + if let ast::UseTreeKind::Simple(..) = use_tree.kind + && use_tree.ident().name == kw::SelfLower + { + return Some(use_tree.span); } None @@ -947,19 +947,19 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { let imported_binding = self.r.import(binding, import); if parent == self.r.graph_root { let ident = ident.normalize_to_macros_2_0(); - if let Some(entry) = self.r.extern_prelude.get(&ident) { - if expansion != LocalExpnId::ROOT && orig_name.is_some() && !entry.is_import() { - self.r.dcx().emit_err( - errors::MacroExpandedExternCrateCannotShadowExternArguments { - span: item.span, - }, - ); - // `return` is intended to discard this binding because it's an - // unregistered ambiguity error which would result in a panic - // caused by inconsistency `path_res` - // more details: https://github.com/rust-lang/rust/pull/111761 - return; - } + if let Some(entry) = self.r.extern_prelude.get(&ident) + && expansion != LocalExpnId::ROOT + && orig_name.is_some() + && !entry.is_import() + { + self.r.dcx().emit_err( + errors::MacroExpandedExternCrateCannotShadowExternArguments { span: item.span }, + ); + // `return` is intended to discard this binding because it's an + // unregistered ambiguity error which would result in a panic + // caused by inconsistency `path_res` + // more details: https://github.com/rust-lang/rust/pull/111761 + return; } let entry = self .r @@ -1040,10 +1040,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { span: item.span, }); } - if let ItemKind::ExternCrate(Some(orig_name)) = item.kind { - if orig_name == kw::SelfLower { - self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span: attr.span }); - } + if let ItemKind::ExternCrate(Some(orig_name)) = item.kind + && orig_name == kw::SelfLower + { + self.r.dcx().emit_err(errors::MacroUseExternCrateSelf { span: attr.span }); } let ill_formed = |span| { self.r.dcx().emit_err(errors::BadMacroImport { span }); @@ -1179,14 +1179,12 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { return Some((MacroKind::Bang, item.ident, item.span)); } else if ast::attr::contains_name(&item.attrs, sym::proc_macro_attribute) { return Some((MacroKind::Attr, item.ident, item.span)); - } else if let Some(attr) = ast::attr::find_by_name(&item.attrs, sym::proc_macro_derive) { - if let Some(meta_item_inner) = + } else if let Some(attr) = ast::attr::find_by_name(&item.attrs, sym::proc_macro_derive) + && let Some(meta_item_inner) = attr.meta_item_list().and_then(|list| list.get(0).cloned()) - { - if let Some(ident) = meta_item_inner.ident() { - return Some((MacroKind::Derive, ident, ident.span)); - } - } + && let Some(ident) = meta_item_inner.ident() + { + return Some((MacroKind::Derive, ident, ident.span)); } None } diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 5a605df88c2..1c1e8494ffc 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -397,7 +397,7 @@ impl Resolver<'_, '_> { } ImportKind::ExternCrate { id, .. } => { let def_id = self.local_def_id(id); - if self.extern_crate_map.get(&def_id).map_or(true, |&cnum| { + if self.extern_crate_map.get(&def_id).is_none_or(|&cnum| { !tcx.is_compiler_builtins(cnum) && !tcx.is_panic_runtime(cnum) && !tcx.has_global_allocator(cnum) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index dc26d4de57a..8dc752c2cb3 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -24,13 +24,14 @@ use rustc_session::lint::builtin::{ MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, }; use rustc_session::lint::{AmbiguityErrorDiag, BuiltinLintDiag}; +use rustc_session::utils::was_invoked_from_cargo; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym}; use thin_vec::{ThinVec, thin_vec}; -use tracing::debug; +use tracing::{debug, instrument}; use crate::errors::{ self, AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive, @@ -225,10 +226,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let (name, span) = (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span)); - if let Some(s) = self.name_already_seen.get(&name) { - if s == &span { - return; - } + if self.name_already_seen.get(&name) == Some(&span) { + return; } let old_kind = match (ns, old_binding.module()) { @@ -322,7 +321,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); let from_item = - self.extern_prelude.get(&ident).map_or(true, |entry| entry.introduced_by_item); + self.extern_prelude.get(&ident).is_none_or(|entry| entry.introduced_by_item); // Only suggest removing an import if both bindings are to the same def, if both spans // aren't dummy spans. Further, if both bindings are imports, then the ident must have // been introduced by an item. @@ -380,20 +379,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { suggestion = Some(format!("self as {suggested_name}")) } ImportKind::Single { source, .. } => { - if let Some(pos) = - source.span.hi().0.checked_sub(binding_span.lo().0).map(|pos| pos as usize) + if let Some(pos) = source.span.hi().0.checked_sub(binding_span.lo().0) + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span) + && pos as usize <= snippet.len() { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span) { - if pos <= snippet.len() { - span = binding_span - .with_lo(binding_span.lo() + BytePos(pos as u32)) - .with_hi( - binding_span.hi() - - BytePos(if snippet.ends_with(';') { 1 } else { 0 }), - ); - suggestion = Some(format!(" as {suggested_name}")); - } - } + span = binding_span.with_lo(binding_span.lo() + BytePos(pos)).with_hi( + binding_span.hi() - BytePos(if snippet.ends_with(';') { 1 } else { 0 }), + ); + suggestion = Some(format!(" as {suggested_name}")); } } ImportKind::ExternCrate { source, target, .. } => { @@ -510,13 +503,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // If the first element of our path was actually resolved to an // `ExternCrate` (also used for `crate::...`) then no need to issue a // warning, this looks all good! - if let Some(binding) = second_binding { - if let NameBindingKind::Import { import, .. } = binding.kind { - // Careful: we still want to rewrite paths from renamed extern crates. - if let ImportKind::ExternCrate { source: None, .. } = import.kind { - return; - } - } + if let Some(binding) = second_binding + && let NameBindingKind::Import { import, .. } = binding.kind + // Careful: we still want to rewrite paths from renamed extern crates. + && let ImportKind::ExternCrate { source: None, .. } = import.kind + { + return; } let diag = BuiltinLintDiag::AbsPathWithModule(root_span); @@ -537,7 +529,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) { module.for_each_child(self, |_this, ident, _ns, binding| { let res = binding.res(); - if filter_fn(res) && ctxt.map_or(true, |ctxt| ctxt == ident.span.ctxt()) { + if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == ident.span.ctxt()) { names.push(TypoSuggestion::typo_from_ident(ident, res)); } }); @@ -685,7 +677,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } if could_be_path { let import_suggestions = self.lookup_import_candidates( - Ident::with_dummy_span(name), + name, Namespace::ValueNS, &parent_scope, &|res: Res| { @@ -809,7 +801,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } err.multipart_suggestion(msg, suggestions, applicability); } - if let Some(ModuleOrUniformRoot::Module(module)) = module && let Some(module) = module.opt_def_id() && let Some(segment) = segment @@ -1047,20 +1038,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if filter_fn(res) { for derive in parent_scope.derives { let parent_scope = &ParentScope { derives: &[], ..*parent_scope }; - if let Ok((Some(ext), _)) = this.resolve_macro_path( + let Ok((Some(ext), _)) = this.resolve_macro_path( derive, Some(MacroKind::Derive), parent_scope, false, false, None, - ) { - suggestions.extend( - ext.helper_attrs - .iter() - .map(|name| TypoSuggestion::typo_from_name(*name, res)), - ); - } + ) else { + continue; + }; + suggestions.extend( + ext.helper_attrs + .iter() + .map(|name| TypoSuggestion::typo_from_name(*name, res)), + ); } } } @@ -1215,12 +1207,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } // #90113: Do not count an inaccessible reexported item as a candidate. - if let NameBindingKind::Import { binding, .. } = name_binding.kind { - if this.is_accessible_from(binding.vis, parent_scope.module) - && !this.is_accessible_from(name_binding.vis, parent_scope.module) - { - return; - } + if let NameBindingKind::Import { binding, .. } = name_binding.kind + && this.is_accessible_from(binding.vis, parent_scope.module) + && !this.is_accessible_from(name_binding.vis, parent_scope.module) + { + return; } let res = name_binding.res(); @@ -1229,7 +1220,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { _ => res.opt_def_id(), }; let child_doc_visible = doc_visible - && (did.map_or(true, |did| did.is_local() || !this.tcx.is_doc_hidden(did))); + && did.is_none_or(|did| did.is_local() || !this.tcx.is_doc_hidden(did)); // collect results based on the filter function // avoid suggesting anything from the same module in which we are resolving @@ -1253,14 +1244,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { segms.push(ast::PathSegment::from_ident(ident)); let path = Path { span: name_binding.span, segments: segms, tokens: None }; - if child_accessible { + if child_accessible // Remove invisible match if exists - if let Some(idx) = candidates + && let Some(idx) = candidates .iter() .position(|v: &ImportSuggestion| v.did == did && !v.accessible) - { - candidates.remove(idx); - } + { + candidates.remove(idx); } if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { @@ -1373,48 +1363,50 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // otherwise cause duplicate suggestions. continue; } - let crate_id = self.crate_loader(|c| c.maybe_process_path_extern(ident.name)); - if let Some(crate_id) = crate_id { - let crate_def_id = crate_id.as_def_id(); - let crate_root = self.expect_module(crate_def_id); - - // Check if there's already an item in scope with the same name as the crate. - // If so, we have to disambiguate the potential import suggestions by making - // the paths *global* (i.e., by prefixing them with `::`). - let needs_disambiguation = - self.resolutions(parent_scope.module).borrow().iter().any( - |(key, name_resolution)| { - if key.ns == TypeNS - && key.ident == ident - && let Some(binding) = name_resolution.borrow().binding - { - match binding.res() { - // No disambiguation needed if the identically named item we - // found in scope actually refers to the crate in question. - Res::Def(_, def_id) => def_id != crate_def_id, - Res::PrimTy(_) => true, - _ => false, - } - } else { - false - } - }, - ); - let mut crate_path = ThinVec::new(); - if needs_disambiguation { - crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); - } - crate_path.push(ast::PathSegment::from_ident(ident)); + let Some(crate_id) = self.crate_loader(|c| c.maybe_process_path_extern(ident.name)) + else { + continue; + }; - suggestions.extend(self.lookup_import_candidates_from_module( - lookup_ident, - namespace, - parent_scope, - crate_root, - crate_path, - &filter_fn, - )); + let crate_def_id = crate_id.as_def_id(); + let crate_root = self.expect_module(crate_def_id); + + // Check if there's already an item in scope with the same name as the crate. + // If so, we have to disambiguate the potential import suggestions by making + // the paths *global* (i.e., by prefixing them with `::`). + let needs_disambiguation = + self.resolutions(parent_scope.module).borrow().iter().any( + |(key, name_resolution)| { + if key.ns == TypeNS + && key.ident == ident + && let Some(binding) = name_resolution.borrow().binding + { + match binding.res() { + // No disambiguation needed if the identically named item we + // found in scope actually refers to the crate in question. + Res::Def(_, def_id) => def_id != crate_def_id, + Res::PrimTy(_) => true, + _ => false, + } + } else { + false + } + }, + ); + let mut crate_path = ThinVec::new(); + if needs_disambiguation { + crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); } + crate_path.push(ast::PathSegment::from_ident(ident)); + + suggestions.extend(self.lookup_import_candidates_from_module( + lookup_ident, + namespace, + parent_scope, + crate_root, + crate_path, + &filter_fn, + )); } } @@ -1511,7 +1503,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }); } for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { - if let Ok(binding) = self.early_resolve_ident_in_lexical_scope( + let Ok(binding) = self.early_resolve_ident_in_lexical_scope( ident, ScopeSet::All(ns), parent_scope, @@ -1519,53 +1511,53 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { false, None, None, - ) { - let desc = match binding.res() { - Res::Def(DefKind::Macro(MacroKind::Bang), _) => { - "a function-like macro".to_string() - } - Res::Def(DefKind::Macro(MacroKind::Attr), _) | Res::NonMacroAttr(..) => { - format!("an attribute: `#[{ident}]`") - } - Res::Def(DefKind::Macro(MacroKind::Derive), _) => { - format!("a derive macro: `#[derive({ident})]`") - } - Res::ToolMod => { - // Don't confuse the user with tool modules. - continue; - } - Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { - "only a trait, without a derive macro".to_string() - } - res => format!( - "{} {}, not {} {}", - res.article(), - res.descr(), - macro_kind.article(), - macro_kind.descr_expected(), - ), - }; - if let crate::NameBindingKind::Import { import, .. } = binding.kind { - if !import.span.is_dummy() { - let note = errors::IdentImporterHereButItIsDesc { - span: import.span, - imported_ident: ident, - imported_ident_desc: &desc, - }; - err.subdiagnostic(note); - // Silence the 'unused import' warning we might get, - // since this diagnostic already covers that import. - self.record_use(ident, binding, Used::Other); - return; - } + ) else { + continue; + }; + + let desc = match binding.res() { + Res::Def(DefKind::Macro(MacroKind::Bang), _) => "a function-like macro".to_string(), + Res::Def(DefKind::Macro(MacroKind::Attr), _) | Res::NonMacroAttr(..) => { + format!("an attribute: `#[{ident}]`") + } + Res::Def(DefKind::Macro(MacroKind::Derive), _) => { + format!("a derive macro: `#[derive({ident})]`") } - let note = errors::IdentInScopeButItIsDesc { + Res::ToolMod => { + // Don't confuse the user with tool modules. + continue; + } + Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { + "only a trait, without a derive macro".to_string() + } + res => format!( + "{} {}, not {} {}", + res.article(), + res.descr(), + macro_kind.article(), + macro_kind.descr_expected(), + ), + }; + if let crate::NameBindingKind::Import { import, .. } = binding.kind + && !import.span.is_dummy() + { + let note = errors::IdentImporterHereButItIsDesc { + span: import.span, imported_ident: ident, imported_ident_desc: &desc, }; err.subdiagnostic(note); + // Silence the 'unused import' warning we might get, + // since this diagnostic already covers that import. + self.record_use(ident, binding, Used::Other); return; } + let note = errors::IdentInScopeButItIsDesc { + imported_ident: ident, + imported_ident_desc: &desc, + }; + err.subdiagnostic(note); + return; } } @@ -1749,15 +1741,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// If the binding refers to a tuple struct constructor with fields, /// returns the span of its fields. fn ctor_fields_span(&self, binding: NameBinding<'_>) -> Option<Span> { - if let NameBindingKind::Res(Res::Def( + let NameBindingKind::Res(Res::Def( DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id, )) = binding.kind - { - let def_id = self.tcx.parent(ctor_def_id); - return self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to); // None for `struct Foo()` - } - None + else { + return None; + }; + + let def_id = self.tcx.parent(ctor_def_id); + self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to) // None for `struct Foo()` } fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { @@ -1983,10 +1976,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { .collect::<Vec<_>>(); candidates.sort(); candidates.dedup(); - match find_best_match_for_name(&candidates, ident, None) { - Some(sugg) if sugg == ident => None, - sugg => sugg, - } + find_best_match_for_name(&candidates, ident, None).filter(|sugg| *sugg != ident) } pub(crate) fn report_path_resolution_error( @@ -2044,13 +2034,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { (format!("`_` is not a valid crate or module name"), None) } else if self.tcx.sess.is_rust_2015() { ( - format!("you might be missing crate `{ident}`"), + format!("use of unresolved module or unlinked crate `{ident}`"), Some(( vec![( self.current_crate_outer_attr_insert_span, format!("extern crate {ident};\n"), )], - format!("consider importing the `{ident}` crate"), + if was_invoked_from_cargo() { + format!( + "if you wanted to use a crate named `{ident}`, use `cargo add {ident}` \ + to add it to your `Cargo.toml` and import it in your code", + ) + } else { + format!( + "you might be missing a crate named `{ident}`, add it to your \ + project and import it in your code", + ) + }, Applicability::MaybeIncorrect, )), ) @@ -2229,20 +2229,37 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let descr = binding.res().descr(); (format!("{descr} `{ident}` is not a crate or module"), suggestion) } else { - (format!("use of undeclared crate or module `{ident}`"), suggestion) + let suggestion = if suggestion.is_some() { + suggestion + } else if was_invoked_from_cargo() { + Some(( + vec![], + format!( + "if you wanted to use a crate named `{ident}`, use `cargo add {ident}` \ + to add it to your `Cargo.toml`", + ), + Applicability::MaybeIncorrect, + )) + } else { + Some(( + vec![], + format!("you might be missing a crate named `{ident}`",), + Applicability::MaybeIncorrect, + )) + }; + (format!("use of unresolved module or unlinked crate `{ident}`"), suggestion) } } } /// Adds suggestions for a path that cannot be resolved. + #[instrument(level = "debug", skip(self, parent_scope))] pub(crate) fn make_path_suggestion( &mut self, span: Span, mut path: Vec<Segment>, parent_scope: &ParentScope<'ra>, ) -> Option<(Vec<Segment>, Option<String>)> { - debug!("make_path_suggestion: span={:?} path={:?}", span, path); - match (path.get(0), path.get(1)) { // `{{root}}::ident::...` on both editions. // On 2015 `{{root}}` is usually added implicitly. @@ -2271,6 +2288,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// LL | use foo::Bar; /// | ^^^ did you mean `self::foo`? /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] fn make_missing_self_suggestion( &mut self, mut path: Vec<Segment>, @@ -2279,7 +2297,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Replace first ident with `self` and check if that is valid. path[0].ident.name = kw::SelfLower; let result = self.maybe_resolve_path(&path, None, parent_scope, None); - debug!("make_missing_self_suggestion: path={:?} result={:?}", path, result); + debug!(?path, ?result); if let PathResult::Module(..) = result { Some((path, None)) } else { None } } @@ -2290,6 +2308,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// LL | use foo::Bar; /// | ^^^ did you mean `crate::foo`? /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] fn make_missing_crate_suggestion( &mut self, mut path: Vec<Segment>, @@ -2298,7 +2317,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Crate; let result = self.maybe_resolve_path(&path, None, parent_scope, None); - debug!("make_missing_crate_suggestion: path={:?} result={:?}", path, result); + debug!(?path, ?result); if let PathResult::Module(..) = result { Some(( path, @@ -2321,6 +2340,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// LL | use foo::Bar; /// | ^^^ did you mean `super::foo`? /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] fn make_missing_super_suggestion( &mut self, mut path: Vec<Segment>, @@ -2329,7 +2349,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Replace first ident with `crate` and check if that is valid. path[0].ident.name = kw::Super; let result = self.maybe_resolve_path(&path, None, parent_scope, None); - debug!("make_missing_super_suggestion: path={:?} result={:?}", path, result); + debug!(?path, ?result); if let PathResult::Module(..) = result { Some((path, None)) } else { None } } @@ -2343,6 +2363,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// /// Used when importing a submodule of an external crate but missing that crate's /// name as the first part of path. + #[instrument(level = "debug", skip(self, parent_scope))] fn make_external_crate_suggestion( &mut self, mut path: Vec<Segment>, @@ -2363,10 +2384,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Replace first ident with a crate name and check if that is valid. path[0].ident.name = name; let result = self.maybe_resolve_path(&path, None, parent_scope, None); - debug!( - "make_external_crate_suggestion: name={:?} path={:?} result={:?}", - name, path, result - ); + debug!(?path, ?name, ?result); if let PathResult::Module(..) = result { return Some((path, None)); } @@ -2410,121 +2428,115 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let binding_key = BindingKey::new(ident, MacroNS); let resolution = resolutions.get(&binding_key)?; let binding = resolution.borrow().binding()?; - if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() { - let module_name = crate_module.kind.name().unwrap(); - let import_snippet = match import.kind { - ImportKind::Single { source, target, .. } if source != target => { - format!("{source} as {target}") - } - _ => format!("{ident}"), - }; + let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() else { + return None; + }; + let module_name = crate_module.kind.name().unwrap(); + let import_snippet = match import.kind { + ImportKind::Single { source, target, .. } if source != target => { + format!("{source} as {target}") + } + _ => format!("{ident}"), + }; - let mut corrections: Vec<(Span, String)> = Vec::new(); - if !import.is_nested() { - // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove - // intermediate segments. - corrections.push((import.span, format!("{module_name}::{import_snippet}"))); - } else { - // Find the binding span (and any trailing commas and spaces). - // ie. `use a::b::{c, d, e};` - // ^^^ - let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( - self.tcx.sess, - import.span, - import.use_span, - ); - debug!( - "check_for_module_export_macro: found_closing_brace={:?} binding_span={:?}", - found_closing_brace, binding_span - ); + let mut corrections: Vec<(Span, String)> = Vec::new(); + if !import.is_nested() { + // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove + // intermediate segments. + corrections.push((import.span, format!("{module_name}::{import_snippet}"))); + } else { + // Find the binding span (and any trailing commas and spaces). + // ie. `use a::b::{c, d, e};` + // ^^^ + let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( + self.tcx.sess, + import.span, + import.use_span, + ); + debug!(found_closing_brace, ?binding_span); + + let mut removal_span = binding_span; + + // If the binding span ended with a closing brace, as in the below example: + // ie. `use a::b::{c, d};` + // ^ + // Then expand the span of characters to remove to include the previous + // binding's trailing comma. + // ie. `use a::b::{c, d};` + // ^^^ + if found_closing_brace + && let Some(previous_span) = + extend_span_to_previous_binding(self.tcx.sess, binding_span) + { + debug!(?previous_span); + removal_span = removal_span.with_lo(previous_span.lo()); + } + debug!(?removal_span); - let mut removal_span = binding_span; - if found_closing_brace { - // If the binding span ended with a closing brace, as in the below example: - // ie. `use a::b::{c, d};` - // ^ - // Then expand the span of characters to remove to include the previous - // binding's trailing comma. - // ie. `use a::b::{c, d};` - // ^^^ - if let Some(previous_span) = - extend_span_to_previous_binding(self.tcx.sess, binding_span) - { - debug!("check_for_module_export_macro: previous_span={:?}", previous_span); - removal_span = removal_span.with_lo(previous_span.lo()); - } - } - debug!("check_for_module_export_macro: removal_span={:?}", removal_span); - - // Remove the `removal_span`. - corrections.push((removal_span, "".to_string())); - - // Find the span after the crate name and if it has nested imports immediately - // after the crate name already. - // ie. `use a::b::{c, d};` - // ^^^^^^^^^ - // or `use a::{b, c, d}};` - // ^^^^^^^^^^^ - let (has_nested, after_crate_name) = find_span_immediately_after_crate_name( - self.tcx.sess, - module_name, - import.use_span, - ); - debug!( - "check_for_module_export_macro: has_nested={:?} after_crate_name={:?}", - has_nested, after_crate_name - ); + // Remove the `removal_span`. + corrections.push((removal_span, "".to_string())); - let source_map = self.tcx.sess.source_map(); + // Find the span after the crate name and if it has nested imports immediately + // after the crate name already. + // ie. `use a::b::{c, d};` + // ^^^^^^^^^ + // or `use a::{b, c, d}};` + // ^^^^^^^^^^^ + let (has_nested, after_crate_name) = + find_span_immediately_after_crate_name(self.tcx.sess, module_name, import.use_span); + debug!(has_nested, ?after_crate_name); - // Make sure this is actually crate-relative. - let is_definitely_crate = import - .module_path - .first() - .is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super); + let source_map = self.tcx.sess.source_map(); - // Add the import to the start, with a `{` if required. - let start_point = source_map.start_point(after_crate_name); - if is_definitely_crate - && let Ok(start_snippet) = source_map.span_to_snippet(start_point) - { - corrections.push(( - start_point, - if has_nested { - // In this case, `start_snippet` must equal '{'. - format!("{start_snippet}{import_snippet}, ") - } else { - // In this case, add a `{`, then the moved import, then whatever - // was there before. - format!("{{{import_snippet}, {start_snippet}") - }, - )); + // Make sure this is actually crate-relative. + let is_definitely_crate = import + .module_path + .first() + .is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super); - // Add a `};` to the end if nested, matching the `{` added at the start. - if !has_nested { - corrections - .push((source_map.end_point(after_crate_name), "};".to_string())); - } - } else { - // If the root import is module-relative, add the import separately - corrections.push(( - import.use_span.shrink_to_lo(), - format!("use {module_name}::{import_snippet};\n"), - )); + // Add the import to the start, with a `{` if required. + let start_point = source_map.start_point(after_crate_name); + if is_definitely_crate + && let Ok(start_snippet) = source_map.span_to_snippet(start_point) + { + corrections.push(( + start_point, + if has_nested { + // In this case, `start_snippet` must equal '{'. + format!("{start_snippet}{import_snippet}, ") + } else { + // In this case, add a `{`, then the moved import, then whatever + // was there before. + format!("{{{import_snippet}, {start_snippet}") + }, + )); + + // Add a `};` to the end if nested, matching the `{` added at the start. + if !has_nested { + corrections.push((source_map.end_point(after_crate_name), "};".to_string())); } + } else { + // If the root import is module-relative, add the import separately + corrections.push(( + import.use_span.shrink_to_lo(), + format!("use {module_name}::{import_snippet};\n"), + )); } + } - let suggestion = Some(( - corrections, - String::from("a macro with this name exists at the root of the crate"), - Applicability::MaybeIncorrect, - )); - Some((suggestion, Some("this could be because a macro annotated with `#[macro_export]` will be exported \ + let suggestion = Some(( + corrections, + String::from("a macro with this name exists at the root of the crate"), + Applicability::MaybeIncorrect, + )); + Some(( + suggestion, + Some( + "this could be because a macro annotated with `#[macro_export]` will be exported \ at the root of the crate instead of the module where it is defined" - .to_string()))) - } else { - None - } + .to_string(), + ), + )) } /// Finds a cfg-ed out item inside `module` with the matching name. @@ -2677,15 +2689,12 @@ fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option /// use foo::{a, b::{c, d}}; /// // ^^^^^^^^^^^^^^^ -- true /// ``` +#[instrument(level = "debug", skip(sess))] fn find_span_immediately_after_crate_name( sess: &Session, module_name: Symbol, use_span: Span, ) -> (bool, Span) { - debug!( - "find_span_immediately_after_crate_name: module_name={:?} use_span={:?}", - module_name, use_span - ); let source_map = sess.source_map(); // Using `use issue_59764::foo::{baz, makro};` as an example throughout.. @@ -3051,7 +3060,6 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { self.first_legal_span = Some(inject); } self.first_use_span = search_for_any_use_in_items(&c.items); - return; } else { visit::walk_crate(self, c); } @@ -3065,7 +3073,6 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { self.first_legal_span = Some(inject); } self.first_use_span = search_for_any_use_in_items(items); - return; } } else { visit::walk_item(self, item); @@ -3075,16 +3082,16 @@ impl<'tcx> visit::Visitor<'tcx> for UsePlacementFinder { fn search_for_any_use_in_items(items: &[P<ast::Item>]) -> Option<Span> { for item in items { - if let ItemKind::Use(..) = item.kind { - if is_span_suitable_for_use_injection(item.span) { - let mut lo = item.span.lo(); - for attr in &item.attrs { - if attr.span.eq_ctxt(item.span) { - lo = std::cmp::min(lo, attr.span.lo()); - } + if let ItemKind::Use(..) = item.kind + && is_span_suitable_for_use_injection(item.span) + { + let mut lo = item.span.lo(); + for attr in &item.attrs { + if attr.span.eq_ctxt(item.span) { + lo = std::cmp::min(lo, attr.span.lo()); } - return Some(Span::new(lo, lo, item.span.ctxt(), item.span.parent())); } + return Some(Span::new(lo, lo, item.span.ctxt(), item.span.parent())); } } None diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs index e279d14704e..6ef4aa40725 100644 --- a/compiler/rustc_resolve/src/effective_visibilities.rs +++ b/compiler/rustc_resolve/src/effective_visibilities.rs @@ -118,37 +118,38 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> { let resolutions = self.r.resolutions(module); for (_, name_resolution) in resolutions.borrow().iter() { - if let Some(mut binding) = name_resolution.borrow().binding() { - // Set the given effective visibility level to `Level::Direct` and - // sets the rest of the `use` chain to `Level::Reexported` until - // we hit the actual exported item. - // - // If the binding is ambiguous, put the root ambiguity binding and all reexports - // leading to it into the table. They are used by the `ambiguous_glob_reexports` - // lint. For all bindings added to the table this way `is_ambiguity` returns true. - let is_ambiguity = - |binding: NameBinding<'ra>, warn: bool| binding.ambiguity.is_some() && !warn; - let mut parent_id = ParentId::Def(module_id); - let mut warn_ambiguity = binding.warn_ambiguity; - while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind { - self.update_import(binding, parent_id); + let Some(mut binding) = name_resolution.borrow().binding() else { + continue; + }; + // Set the given effective visibility level to `Level::Direct` and + // sets the rest of the `use` chain to `Level::Reexported` until + // we hit the actual exported item. + // + // If the binding is ambiguous, put the root ambiguity binding and all reexports + // leading to it into the table. They are used by the `ambiguous_glob_reexports` + // lint. For all bindings added to the table this way `is_ambiguity` returns true. + let is_ambiguity = + |binding: NameBinding<'ra>, warn: bool| binding.ambiguity.is_some() && !warn; + let mut parent_id = ParentId::Def(module_id); + let mut warn_ambiguity = binding.warn_ambiguity; + while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind { + self.update_import(binding, parent_id); - if is_ambiguity(binding, warn_ambiguity) { - // Stop at the root ambiguity, further bindings in the chain should not - // be reexported because the root ambiguity blocks any access to them. - // (Those further bindings are most likely not ambiguities themselves.) - break; - } - - parent_id = ParentId::Import(binding); - binding = nested_binding; - warn_ambiguity |= nested_binding.warn_ambiguity; - } - if !is_ambiguity(binding, warn_ambiguity) - && let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) - { - self.update_def(def_id, binding.vis.expect_local(), parent_id); + if is_ambiguity(binding, warn_ambiguity) { + // Stop at the root ambiguity, further bindings in the chain should not + // be reexported because the root ambiguity blocks any access to them. + // (Those further bindings are most likely not ambiguities themselves.) + break; } + + parent_id = ParentId::Import(binding); + binding = nested_binding; + warn_ambiguity |= nested_binding.warn_ambiguity; + } + if !is_ambiguity(binding, warn_ambiguity) + && let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) + { + self.update_def(def_id, binding.vis.expect_local(), parent_id); } } } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 3bfe98f7091..7eb795034b0 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -59,7 +59,7 @@ pub(crate) struct NameAlreadyUsedInParameterList { pub(crate) span: Span, #[label(resolve_first_use_of_name)] pub(crate) first_use_span: Span, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(Diagnostic)] @@ -142,7 +142,7 @@ pub(crate) struct VariableBoundWithDifferentMode { pub(crate) span: Span, #[label(resolve_first_binding_span)] pub(crate) first_binding_span: Span, - pub(crate) variable_name: Symbol, + pub(crate) variable_name: Ident, } #[derive(Diagnostic)] @@ -151,7 +151,7 @@ pub(crate) struct IdentifierBoundMoreThanOnceInParameterList { #[primary_span] #[label] pub(crate) span: Span, - pub(crate) identifier: Symbol, + pub(crate) identifier: Ident, } #[derive(Diagnostic)] @@ -160,7 +160,7 @@ pub(crate) struct IdentifierBoundMoreThanOnceInSamePattern { #[primary_span] #[label] pub(crate) span: Span, - pub(crate) identifier: Symbol, + pub(crate) identifier: Ident, } #[derive(Diagnostic)] @@ -478,7 +478,7 @@ pub(crate) struct TraitImplDuplicate { pub(crate) old_span: Span, #[label(resolve_trait_item_span)] pub(crate) trait_item_span: Span, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(Diagnostic)] @@ -976,7 +976,7 @@ pub(crate) struct AttemptToDefineBuiltinMacroTwice { pub(crate) struct VariableIsNotBoundInAllPatterns { #[primary_span] pub(crate) multispan: MultiSpan, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(Subdiagnostic, Debug, Clone)] @@ -984,7 +984,7 @@ pub(crate) struct VariableIsNotBoundInAllPatterns { pub(crate) struct PatternDoesntBindName { #[primary_span] pub(crate) span: Span, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(Subdiagnostic, Debug, Clone)] @@ -1260,7 +1260,7 @@ pub(crate) struct TraitImplMismatch { #[primary_span] #[label] pub(crate) span: Span, - pub(crate) name: Symbol, + pub(crate) name: Ident, pub(crate) kind: &'static str, pub(crate) trait_path: String, #[label(resolve_trait_impl_mismatch_label_item)] diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 45e87edc53e..a3d3e87ade0 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -246,23 +246,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // ---- end // ``` // So we have to fall back to the module's parent during lexical resolution in this case. - if derive_fallback_lint_id.is_some() { - if let Some(parent) = module.parent { - // Inner module is inside the macro, parent module is outside of the macro. - if module.expansion != parent.expansion - && module.expansion.is_descendant_of(parent.expansion) - { - // The macro is a proc macro derive - if let Some(def_id) = module.expansion.expn_data().macro_def_id { - let ext = &self.get_macro_by_def_id(def_id).ext; - if ext.builtin_name.is_none() - && ext.macro_kind() == MacroKind::Derive - && parent.expansion.outer_expn_is_descendant_of(*ctxt) - { - return Some((parent, derive_fallback_lint_id)); - } - } - } + if derive_fallback_lint_id.is_some() + && let Some(parent) = module.parent + // Inner module is inside the macro + && module.expansion != parent.expansion + // Parent module is outside of the macro + && module.expansion.is_descendant_of(parent.expansion) + // The macro is a proc macro derive + && let Some(def_id) = module.expansion.expn_data().macro_def_id + { + let ext = &self.get_macro_by_def_id(def_id).ext; + if ext.builtin_name.is_none() + && ext.macro_kind() == MacroKind::Derive + && parent.expansion.outer_expn_is_descendant_of(*ctxt) + { + return Some((parent, derive_fallback_lint_id)); } } @@ -593,8 +591,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }, Scope::StdLibPrelude => { let mut result = Err(Determinacy::Determined); - if let Some(prelude) = this.prelude { - if let Ok(binding) = this.resolve_ident_in_module_unadjusted( + if let Some(prelude) = this.prelude + && let Ok(binding) = this.resolve_ident_in_module_unadjusted( ModuleOrUniformRoot::Module(prelude), ident, ns, @@ -603,14 +601,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None, ignore_binding, ignore_import, - ) { - if matches!(use_prelude, UsePrelude::Yes) - || this.is_builtin_macro(binding.res()) - { - result = Ok((binding, Flags::MISC_FROM_PRELUDE)); - } - } + ) + && (matches!(use_prelude, UsePrelude::Yes) + || this.is_builtin_macro(binding.res())) + { + result = Ok((binding, Flags::MISC_FROM_PRELUDE)); } + result } Scope::BuiltinTypes => match this.builtin_types_bindings.get(&ident.name) { @@ -939,10 +936,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; // Items and single imports are not shadowable, if we have one, then it's determined. - if let Some(binding) = binding { - if !binding.is_glob_import() { - return check_usable(self, binding); - } + if let Some(binding) = binding + && !binding.is_glob_import() + { + return check_usable(self, binding); } // --- From now on we either have a glob resolution or no resolution. --- @@ -1437,13 +1434,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for (segment_idx, &Segment { ident, id, .. }) in path.iter().enumerate() { debug!("resolve_path ident {} {:?} {:?}", segment_idx, ident, id); let record_segment_res = |this: &mut Self, res| { - if finalize.is_some() { - if let Some(id) = id { - if !this.partial_res_map.contains_key(&id) { - assert!(id != ast::DUMMY_NODE_ID, "Trying to resolve dummy id"); - this.record_partial_res(id, PartialRes::new(res)); - } - } + if finalize.is_some() + && let Some(id) = id + && !this.partial_res_map.contains_key(&id) + { + assert!(id != ast::DUMMY_NODE_ID, "Trying to resolve dummy id"); + this.record_partial_res(id, PartialRes::new(res)); } }; @@ -1463,13 +1459,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { _ => None, }, }; - if let Some(self_module) = self_module { - if let Some(parent) = self_module.parent { - module = Some(ModuleOrUniformRoot::Module( - self.resolve_self(&mut ctxt, parent), - )); - continue; - } + if let Some(self_module) = self_module + && let Some(parent) = self_module.parent + { + module = + Some(ModuleOrUniformRoot::Module(self.resolve_self(&mut ctxt, parent))); + continue; } return PathResult::failed( ident, @@ -1644,13 +1639,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } Err(Undetermined) => return PathResult::Indeterminate, Err(Determined) => { - if let Some(ModuleOrUniformRoot::Module(module)) = module { - if opt_ns.is_some() && !module.is_normal() { - return PathResult::NonModule(PartialRes::with_unresolved_segments( - module.res().unwrap(), - path.len() - segment_idx, - )); - } + if let Some(ModuleOrUniformRoot::Module(module)) = module + && opt_ns.is_some() + && !module.is_normal() + { + return PathResult::NonModule(PartialRes::with_unresolved_segments( + module.res().unwrap(), + path.len() - segment_idx, + )); } return PathResult::failed( diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index cad45d3c293..d555c938770 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -287,12 +287,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { binding.vis }; - if let ImportKind::Glob { ref max_vis, .. } = import.kind { - if vis == import_vis - || max_vis.get().map_or(true, |max_vis| vis.is_at_least(max_vis, self.tcx)) - { - max_vis.set(Some(vis.expect_local())) - } + if let ImportKind::Glob { ref max_vis, .. } = import.kind + && (vis == import_vis + || max_vis.get().is_none_or(|max_vis| vis.is_at_least(max_vis, self.tcx))) + { + max_vis.set(Some(vis.expect_local())) } self.arenas.alloc_name_binding(NameBindingData { @@ -543,31 +542,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // resolution for it so that later resolve stages won't complain. self.import_dummy_binding(*import, is_indeterminate); - if let Some(err) = unresolved_import_error { - glob_error |= import.is_glob(); + let Some(err) = unresolved_import_error else { continue }; - if let ImportKind::Single { source, ref source_bindings, .. } = import.kind { - if source.name == kw::SelfLower { - // Silence `unresolved import` error if E0429 is already emitted - if let Err(Determined) = source_bindings.value_ns.get() { - continue; - } - } - } + glob_error |= import.is_glob(); - if prev_root_id != NodeId::ZERO - && prev_root_id != import.root_id - && !errors.is_empty() - { - // In the case of a new import line, throw a diagnostic message - // for the previous line. - self.throw_unresolved_import_error(errors, glob_error); - errors = vec![]; - } - if seen_spans.insert(err.span) { - errors.push((*import, err)); - prev_root_id = import.root_id; - } + if let ImportKind::Single { source, ref source_bindings, .. } = import.kind + && source.name == kw::SelfLower + // Silence `unresolved import` error if E0429 is already emitted + && let Err(Determined) = source_bindings.value_ns.get() + { + continue; + } + + if prev_root_id != NodeId::ZERO && prev_root_id != import.root_id && !errors.is_empty() + { + // In the case of a new import line, throw a diagnostic message + // for the previous line. + self.throw_unresolved_import_error(errors, glob_error); + errors = vec![]; + } + if seen_spans.insert(err.span) { + errors.push((*import, err)); + prev_root_id = import.root_id; } } @@ -609,60 +605,60 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for (key, resolution) in self.resolutions(*module).borrow().iter() { let resolution = resolution.borrow(); - if let Some(binding) = resolution.binding { - if let NameBindingKind::Import { import, .. } = binding.kind - && let Some((amb_binding, _)) = binding.ambiguity - && binding.res() != Res::Err - && exported_ambiguities.contains(&binding) + let Some(binding) = resolution.binding else { continue }; + + if let NameBindingKind::Import { import, .. } = binding.kind + && let Some((amb_binding, _)) = binding.ambiguity + && binding.res() != Res::Err + && exported_ambiguities.contains(&binding) + { + self.lint_buffer.buffer_lint( + AMBIGUOUS_GLOB_REEXPORTS, + import.root_id, + import.root_span, + BuiltinLintDiag::AmbiguousGlobReexports { + name: key.ident.to_string(), + namespace: key.ns.descr().to_string(), + first_reexport_span: import.root_span, + duplicate_reexport_span: amb_binding.span, + }, + ); + } + + if let Some(glob_binding) = resolution.shadowed_glob { + let binding_id = match binding.kind { + NameBindingKind::Res(res) => { + Some(self.def_id_to_node_id[res.def_id().expect_local()]) + } + NameBindingKind::Module(module) => { + Some(self.def_id_to_node_id[module.def_id().expect_local()]) + } + NameBindingKind::Import { import, .. } => import.id(), + }; + + if binding.res() != Res::Err + && glob_binding.res() != Res::Err + && let NameBindingKind::Import { import: glob_import, .. } = + glob_binding.kind + && let Some(binding_id) = binding_id + && let Some(glob_import_id) = glob_import.id() + && let glob_import_def_id = self.local_def_id(glob_import_id) + && self.effective_visibilities.is_exported(glob_import_def_id) + && glob_binding.vis.is_public() + && !binding.vis.is_public() { self.lint_buffer.buffer_lint( - AMBIGUOUS_GLOB_REEXPORTS, - import.root_id, - import.root_span, - BuiltinLintDiag::AmbiguousGlobReexports { - name: key.ident.to_string(), - namespace: key.ns.descr().to_string(), - first_reexport_span: import.root_span, - duplicate_reexport_span: amb_binding.span, + HIDDEN_GLOB_REEXPORTS, + binding_id, + binding.span, + BuiltinLintDiag::HiddenGlobReexports { + name: key.ident.name.to_string(), + namespace: key.ns.descr().to_owned(), + glob_reexport_span: glob_binding.span, + private_item_span: binding.span, }, ); } - - if let Some(glob_binding) = resolution.shadowed_glob { - let binding_id = match binding.kind { - NameBindingKind::Res(res) => { - Some(self.def_id_to_node_id[res.def_id().expect_local()]) - } - NameBindingKind::Module(module) => { - Some(self.def_id_to_node_id[module.def_id().expect_local()]) - } - NameBindingKind::Import { import, .. } => import.id(), - }; - - if binding.res() != Res::Err - && glob_binding.res() != Res::Err - && let NameBindingKind::Import { import: glob_import, .. } = - glob_binding.kind - && let Some(binding_id) = binding_id - && let Some(glob_import_id) = glob_import.id() - && let glob_import_def_id = self.local_def_id(glob_import_id) - && self.effective_visibilities.is_exported(glob_import_def_id) - && glob_binding.vis.is_public() - && !binding.vis.is_public() - { - self.lint_buffer.buffer_lint( - HIDDEN_GLOB_REEXPORTS, - binding_id, - binding.span, - BuiltinLintDiag::HiddenGlobReexports { - name: key.ident.name.to_string(), - namespace: key.ns.descr().to_owned(), - glob_reexport_span: glob_binding.span, - private_item_span: binding.span, - }, - ); - } - } } } } @@ -1006,21 +1002,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.lint_if_path_starts_with_module(Some(finalize), &full_path, None); } - if let ModuleOrUniformRoot::Module(module) = module { - if module == import.parent_scope.module { - // Importing a module into itself is not allowed. - return Some(UnresolvedImportError { - span: import.span, - label: Some(String::from( - "cannot glob-import a module into itself", - )), - note: None, - suggestion: None, - candidates: None, - segment: None, - module: None, - }); - } + if let ModuleOrUniformRoot::Module(module) = module + && module == import.parent_scope.module + { + // Importing a module into itself is not allowed. + return Some(UnresolvedImportError { + span: import.span, + label: Some(String::from("cannot glob-import a module into itself")), + note: None, + suggestion: None, + candidates: None, + segment: None, + module: None, + }); } if !is_prelude && let Some(max_vis) = max_vis.get() @@ -1081,18 +1075,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Consistency checks, analogous to `finalize_macro_resolutions`. let initial_res = source_bindings[ns].get().map(|initial_binding| { all_ns_err = false; - if let Some(target_binding) = target_bindings[ns].get() { - if target.name == kw::Underscore - && initial_binding.is_extern_crate() - && !initial_binding.is_import() - { - let used = if import.module_path.is_empty() { - Used::Scope - } else { - Used::Other - }; - this.record_use(ident, target_binding, used); - } + if let Some(target_binding) = target_bindings[ns].get() + && target.name == kw::Underscore + && initial_binding.is_extern_crate() + && !initial_binding.is_import() + { + let used = if import.module_path.is_empty() { + Used::Scope + } else { + Used::Other + }; + this.record_use(ident, target_binding, used); } initial_binding.res() }); @@ -1247,17 +1240,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut any_successful_reexport = false; let mut crate_private_reexport = false; self.per_ns(|this, ns| { - if let Ok(binding) = source_bindings[ns].get() { - if !binding.vis.is_at_least(import.vis, this.tcx) { - reexport_error = Some((ns, binding)); - if let ty::Visibility::Restricted(binding_def_id) = binding.vis { - if binding_def_id.is_top_level_module() { - crate_private_reexport = true; - } - } - } else { - any_successful_reexport = true; + let Ok(binding) = source_bindings[ns].get() else { + return; + }; + + if !binding.vis.is_at_least(import.vis, this.tcx) { + reexport_error = Some((ns, binding)); + if let ty::Visibility::Restricted(binding_def_id) = binding.vis + && binding_def_id.is_top_level_module() + { + crate_private_reexport = true; } + } else { + any_successful_reexport = true; } }); @@ -1474,28 +1469,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // Since import resolution is finished, globs will not define any more names. *module.globs.borrow_mut() = Vec::new(); - if let Some(def_id) = module.opt_def_id() { - let mut children = Vec::new(); - - module.for_each_child(self, |this, ident, _, binding| { - let res = binding.res().expect_non_local(); - let error_ambiguity = binding.is_ambiguity_recursive() && !binding.warn_ambiguity; - if res != def::Res::Err && !error_ambiguity { - let mut reexport_chain = SmallVec::new(); - let mut next_binding = binding; - while let NameBindingKind::Import { binding, import, .. } = next_binding.kind { - reexport_chain.push(import.simplify(this)); - next_binding = binding; - } + let Some(def_id) = module.opt_def_id() else { return }; + + let mut children = Vec::new(); - children.push(ModChild { ident, res, vis: binding.vis, reexport_chain }); + module.for_each_child(self, |this, ident, _, binding| { + let res = binding.res().expect_non_local(); + let error_ambiguity = binding.is_ambiguity_recursive() && !binding.warn_ambiguity; + if res != def::Res::Err && !error_ambiguity { + let mut reexport_chain = SmallVec::new(); + let mut next_binding = binding; + while let NameBindingKind::Import { binding, import, .. } = next_binding.kind { + reexport_chain.push(import.simplify(this)); + next_binding = binding; } - }); - if !children.is_empty() { - // Should be fine because this code is only called for local modules. - self.module_children.insert(def_id.expect_local(), children); + children.push(ModChild { ident, res, vis: binding.vis, reexport_chain }); } + }); + + if !children.is_empty() { + // Should be fine because this code is only called for local modules. + self.module_children.insert(def_id.expect_local(), children); } } } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 1a0292ebbde..68d3351f174 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -468,16 +468,12 @@ impl<'a> PathSource<'a> { { "external crate" } - ExprKind::Path(_, path) => { - let mut msg = "function"; - if let Some(segment) = path.segments.iter().last() { - if let Some(c) = segment.ident.to_string().chars().next() { - if c.is_uppercase() { - msg = "function, tuple struct or tuple variant"; - } - } - } - msg + ExprKind::Path(_, path) + if let Some(segment) = path.segments.last() + && let Some(c) = segment.ident.to_string().chars().next() + && c.is_uppercase() => + { + "function, tuple struct or tuple variant" } _ => "function", }, @@ -612,7 +608,7 @@ impl MaybeExported<'_> { return vis.kind.is_pub(); } }; - def_id.map_or(true, |def_id| r.effective_visibilities.is_exported(def_id)) + def_id.is_none_or(|def_id| r.effective_visibilities.is_exported(def_id)) } } @@ -1182,32 +1178,27 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r // namespace first, and if that fails we try again in the value namespace. If // resolution in the value namespace succeeds, we have an generic const argument on // our hands. - if let TyKind::Path(None, ref path) = ty.kind { + if let TyKind::Path(None, ref path) = ty.kind // We cannot disambiguate multi-segment paths right now as that requires type // checking. - if path.is_potential_trivial_const_arg() { - let mut check_ns = |ns| { - self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns) - .is_some() - }; - if !check_ns(TypeNS) && check_ns(ValueNS) { - self.resolve_anon_const_manual( - true, - AnonConstKind::ConstArg(IsRepeatExpr::No), - |this| { - this.smart_resolve_path( - ty.id, - &None, - path, - PathSource::Expr(None), - ); - this.visit_path(path, ty.id); - }, - ); + && path.is_potential_trivial_const_arg() + { + let mut check_ns = |ns| { + self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns) + .is_some() + }; + if !check_ns(TypeNS) && check_ns(ValueNS) { + self.resolve_anon_const_manual( + true, + AnonConstKind::ConstArg(IsRepeatExpr::No), + |this| { + this.smart_resolve_path(ty.id, &None, path, PathSource::Expr(None)); + this.visit_path(path, ty.id); + }, + ); - self.diag_metadata.currently_processing_generic_args = prev; - return; - } + self.diag_metadata.currently_processing_generic_args = prev; + return; } } @@ -1243,54 +1234,56 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r } fn visit_path_segment(&mut self, path_segment: &'ast PathSegment) { - if let Some(ref args) = path_segment.args { - match &**args { - GenericArgs::AngleBracketed(..) => visit::walk_generic_args(self, args), - GenericArgs::Parenthesized(p_args) => { - // Probe the lifetime ribs to know how to behave. - for rib in self.lifetime_ribs.iter().rev() { - match rib.kind { - // We are inside a `PolyTraitRef`. The lifetimes are - // to be introduced in that (maybe implicit) `for<>` binder. - LifetimeRibKind::Generics { - binder, - kind: LifetimeBinderKind::PolyTrait, - .. - } => { - self.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { + let Some(ref args) = path_segment.args else { + return; + }; + + match &**args { + GenericArgs::AngleBracketed(..) => visit::walk_generic_args(self, args), + GenericArgs::Parenthesized(p_args) => { + // Probe the lifetime ribs to know how to behave. + for rib in self.lifetime_ribs.iter().rev() { + match rib.kind { + // We are inside a `PolyTraitRef`. The lifetimes are + // to be introduced in that (maybe implicit) `for<>` binder. + LifetimeRibKind::Generics { + binder, + kind: LifetimeBinderKind::PolyTrait, + .. + } => { + self.with_lifetime_rib( + LifetimeRibKind::AnonymousCreateParameter { + binder, + report_in_path: false, + }, + |this| { + this.resolve_fn_signature( binder, - report_in_path: false, - }, - |this| { - this.resolve_fn_signature( - binder, - false, - p_args.inputs.iter().map(|ty| (None, &**ty)), - &p_args.output, - ) - }, - ); - break; - } - // We have nowhere to introduce generics. Code is malformed, - // so use regular lifetime resolution to avoid spurious errors. - LifetimeRibKind::Item | LifetimeRibKind::Generics { .. } => { - visit::walk_generic_args(self, args); - break; - } - LifetimeRibKind::AnonymousCreateParameter { .. } - | LifetimeRibKind::AnonymousReportError - | LifetimeRibKind::StaticIfNoLifetimeInScope { .. } - | LifetimeRibKind::Elided(_) - | LifetimeRibKind::ElisionFailure - | LifetimeRibKind::ConcreteAnonConst(_) - | LifetimeRibKind::ConstParamTy => {} + false, + p_args.inputs.iter().map(|ty| (None, &**ty)), + &p_args.output, + ) + }, + ); + break; + } + // We have nowhere to introduce generics. Code is malformed, + // so use regular lifetime resolution to avoid spurious errors. + LifetimeRibKind::Item | LifetimeRibKind::Generics { .. } => { + visit::walk_generic_args(self, args); + break; } + LifetimeRibKind::AnonymousCreateParameter { .. } + | LifetimeRibKind::AnonymousReportError + | LifetimeRibKind::StaticIfNoLifetimeInScope { .. } + | LifetimeRibKind::Elided(_) + | LifetimeRibKind::ElisionFailure + | LifetimeRibKind::ConcreteAnonConst(_) + | LifetimeRibKind::ConstParamTy => {} } } - GenericArgs::ParenthesizedElided(_) => {} } + GenericArgs::ParenthesizedElided(_) => {} } } @@ -1735,13 +1728,8 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } let normalized_ident = ident.normalize_to_macros_2_0(); - let mut outer_res = None; - for rib in lifetime_rib_iter { - if let Some((&outer, _)) = rib.bindings.get_key_value(&normalized_ident) { - outer_res = Some(outer); - break; - } - } + let outer_res = lifetime_rib_iter + .find_map(|rib| rib.bindings.get_key_value(&normalized_ident).map(|(&outer, _)| outer)); self.emit_undeclared_lifetime_error(lifetime, outer_res); self.record_lifetime_res(lifetime.id, LifetimeRes::Error, LifetimeElisionCandidate::Named); @@ -1808,23 +1796,21 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } LifetimeRibKind::AnonymousReportError => { if elided { - let mut suggestion = None; - for rib in self.lifetime_ribs[i..].iter().rev() { + let suggestion = self.lifetime_ribs[i..].iter().rev().find_map(|rib| { if let LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::PolyTrait | LifetimeBinderKind::WhereBound, .. - } = &rib.kind + } = rib.kind { - suggestion = - Some(errors::ElidedAnonymousLivetimeReportErrorSuggestion { - lo: span.shrink_to_lo(), - hi: lifetime.ident.span.shrink_to_hi(), - }); - break; + Some(errors::ElidedAnonymousLivetimeReportErrorSuggestion { + lo: span.shrink_to_lo(), + hi: lifetime.ident.span.shrink_to_hi(), + }) + } else { + None } - } - + }); // are we trying to use an anonymous lifetime // on a non GAT associated trait type? if !self.in_func_body @@ -2460,12 +2446,12 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { for i in (0..self.label_ribs.len()).rev() { let rib = &self.label_ribs[i]; - if let RibKind::MacroDefinition(def) = rib.kind { + if let RibKind::MacroDefinition(def) = rib.kind // If an invocation of this macro created `ident`, give up on `ident` // and switch to `ident`'s source from the macro definition. - if def == self.r.macro_def(label.span.ctxt()) { - label.span.remove_mark(); - } + && def == self.r.macro_def(label.span.ctxt()) + { + label.span.remove_mark(); } let ident = label.normalize_to_macro_rules(); @@ -2493,14 +2479,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { /// Determine whether or not a label from the `rib_index`th label rib is reachable. fn is_label_valid_from_rib(&self, rib_index: usize) -> bool { let ribs = &self.label_ribs[rib_index + 1..]; - - for rib in ribs { - if rib.kind.is_label_barrier() { - return false; - } - } - - true + ribs.iter().all(|rib| !rib.kind.is_label_barrier()) } fn resolve_adt(&mut self, item: &'ast Item, generics: &'ast Generics) { @@ -2856,7 +2835,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { match seen_bindings.entry(ident) { Entry::Occupied(entry) => { let span = *entry.get(); - let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span); + let err = ResolutionError::NameAlreadyUsedInParameterList(ident, span); self.report_error(param.ident.span, err); let rib = match param.kind { GenericParamKind::Lifetime => { @@ -3443,7 +3422,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { match seen_trait_items.entry(id_in_trait) { Entry::Occupied(entry) => { self.report_error(span, ResolutionError::TraitImplDuplicate { - name: ident.name, + name: ident, old_span: *entry.get(), trait_item_span: binding.span, }); @@ -3478,7 +3457,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }; let trait_path = path_names_to_string(path); self.report_error(span, ResolutionError::TraitImplMismatch { - name: ident.name, + name: ident, kind, code, trait_path, @@ -3505,21 +3484,20 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.visit_ty(&qself.ty); } self.visit_path(&delegation.path, delegation.id); - if let Some(body) = &delegation.body { - self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { - // `PatBoundCtx` is not necessary in this context - let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; - - let span = delegation.path.segments.last().unwrap().ident.span; - this.fresh_binding( - Ident::new(kw::SelfLower, span), - delegation.id, - PatternSource::FnParam, - &mut bindings, - ); - this.visit_block(body); - }); - } + let Some(body) = &delegation.body else { return }; + self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| { + // `PatBoundCtx` is not necessary in this context + let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; + + let span = delegation.path.segments.last().unwrap().ident.span; + this.fresh_binding( + Ident::new(kw::SelfLower, span), + delegation.id, + PatternSource::FnParam, + &mut bindings, + ); + this.visit_block(body); + }); } fn resolve_params(&mut self, params: &'ast [Param]) { @@ -3662,9 +3640,8 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { .filter(|(_, pat)| pat.id != pat_outer.id) .flat_map(|(map, _)| map); - for (key, binding_inner) in inners { - let name = key.name; - match map_outer.get(key) { + for (&name, binding_inner) in inners { + match map_outer.get(&name) { None => { // The inner binding is missing in the outer. let binding_error = @@ -3902,7 +3879,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // `Variant(a, a)`: _ => IdentifierBoundMoreThanOnceInSamePattern, }; - self.report_error(ident.span, error(ident.name)); + self.report_error(ident.span, error(ident)); } // Record as bound if it's valid: @@ -5104,7 +5081,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn def_id_matches_path(tcx: TyCtxt<'_>, mut def_id: DefId, expected_path: &[&str]) -> bool { let mut path = expected_path.iter().rev(); while let (Some(parent), Some(next_step)) = (tcx.opt_parent(def_id), path.next()) { - if !tcx.opt_item_name(def_id).map_or(false, |n| n.as_str() == *next_step) { + if !tcx.opt_item_name(def_id).is_some_and(|n| n.as_str() == *next_step) { return false; } def_id = parent; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 2936d722aa7..2db8087fd83 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1636,13 +1636,12 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .enumerate() .map(|(idx, new)| (new, old_fields.get(idx))) .map(|(new, old)| { - let new = new.name.to_ident_string(); if let Some(Some(old)) = old - && new != *old + && new.as_str() != old { format!("{new}: {old}") } else { - new + new.to_string() } }) .collect::<Vec<String>>() @@ -1696,7 +1695,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { ) => { // Don't suggest macro if it's unstable. let suggestable = def_id.is_local() - || self.r.tcx.lookup_stability(def_id).map_or(true, |s| s.is_stable()); + || self.r.tcx.lookup_stability(def_id).is_none_or(|s| s.is_stable()); err.span_label(span, fallback_label.to_string()); @@ -2135,7 +2134,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .r .delegation_fn_sigs .get(&self.r.local_def_id(assoc_item.id)) - .map_or(false, |sig| sig.has_self) => + .is_some_and(|sig| sig.has_self) => { AssocSuggestion::MethodWithSelf { called } } @@ -2166,7 +2165,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .r .delegation_fn_sigs .get(&def_id) - .map_or(false, |sig| sig.has_self), + .is_some_and(|sig| sig.has_self), None => self .r .tcx diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 8e457e68eec..04144eb616f 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -214,7 +214,7 @@ enum Used { #[derive(Debug)] struct BindingError { - name: Symbol, + name: Ident, origin: BTreeSet<Span>, target: BTreeSet<Span>, could_be_path: bool, @@ -226,7 +226,7 @@ enum ResolutionError<'ra> { GenericParamsFromOuterItem(Res, HasGenericParams, DefKind), /// Error E0403: the name is already used for a type or const parameter in this generic /// parameter list. - NameAlreadyUsedInParameterList(Symbol, Span), + NameAlreadyUsedInParameterList(Ident, Span), /// Error E0407: method is not a member of trait. MethodNotMemberOfTrait(Ident, String, Option<Symbol>), /// Error E0437: type is not a member of trait. @@ -236,11 +236,11 @@ enum ResolutionError<'ra> { /// Error E0408: variable `{}` is not bound in all patterns. VariableNotBoundInPattern(BindingError, ParentScope<'ra>), /// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm. - VariableBoundWithDifferentMode(Symbol, Span), + VariableBoundWithDifferentMode(Ident, Span), /// Error E0415: identifier is bound more than once in this parameter list. - IdentifierBoundMoreThanOnceInParameterList(Symbol), + IdentifierBoundMoreThanOnceInParameterList(Ident), /// Error E0416: identifier is bound more than once in the same pattern. - IdentifierBoundMoreThanOnceInSamePattern(Symbol), + IdentifierBoundMoreThanOnceInSamePattern(Ident), /// Error E0426: use of undeclared label. UndeclaredLabel { name: Symbol, suggestion: Option<LabelSuggestion> }, /// Error E0429: `self` imports are only allowed within a `{ }` list. @@ -292,14 +292,14 @@ enum ResolutionError<'ra> { UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option<LabelSuggestion> }, /// Error E0323, E0324, E0325: mismatch between trait item and impl item. TraitImplMismatch { - name: Symbol, + name: Ident, kind: &'static str, trait_path: String, trait_item_span: Span, code: ErrCode, }, /// Error E0201: multiple impl items for the same trait item. - TraitImplDuplicate { name: Symbol, trait_item_span: Span, old_span: Span }, + TraitImplDuplicate { name: Ident, trait_item_span: Span, old_span: Span }, /// Inline asm `sym` operand must refer to a `fn` or `static`. InvalidAsmSym, /// `self` used instead of `Self` in a generic parameter @@ -1242,7 +1242,7 @@ impl<'ra> ResolverArenas<'ra> { no_implicit_prelude, )))); let def_id = module.opt_def_id(); - if def_id.map_or(true, |def_id| def_id.is_local()) { + if def_id.is_none_or(|def_id| def_id.is_local()) { self.local_modules.borrow_mut().push(module); } if let Some(def_id) = def_id { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index b6b2971263a..4ff54f47435 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -1022,7 +1022,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { span: Span, ) { if let Some(Res::NonMacroAttr(kind)) = res { - if kind != NonMacroAttrKind::Tool && binding.map_or(true, |b| b.is_import()) { + if kind != NonMacroAttrKind::Tool && binding.is_none_or(|b| b.is_import()) { let binding_span = binding.map(|binding| binding.span); self.dcx().emit_err(errors::CannotUseThroughAnImport { span, diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 7998596c59e..fecb9735019 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -350,10 +350,7 @@ pub fn strip_generics_from_path(path_str: &str) -> Result<Box<str>, MalformedGen /// If there are no doc-comments, return true. /// FIXME(#78591): Support both inner and outer attributes on the same item. pub fn inner_docs(attrs: &[impl AttributeExt]) -> bool { - attrs - .iter() - .find(|a| a.doc_str().is_some()) - .map_or(true, |a| a.style() == ast::AttrStyle::Inner) + attrs.iter().find(|a| a.doc_str().is_some()).is_none_or(|a| a.style() == ast::AttrStyle::Inner) } /// Has `#[rustc_doc_primitive]` or `#[doc(keyword)]`. diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index e9983699609..b9c535df4bd 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -31,7 +31,7 @@ libc = "0.2" # tidy-alphabetical-end [target.'cfg(windows)'.dependencies.windows] -version = "0.57.0" +version = "0.59.0" features = [ "Win32_Foundation", "Win32_System_LibraryLoader", diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 21afb7df7cb..97bd2670aa6 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -675,7 +675,7 @@ impl OutputTypes { /// Returns `true` if user specified a name and not just produced type pub fn contains_explicit_name(&self, key: &OutputType) -> bool { - self.0.get(key).map_or(false, |f| f.is_some()) + matches!(self.0.get(key), Some(Some(..))) } pub fn iter(&self) -> BTreeMapIter<'_, OutputType, Option<OutFileName>> { @@ -888,7 +888,7 @@ impl Input { Input::File(file) => Some(file), Input::Str { name, .. } => match name { FileName::Real(real) => real.local_path(), - FileName::QuoteExpansion(_) => None, + FileName::CfgSpec(_) => None, FileName::Anon(_) => None, FileName::MacroExpansion(_) => None, FileName::ProcMacroSourceCode(_) => None, @@ -1268,7 +1268,6 @@ pub enum EntryFnType { /// and an `include!()`. sigpipe: u8, }, - Start, } #[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug, Encodable, Decodable)] @@ -1346,8 +1345,12 @@ pub fn build_configuration(sess: &Session, mut user_cfg: Cfg) -> Cfg { user_cfg } -pub fn build_target_config(early_dcx: &EarlyDiagCtxt, opts: &Options, sysroot: &Path) -> Target { - match Target::search(&opts.target_triple, sysroot) { +pub fn build_target_config( + early_dcx: &EarlyDiagCtxt, + target: &TargetTuple, + sysroot: &Path, +) -> Target { + match Target::search(target, sysroot) { Ok((target, warnings)) => { for warning in warnings.warning_messages() { early_dcx.early_warn(warning) @@ -1951,7 +1954,7 @@ fn collect_print_requests( matches: &getopts::Matches, ) -> Vec<PrintRequest> { let mut prints = Vec::<PrintRequest>::new(); - if cg.target_cpu.as_ref().is_some_and(|s| s == "help") { + if cg.target_cpu.as_deref() == Some("help") { prints.push(PrintRequest { kind: PrintKind::TargetCPUs, out: OutFileName::Stdout }); cg.target_cpu = None; }; diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs index 4be013fd6fd..ec83761da4a 100644 --- a/compiler/rustc_session/src/filesearch.rs +++ b/compiler/rustc_session/src/filesearch.rs @@ -145,7 +145,7 @@ fn current_dll_path() -> Result<PathBuf, String> { .map_err(|e| e.to_string())?; let mut filename = vec![0; 1024]; - let n = unsafe { GetModuleFileNameW(module, &mut filename) } as usize; + let n = unsafe { GetModuleFileNameW(Some(module), &mut filename) } as usize; if n == 0 { return Err(format!("GetModuleFileNameW failed: {}", io::Error::last_os_error())); } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 60f1154dc6d..1f03de3f53d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -189,7 +189,7 @@ pub struct Session { /// enabled. Makes it so that "please report a bug" is hidden, as ICEs with /// internal features are wontfix, and they are usually the cause of the ICEs. /// None signifies that this is not tracked. - pub using_internal_features: Arc<AtomicBool>, + pub using_internal_features: &'static AtomicBool, /// All commandline args used to invoke the compiler, with @file args fully expanded. /// This will only be used within debug info, e.g. in the pdb file on windows @@ -895,13 +895,16 @@ fn default_emitter( } t => t, }; + + let source_map = if sopts.unstable_opts.link_only { None } else { Some(source_map) }; + match sopts.error_format { config::ErrorOutputType::HumanReadable(kind, color_config) => { let short = kind.short(); if let HumanReadableErrorType::AnnotateSnippet = kind { let emitter = AnnotateSnippetEmitter::new( - Some(source_map), + source_map, bundle, fallback_bundle, short, @@ -911,7 +914,7 @@ fn default_emitter( } else { let emitter = HumanEmitter::new(stderr_destination(color_config), fallback_bundle) .fluent_bundle(bundle) - .sm(Some(source_map)) + .sm(source_map) .short_message(short) .teach(sopts.unstable_opts.teach) .diagnostic_width(sopts.diagnostic_width) @@ -966,7 +969,7 @@ pub fn build_session( sysroot: PathBuf, cfg_version: &'static str, ice_file: Option<PathBuf>, - using_internal_features: Arc<AtomicBool>, + using_internal_features: &'static AtomicBool, expanded_args: Vec<String>, ) -> Session { // FIXME: This is not general enough to make the warning lint completely override @@ -1442,7 +1445,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> { config::ErrorOutputType::Json { pretty, json_rendered, color_config } => { Box::new(JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), - Lrc::new(SourceMap::new(FilePathMapping::empty())), + Some(Lrc::new(SourceMap::new(FilePathMapping::empty()))), fallback_bundle, pretty, json_rendered, diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index dc63ea1999e..ad38ea228bf 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -316,7 +316,7 @@ macro_rules! optional { #[doc(hidden)] macro_rules! run_driver { ($args:expr, $callback:expr $(, $with_tcx:ident)?) => {{ - use rustc_driver::{Callbacks, Compilation, RunCompiler}; + use rustc_driver::{Callbacks, Compilation, run_compiler}; use rustc_middle::ty::TyCtxt; use rustc_interface::interface; use stable_mir::CompilerError; @@ -347,7 +347,7 @@ macro_rules! run_driver { /// Runs the compiler against given target and tests it with `test_function` pub fn run(&mut self) -> Result<C, CompilerError<B>> { let compiler_result = rustc_driver::catch_fatal_errors(|| -> interface::Result::<()> { - RunCompiler::new(&self.args.clone(), self).run(); + run_compiler(&self.args.clone(), self); Ok(()) }); match (compiler_result, self.result.take()) { diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 9793a4d4162..31c7e6c3eb4 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -451,16 +451,13 @@ impl<'tcx> Context for TablesWrapper<'tcx> { let ty = ty::Ty::new_static_str(tcx); let bytes = value.as_bytes(); let val_tree = ty::ValTree::from_raw_bytes(tcx, bytes); - - let ct = ty::Const::new_value(tcx, val_tree, ty); - super::convert::mir_const_from_ty_const(&mut *tables, ct, ty) + let val = tcx.valtree_to_const_val((ty, val_tree)); + mir::Const::from_value(val, ty).stable(&mut tables) } fn new_const_bool(&self, value: bool) -> MirConst { let mut tables = self.0.borrow_mut(); - let ct = ty::Const::from_bool(tables.tcx, value); - let ty = tables.tcx.types.bool; - super::convert::mir_const_from_ty_const(&mut *tables, ct, ty) + mir::Const::from_bool(tables.tcx, value).stable(&mut tables) } fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result<MirConst, Error> { @@ -472,13 +469,11 @@ impl<'tcx> Context for TablesWrapper<'tcx> { .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) .unwrap() .size; - - // We don't use Const::from_bits since it doesn't have any error checking. let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| { Error::new(format!("Value overflow: cannot convert `{value}` to `{ty}`.")) })?; - let ct = ty::Const::new_value(tables.tcx, ValTree::from_scalar_int(scalar), ty); - Ok(super::convert::mir_const_from_ty_const(&mut *tables, ct, ty)) + Ok(mir::Const::from_scalar(tcx, mir::interpret::Scalar::Int(scalar), ty) + .stable(&mut tables)) } fn try_new_ty_const_uint( &self, diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs index 4f8da08eff9..a3da563af50 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mod.rs @@ -9,8 +9,6 @@ mod error; mod mir; mod ty; -pub(crate) use ty::mir_const_from_ty_const; - impl<'tcx> Stable<'tcx> for rustc_hir::Safety { type T = stable_mir::mir::Safety; fn stable(&self, _: &mut Tables<'_>) -> Self::T { diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index a7e122639ea..ff452eea23d 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -414,48 +414,6 @@ impl<'tcx> Stable<'tcx> for ty::Pattern<'tcx> { } } -pub(crate) fn mir_const_from_ty_const<'tcx>( - tables: &mut Tables<'tcx>, - ty_const: ty::Const<'tcx>, - ty: Ty<'tcx>, -) -> stable_mir::ty::MirConst { - let kind = match ty_const.kind() { - ty::ConstKind::Value(ty, val) => { - let val = match val { - ty::ValTree::Leaf(scalar) => ty::ValTree::Leaf(scalar), - ty::ValTree::Branch(branch) => { - ty::ValTree::Branch(tables.tcx.lift(branch).unwrap()) - } - }; - let ty = tables.tcx.lift(ty).unwrap(); - let const_val = tables.tcx.valtree_to_const_val((ty, val)); - if matches!(const_val, mir::ConstValue::ZeroSized) { - stable_mir::ty::ConstantKind::ZeroSized - } else { - stable_mir::ty::ConstantKind::Allocated(alloc::new_allocation( - ty, const_val, tables, - )) - } - } - ty::ConstKind::Param(param) => stable_mir::ty::ConstantKind::Param(param.stable(tables)), - ty::ConstKind::Error(_) => unreachable!(), - ty::ConstKind::Infer(_) => unreachable!(), - ty::ConstKind::Bound(_, _) => unimplemented!(), - ty::ConstKind::Placeholder(_) => unimplemented!(), - ty::ConstKind::Unevaluated(uv) => { - stable_mir::ty::ConstantKind::Unevaluated(stable_mir::ty::UnevaluatedConst { - def: tables.const_def(uv.def), - args: uv.args.stable(tables), - promoted: None, - }) - } - ty::ConstKind::Expr(_) => unimplemented!(), - }; - let stable_ty = tables.intern_ty(ty); - let id = tables.intern_mir_const(mir::Const::Ty(ty, ty_const)); - stable_mir::ty::MirConst::new(kind, stable_ty, id) -} - impl<'tcx> Stable<'tcx> for ty::Const<'tcx> { type T = stable_mir::ty::TyConst; diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 51cfbf59471..51d809bd65d 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -25,6 +25,7 @@ #![feature(hash_set_entry)] #![feature(if_let_guard)] #![feature(let_chains)] +#![feature(map_try_insert)] #![feature(negative_impls)] #![feature(read_buf)] #![feature(round_char_boundary)] @@ -85,9 +86,9 @@ use std::str::FromStr; use std::{fmt, iter}; use md5::{Digest, Md5}; -use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{Hash64, Hash128, HashStable, StableHasher}; use rustc_data_structures::sync::{FreezeLock, FreezeWriteGuard, Lock, Lrc}; +use rustc_data_structures::unord::UnordMap; use sha1::Sha1; use sha2::Sha256; @@ -103,7 +104,7 @@ pub struct SessionGlobals { span_interner: Lock<span_encoding::SpanInterner>, /// Maps a macro argument token into use of the corresponding metavariable in the macro body. /// Collisions are possible and processed in `maybe_use_metavar_location` on best effort basis. - metavar_spans: Lock<FxHashMap<Span, Span>>, + metavar_spans: MetavarSpansMap, hygiene_data: Lock<hygiene::HygieneData>, /// The session's source map, if there is one. This field should only be @@ -177,9 +178,42 @@ pub fn create_default_session_globals_then<R>(f: impl FnOnce() -> R) -> R { // deserialization. scoped_tls::scoped_thread_local!(static SESSION_GLOBALS: SessionGlobals); +#[derive(Default)] +pub struct MetavarSpansMap(FreezeLock<UnordMap<Span, (Span, bool)>>); + +impl MetavarSpansMap { + pub fn insert(&self, span: Span, var_span: Span) -> bool { + match self.0.write().try_insert(span, (var_span, false)) { + Ok(_) => true, + Err(entry) => entry.entry.get().0 == var_span, + } + } + + /// Read a span and record that it was read. + pub fn get(&self, span: Span) -> Option<Span> { + if let Some(mut mspans) = self.0.try_write() { + if let Some((var_span, read)) = mspans.get_mut(&span) { + *read = true; + Some(*var_span) + } else { + None + } + } else { + if let Some((span, true)) = self.0.read().get(&span) { Some(*span) } else { None } + } + } + + /// Freeze the set, and return the spans which have been read. + /// + /// After this is frozen, no spans that have not been read can be read. + pub fn freeze_and_get_read_spans(&self) -> UnordMap<Span, Span> { + self.0.freeze().items().filter(|(_, (_, b))| *b).map(|(s1, (s2, _))| (*s1, *s2)).collect() + } +} + #[inline] -pub fn with_metavar_spans<R>(f: impl FnOnce(&mut FxHashMap<Span, Span>) -> R) -> R { - with_session_globals(|session_globals| f(&mut session_globals.metavar_spans.lock())) +pub fn with_metavar_spans<R>(f: impl FnOnce(&MetavarSpansMap) -> R) -> R { + with_session_globals(|session_globals| f(&session_globals.metavar_spans)) } // FIXME: We should use this enum or something like it to get rid of the @@ -305,8 +339,8 @@ impl RealFileName { #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Hash, Decodable, Encodable)] pub enum FileName { Real(RealFileName), - /// Call to `quote!`. - QuoteExpansion(Hash64), + /// Strings provided as `--cfg [cfgspec]`. + CfgSpec(Hash64), /// Command line. Anon(Hash64), /// Hack in `src/librustc_ast/parse.rs`. @@ -353,7 +387,7 @@ impl fmt::Display for FileNameDisplay<'_> { Real(ref name) => { write!(fmt, "{}", name.to_string_lossy(self.display_pref)) } - QuoteExpansion(_) => write!(fmt, "<quote expansion>"), + CfgSpec(_) => write!(fmt, "<cfgspec>"), MacroExpansion(_) => write!(fmt, "<macro expansion>"), Anon(_) => write!(fmt, "<anon>"), ProcMacroSourceCode(_) => write!(fmt, "<proc-macro source code>"), @@ -384,7 +418,7 @@ impl FileName { | ProcMacroSourceCode(_) | CliCrateAttr(_) | Custom(_) - | QuoteExpansion(_) + | CfgSpec(_) | DocTest(_, _) | InlineAsm(_) => false, } @@ -425,7 +459,7 @@ impl FileName { pub fn cfg_spec_source_code(src: &str) -> FileName { let mut hasher = StableHasher::new(); src.hash(&mut hasher); - FileName::QuoteExpansion(hasher.finish()) + FileName::CfgSpec(hasher.finish()) } pub fn cli_crate_attr_source_code(src: &str) -> FileName { @@ -872,8 +906,7 @@ impl Span { /// Check if you can select metavar spans for the given spans to get matching contexts. fn try_metavars(a: SpanData, b: SpanData, a_orig: Span, b_orig: Span) -> (SpanData, SpanData) { - let get = |mspans: &FxHashMap<_, _>, s| mspans.get(&s).copied(); - match with_metavar_spans(|mspans| (get(mspans, a_orig), get(mspans, b_orig))) { + match with_metavar_spans(|mspans| (mspans.get(a_orig), mspans.get(b_orig))) { (None, None) => {} (Some(meta_a), None) => { let meta_a = meta_a.data(); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f5ce5dbc9d6..1fb15fe9800 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -707,7 +707,6 @@ symbols! { coverage, coverage_attribute, cr, - crate_id, crate_in_paths, crate_local, crate_name, @@ -1189,6 +1188,7 @@ symbols! { link_section, linkage, linker, + linker_messages, lint_reasons, literal, load, @@ -1389,7 +1389,6 @@ symbols! { no_mangle, no_sanitize, no_stack_check, - no_start, no_std, nomem, non_ascii_idents, diff --git a/compiler/rustc_target/src/callconv/powerpc64.rs b/compiler/rustc_target/src/callconv/powerpc64.rs index 3a71592cbe0..92c1f6e7148 100644 --- a/compiler/rustc_target/src/callconv/powerpc64.rs +++ b/compiler/rustc_target/src/callconv/powerpc64.rs @@ -58,8 +58,10 @@ where // The AIX ABI expect byval for aggregates // See https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/Targets/PPC.cpp. + // The incoming parameter is represented as a pointer in the IR, + // the alignment is associated with the size of the register. (align 8 for 64bit) if !is_ret && abi == AIX { - arg.pass_by_stack_offset(None); + arg.pass_by_stack_offset(Some(Align::from_bytes(8).unwrap())); return; } diff --git a/compiler/rustc_target/src/callconv/s390x.rs b/compiler/rustc_target/src/callconv/s390x.rs index c99eb9226ef..a73c1a0f46c 100644 --- a/compiler/rustc_target/src/callconv/s390x.rs +++ b/compiler/rustc_target/src/callconv/s390x.rs @@ -38,9 +38,17 @@ where } let size = arg.layout.size; - if size.bits() <= 128 && arg.layout.is_single_vector_element(cx, size) { - arg.cast_to(Reg { kind: RegKind::Vector, size }); - return; + if size.bits() <= 128 { + if let BackendRepr::Vector { .. } = arg.layout.backend_repr { + // pass non-wrapped vector types using `PassMode::Direct` + return; + } + + if arg.layout.is_single_vector_element(cx, size) { + // pass non-transparant wrappers around a vector as `PassMode::Cast` + arg.cast_to(Reg { kind: RegKind::Vector, size }); + return; + } } if !arg.layout.is_aggregate() && size.bits() <= 64 { arg.extend_integer_width_to(64); diff --git a/compiler/rustc_target/src/callconv/x86_win64.rs b/compiler/rustc_target/src/callconv/x86_win64.rs index 0944bda2687..816564d2fed 100644 --- a/compiler/rustc_target/src/callconv/x86_win64.rs +++ b/compiler/rustc_target/src/callconv/x86_win64.rs @@ -1,4 +1,4 @@ -use rustc_abi::{BackendRepr, Float, Primitive}; +use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind, Size}; use crate::abi::call::{ArgAbi, FnAbi, Reg}; use crate::spec::HasTargetSpec; @@ -6,7 +6,7 @@ use crate::spec::HasTargetSpec; // Win64 ABI: https://docs.microsoft.com/en-us/cpp/build/parameter-passing pub(crate) fn compute_abi_info<Ty>(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) { - let fixup = |a: &mut ArgAbi<'_, Ty>| { + let fixup = |a: &mut ArgAbi<'_, Ty>, is_ret: bool| { match a.layout.backend_repr { BackendRepr::Uninhabited | BackendRepr::Memory { sized: false } => {} BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => { @@ -23,11 +23,16 @@ pub(crate) fn compute_abi_info<Ty>(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi< // (probably what clang calls "illegal vectors"). } BackendRepr::Scalar(scalar) => { - // Match what LLVM does for `f128` so that `compiler-builtins` builtins match up - // with what LLVM expects. - if a.layout.size.bytes() > 8 + if is_ret && matches!(scalar.primitive(), Primitive::Int(Integer::I128, _)) { + // `i128` is returned in xmm0 by Clang and GCC + // FIXME(#134288): This may change for the `-msvc` targets in the future. + let reg = Reg { kind: RegKind::Vector, size: Size::from_bits(128) }; + a.cast_to(reg); + } else if a.layout.size.bytes() > 8 && !matches!(scalar.primitive(), Primitive::Float(Float::F128)) { + // Match what LLVM does for `f128` so that `compiler-builtins` builtins match up + // with what LLVM expects. a.make_indirect(); } else { a.extend_integer_width_to(32); @@ -37,8 +42,9 @@ pub(crate) fn compute_abi_info<Ty>(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi< }; if !fn_abi.ret.is_ignore() { - fixup(&mut fn_abi.ret); + fixup(&mut fn_abi.ret, true); } + for arg in fn_abi.args.iter_mut() { if arg.is_ignore() && arg.layout.is_zst() { // Windows ABIs do not talk about ZST since such types do not exist in MSVC. @@ -49,7 +55,7 @@ pub(crate) fn compute_abi_info<Ty>(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi< arg.make_indirect_from_ignore(); continue; } - fixup(arg); + fixup(arg, false); } // FIXME: We should likely also do something about ZST return types, similar to above. // However, that's non-trivial due to `()`. diff --git a/compiler/rustc_target/src/spec/base/nto_qnx.rs b/compiler/rustc_target/src/spec/base/nto_qnx.rs index 65475d068d7..819e68a0500 100644 --- a/compiler/rustc_target/src/spec/base/nto_qnx.rs +++ b/compiler/rustc_target/src/spec/base/nto_qnx.rs @@ -1,4 +1,6 @@ -use crate::spec::{RelroLevel, TargetOptions, cvs}; +use crate::spec::{ + Cc, LinkArgs, LinkerFlavor, Lld, RelroLevel, Target, TargetMetadata, TargetOptions, cvs, +}; pub(crate) fn opts() -> TargetOptions { TargetOptions { @@ -16,3 +18,96 @@ pub(crate) fn opts() -> TargetOptions { ..Default::default() } } + +pub(crate) fn meta() -> TargetMetadata { + TargetMetadata { description: None, tier: Some(3), host_tools: Some(false), std: Some(true) } +} + +pub(crate) fn aarch64() -> Target { + Target { + llvm_target: "aarch64-unknown-unknown".into(), + metadata: meta(), + pointer_width: 64, + // from: https://llvm.org/docs/LangRef.html#data-layout + // e = little endian + // m:e = ELF mangling: Private symbols get a .L prefix + // i8:8:32 = 8-bit-integer, minimum_alignment=8, preferred_alignment=32 + // i16:16:32 = 16-bit-integer, minimum_alignment=16, preferred_alignment=32 + // i64:64 = 64-bit-integer, minimum_alignment=64, preferred_alignment=64 + // i128:128 = 128-bit-integer, minimum_alignment=128, preferred_alignment=128 + // n32:64 = 32 and 64 are native integer widths; Elements of this set are considered to support most general arithmetic operations efficiently. + // S128 = 128 bits are the natural alignment of the stack in bits. + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: "aarch64".into(), + options: TargetOptions { + features: "+v8a".into(), + max_atomic_width: Some(128), + ..opts() + } + } +} + +pub(crate) fn x86_64() -> Target { + Target { + llvm_target: "x86_64-pc-unknown".into(), + metadata: meta(), + pointer_width: 64, + data_layout: + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), + arch: "x86_64".into(), + options: TargetOptions { + cpu: "x86-64".into(), + plt_by_default: false, + max_atomic_width: Some(64), + vendor: "pc".into(), + ..opts() + }, + } +} + +pub(crate) fn pre_link_args(api_var: ApiVariant, arch: Arch) -> LinkArgs { + let (qcc_arg, arch_lib_dir) = match arch { + Arch::Aarch64 => ("-Vgcc_ntoaarch64le_cxx", "aarch64le"), + Arch::I586 => { + ("-Vgcc_ntox86_cxx", "notSupportedByQnx_compiler/rustc_target/src/spec/base/nto_qnx.rs") + } + Arch::X86_64 => ("-Vgcc_ntox86_64_cxx", "x86_64"), + }; + match api_var { + ApiVariant::Default => { + TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[qcc_arg]) + } + ApiVariant::IoSock => TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ + qcc_arg, + get_iosock_param(arch_lib_dir), + ]), + } +} + +pub(crate) enum ApiVariant { + Default, + IoSock, +} + +pub(crate) enum Arch { + Aarch64, + I586, + X86_64, +} + +// When using `io-sock` on QNX, we must add a search path for the linker so +// that it prefers the io-sock version. +// The path depends on the host, i.e. we cannot hard-code it here, but have +// to determine it when the compiler runs. +// When using the QNX toolchain, the environment variable QNX_TARGET is always set. +// More information: +// https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.io_sock/topic/migrate_app.html +fn get_iosock_param(arch_lib_dir: &str) -> &'static str { + let target_dir = std::env::var("QNX_TARGET") + .unwrap_or_else(|_| "QNX_TARGET_not_set_please_source_qnxsdp-env.sh".into()); + let linker_param = format!("-L{target_dir}/{arch_lib_dir}/io-sock/lib"); + + // FIXME: leaking this is kind of weird: we're feeding these into something that expects an + // `AsRef<OsStr>`, but often converts to `OsString` anyways, so shouldn't we just demand an `OsString`? + linker_param.leak() +} diff --git a/compiler/rustc_target/src/spec/base/windows_gnu.rs b/compiler/rustc_target/src/spec/base/windows_gnu.rs index e975102e238..d8b6ae8cf32 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnu.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnu.rs @@ -97,9 +97,9 @@ pub(crate) fn opts() -> TargetOptions { emit_debug_gdb_scripts: false, requires_uwtable: true, eh_frame_header: false, + debuginfo_kind: DebuginfoKind::Dwarf, // FIXME(davidtwco): Support Split DWARF on Windows GNU - may require LLVM changes to // output DWO, despite using DWARF, doesn't use ELF.. - debuginfo_kind: DebuginfoKind::Pdb, supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), ..Default::default() } diff --git a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs index 4f370ec8bd0..86e52117dbf 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs @@ -44,9 +44,9 @@ pub(crate) fn opts() -> TargetOptions { has_thread_local: true, crt_static_allows_dylibs: true, crt_static_respected: true, + debuginfo_kind: DebuginfoKind::Dwarf, // FIXME(davidtwco): Support Split DWARF on Windows GNU - may require LLVM changes to // output DWO, despite using DWARF, doesn't use ELF.. - debuginfo_kind: DebuginfoKind::Pdb, supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]), ..Default::default() } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 1f2df7f0168..bcd2aff54bb 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1853,6 +1853,8 @@ supported_targets! { ("armv7a-none-eabi", armv7a_none_eabi), ("armv7a-none-eabihf", armv7a_none_eabihf), + ("armv7a-nuttx-eabi", armv7a_nuttx_eabi), + ("armv7a-nuttx-eabihf", armv7a_nuttx_eabihf), ("msp430-none-elf", msp430_none_elf), @@ -1896,6 +1898,7 @@ supported_targets! { ("aarch64-unknown-none", aarch64_unknown_none), ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat), + ("aarch64-unknown-nuttx", aarch64_unknown_nuttx), ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx), @@ -1960,7 +1963,11 @@ supported_targets! { ("aarch64-unknown-nto-qnx700", aarch64_unknown_nto_qnx700), ("aarch64-unknown-nto-qnx710", aarch64_unknown_nto_qnx710), + ("aarch64-unknown-nto-qnx710_iosock", aarch64_unknown_nto_qnx710_iosock), + ("aarch64-unknown-nto-qnx800", aarch64_unknown_nto_qnx800), ("x86_64-pc-nto-qnx710", x86_64_pc_nto_qnx710), + ("x86_64-pc-nto-qnx710_iosock", x86_64_pc_nto_qnx710_iosock), + ("x86_64-pc-nto-qnx800", x86_64_pc_nto_qnx800), ("i586-pc-nto-qnx700", i586_pc_nto_qnx700), ("aarch64-unknown-linux-ohos", aarch64_unknown_linux_ohos), @@ -1971,6 +1978,8 @@ supported_targets! { ("x86_64-unknown-linux-none", x86_64_unknown_linux_none), ("thumbv6m-nuttx-eabi", thumbv6m_nuttx_eabi), + ("thumbv7a-nuttx-eabi", thumbv7a_nuttx_eabi), + ("thumbv7a-nuttx-eabihf", thumbv7a_nuttx_eabihf), ("thumbv7m-nuttx-eabi", thumbv7m_nuttx_eabi), ("thumbv7em-nuttx-eabi", thumbv7em_nuttx_eabi), ("thumbv7em-nuttx-eabihf", thumbv7em_nuttx_eabihf), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs index d6b77ffd091..3b719ebaf07 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs @@ -7,7 +7,8 @@ // For example, `-C target-cpu=cortex-a53`. use crate::spec::{ - Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, StackProbeType, Target, TargetOptions, + Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, + TargetOptions, }; pub(crate) fn target() -> Target { @@ -19,6 +20,7 @@ pub(crate) fn target() -> Target { relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(128), + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, stack_probes: StackProbeType::Inline, panic_strategy: PanicStrategy::Abort, ..Default::default() diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs index 441ed7cf153..b26e6f19e1a 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx700.rs @@ -1,37 +1,11 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, Target, TargetOptions, base}; +use crate::spec::Target; +use crate::spec::base::nto_qnx; pub(crate) fn target() -> Target { - // In QNX, libc does not provide a compatible ABI between versions. - // To distinguish between QNX versions, we needed a stable conditional compilation switch, - // which is why we needed to implement different targets in the compiler. - Target { - llvm_target: "aarch64-unknown-unknown".into(), - metadata: crate::spec::TargetMetadata { - description: Some("ARM64 QNX Neutrino 7.0 RTOS".into()), - tier: Some(3), - host_tools: Some(false), - std: Some(true), - }, - pointer_width: 64, - // from: https://llvm.org/docs/LangRef.html#data-layout - // e = little endian - // m:e = ELF mangling: Private symbols get a .L prefix - // i8:8:32 = 8-bit-integer, minimum_alignment=8, preferred_alignment=32 - // i16:16:32 = 16-bit-integer, minimum_alignment=16, preferred_alignment=32 - // i64:64 = 64-bit-integer, minimum_alignment=64, preferred_alignment=64 - // i128:128 = 128-bit-integer, minimum_alignment=128, preferred_alignment=128 - // n32:64 = 32 and 64 are native integer widths; Elements of this set are considered to support most general arithmetic operations efficiently. - // S128 = 128 bits are the natural alignment of the stack in bits. - data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), - arch: "aarch64".into(), - options: TargetOptions { - features: "+v8a".into(), - max_atomic_width: Some(128), - pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-Vgcc_ntoaarch64le_cxx", - ]), - env: "nto70".into(), - ..base::nto_qnx::opts() - }, - } + let mut target = nto_qnx::aarch64(); + target.metadata.description = Some("ARM64 QNX Neutrino 7.0 RTOS".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::Aarch64); + target.options.env = "nto70".into(); + target } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs index 5b5a21fca95..3a78952c36c 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710.rs @@ -1,8 +1,12 @@ use crate::spec::Target; +use crate::spec::base::nto_qnx; pub(crate) fn target() -> Target { - let mut base = super::aarch64_unknown_nto_qnx700::target(); - base.metadata.description = Some("ARM64 QNX Neutrino 7.1 RTOS".into()); - base.options.env = "nto71".into(); - base + let mut target = nto_qnx::aarch64(); + target.metadata.description = + Some("ARM64 QNX Neutrino 7.1 RTOS with io-pkt network stack".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::Aarch64); + target.options.env = "nto71".into(); + target } diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs new file mode 100644 index 00000000000..4964f4078f5 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx710_iosock.rs @@ -0,0 +1,12 @@ +use crate::spec::Target; +use crate::spec::base::nto_qnx; + +pub(crate) fn target() -> Target { + let mut target = nto_qnx::aarch64(); + target.metadata.description = + Some("ARM64 QNX Neutrino 7.1 RTOS with io-sock network stack".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::IoSock, nto_qnx::Arch::Aarch64); + target.options.env = "nto71_iosock".into(); + target +} diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs new file mode 100644 index 00000000000..5b820681efe --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nto_qnx800.rs @@ -0,0 +1,11 @@ +use crate::spec::Target; +use crate::spec::base::nto_qnx; + +pub(crate) fn target() -> Target { + let mut target = nto_qnx::aarch64(); + target.metadata.description = Some("ARM64 QNX Neutrino 8.0 RTOS".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::Aarch64); + target.options.env = "nto80".into(); + target +} diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs new file mode 100644 index 00000000000..582211b02b6 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs @@ -0,0 +1,46 @@ +// Generic AArch64 target for NuttX OS +// +// Can be used in conjunction with the `target-feature` and +// `target-cpu` compiler flags to opt-in more hardware-specific +// features. +// +// For example, `-C target-cpu=cortex-a53`. + +use crate::spec::{ + Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, + TargetOptions, cvs, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + // Enable the Cortex-A53 errata 843419 mitigation by default + pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ + "--fix-cortex-a53-843419", + ]), + features: "+v8a,+strict-align,+neon,+fp-armv8".into(), + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(128), + stack_probes: StackProbeType::Inline, + panic_strategy: PanicStrategy::Abort, + families: cvs!["unix"], + os: "nuttx".into(), + ..Default::default() + }; + Target { + llvm_target: "aarch64-unknown-none".into(), + metadata: crate::spec::TargetMetadata { + description: Some("AArch64 NuttX".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: "aarch64".into(), + options: opts, + } +} diff --git a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs new file mode 100644 index 00000000000..08cbfc74396 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs @@ -0,0 +1,41 @@ +// Targets Cortex-A7/A8/A9 processors (ARMv7-A) running NuttX +// +// This target assumes that the device does NOT have a FPU (Floating Point Unit) +// and will use software floating point operations. This matches the NuttX EABI +// configuration without hardware floating point support. + +use crate::spec::{ + Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions, cvs, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + abi: "eabi".into(), + llvm_floatabi: Some(FloatAbi::Soft), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + features: "+v7,+thumb2,+soft-float,-neon,+strict-align".into(), + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(64), + panic_strategy: PanicStrategy::Abort, + emit_debug_gdb_scripts: false, + c_enum_min_bits: Some(8), + families: cvs!["unix"], + os: "nuttx".into(), + ..Default::default() + }; + Target { + llvm_target: "armv7a-none-eabi".into(), + metadata: crate::spec::TargetMetadata { + description: Some("ARMv7-A Cortex-A with NuttX".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + options: opts, + } +} diff --git a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs new file mode 100644 index 00000000000..f68c11a9c68 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs @@ -0,0 +1,41 @@ +// Targets Cortex-A7/A8/A9 processors (ARMv7-A) running NuttX with hardware floating point +// +// This target assumes that the device has a FPU (Floating Point Unit) +// and will use hardware floating point operations. This matches the NuttX EABI +// configuration with hardware floating point support. + +use crate::spec::{ + Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions, cvs, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + abi: "eabihf".into(), + llvm_floatabi: Some(FloatAbi::Hard), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + features: "+v7,+thumb2,+vfp3,+neon,+strict-align".into(), + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(64), + panic_strategy: PanicStrategy::Abort, + emit_debug_gdb_scripts: false, + c_enum_min_bits: Some(8), + families: cvs!["unix"], + os: "nuttx".into(), + ..Default::default() + }; + Target { + llvm_target: "armv7a-none-eabihf".into(), + metadata: crate::spec::TargetMetadata { + description: Some("ARMv7-A Cortex-A with NuttX (hard float)".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + options: opts, + } +} diff --git a/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs b/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs index 7648f81fd4d..fa21cfbab8a 100644 --- a/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs +++ b/compiler/rustc_target/src/spec/targets/i586_pc_nto_qnx700.rs @@ -1,14 +1,13 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions, base}; +use crate::spec::base::nto_qnx; +use crate::spec::{StackProbeType, Target, TargetOptions, base}; pub(crate) fn target() -> Target { + let mut meta = nto_qnx::meta(); + meta.description = Some("32-bit x86 QNX Neutrino 7.0 RTOS".into()); + meta.std = Some(false); Target { llvm_target: "i586-pc-unknown".into(), - metadata: crate::spec::TargetMetadata { - description: Some("32-bit x86 QNX Neutrino 7.0 RTOS".into()), - tier: Some(3), - host_tools: Some(false), - std: Some(false), - }, + metadata: meta, pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ i128:128-f64:32:64-f80:32-n8:16:32-S128" @@ -17,9 +16,10 @@ pub(crate) fn target() -> Target { options: TargetOptions { cpu: "pentium4".into(), max_atomic_width: Some(64), - pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-Vgcc_ntox86_cxx", - ]), + pre_link_args: nto_qnx::pre_link_args( + nto_qnx::ApiVariant::Default, + nto_qnx::Arch::I586, + ), env: "nto70".into(), vendor: "pc".into(), stack_probes: StackProbeType::Inline, diff --git a/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs index b1f52973c10..771ffac7d80 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32e_unknown_none_elf.rs @@ -1,6 +1,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; pub(crate) fn target() -> Target { + let abi = "ilp32e"; Target { // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also // `options.llvm_abiname`. @@ -16,11 +17,12 @@ pub(crate) fn target() -> Target { arch: "riscv32".into(), options: TargetOptions { + abi: abi.into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), // The ilp32e ABI specifies the `data_layout` - llvm_abiname: "ilp32e".into(), + llvm_abiname: abi.into(), max_atomic_width: Some(32), atomic_cas: false, features: "+e,+forced-atomics".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs index feeaa48778d..3b81c278d3a 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32em_unknown_none_elf.rs @@ -1,6 +1,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; pub(crate) fn target() -> Target { + let abi = "ilp32e"; Target { // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also // `options.llvm_abiname`. @@ -16,11 +17,12 @@ pub(crate) fn target() -> Target { arch: "riscv32".into(), options: TargetOptions { + abi: abi.into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), // The ilp32e ABI specifies the `data_layout` - llvm_abiname: "ilp32e".into(), + llvm_abiname: abi.into(), max_atomic_width: Some(32), atomic_cas: false, features: "+e,+m,+forced-atomics".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs index 45d73c13233..c18b51ad46e 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32emc_unknown_none_elf.rs @@ -1,6 +1,7 @@ use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; pub(crate) fn target() -> Target { + let abi = "ilp32e"; Target { // The below `data_layout` is explicitly specified by the ilp32e ABI in LLVM. See also // `options.llvm_abiname`. @@ -16,11 +17,12 @@ pub(crate) fn target() -> Target { arch: "riscv32".into(), options: TargetOptions { + abi: abi.into(), linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), // The ilp32e ABI specifies the `data_layout` - llvm_abiname: "ilp32e".into(), + llvm_abiname: abi.into(), max_atomic_width: Some(32), atomic_cas: false, features: "+e,+m,+c,+forced-atomics".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs index 31c9180c509..3eb3d18faf4 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs @@ -6,9 +6,9 @@ pub(crate) fn target() -> Target { llvm_target: "riscv32".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, arch: "riscv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs index 08dd3cc2a09..7864f7f8f9a 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs @@ -6,9 +6,9 @@ pub(crate) fn target() -> Target { llvm_target: "riscv32".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, arch: "riscv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs index e86549806dd..60d8ec576af 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs @@ -6,9 +6,9 @@ pub(crate) fn target() -> Target { llvm_target: "riscv32".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, arch: "riscv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs index c389759aecd..2cbb8c19b84 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs @@ -8,9 +8,9 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, llvm_target: "riscv64".into(), pointer_width: 64, diff --git a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs index 9c181665581..306b23d2787 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs @@ -8,9 +8,9 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, llvm_target: "riscv64".into(), pointer_width: 64, diff --git a/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs index 5799bbf551f..dcf98acc41f 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs @@ -7,9 +7,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv6m-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), @@ -22,12 +22,9 @@ pub(crate) fn target() -> Target { llvm_floatabi: Some(FloatAbi::Soft), // The ARMv6-M architecture doesn't support unaligned loads/stores so we disable them // with +strict-align. - // Also force-enable 32-bit atomics, which allows the use of atomic load/store only. - // The resulting atomics are ABI incompatible with atomics backed by libatomic. - features: "+strict-align,+atomics-32".into(), - // There are no atomic CAS instructions available in the instruction set of the ARMv6-M - // architecture - atomic_cas: false, + // The ARMv6-M doesn't support hardware atomic operations, use atomic builtins instead. + features: "+strict-align".into(), + max_atomic_width: Some(32), ..base::thumb::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs new file mode 100644 index 00000000000..b5cb393f4b0 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs @@ -0,0 +1,33 @@ +// Targets Cortex-A7/A8/A9 processors (ARMv7-A) +// +// This target assumes that the device does NOT have a FPU (Floating Point Unit) +// and will use software floating point operations. This matches the NuttX EABI +// configuration without hardware floating point support. + +use crate::spec::{FloatAbi, Target, TargetOptions, base, cvs}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "thumbv7a-none-eabi".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: Some(3), + host_tools: None, + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "nuttx".into(), + abi: "eabi".into(), + llvm_floatabi: Some(FloatAbi::Soft), + // Cortex-A7/A8/A9 with software floating point + features: "+soft-float,-neon".into(), + max_atomic_width: Some(64), + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs new file mode 100644 index 00000000000..1aa44a8cc93 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs @@ -0,0 +1,37 @@ +// Targets Cortex-A7/A8/A9 processors (ARMv7-A) +// +// This target assumes that the device has a FPU (Floating Point Unit) and lowers all (single +// precision) floating point operations to hardware instructions. Cortex-A7/A8/A9 processors +// support VFPv3-D32 or VFPv4-D32 floating point units with optional double-precision support. +// +// This target uses the "hard" floating convention (ABI) where floating point values +// are passed to/from subroutines via FPU registers (S0, S1, D0, D1, etc.). + +use crate::spec::{FloatAbi, Target, TargetOptions, base, cvs}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "thumbv7a-none-eabihf".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: Some(3), + host_tools: None, + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "nuttx".into(), + abi: "eabihf".into(), + llvm_floatabi: Some(FloatAbi::Hard), + // Cortex-A7/A8/A9 support VFPv3-D32/VFPv4-D32 with optional double-precision + // and NEON SIMD instructions + features: "+vfp3,+neon".into(), + max_atomic_width: Some(64), + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs index 536d128590f..a3bc4013e53 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs @@ -16,9 +16,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv7em-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs index 35e92b81d87..14bbe38257d 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs @@ -15,9 +15,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv7em-none-eabihf".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs index 320867444ad..2a77f48a9cd 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs @@ -7,9 +7,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv7m-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs index 1af01b97666..25a100e9c7e 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs @@ -7,9 +7,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv8m.base-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs index 661d74217ad..0bfe2b32ad4 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs @@ -8,9 +8,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv8m.main-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs index 484d35bfc20..9f75f23aa93 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs @@ -8,9 +8,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv8m.main-none-eabihf".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs index 245a5f06765..248aa91862c 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710.rs @@ -1,28 +1,12 @@ -use crate::spec::{Cc, LinkerFlavor, Lld, Target, TargetOptions, base}; +use crate::spec::Target; +use crate::spec::base::nto_qnx; pub(crate) fn target() -> Target { - Target { - llvm_target: "x86_64-pc-unknown".into(), - metadata: crate::spec::TargetMetadata { - description: Some("x86 64-bit QNX Neutrino 7.1 RTOS".into()), - tier: Some(3), - host_tools: Some(false), - std: Some(true), - }, - pointer_width: 64, - data_layout: - "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(), - arch: "x86_64".into(), - options: TargetOptions { - cpu: "x86-64".into(), - plt_by_default: false, - max_atomic_width: Some(64), - pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &[ - "-Vgcc_ntox86_64_cxx", - ]), - env: "nto71".into(), - vendor: "pc".into(), - ..base::nto_qnx::opts() - }, - } + let mut target = nto_qnx::x86_64(); + target.metadata.description = + Some("x86 64-bit QNX Neutrino 7.1 RTOS with io-pkt network stack".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::X86_64); + target.options.env = "nto71".into(); + target } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs new file mode 100644 index 00000000000..8f4c4924a29 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx710_iosock.rs @@ -0,0 +1,12 @@ +use crate::spec::Target; +use crate::spec::base::nto_qnx; + +pub(crate) fn target() -> Target { + let mut target = nto_qnx::x86_64(); + target.metadata.description = + Some("x86 64-bit QNX Neutrino 7.1 RTOS with io-sock network stack".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::IoSock, nto_qnx::Arch::X86_64); + target.options.env = "nto71_iosock".into(); + target +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs new file mode 100644 index 00000000000..d91a94a2ba5 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_nto_qnx800.rs @@ -0,0 +1,11 @@ +use crate::spec::Target; +use crate::spec::base::nto_qnx; + +pub(crate) fn target() -> Target { + let mut target = nto_qnx::x86_64(); + target.metadata.description = Some("x86 64-bit QNX Neutrino 8.0 RTOS".into()); + target.options.pre_link_args = + nto_qnx::pre_link_args(nto_qnx::ApiVariant::Default, nto_qnx::Arch::X86_64); + target.options.env = "nto80".into(); + target +} diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index b82bb27eb79..750d2756b4a 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -261,7 +261,6 @@ trait_selection_oc_fn_lang_correct_type = {$lang_item_name -> *[lang_item_name] lang item `{$lang_item_name}` } function has wrong type trait_selection_oc_fn_main_correct_type = `main` function has wrong type -trait_selection_oc_fn_start_correct_type = `#[start]` function has wrong type trait_selection_oc_generic = mismatched types trait_selection_oc_if_else_different = `if` and `else` have incompatible types @@ -396,7 +395,6 @@ trait_selection_subtype = ...so that the {$requirement -> [if_else_different] `if` and `else` have incompatible types [no_else] `if` missing an `else` returns `()` [fn_main_correct_type] `main` function has the correct type - [fn_start_correct_type] `#[start]` function has the correct type [fn_lang_correct_type] lang item function has the correct type [intrinsic_correct_type] intrinsic has the correct type [method_correct_type] method receiver has the correct type @@ -410,7 +408,6 @@ trait_selection_subtype_2 = ...so that {$requirement -> [if_else_different] `if` and `else` have incompatible types [no_else] `if` missing an `else` returns `()` [fn_main_correct_type] `main` function has the correct type - [fn_start_correct_type] `#[start]` function has the correct type [fn_lang_correct_type] lang item function has the correct type [intrinsic_correct_type] intrinsic has the correct type [method_correct_type] method receiver has the correct type diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 9778299eb19..bcb6ac13b8f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -435,6 +435,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>, terr: TypeError<'tcx>, param_env: Option<ParamEnv<'tcx>>, + path: &mut Option<PathBuf>, ) { match *cause.code() { ObligationCauseCode::Pattern { @@ -457,6 +458,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { format!("this is an iterator with items of type `{}`", args.type_at(0)), ); } else { + let expected_ty = self.tcx.short_ty_string(expected_ty, path); err.span_label(span, format!("this expression has type `{expected_ty}`")); } } @@ -717,53 +719,47 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { value: &mut DiagStyledString, other_value: &mut DiagStyledString, name: String, - sub: ty::GenericArgsRef<'tcx>, + args: &[ty::GenericArg<'tcx>], pos: usize, other_ty: Ty<'tcx>, ) { // `value` and `other_value` hold two incomplete type representation for display. // `name` is the path of both types being compared. `sub` value.push_highlighted(name); - let len = sub.len(); - if len > 0 { - value.push_highlighted("<"); - } - // Output the lifetimes for the first type - let lifetimes = sub - .regions() - .map(|lifetime| { - let s = lifetime.to_string(); - if s.is_empty() { "'_".to_string() } else { s } - }) - .collect::<Vec<_>>() - .join(", "); - if !lifetimes.is_empty() { - if sub.regions().count() < len { - value.push_normal(lifetimes + ", "); - } else { - value.push_normal(lifetimes); - } + if args.is_empty() { + return; } + value.push_highlighted("<"); - // Highlight all the type arguments that aren't at `pos` and compare the type argument at - // `pos` and `other_ty`. - for (i, type_arg) in sub.types().enumerate() { - if i == pos { - let values = self.cmp(type_arg, other_ty); - value.0.extend((values.0).0); - other_value.0.extend((values.1).0); - } else { - value.push_highlighted(type_arg.to_string()); + for (i, arg) in args.iter().enumerate() { + if i > 0 { + value.push_normal(", "); } - if len > 0 && i != len - 1 { - value.push_normal(", "); + match arg.unpack() { + ty::GenericArgKind::Lifetime(lt) => { + let s = lt.to_string(); + value.push_normal(if s.is_empty() { "'_" } else { &s }); + } + ty::GenericArgKind::Const(ct) => { + value.push_normal(ct.to_string()); + } + // Highlight all the type arguments that aren't at `pos` and compare + // the type argument at `pos` and `other_ty`. + ty::GenericArgKind::Type(type_arg) => { + if i == pos { + let values = self.cmp(type_arg, other_ty); + value.0.extend((values.0).0); + other_value.0.extend((values.1).0); + } else { + value.push_highlighted(type_arg.to_string()); + } + } } } - if len > 0 { - value.push_highlighted(">"); - } + + value.push_highlighted(">"); } /// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`, @@ -791,27 +787,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { t1_out: &mut DiagStyledString, t2_out: &mut DiagStyledString, path: String, - sub: &'tcx [ty::GenericArg<'tcx>], + args: &'tcx [ty::GenericArg<'tcx>], other_path: String, other_ty: Ty<'tcx>, - ) -> Option<()> { - // FIXME/HACK: Go back to `GenericArgsRef` to use its inherent methods, - // ideally that shouldn't be necessary. - let sub = self.tcx.mk_args(sub); - for (i, ta) in sub.types().enumerate() { - if ta == other_ty { - self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); - return Some(()); - } - if let ty::Adt(def, _) = ta.kind() { - let path_ = self.tcx.def_path_str(def.did()); - if path_ == other_path { - self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); - return Some(()); + ) -> bool { + for (i, arg) in args.iter().enumerate() { + if let Some(ta) = arg.as_type() { + if ta == other_ty { + self.highlight_outer(t1_out, t2_out, path, args, i, other_ty); + return true; + } + if let ty::Adt(def, _) = ta.kind() { + let path_ = self.tcx.def_path_str(def.did()); + if path_ == other_path { + self.highlight_outer(t1_out, t2_out, path, args, i, other_ty); + return true; + } } } } - None + false } /// Adds a `,` to the type representation only if it is appropriate. @@ -819,10 +814,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, value: &mut DiagStyledString, other_value: &mut DiagStyledString, - len: usize, pos: usize, ) { - if len > 0 && pos != len - 1 { + if pos > 0 { value.push_normal(", "); other_value.push_normal(", "); } @@ -899,10 +893,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let len2 = sig2.inputs().len(); if len1 == len2 { for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() { + self.push_comma(&mut values.0, &mut values.1, i); let (x1, x2) = self.cmp(*l, *r); (values.0).0.extend(x1.0); (values.1).0.extend(x2.0); - self.push_comma(&mut values.0, &mut values.1, len1, i); } } else { for (i, l) in sig1.inputs().iter().enumerate() { @@ -1150,14 +1144,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let len1 = sub_no_defaults_1.len(); let len2 = sub_no_defaults_2.len(); let common_len = cmp::min(len1, len2); - let remainder1: Vec<_> = sub1.types().skip(common_len).collect(); - let remainder2: Vec<_> = sub2.types().skip(common_len).collect(); + let remainder1 = &sub1[common_len..]; + let remainder2 = &sub2[common_len..]; let common_default_params = iter::zip(remainder1.iter().rev(), remainder2.iter().rev()) .filter(|(a, b)| a == b) .count(); let len = sub1.len() - common_default_params; - let consts_offset = len - sub1.consts().count(); // Only draw `<...>` if there are lifetime/type arguments. if len > 0 { @@ -1169,70 +1162,68 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let s = lifetime.to_string(); if s.is_empty() { "'_".to_string() } else { s } } - // At one point we'd like to elide all lifetimes here, they are irrelevant for - // all diagnostics that use this output - // - // Foo<'x, '_, Bar> - // Foo<'y, '_, Qux> - // ^^ ^^ --- type arguments are not elided - // | | - // | elided as they were the same - // not elided, they were different, but irrelevant - // - // For bound lifetimes, keep the names of the lifetimes, - // even if they are the same so that it's clear what's happening - // if we have something like - // - // for<'r, 's> fn(Inv<'r>, Inv<'s>) - // for<'r> fn(Inv<'r>, Inv<'r>) - let lifetimes = sub1.regions().zip(sub2.regions()); - for (i, lifetimes) in lifetimes.enumerate() { - let l1 = lifetime_display(lifetimes.0); - let l2 = lifetime_display(lifetimes.1); - if lifetimes.0 != lifetimes.1 { - values.0.push_highlighted(l1); - values.1.push_highlighted(l2); - } else if lifetimes.0.is_bound() || self.tcx.sess.opts.verbose { - values.0.push_normal(l1); - values.1.push_normal(l2); - } else { - values.0.push_normal("'_"); - values.1.push_normal("'_"); - } - self.push_comma(&mut values.0, &mut values.1, len, i); - } - // We're comparing two types with the same path, so we compare the type - // arguments for both. If they are the same, do not highlight and elide from the - // output. - // Foo<_, Bar> - // Foo<_, Qux> - // ^ elided type as this type argument was the same in both sides - let type_arguments = sub1.types().zip(sub2.types()); - let regions_len = sub1.regions().count(); - let num_display_types = consts_offset - regions_len; - for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() { - let i = i + regions_len; - if ta1 == ta2 && !self.tcx.sess.opts.verbose { - values.0.push_normal("_"); - values.1.push_normal("_"); - } else { - recurse(ta1, ta2, &mut values); + for (i, (arg1, arg2)) in sub1.iter().zip(sub2).enumerate().take(len) { + self.push_comma(&mut values.0, &mut values.1, i); + match arg1.unpack() { + // At one point we'd like to elide all lifetimes here, they are + // irrelevant for all diagnostics that use this output. + // + // Foo<'x, '_, Bar> + // Foo<'y, '_, Qux> + // ^^ ^^ --- type arguments are not elided + // | | + // | elided as they were the same + // not elided, they were different, but irrelevant + // + // For bound lifetimes, keep the names of the lifetimes, + // even if they are the same so that it's clear what's happening + // if we have something like + // + // for<'r, 's> fn(Inv<'r>, Inv<'s>) + // for<'r> fn(Inv<'r>, Inv<'r>) + ty::GenericArgKind::Lifetime(l1) => { + let l1_str = lifetime_display(l1); + let l2 = arg2.expect_region(); + let l2_str = lifetime_display(l2); + if l1 != l2 { + values.0.push_highlighted(l1_str); + values.1.push_highlighted(l2_str); + } else if l1.is_bound() || self.tcx.sess.opts.verbose { + values.0.push_normal(l1_str); + values.1.push_normal(l2_str); + } else { + values.0.push_normal("'_"); + values.1.push_normal("'_"); + } + } + ty::GenericArgKind::Type(ta1) => { + let ta2 = arg2.expect_ty(); + if ta1 == ta2 && !self.tcx.sess.opts.verbose { + values.0.push_normal("_"); + values.1.push_normal("_"); + } else { + recurse(ta1, ta2, &mut values); + } + } + // We're comparing two types with the same path, so we compare the type + // arguments for both. If they are the same, do not highlight and elide + // from the output. + // Foo<_, Bar> + // Foo<_, Qux> + // ^ elided type as this type argument was the same in both sides + + // Do the same for const arguments, if they are equal, do not highlight and + // elide them from the output. + ty::GenericArgKind::Const(ca1) => { + let ca2 = arg2.expect_const(); + maybe_highlight(ca1, ca2, &mut values, self.tcx); + } } - self.push_comma(&mut values.0, &mut values.1, len, i); - } - - // Do the same for const arguments, if they are equal, do not highlight and - // elide them from the output. - let const_arguments = sub1.consts().zip(sub2.consts()); - for (i, (ca1, ca2)) in const_arguments.enumerate() { - let i = i + consts_offset; - maybe_highlight(ca1, ca2, &mut values, self.tcx); - self.push_comma(&mut values.0, &mut values.1, len, i); } // Close the type argument bracket. - // Only draw `<...>` if there are lifetime/type arguments. + // Only draw `<...>` if there are arguments. if len > 0 { values.0.push_normal(">"); values.1.push_normal(">"); @@ -1244,17 +1235,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Foo<Bar<Qux> // ------- this type argument is exactly the same as the other type // Bar<Qux> - if self - .cmp_type_arg( - &mut values.0, - &mut values.1, - path1.clone(), - sub_no_defaults_1, - path2.clone(), - t2, - ) - .is_some() - { + if self.cmp_type_arg( + &mut values.0, + &mut values.1, + path1.clone(), + sub_no_defaults_1, + path2.clone(), + t2, + ) { return values; } // Check for case: @@ -1262,17 +1250,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Bar<Qux> // Foo<Bar<Qux>> // ------- this type argument is exactly the same as the other type - if self - .cmp_type_arg( - &mut values.1, - &mut values.0, - path2, - sub_no_defaults_2, - path1, - t1, - ) - .is_some() - { + if self.cmp_type_arg( + &mut values.1, + &mut values.0, + path2, + sub_no_defaults_2, + path1, + t1, + ) { return values; } @@ -1343,8 +1328,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut values = (DiagStyledString::normal("("), DiagStyledString::normal("(")); let len = args1.len(); for (i, (left, right)) in args1.iter().zip(args2).enumerate() { + self.push_comma(&mut values.0, &mut values.1, i); recurse(left, right, &mut values); - self.push_comma(&mut values.0, &mut values.1, len, i); } if len == 1 { // Keep the output for single element tuples as `(ty,)`. @@ -1611,7 +1596,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return; } - if let Some((expected, found, path)) = expected_found { + let mut path = None; + if let Some((expected, found, p)) = expected_found { + path = p; let (expected_label, found_label, exp_found) = match exp_found { Mismatch::Variable(ef) => ( ef.expected.prefix_string(self.tcx), @@ -1792,13 +1779,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &sort_string(values.expected), &sort_string(values.found), ); - if let Some(path) = path { - diag.note(format!( - "the full type name has been written to '{}'", - path.display(), - )); - diag.note("consider using `--verbose` to print the full type name to the console"); - } } } } @@ -1894,7 +1874,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // It reads better to have the error origin as the final // thing. - self.note_error_origin(diag, cause, exp_found, terr, param_env); + self.note_error_origin(diag, cause, exp_found, terr, param_env, &mut path); + if let Some(path) = path { + diag.note(format!("the full type name has been written to '{}'", path.display())); + diag.note("consider using `--verbose` to print the full type name to the console"); + } debug!(?diag); } @@ -2318,7 +2302,6 @@ impl<'tcx> ObligationCause<'tcx> { | ObligationCauseCode::MatchExpressionArm(_) | ObligationCauseCode::IfExpression { .. } | ObligationCauseCode::LetElse - | ObligationCauseCode::StartFunctionType | ObligationCauseCode::LangFunctionType(_) | ObligationCauseCode::IntrinsicType | ObligationCauseCode::MethodReceiver => FailureCode::Error0308, @@ -2376,9 +2359,6 @@ impl<'tcx> ObligationCause<'tcx> { ObligationCauseCode::MainFunctionType => { ObligationCauseFailureCode::FnMainCorrectType { span } } - ObligationCauseCode::StartFunctionType => { - ObligationCauseFailureCode::FnStartCorrectType { span, subdiags } - } &ObligationCauseCode::LangFunctionType(lang_item_name) => { ObligationCauseFailureCode::FnLangCorrectType { span, subdiags, lang_item_name } } @@ -2421,7 +2401,6 @@ impl<'tcx> ObligationCause<'tcx> { "const is compatible with trait" } ObligationCauseCode::MainFunctionType => "`main` function has the correct type", - ObligationCauseCode::StartFunctionType => "`#[start]` function has the correct type", ObligationCauseCode::LangFunctionType(_) => "lang item function has the correct type", ObligationCauseCode::IntrinsicType => "intrinsic has the correct type", ObligationCauseCode::MethodReceiver => "method receiver has the correct type", @@ -2442,7 +2421,6 @@ impl IntoDiagArg for ObligationCauseAsDiagArg<'_> { "const_compat" } ObligationCauseCode::MainFunctionType => "fn_main_correct_type", - ObligationCauseCode::StartFunctionType => "fn_start_correct_type", ObligationCauseCode::LangFunctionType(_) => "fn_lang_correct_type", ObligationCauseCode::IntrinsicType => "intrinsic_correct_type", ObligationCauseCode::MethodReceiver => "method_correct_type", diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs index 2a487a48a8e..b9f3abc2534 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -1,8 +1,8 @@ use core::ops::ControlFlow; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars as rbv; @@ -48,7 +48,7 @@ fn find_component_for_bound_region<'tcx>( region_def_id: DefId, ) -> Option<&'tcx hir::Ty<'tcx>> { FindNestedTypeVisitor { tcx, region_def_id, current_index: ty::INNERMOST } - .visit_ty(arg) + .visit_ty_unambig(arg) .break_value() } @@ -74,7 +74,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { self.tcx.hir() } - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { match arg.kind { hir::TyKind::BareFn(_) => { self.current_index.shift_in(1); @@ -101,7 +101,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { Some(rbv::ResolvedArg::EarlyBound(id)) => { debug!("EarlyBound id={:?}", id); if id.to_def_id() == self.region_def_id { - return ControlFlow::Break(arg); + return ControlFlow::Break(arg.as_unambig_ty()); } } @@ -117,7 +117,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { if debruijn_index == self.current_index && id.to_def_id() == self.region_def_id { - return ControlFlow::Break(arg); + return ControlFlow::Break(arg.as_unambig_ty()); } } @@ -147,7 +147,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { ) .is_break() { - ControlFlow::Break(arg) + ControlFlow::Break(arg.as_unambig_ty()) } else { ControlFlow::Continue(()) }; @@ -210,7 +210,7 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { ControlFlow::Continue(()) } - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { // ignore nested types // // If you have a type like `Foo<'a, &Ty>` we diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs index 54d8a9e25ca..886581bc35f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs @@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; use rustc_middle::bug; use rustc_middle::ty::TypeVisitor; use tracing::debug; @@ -87,7 +87,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { for matching_def_id in v.0 { let mut hir_v = super::static_impl_trait::HirTraitObjectVisitor(&mut traits, matching_def_id); - hir_v.visit_ty(impl_self_ty); + hir_v.visit_ty_unambig(impl_self_ty); } if traits.is_empty() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 3416a17624e..dfbef39e9e1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -3,9 +3,9 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnostic}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{Visitor, walk_ty}; +use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty}; use rustc_hir::{ - self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime, + self as hir, AmbigArg, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind, }; use rustc_middle::ty::{ @@ -153,7 +153,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { 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); + v.visit_ty_unambig(ty); if !v.0.is_empty() { span = v.0.clone().into(); spans = v.0; @@ -374,7 +374,7 @@ pub fn suggest_new_region_bound( } } } - TyKind::TraitObject(_, lt, _) => { + TyKind::TraitObject(_, lt) => { if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), @@ -420,8 +420,8 @@ fn make_elided_region_spans_suggs<'a>( let mut process_consecutive_brackets = |span: Option<Span>, spans_suggs: &mut Vec<(Span, String)>| { - if span - .is_some_and(|span| bracket_span.map_or(true, |bracket_span| span == bracket_span)) + if let Some(span) = span + && bracket_span.is_none_or(|bracket_span| span == bracket_span) { consecutive_brackets += 1; } else if let Some(bracket_span) = bracket_span.take() { @@ -500,7 +500,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // In that case, only the first one will get suggestions. let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *did); - hir_v.visit_ty(self_ty); + hir_v.visit_ty_unambig(self_ty); !traits.is_empty() }) { @@ -560,7 +560,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { for found_did in found_dids { let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); - hir_v.visit_ty(self_ty); + hir_v.visit_ty_unambig(self_ty); for &span in &traits { let subdiag = DynTraitConstraintSuggestion { span, ident }; subdiag.add_to_diag(err); @@ -591,12 +591,10 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for TraitObjectVisitor { pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId); impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { - if let TyKind::TraitObject( - poly_trait_refs, - Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. }, - _, - ) = t.kind + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) { + if let TyKind::TraitObject(poly_trait_refs, lifetime_ptr) = t.kind + && let Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. } = + lifetime_ptr.pointer() { for ptr in poly_trait_refs { if Some(self.1) == ptr.trait_ref.trait_def_id() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs index 95dd1b28a39..bbcd28c0835 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs @@ -1,10 +1,10 @@ //! Error Reporting for `impl` items that do not match the obligations from their `trait`. use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{Visitor, walk_ty}; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::ExpectedFound; @@ -137,11 +137,13 @@ impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { self.tcx.hir() } - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) { match arg.kind { hir::TyKind::Ref(_, ref mut_ty) => { // We don't want to suggest looking into borrowing `&T` or `&Self`. - hir::intravisit::walk_ty(self, mut_ty.ty); + if let Some(ambig_ty) = mut_ty.ty.try_as_ambig_ty() { + walk_ty(self, ambig_ty); + } return; } hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index 36270e0da78..af7e56961b7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -167,6 +167,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { exp_span, exp_found.expected, exp_found.found, ); + match self.tcx.coroutine_kind(cause.body_id) { + Some(hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async | hir::CoroutineDesugaring::AsyncGen, + _, + )) => (), + None + | Some( + hir::CoroutineKind::Coroutine(_) + | hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _), + ) => return, + } + if let ObligationCauseCode::CompareImplItem { .. } = cause.code() { return; } @@ -643,7 +655,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && let ty::Ref(found_region, _, _) = found.kind() && expected_region.is_bound() && !found_region.is_bound() - && let hir::TyKind::Infer = arg_hir.kind + && let hir::TyKind::Infer(()) = arg_hir.kind { // If the expected region is late bound, the found region is not, and users are asking compiler // to infer the type, we can suggest adding `: &_`. diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index c40ba330845..5021fd8bf83 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -462,6 +462,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.note( "`#[target_feature]` functions do not implement the `Fn` traits", ); + err.note( + "try casting the function to a `fn` pointer or wrapping it in a closure", + ); } self.try_to_add_help_message( @@ -580,8 +583,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.tcx.hir_node_by_def_id(obligation.cause.body_id) && let hir::ItemKind::Impl(impl_) = item.kind && let None = impl_.of_trait - && let hir::TyKind::TraitObject(_, _, syntax) = impl_.self_ty.kind - && let TraitObjectSyntax::None = syntax + && let hir::TyKind::TraitObject(_, tagged_ptr) = impl_.self_ty.kind + && let TraitObjectSyntax::None = tagged_ptr.tag() && impl_.self_ty.span.edition().at_least_rust_2021() { // Silence the dyn-compatibility error in favor of the missing dyn on @@ -1338,20 +1341,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let derive_better_type_error = |alias_term: ty::AliasTerm<'tcx>, expected_term: ty::Term<'tcx>| { let ocx = ObligationCtxt::new(self); - let normalized_term = match expected_term.unpack() { - ty::TermKind::Ty(_) => self.next_ty_var(DUMMY_SP).into(), - ty::TermKind::Const(_) => self.next_const_var(DUMMY_SP).into(), - }; - ocx.register_obligation(Obligation::new( - self.tcx, - ObligationCause::dummy(), + + let Ok(normalized_term) = ocx.structurally_normalize_term( + &ObligationCause::dummy(), obligation.param_env, - ty::PredicateKind::NormalizesTo(ty::NormalizesTo { - alias: alias_term, - term: normalized_term, - }), - )); - let _ = ocx.select_where_possible(); + alias_term.to_term(self.tcx), + ) else { + return None; + }; + if let Err(terr) = ocx.eq( &ObligationCause::dummy(), obligation.param_env, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index cd4f77bb4cf..8111983c539 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, AmbigArg, LangItem}; use rustc_infer::traits::{ DynCompatibilityViolation, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError, @@ -87,9 +87,9 @@ impl<'v> Visitor<'v> for FindExprBySpan<'v> { } } - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { if self.span == ty.span { - self.ty_result = Some(ty); + self.ty_result = Some(ty.as_unambig_ty()); } else { hir::intravisit::walk_ty(self, ty); } @@ -433,33 +433,19 @@ pub fn report_dyn_incompatibility<'tcx>( hir::Node::Item(item) => Some(item.ident.span), _ => None, }); + let mut err = struct_span_code_err!( tcx.dcx(), span, E0038, - "the trait `{}` cannot be made into an object", + "the {} `{}` is not dyn compatible", + tcx.def_descr(trait_def_id), trait_str ); - err.span_label(span, format!("`{trait_str}` cannot be made into an object")); - - if let Some(hir_id) = hir_id - && let hir::Node::Ty(ty) = tcx.hir_node(hir_id) - && let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind - { - let mut hir_id = hir_id; - while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) { - hir_id = ty.hir_id; - } - if tcx.parent_hir_node(hir_id).fn_sig().is_some() { - // Do not suggest `impl Trait` when dealing with things like super-traits. - err.span_suggestion_verbose( - ty.span.until(trait_ref.span), - "consider using an opaque type instead", - "impl ", - Applicability::MaybeIncorrect, - ); - } - } + err.span_label(span, format!("`{trait_str}` is not dyn compatible")); + + attempt_dyn_to_impl_suggestion(tcx, hir_id, &mut err); + let mut reported_violations = FxIndexSet::default(); let mut multi_span = vec![]; let mut messages = vec![]; @@ -474,7 +460,7 @@ pub fn report_dyn_incompatibility<'tcx>( if reported_violations.insert(violation.clone()) { let spans = violation.spans(); let msg = if trait_span.is_none() || spans.is_empty() { - format!("the trait cannot be made into an object because {}", violation.error_msg()) + format!("the trait is not dyn compatible because {}", violation.error_msg()) } else { format!("...because {}", violation.error_msg()) }; @@ -491,7 +477,7 @@ pub fn report_dyn_incompatibility<'tcx>( let has_multi_span = !multi_span.is_empty(); let mut note_span = MultiSpan::from_spans(multi_span.clone()); if let (Some(trait_span), true) = (trait_span, has_multi_span) { - note_span.push_span_label(trait_span, "this trait cannot be made into an object..."); + note_span.push_span_label(trait_span, "this trait is not dyn compatible..."); } for (span, msg) in iter::zip(multi_span, messages) { note_span.push_span_label(span, msg); @@ -499,16 +485,12 @@ pub fn report_dyn_incompatibility<'tcx>( // FIXME(dyn_compat_renaming): Update the URL. err.span_note( note_span, - "for a trait to be \"dyn-compatible\" it needs to allow building a vtable to allow the call \ - to be resolvable dynamically; for more information visit \ - <https://doc.rust-lang.org/reference/items/traits.html#object-safety>", + "for a trait to be dyn compatible it needs to allow building a vtable\n\ + for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>", ); // Only provide the help if its a local trait, otherwise it's not actionable. if trait_span.is_some() { - let mut reported_violations: Vec<_> = reported_violations.into_iter().collect(); - reported_violations.sort(); - let mut potential_solutions: Vec<_> = reported_violations.into_iter().map(|violation| violation.solution()).collect(); potential_solutions.sort(); @@ -519,68 +501,116 @@ pub fn report_dyn_incompatibility<'tcx>( } } + attempt_dyn_to_enum_suggestion(tcx, trait_def_id, &*trait_str, &mut err); + + err +} + +/// Attempt to suggest converting the `dyn Trait` argument to an enumeration +/// over the types that implement `Trait`. +fn attempt_dyn_to_enum_suggestion( + tcx: TyCtxt<'_>, + trait_def_id: DefId, + trait_str: &str, + err: &mut Diag<'_>, +) { let impls_of = tcx.trait_impls_of(trait_def_id); - let impls = if impls_of.blanket_impls().is_empty() { - impls_of - .non_blanket_impls() - .values() - .flatten() - .filter(|def_id| { - !matches!(tcx.type_of(*def_id).instantiate_identity().kind(), ty::Dynamic(..)) - }) - .collect::<Vec<_>>() - } else { - vec![] - }; - let externally_visible = if !impls.is_empty() - && let Some(def_id) = trait_def_id.as_local() + + if !impls_of.blanket_impls().is_empty() { + return; + } + + let concrete_impls: Option<Vec<Ty<'_>>> = impls_of + .non_blanket_impls() + .values() + .flatten() + .map(|impl_id| { + // Don't suggest conversion to enum if the impl types have type parameters. + // It's unlikely the user wants to define a generic enum. + let Some(impl_type) = tcx.type_of(*impl_id).no_bound_vars() else { return None }; + + // Obviously unsized impl types won't be usable in an enum. + // Note: this doesn't use `Ty::is_trivially_sized` because that function + // defaults to assuming that things are *not* sized, whereas we want to + // fall back to assuming that things may be sized. + match impl_type.kind() { + ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::DynKind::Dyn) => { + return None; + } + _ => {} + } + Some(impl_type) + }) + .collect(); + let Some(concrete_impls) = concrete_impls else { return }; + + const MAX_IMPLS_TO_SUGGEST_CONVERTING_TO_ENUM: usize = 9; + if concrete_impls.is_empty() || concrete_impls.len() > MAX_IMPLS_TO_SUGGEST_CONVERTING_TO_ENUM { + return; + } + + let externally_visible = if let Some(def_id) = trait_def_id.as_local() { // We may be executing this during typeck, which would result in cycle // if we used effective_visibilities query, which looks into opaque types // (and therefore calls typeck). - && tcx.resolutions(()).effective_visibilities.is_exported(def_id) - { - true + tcx.resolutions(()).effective_visibilities.is_exported(def_id) } else { false }; - match &impls[..] { - [] => {} - _ if impls.len() > 9 => {} - [only] if externally_visible => { - err.help(with_no_trimmed_paths!(format!( - "only type `{}` is seen to implement the trait in this crate, consider using it \ - directly instead", - tcx.type_of(*only).instantiate_identity(), - ))); - } - [only] => { - err.help(with_no_trimmed_paths!(format!( - "only type `{}` implements the trait, consider using it directly instead", - tcx.type_of(*only).instantiate_identity(), - ))); - } - impls => { - let types = impls - .iter() - .map(|t| { - with_no_trimmed_paths!(format!(" {}", tcx.type_of(*t).instantiate_identity(),)) - }) - .collect::<Vec<_>>(); - err.help(format!( - "the following types implement the trait, consider defining an enum where each \ - variant holds one of these types, implementing `{}` for this new enum and using \ - it instead:\n{}", - trait_str, - types.join("\n"), - )); - } + + if let [only_impl] = &concrete_impls[..] { + let within = if externally_visible { " within this crate" } else { "" }; + err.help(with_no_trimmed_paths!(format!( + "only type `{only_impl}` implements `{trait_str}`{within}; \ + consider using it directly instead." + ))); + } else { + let types = concrete_impls + .iter() + .map(|t| with_no_trimmed_paths!(format!(" {}", t))) + .collect::<Vec<String>>() + .join("\n"); + + err.help(format!( + "the following types implement `{trait_str}`:\n\ + {types}\n\ + consider defining an enum where each variant holds one of these types,\n\ + implementing `{trait_str}` for this new enum and using it instead", + )); } + if externally_visible { err.note(format!( - "`{trait_str}` can be implemented in other crates; if you want to support your users \ + "`{trait_str}` may be implemented in other crates; if you want to support your users \ passing their own types here, you can't refer to a specific type", )); } +} - err +/// Attempt to suggest that a `dyn Trait` argument or return type be converted +/// to use `impl Trait`. +fn attempt_dyn_to_impl_suggestion(tcx: TyCtxt<'_>, hir_id: Option<hir::HirId>, err: &mut Diag<'_>) { + let Some(hir_id) = hir_id else { return }; + let hir::Node::Ty(ty) = tcx.hir_node(hir_id) else { return }; + let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind else { return }; + + // Only suggest converting `dyn` to `impl` if we're in a function signature. + // This ensures that we don't suggest converting e.g. + // `type Alias = Box<dyn DynIncompatibleTrait>;` to + // `type Alias = Box<impl DynIncompatibleTrait>;` + let Some((_id, first_non_type_parent_node)) = + tcx.hir().parent_iter(hir_id).find(|(_id, node)| !matches!(node, hir::Node::Ty(_))) + else { + return; + }; + if first_non_type_parent_node.fn_sig().is_none() { + return; + } + + err.span_suggestion_verbose( + ty.span.until(trait_ref.span), + "consider using an opaque type instead", + "impl ", + Applicability::MaybeIncorrect, + ); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 2d932e36470..4e0b097db4c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt}; use rustc_parse_format::{ParseMode, Parser, Piece, Position}; use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; -use rustc_span::{Span, Symbol, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use tracing::{debug, info}; use {rustc_attr_parsing as attr, rustc_hir as hir}; @@ -375,7 +375,7 @@ impl IgnoredDiagnosticOption { #[help] pub struct UnknownFormatParameterForOnUnimplementedAttr { argument_name: Symbol, - trait_name: Symbol, + trait_name: Ident, } #[derive(LintDiagnostic)] @@ -792,7 +792,7 @@ impl<'tcx> OnUnimplementedFormatString { tcx.trait_id_of_impl(item_def_id) .expect("expected `on_unimplemented` to correspond to a trait") }; - let trait_name = tcx.item_name(trait_def_id); + let trait_name = tcx.item_ident(trait_def_id); let generics = tcx.generics_of(item_def_id); let s = self.symbol.as_str(); let mut parser = Parser::new(s, None, None, false, ParseMode::Format); @@ -821,7 +821,11 @@ impl<'tcx> OnUnimplementedFormatString { Position::ArgumentNamed(s) => { match Symbol::intern(s) { // `{ThisTraitsName}` is allowed - s if s == trait_name && !self.is_diagnostic_namespace_variant => (), + s if s == trait_name.name + && !self.is_diagnostic_namespace_variant => + { + () + } s if ALLOWED_FORMAT_SYMBOLS.contains(&s) && !self.is_diagnostic_namespace_variant => { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index c2e73b732d3..471105773e2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -14,14 +14,13 @@ use rustc_errors::{ Applicability, Diag, EmissionGuarantee, MultiSpan, Style, SuggestionStyle, pluralize, struct_span_code_err, }; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{Visitor, VisitorExt}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, expr_needs_parens, - is_range_literal, + self as hir, AmbigArg, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, + expr_needs_parens, is_range_literal, }; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_middle::hir::map; @@ -179,7 +178,7 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( let mut ty_spans = vec![]; for input in fn_sig.decl.inputs { ReplaceImplTraitVisitor { ty_spans: &mut ty_spans, param_did: param.def_id } - .visit_ty(input); + .visit_ty_unambig(input); } // The type param `T: Trait` we will suggest to introduce. let type_param = format!("{type_param_name}: {bound_str}"); @@ -2740,7 +2739,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | ObligationCauseCode::IfExpression { .. } | ObligationCauseCode::IfExpressionWithNoElse | ObligationCauseCode::MainFunctionType - | ObligationCauseCode::StartFunctionType | ObligationCauseCode::LangFunctionType(_) | ObligationCauseCode::IntrinsicType | ObligationCauseCode::MethodReceiver @@ -3075,7 +3073,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if let Some(ty) = ty { match ty.kind { - hir::TyKind::TraitObject(traits, _, _) => { + hir::TyKind::TraitObject(traits, _) => { let (span, kw) = match traits { [first, ..] if first.span.lo() == ty.span.lo() => { // Missing `dyn` in front of trait object. @@ -5066,7 +5064,7 @@ pub struct SelfVisitor<'v> { } impl<'v> Visitor<'v> for SelfVisitor<'v> { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { if let hir::TyKind::Path(path) = ty.kind && let hir::QPath::TypeRelative(inner_ty, segment) = path && (Some(segment.ident.name) == self.name || self.name.is_none()) @@ -5074,7 +5072,7 @@ impl<'v> Visitor<'v> for SelfVisitor<'v> { && let hir::QPath::Resolved(None, inner_path) = inner_path && let Res::SelfTyAlias { .. } = inner_path.res { - self.paths.push(ty); + self.paths.push(ty.as_unambig_ty()); } hir::intravisit::walk_ty(self, ty); } @@ -5188,7 +5186,7 @@ struct ReplaceImplTraitVisitor<'a> { } impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { - fn visit_ty(&mut self, t: &'hir hir::Ty<'hir>) { + fn visit_ty(&mut self, t: &'hir hir::Ty<'hir, AmbigArg>) { if let hir::TyKind::Path(hir::QPath::Resolved( None, hir::Path { res: Res::Def(_, segment_did), .. }, @@ -5481,7 +5479,7 @@ impl<'v> Visitor<'v> for FindTypeParam { // Skip where-clauses, to avoid suggesting indirection for type parameters found there. } - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { + fn visit_ty(&mut self, ty: &hir::Ty<'_, AmbigArg>) { // We collect the spans of all uses of the "bare" type param, like in `field: T` or // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized` diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 53a4e5031c6..2dfa72972ba 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -6,11 +6,10 @@ use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic, }; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{Visitor, walk_ty}; -use rustc_hir::{FnRetTy, GenericParamKind, Node}; +use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty}; +use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath}; use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt}; @@ -579,7 +578,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { } impl<'v> Visitor<'v> for ImplicitLifetimeFinder { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { let make_suggestion = |ident: Ident| { if ident.name == kw::Empty && ident.span.is_empty() { format!("{}, ", self.suggestion_param_name) @@ -642,16 +641,16 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { if let Some(fn_decl) = node.fn_decl() && let hir::FnRetTy::Return(ty) = fn_decl.output { - visitor.visit_ty(ty); + visitor.visit_ty_unambig(ty); } if visitor.suggestions.is_empty() { // Do not suggest constraining the `&self` param, but rather the return type. // If that is wrong (because it is not sufficient), a follow up error will tell the // user to fix it. This way we lower the chances of *over* constraining, but still // get the cake of "correctly" contrained in two steps. - visitor.visit_ty(self.ty_sup); + visitor.visit_ty_unambig(self.ty_sup); } - visitor.visit_ty(self.ty_sub); + visitor.visit_ty_unambig(self.ty_sub); if visitor.suggestions.is_empty() { return false; } @@ -1695,13 +1694,6 @@ pub enum ObligationCauseFailureCode { #[primary_span] span: Span, }, - #[diag(trait_selection_oc_fn_start_correct_type, code = E0308)] - FnStartCorrectType { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, #[diag(trait_selection_oc_fn_lang_correct_type, code = E0308)] FnLangCorrectType { #[primary_span] diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 4498beff4ea..2b7da4bc5ff 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -513,8 +513,27 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { _ => ChildMode::PassThrough, }; + let nested_goals = candidate.instantiate_nested_goals(self.span()); + + // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as + // an actual candidate, instead we should treat them as if the impl was never considered to + // have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written + // instead of `impl<T: FnPtr> Trait for T`. + // + // We do this as a separate loop so that we do not choose to tell the user about some nested + // goal before we encounter a `T: FnPtr` nested goal. + for nested_goal in &nested_goals { + if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait() + && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() + && poly_trait_pred.def_id() == fn_ptr_trait + && let Err(NoSolution) = nested_goal.result() + { + return ControlFlow::Break(self.obligation.clone()); + } + } + let mut impl_where_bound_count = 0; - for nested_goal in candidate.instantiate_nested_goals(self.span()) { + for nested_goal in nested_goals { trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result())); let make_obligation = |cause| Obligation { @@ -605,7 +624,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { } } -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] enum ChildMode<'tcx> { // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`, // and skip all `GoalSource::Misc`, which represent useless obligations diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index e27143f1396..50d47d20e1a 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -709,7 +709,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { if matches!(ty.kind(), ty::Alias(..)) { let ocx = ObligationCtxt::new(infcx); ty = ocx - .structurally_normalize(&ObligationCause::dummy(), param_env, ty) + .structurally_normalize_ty(&ObligationCause::dummy(), param_env, ty) .map_err(|_| ())?; if !ocx.select_where_possible().is_empty() { return Err(()); diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index d2abd881c45..66491d9abe1 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -19,6 +19,7 @@ use rustc_middle::ty::{ TypeVisitableExt, TypeVisitor, TypingMode, Upcast, }; use rustc_span::Span; +use rustc_type_ir::elaborate; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -39,7 +40,7 @@ pub fn hir_ty_lowering_dyn_compatibility_violations( trait_def_id: DefId, ) -> Vec<DynCompatibilityViolation> { debug_assert!(tcx.generics_of(trait_def_id).has_self); - tcx.supertrait_def_ids(trait_def_id) + elaborate::supertrait_def_ids(tcx, trait_def_id) .map(|def_id| predicates_reference_self(tcx, def_id, true)) .filter(|spans| !spans.is_empty()) .map(DynCompatibilityViolation::SupertraitSelf) @@ -52,9 +53,8 @@ fn dyn_compatibility_violations( ) -> &'_ [DynCompatibilityViolation] { debug_assert!(tcx.generics_of(trait_def_id).has_self); debug!("dyn_compatibility_violations: {:?}", trait_def_id); - tcx.arena.alloc_from_iter( - tcx.supertrait_def_ids(trait_def_id) + elaborate::supertrait_def_ids(tcx, trait_def_id) .flat_map(|def_id| dyn_compatibility_violations_for_trait(tcx, def_id)), ) } diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 5e270b62b00..4a3983fca31 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -319,7 +319,7 @@ where self.infcx.at(cause, param_env).deeply_normalize(value, &mut **self.engine.borrow_mut()) } - pub fn structurally_normalize( + pub fn structurally_normalize_ty( &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -327,7 +327,7 @@ where ) -> Result<Ty<'tcx>, Vec<E>> { self.infcx .at(cause, param_env) - .structurally_normalize(value, &mut **self.engine.borrow_mut()) + .structurally_normalize_ty(value, &mut **self.engine.borrow_mut()) } pub fn structurally_normalize_const( @@ -340,4 +340,15 @@ where .at(cause, param_env) .structurally_normalize_const(value, &mut **self.engine.borrow_mut()) } + + pub fn structurally_normalize_term( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: ty::Term<'tcx>, + ) -> Result<ty::Term<'tcx>, Vec<E>> { + self.infcx + .at(cause, param_env) + .structurally_normalize_term(value, &mut **self.engine.borrow_mut()) + } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index da16a742099..fe5ad003a7e 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -66,9 +66,9 @@ pub use self::specialize::{ }; pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::{ - BoundVarReplacer, PlaceholderReplacer, TraitAliasExpander, TraitAliasExpansionInfo, elaborate, - expand_trait_aliases, impl_item_is_final, supertraits, - transitive_bounds_that_define_assoc_item, upcast_choices, with_replaced_escaping_bound_vars, + BoundVarReplacer, PlaceholderReplacer, elaborate, expand_trait_aliases, impl_item_is_final, + supertrait_def_ids, supertraits, transitive_bounds_that_define_assoc_item, upcast_choices, + with_replaced_escaping_bound_vars, }; use crate::error_reporting::InferCtxtErrorExt; use crate::infer::outlives::env::OutlivesEnvironment; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index d59cf88875e..537b042bde5 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -18,6 +18,7 @@ use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Term, Ty, TyCtxt, TypingMode, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::sym; +use rustc_type_ir::elaborate; use thin_vec::thin_vec; use tracing::{debug, instrument}; @@ -836,8 +837,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( if tcx.is_impl_trait_in_trait(obligation.predicate.def_id) && let Some(out_trait_def_id) = data.principal_def_id() && let rpitit_trait_def_id = tcx.parent(obligation.predicate.def_id) - && tcx - .supertrait_def_ids(out_trait_def_id) + && elaborate::supertrait_def_ids(tcx, out_trait_def_id) .any(|trait_def_id| trait_def_id == rpitit_trait_def_id) { candidate_set.push_candidate(ProjectionCandidate::ObjectRpitit); diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 4004e354dc1..26ba1511b54 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -6,8 +6,7 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; use crate::traits::query::NoSolution; -use crate::traits::query::normalize::QueryNormalizeExt; -use crate::traits::{Normalized, ObligationCause, ObligationCtxt}; +use crate::traits::{ObligationCause, ObligationCtxt}; /// This returns true if the type `ty` is "trivial" for /// dropck-outlives -- that is, if it doesn't require any types to @@ -172,13 +171,18 @@ pub fn compute_dropck_outlives_inner<'tcx>( // do not themselves define a destructor", more or less. We have // to push them onto the stack to be expanded. for ty in constraints.dtorck_types.drain(..) { - let Normalized { value: ty, obligations } = - ocx.infcx.at(&cause, param_env).query_normalize(ty)?; - ocx.register_obligations(obligations); + let normalized_ty = ocx.normalize(&cause, param_env, ty); - debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + debug!("failed to normalize dtorck type: {ty} ~> {errors:#?}"); + return Err(NoSolution); + } + + let normalized_ty = ocx.infcx.resolve_vars_if_possible(normalized_ty); + debug!("dropck_outlives: ty from dtorck_types = {:?}", normalized_ty); - match ty.kind() { + match normalized_ty.kind() { // All parameters live for the duration of the // function. ty::Param(..) => {} @@ -186,12 +190,12 @@ pub fn compute_dropck_outlives_inner<'tcx>( // A projection that we couldn't resolve - it // might have a destructor. ty::Alias(..) => { - result.kinds.push(ty.into()); + result.kinds.push(normalized_ty.into()); } _ => { - if ty_set.insert(ty) { - ty_stack.push((ty, depth + 1)); + if ty_set.insert(normalized_ty) { + ty_stack.push((normalized_ty, depth + 1)); } } } 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 b370f802052..13a6744c2e9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -18,7 +18,7 @@ use rustc_infer::traits::{ use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode}; use rustc_middle::{bug, span_bug}; -use rustc_type_ir::Interner; +use rustc_type_ir::{Interner, elaborate}; use tracing::{debug, instrument, trace}; use super::SelectionCandidate::*; @@ -1003,8 +1003,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let a_auto_traits: FxIndexSet<DefId> = a_data .auto_traits() .chain(principal_def_id_a.into_iter().flat_map(|principal_def_id| { - self.tcx() - .supertrait_def_ids(principal_def_id) + elaborate::supertrait_def_ids(self.tcx(), principal_def_id) .filter(|def_id| self.tcx().trait_is_auto(*def_id)) })) .collect(); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 5581ea46882..0cc0d7f786b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -32,6 +32,7 @@ use rustc_middle::ty::{ TypingMode, Upcast, }; use rustc_span::{Symbol, sym}; +use rustc_type_ir::elaborate; use tracing::{debug, instrument, trace}; use self::EvaluationResult::*; @@ -2531,7 +2532,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { let a_auto_traits: FxIndexSet<DefId> = a_data .auto_traits() .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| { - tcx.supertrait_def_ids(principal_def_id).filter(|def_id| tcx.trait_is_auto(*def_id)) + elaborate::supertrait_def_ids(tcx, principal_def_id) + .filter(|def_id| tcx.trait_is_auto(*def_id)) })) .collect(); diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index f8e8f2176c1..e6d5d336b8d 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -7,44 +7,12 @@ use crate::traits::{NormalizeExt, Obligation}; #[extension(pub trait StructurallyNormalizeExt<'tcx>)] impl<'tcx> At<'_, 'tcx> { - fn structurally_normalize<E: 'tcx>( + fn structurally_normalize_ty<E: 'tcx>( &self, ty: Ty<'tcx>, fulfill_cx: &mut dyn TraitEngine<'tcx, E>, ) -> Result<Ty<'tcx>, Vec<E>> { - assert!(!ty.is_ty_var(), "should have resolved vars before calling"); - - if self.infcx.next_trait_solver() { - let ty::Alias(..) = *ty.kind() else { - return Ok(ty); - }; - - let new_infer_ty = self.infcx.next_ty_var(self.cause.span); - - // We simply emit an `alias-eq` goal here, since that will take care of - // normalizing the LHS of the projection until it is a rigid projection - // (or a not-yet-defined opaque in scope). - let obligation = Obligation::new( - self.infcx.tcx, - self.cause.clone(), - self.param_env, - ty::PredicateKind::AliasRelate( - ty.into(), - new_infer_ty.into(), - ty::AliasRelationDirection::Equate, - ), - ); - - fulfill_cx.register_predicate_obligation(self.infcx, obligation); - let errors = fulfill_cx.select_where_possible(self.infcx); - if !errors.is_empty() { - return Err(errors); - } - - Ok(self.infcx.resolve_vars_if_possible(new_infer_ty)) - } else { - Ok(self.normalize(ty).into_value_registering_obligations(self.infcx, fulfill_cx)) - } + self.structurally_normalize_term(ty.into(), fulfill_cx).map(|term| term.expect_type()) } fn structurally_normalize_const<E: 'tcx>( @@ -52,14 +20,29 @@ impl<'tcx> At<'_, 'tcx> { ct: ty::Const<'tcx>, fulfill_cx: &mut dyn TraitEngine<'tcx, E>, ) -> Result<ty::Const<'tcx>, Vec<E>> { - assert!(!ct.is_ct_infer(), "should have resolved vars before calling"); + if self.infcx.tcx.features().generic_const_exprs() { + return Ok(super::evaluate_const(&self.infcx, ct, self.param_env)); + } + + self.structurally_normalize_term(ct.into(), fulfill_cx).map(|term| term.expect_const()) + } + + fn structurally_normalize_term<E: 'tcx>( + &self, + term: ty::Term<'tcx>, + fulfill_cx: &mut dyn TraitEngine<'tcx, E>, + ) -> Result<ty::Term<'tcx>, Vec<E>> { + assert!(!term.is_infer(), "should have resolved vars before calling"); if self.infcx.next_trait_solver() { - let ty::ConstKind::Unevaluated(..) = ct.kind() else { - return Ok(ct); - }; + if let None = term.to_alias_term() { + return Ok(term); + } - let new_infer_ct = self.infcx.next_const_var(self.cause.span); + let new_infer = match term.unpack() { + ty::TermKind::Ty(_) => self.infcx.next_ty_var(self.cause.span).into(), + ty::TermKind::Const(_) => self.infcx.next_const_var(self.cause.span).into(), + }; // We simply emit an `alias-eq` goal here, since that will take care of // normalizing the LHS of the projection until it is a rigid projection @@ -68,11 +51,7 @@ impl<'tcx> At<'_, 'tcx> { self.infcx.tcx, self.cause.clone(), self.param_env, - ty::PredicateKind::AliasRelate( - ct.into(), - new_infer_ct.into(), - ty::AliasRelationDirection::Equate, - ), + ty::PredicateKind::AliasRelate(term, new_infer, ty::AliasRelationDirection::Equate), ); fulfill_cx.register_predicate_obligation(self.infcx, obligation); @@ -81,11 +60,9 @@ impl<'tcx> At<'_, 'tcx> { return Err(errors); } - Ok(self.infcx.resolve_vars_if_possible(new_infer_ct)) - } else if self.infcx.tcx.features().generic_const_exprs() { - Ok(super::evaluate_const(&self.infcx, ct, self.param_env)) + Ok(self.infcx.resolve_vars_if_possible(new_infer)) } else { - Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx)) + Ok(self.normalize(term).into_value_registering_obligations(self.infcx, fulfill_cx)) } } } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index da1045b664a..c9fb2a757e1 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -1,162 +1,85 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, VecDeque}; -use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::Diag; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir::def_id::DefId; use rustc_infer::infer::InferCtxt; pub use rustc_infer::traits::util::*; use rustc_middle::bug; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast, + self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use rustc_span::Span; use smallvec::{SmallVec, smallvec}; use tracing::debug; -/////////////////////////////////////////////////////////////////////////// -// `TraitAliasExpander` iterator -/////////////////////////////////////////////////////////////////////////// - -/// "Trait alias expansion" is the process of expanding a sequence of trait -/// references into another sequence by transitively following all trait -/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias -/// `trait Foo = Bar + Sync;`, and another trait alias -/// `trait Bar = Read + Write`, then the bounds would expand to -/// `Read + Write + Sync + Send`. -/// Expansion is done via a DFS (depth-first search), and the `visited` field -/// is used to avoid cycles. -pub struct TraitAliasExpander<'tcx> { - tcx: TyCtxt<'tcx>, - stack: Vec<TraitAliasExpansionInfo<'tcx>>, -} - -/// Stores information about the expansion of a trait via a path of zero or more trait aliases. -#[derive(Debug, Clone)] -pub struct TraitAliasExpansionInfo<'tcx> { - pub path: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>, -} - -impl<'tcx> TraitAliasExpansionInfo<'tcx> { - fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { - Self { path: smallvec![(trait_ref, span)] } - } - - /// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate - /// trait aliases. - pub fn label_with_exp_info( - &self, - diag: &mut Diag<'_>, - top_label: &'static str, - use_desc: &str, - ) { - diag.span_label(self.top().1, top_label); - if self.path.len() > 1 { - for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) { - diag.span_label(*sp, format!("referenced here ({use_desc})")); - } - } - if self.top().1 != self.bottom().1 { - // When the trait object is in a return type these two spans match, we don't want - // redundant labels. - diag.span_label( - self.bottom().1, - format!("trait alias used in trait object type ({use_desc})"), - ); - } - } - - pub fn trait_ref(&self) -> ty::PolyTraitRef<'tcx> { - self.top().0 - } - - pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { - self.path.last().unwrap() - } - - pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { - self.path.first().unwrap() - } - - fn clone_and_push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { - let mut path = self.path.clone(); - path.push((trait_ref, span)); - - Self { path } - } -} - +/// Return the trait and projection predicates that come from eagerly expanding the +/// trait aliases in the list of clauses. For each trait predicate, record a stack +/// of spans that trace from the user-written trait alias bound. For projection predicates, +/// just record the span of the projection itself. +/// +/// For trait aliases, we don't deduplicte the predicates, since we currently do not +/// consider duplicated traits as a single trait for the purposes of our "one trait principal" +/// restriction; however, for projections we do deduplicate them. +/// +/// ```rust,ignore (fails) +/// trait Bar {} +/// trait Foo = Bar + Bar; +/// +/// let not_object_safe: dyn Foo; // bad, two `Bar` principals. +/// ``` pub fn expand_trait_aliases<'tcx>( tcx: TyCtxt<'tcx>, - trait_refs: impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)>, -) -> TraitAliasExpander<'tcx> { - let items: Vec<_> = - trait_refs.map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)).collect(); - TraitAliasExpander { tcx, stack: items } -} - -impl<'tcx> TraitAliasExpander<'tcx> { - /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item` - /// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`. - /// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a - /// trait alias. - /// The return value indicates whether `item` should be yielded to the user. - fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool { - let tcx = self.tcx; - let trait_ref = item.trait_ref(); - let pred = trait_ref.upcast(tcx); - - debug!("expand_trait_aliases: trait_ref={:?}", trait_ref); - - // Don't recurse if this bound is not a trait alias. - let is_alias = tcx.is_trait_alias(trait_ref.def_id()); - if !is_alias { - return true; - } - - // Don't recurse if this trait alias is already on the stack for the DFS search. - let anon_pred = anonymize_predicate(tcx, pred); - if item - .path - .iter() - .rev() - .skip(1) - .any(|&(tr, _)| anonymize_predicate(tcx, tr.upcast(tcx)) == anon_pred) - { - return false; - } - - // Get components of trait alias. - let predicates = tcx.explicit_super_predicates_of(trait_ref.def_id()); - debug!(?predicates); - - let items = predicates.skip_binder().iter().rev().filter_map(|(pred, span)| { - pred.instantiate_supertrait(tcx, trait_ref) - .as_trait_clause() - .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span)) - }); - debug!("expand_trait_aliases: items={:?}", items.clone().collect::<Vec<_>>()); - - self.stack.extend(items); - - false - } -} - -impl<'tcx> Iterator for TraitAliasExpander<'tcx> { - type Item = TraitAliasExpansionInfo<'tcx>; - - fn size_hint(&self) -> (usize, Option<usize>) { - (self.stack.len(), None) - } - - fn next(&mut self) -> Option<TraitAliasExpansionInfo<'tcx>> { - while let Some(item) = self.stack.pop() { - if self.expand(&item) { - return Some(item); + clauses: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>, +) -> ( + Vec<(ty::PolyTraitPredicate<'tcx>, SmallVec<[Span; 1]>)>, + Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>, +) { + let mut trait_preds = vec![]; + let mut projection_preds = vec![]; + let mut seen_projection_preds = FxHashSet::default(); + + let mut queue: VecDeque<_> = clauses.into_iter().map(|(p, s)| (p, smallvec![s])).collect(); + + while let Some((clause, spans)) = queue.pop_front() { + match clause.kind().skip_binder() { + ty::ClauseKind::Trait(trait_pred) => { + if tcx.is_trait_alias(trait_pred.def_id()) { + queue.extend( + tcx.explicit_super_predicates_of(trait_pred.def_id()) + .iter_identity_copied() + .map(|(clause, span)| { + let mut spans = spans.clone(); + spans.push(span); + ( + clause.instantiate_supertrait( + tcx, + clause.kind().rebind(trait_pred.trait_ref), + ), + spans, + ) + }), + ); + } else { + trait_preds.push((clause.kind().rebind(trait_pred), spans)); + } } + ty::ClauseKind::Projection(projection_pred) => { + let projection_pred = clause.kind().rebind(projection_pred); + if !seen_projection_preds.insert(tcx.anonymize_bound_vars(projection_pred)) { + continue; + } + projection_preds.push((projection_pred, *spans.last().unwrap())); + } + ty::ClauseKind::RegionOutlives(..) + | ty::ClauseKind::TypeOutlives(..) + | ty::ClauseKind::ConstArgHasType(_, _) + | ty::ClauseKind::WellFormed(_) + | ty::ClauseKind::ConstEvaluatable(_) + | ty::ClauseKind::HostEffect(..) => {} } - None } + + (trait_preds, projection_preds) } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index 71088a598bb..5d041c2623a 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -6,13 +6,12 @@ use rustc_middle::query::Providers; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{Clause, FnSig, ParamEnvAnd, PolyFnSig, Ty, TyCtxt, TypeFoldable}; use rustc_trait_selection::infer::InferCtxtBuilderExt; -use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::query::type_op::ascribe_user_type::{ AscribeUserType, type_op_ascribe_user_type_with_span, }; use rustc_trait_selection::traits::query::type_op::normalize::Normalize; use rustc_trait_selection::traits::query::type_op::prove_predicate::ProvePredicate; -use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, ObligationCtxt}; +use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; pub(crate) fn provide(p: &mut Providers) { *p = Providers { @@ -43,10 +42,7 @@ where T: fmt::Debug + TypeFoldable<TyCtxt<'tcx>>, { let (param_env, Normalize { value }) = key.into_parts(); - let Normalized { value, obligations } = - ocx.infcx.at(&ObligationCause::dummy(), param_env).query_normalize(value)?; - ocx.register_obligations(obligations); - Ok(value) + Ok(ocx.normalize(&ObligationCause::dummy(), param_env, value)) } fn type_op_normalize_ty<'tcx>( diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 6ce9969aefe..b00585d2922 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -197,6 +197,7 @@ pub(crate) mod rustc { match err { LayoutError::Unknown(..) | LayoutError::ReferencesError(..) + | LayoutError::TooGeneric(..) | LayoutError::NormalizationFailure(..) => Self::UnknownLayout, LayoutError::SizeOverflow(..) => Self::SizeOverflow, LayoutError::Cycle(err) => Self::TypeError(*err), diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 8f9f66db1bd..f71b924b177 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxIndexSet; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -187,7 +187,7 @@ fn associated_types_for_impl_traits_in_associated_fn( } impl<'tcx> Visitor<'tcx> for RPITVisitor { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { if let hir::TyKind::OpaqueDef(opaq) = ty.kind && self.rpits.insert(opaq.def_id) { diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 17be0bd0ab9..a04c7536118 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -104,14 +104,12 @@ fn map_error<'tcx>( // This is sometimes not a compile error if there are trivially false where clauses. // See `tests/ui/layout/trivial-bounds-sized.rs` for an example. assert!(field.layout.is_unsized(), "invalid layout error {err:#?}"); - if !field.ty.is_sized(cx.tcx(), cx.typing_env) { - let guar = cx.tcx().dcx().delayed_bug(format!( + if cx.typing_env.param_env.caller_bounds().is_empty() { + cx.tcx().dcx().delayed_bug(format!( "encountered unexpected unsized field in layout of {ty:?}: {field:#?}" )); - LayoutError::ReferencesError(guar) - } else { - LayoutError::Unknown(ty) } + LayoutError::Unknown(ty) } LayoutCalculatorError::EmptyUnion => { // This is always a compile error. @@ -146,6 +144,35 @@ fn univariant_uninterned<'tcx>( cx.calc.univariant(fields, repr, kind).map_err(|err| map_error(cx, ty, err)) } +fn validate_const_with_value<'tcx>( + const_: ty::Const<'tcx>, + ty: Ty<'tcx>, + cx: &LayoutCx<'tcx>, +) -> Result<ty::Const<'tcx>, &'tcx LayoutError<'tcx>> { + match const_.kind() { + ty::ConstKind::Value(..) => Ok(const_), + ty::ConstKind::Error(guar) => { + return Err(error(cx, LayoutError::ReferencesError(guar))); + } + ty::ConstKind::Param(_) | ty::ConstKind::Expr(_) => { + if !const_.has_param() { + bug!("no generic type found in the type: {ty:?}"); + } + return Err(error(cx, LayoutError::TooGeneric(ty))); + } + ty::ConstKind::Unevaluated(_) => { + if !const_.has_param() { + return Err(error(cx, LayoutError::Unknown(ty))); + } else { + return Err(error(cx, LayoutError::TooGeneric(ty))); + } + } + ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => { + bug!("unexpected type: {ty:?}"); + } + } +} + fn layout_of_uncached<'tcx>( cx: &LayoutCx<'tcx>, ty: Ty<'tcx>, @@ -182,12 +209,13 @@ fn layout_of_uncached<'tcx>( &mut layout.backend_repr { if let Some(start) = start { - scalar.valid_range_mut().start = start - .try_to_bits(tcx, cx.typing_env) - .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; + scalar.valid_range_mut().start = + validate_const_with_value(start, ty, cx)? + .try_to_bits(tcx, cx.typing_env) + .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; } if let Some(end) = end { - let mut end = end + let mut end = validate_const_with_value(end, ty, cx)? .try_to_bits(tcx, cx.typing_env) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; if !include_end { @@ -319,17 +347,13 @@ fn layout_of_uncached<'tcx>( } // Arrays and slices. - ty::Array(element, mut count) => { - if count.has_aliases() { - count = tcx.normalize_erasing_regions(cx.typing_env, count); - if count.has_aliases() { - return Err(error(cx, LayoutError::Unknown(ty))); - } - } - - let count = count + ty::Array(element, count) => { + let count = validate_const_with_value(count, ty, cx)? + .to_valtree() + .0 .try_to_target_usize(tcx) .ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?; + let element = cx.layout_of(element)?; let size = element .size @@ -687,6 +711,9 @@ fn layout_of_uncached<'tcx>( // Types with no meaningful known layout. ty::Alias(..) => { + if ty.has_param() { + return Err(error(cx, LayoutError::TooGeneric(ty))); + } // NOTE(eddyb) `layout_of` query should've normalized these away, // if that was possible, so there's no reason to try again here. return Err(error(cx, LayoutError::Unknown(ty))); @@ -696,7 +723,11 @@ fn layout_of_uncached<'tcx>( bug!("Layout::compute: unexpected type `{}`", ty) } - ty::Placeholder(..) | ty::Param(_) => { + ty::Param(_) => { + return Err(error(cx, LayoutError::TooGeneric(ty))); + } + + ty::Placeholder(..) => { return Err(error(cx, LayoutError::Unknown(ty))); } }) diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs index 10b164eae02..74e4e035c87 100644 --- a/compiler/rustc_type_ir/src/ty_kind/closure.rs +++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs @@ -3,9 +3,10 @@ use std::ops::ControlFlow; use derive_where::derive_where; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; +use crate::data_structures::DelayedMap; use crate::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable, shift_region}; use crate::inherent::*; -use crate::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use crate::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use crate::{self as ty, Interner}; /// A closure can be modeled as a struct that looks like: @@ -471,6 +472,7 @@ impl<I: Interner> CoroutineClosureSignature<I> { interner: cx, region: env_region, debruijn: ty::INNERMOST, + cache: Default::default(), }); Ty::new_tup_from_iter( cx, @@ -498,6 +500,10 @@ struct FoldEscapingRegions<I: Interner> { interner: I, debruijn: ty::DebruijnIndex, region: I::Region, + + // Depends on `debruijn` because we may have types with regions of different + // debruijn depths depending on the binders we've entered. + cache: DelayedMap<(ty::DebruijnIndex, I::Ty), I::Ty>, } impl<I: Interner> TypeFolder<I> for FoldEscapingRegions<I> { @@ -505,6 +511,18 @@ impl<I: Interner> TypeFolder<I> for FoldEscapingRegions<I> { self.interner } + fn fold_ty(&mut self, t: I::Ty) -> I::Ty { + if !t.has_vars_bound_at_or_above(self.debruijn) { + t + } else if let Some(&t) = self.cache.get(&(self.debruijn, t)) { + t + } else { + let res = t.super_fold_with(self); + assert!(self.cache.insert((self.debruijn, t), res)); + res + } + } + fn fold_binder<T>(&mut self, t: ty::Binder<I, T>) -> ty::Binder<I, T> where T: TypeFoldable<I>, diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 71c3646498b..3213638afb2 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -47,6 +47,7 @@ use std::ops::ControlFlow; use rustc_ast_ir::visit::VisitorResult; use rustc_ast_ir::{try_visit, walk_visitable_list}; use rustc_index::{Idx, IndexVec}; +use smallvec::SmallVec; use thin_vec::ThinVec; use crate::data_structures::Lrc; @@ -192,6 +193,13 @@ impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for ThinVec<T> { } } +impl<I: Interner, T: TypeVisitable<I>, const N: usize> TypeVisitable<I> for SmallVec<[T; N]> { + fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result { + walk_visitable_list!(visitor, self.iter()); + V::Result::output() + } +} + // `TypeFoldable` isn't impl'd for `&[T]`. It doesn't make sense in the general // case, because we can't return a new slice. But note that there are a couple // of trivial impls of `TypeFoldable` for specific slice types elsewhere. |
