diff options
Diffstat (limited to 'compiler')
189 files changed, 3241 insertions, 2575 deletions
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 62995dfd2e2..a5f1cbc96da 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -19,6 +19,7 @@ #![feature(rustc_attrs)] #![cfg_attr(test, feature(test))] #![feature(strict_provenance)] +#![feature(ptr_const_cast)] use smallvec::SmallVec; @@ -27,7 +28,7 @@ use std::cell::{Cell, RefCell}; use std::cmp; use std::marker::{PhantomData, Send}; use std::mem::{self, MaybeUninit}; -use std::ptr; +use std::ptr::{self, NonNull}; use std::slice; #[inline(never)] @@ -55,15 +56,24 @@ pub struct TypedArena<T> { struct ArenaChunk<T = u8> { /// The raw storage for the arena chunk. - storage: Box<[MaybeUninit<T>]>, + storage: NonNull<[MaybeUninit<T>]>, /// The number of valid entries in the chunk. entries: usize, } +unsafe impl<#[may_dangle] T> Drop for ArenaChunk<T> { + fn drop(&mut self) { + unsafe { Box::from_raw(self.storage.as_mut()) }; + } +} + impl<T> ArenaChunk<T> { #[inline] unsafe fn new(capacity: usize) -> ArenaChunk<T> { - ArenaChunk { storage: Box::new_uninit_slice(capacity), entries: 0 } + ArenaChunk { + storage: NonNull::new(Box::into_raw(Box::new_uninit_slice(capacity))).unwrap(), + entries: 0, + } } /// Destroys this arena chunk. @@ -72,14 +82,15 @@ impl<T> ArenaChunk<T> { // The branch on needs_drop() is an -O1 performance optimization. // Without the branch, dropping TypedArena<u8> takes linear time. if mem::needs_drop::<T>() { - ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut self.storage[..len])); + let slice = &mut *(self.storage.as_mut()); + ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut slice[..len])); } } // Returns a pointer to the first allocated object. #[inline] fn start(&mut self) -> *mut T { - MaybeUninit::slice_as_mut_ptr(&mut self.storage) + self.storage.as_ptr() as *mut T } // Returns a pointer to the end of the allocated space. @@ -90,7 +101,7 @@ impl<T> ArenaChunk<T> { // A pointer as large as possible for zero-sized elements. ptr::invalid_mut(!0) } else { - self.start().add(self.storage.len()) + self.start().add((*self.storage.as_ptr()).len()) } } } @@ -274,7 +285,7 @@ impl<T> TypedArena<T> { // If the previous chunk's len is less than HUGE_PAGE // bytes, then this chunk will be least double the previous // chunk's size. - new_cap = last_chunk.storage.len().min(HUGE_PAGE / elem_size / 2); + new_cap = (*last_chunk.storage.as_ptr()).len().min(HUGE_PAGE / elem_size / 2); new_cap *= 2; } else { new_cap = PAGE / elem_size; @@ -382,7 +393,7 @@ impl DroplessArena { // If the previous chunk's len is less than HUGE_PAGE // bytes, then this chunk will be least double the previous // chunk's size. - new_cap = last_chunk.storage.len().min(HUGE_PAGE / 2); + new_cap = (*last_chunk.storage.as_ptr()).len().min(HUGE_PAGE / 2); new_cap *= 2; } else { new_cap = PAGE; diff --git a/compiler/rustc_arena/src/tests.rs b/compiler/rustc_arena/src/tests.rs index 911e577c1ed..ad61464343a 100644 --- a/compiler/rustc_arena/src/tests.rs +++ b/compiler/rustc_arena/src/tests.rs @@ -79,7 +79,11 @@ fn test_arena_alloc_nested() { #[test] pub fn test_copy() { let arena = TypedArena::default(); - for _ in 0..100000 { + #[cfg(not(miri))] + const N: usize = 100000; + #[cfg(miri)] + const N: usize = 1000; + for _ in 0..N { arena.alloc(Point { x: 1, y: 2, z: 3 }); } } @@ -106,7 +110,11 @@ struct Noncopy { #[test] pub fn test_noncopy() { let arena = TypedArena::default(); - for _ in 0..100000 { + #[cfg(not(miri))] + const N: usize = 100000; + #[cfg(miri)] + const N: usize = 1000; + for _ in 0..N { arena.alloc(Noncopy { string: "hello world".to_string(), array: vec![1, 2, 3, 4, 5] }); } } @@ -114,7 +122,11 @@ pub fn test_noncopy() { #[test] pub fn test_typed_arena_zero_sized() { let arena = TypedArena::default(); - for _ in 0..100000 { + #[cfg(not(miri))] + const N: usize = 100000; + #[cfg(miri)] + const N: usize = 1000; + for _ in 0..N { arena.alloc(()); } } @@ -124,7 +136,11 @@ pub fn test_typed_arena_clear() { let mut arena = TypedArena::default(); for _ in 0..10 { arena.clear(); - for _ in 0..10000 { + #[cfg(not(miri))] + const N: usize = 10000; + #[cfg(miri)] + const N: usize = 100; + for _ in 0..N { arena.alloc(Point { x: 1, y: 2, z: 3 }); } } diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 2820d5e6e0c..f705d004422 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2036,6 +2036,14 @@ impl TyKind { pub fn is_unit(&self) -> bool { matches!(self, TyKind::Tup(tys) if tys.is_empty()) } + + pub fn is_simple_path(&self) -> Option<Symbol> { + if let TyKind::Path(None, Path { segments, .. }) = &self && segments.len() == 1 { + Some(segments[0].ident.name) + } else { + None + } + } } /// Syntax used to declare a trait object. diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index aab9b90e4b7..0e395d70335 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -24,10 +24,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> &'hir hir::InlineAsm<'hir> { // Rustdoc needs to support asm! from foreign architectures: don't try // lowering the register constraints in this case. - let asm_arch = if self.sess.opts.actually_rustdoc { None } else { self.sess.asm_arch }; - if asm_arch.is_none() && !self.sess.opts.actually_rustdoc { - struct_span_err!(self.sess, sp, E0472, "inline assembly is unsupported on this target") - .emit(); + let asm_arch = + if self.tcx.sess.opts.actually_rustdoc { None } else { self.tcx.sess.asm_arch }; + if asm_arch.is_none() && !self.tcx.sess.opts.actually_rustdoc { + struct_span_err!( + self.tcx.sess, + sp, + E0472, + "inline assembly is unsupported on this target" + ) + .emit(); } if let Some(asm_arch) = asm_arch { // Inline assembly is currently only stable for these architectures. @@ -40,9 +46,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { | asm::InlineAsmArch::RiscV32 | asm::InlineAsmArch::RiscV64 ); - if !is_stable && !self.sess.features_untracked().asm_experimental_arch { + if !is_stable && !self.tcx.features().asm_experimental_arch { feature_err( - &self.sess.parse_sess, + &self.tcx.sess.parse_sess, sym::asm_experimental_arch, sp, "inline assembly is not stable yet on this architecture", @@ -52,17 +58,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } if asm.options.contains(InlineAsmOptions::ATT_SYNTAX) && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64)) - && !self.sess.opts.actually_rustdoc + && !self.tcx.sess.opts.actually_rustdoc { - self.sess + self.tcx + .sess .struct_span_err(sp, "the `att_syntax` option is only supported on x86") .emit(); } - if asm.options.contains(InlineAsmOptions::MAY_UNWIND) - && !self.sess.features_untracked().asm_unwind - { + if asm.options.contains(InlineAsmOptions::MAY_UNWIND) && !self.tcx.features().asm_unwind { feature_err( - &self.sess.parse_sess, + &self.tcx.sess.parse_sess, sym::asm_unwind, sp, "the `may_unwind` option is unstable", @@ -73,12 +78,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let mut clobber_abis = FxHashMap::default(); if let Some(asm_arch) = asm_arch { for (abi_name, abi_span) in &asm.clobber_abis { - match asm::InlineAsmClobberAbi::parse(asm_arch, &self.sess.target, *abi_name) { + match asm::InlineAsmClobberAbi::parse(asm_arch, &self.tcx.sess.target, *abi_name) { Ok(abi) => { // If the abi was already in the list, emit an error match clobber_abis.get(&abi) { Some((prev_name, prev_sp)) => { - let mut err = self.sess.struct_span_err( + let mut err = self.tcx.sess.struct_span_err( *abi_span, &format!("`{}` ABI specified multiple times", prev_name), ); @@ -86,7 +91,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Multiple different abi names may actually be the same ABI // If the specified ABIs are not the same name, alert the user that they resolve to the same ABI - let source_map = self.sess.source_map(); + let source_map = self.tcx.sess.source_map(); if source_map.span_to_snippet(*prev_sp) != source_map.span_to_snippet(*abi_span) { @@ -101,7 +106,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } Err(&[]) => { - self.sess + self.tcx + .sess .struct_span_err( *abi_span, "`clobber_abi` is not supported on this target", @@ -109,8 +115,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .emit(); } Err(supported_abis) => { - let mut err = - self.sess.struct_span_err(*abi_span, "invalid ABI for `clobber_abi`"); + let mut err = self + .tcx + .sess + .struct_span_err(*abi_span, "invalid ABI for `clobber_abi`"); let mut abis = format!("`{}`", supported_abis[0]); for m in &supported_abis[1..] { let _ = write!(abis, ", `{}`", m); @@ -128,7 +136,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Lower operands to HIR. We use dummy register classes if an error // occurs during lowering because we still need to be able to produce a // valid HIR. - let sess = self.sess; + let sess = self.tcx.sess; let mut operands: Vec<_> = asm .operands .iter() @@ -184,9 +192,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } InlineAsmOperand::Const { ref anon_const } => { - if !self.sess.features_untracked().asm_const { + if !self.tcx.features().asm_const { feature_err( - &self.sess.parse_sess, + &sess.parse_sess, sym::asm_const, *op_sp, "const operands for inline assembly are unstable", @@ -198,9 +206,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } InlineAsmOperand::Sym { ref sym } => { - if !self.sess.features_untracked().asm_sym { + if !self.tcx.features().asm_sym { feature_err( - &self.sess.parse_sess, + &sess.parse_sess, sym::asm_sym, *op_sp, "sym operands for inline assembly are unstable", diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 3a7e0a70585..9444fffc331 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -159,9 +159,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span, kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)), }); - if !self.sess.features_untracked().let_else { + if !self.tcx.features().let_else { feature_err( - &self.sess.parse_sess, + &self.tcx.sess.parse_sess, sym::let_else, local.span, "`let...else` statements are unstable", diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 3babe73030a..9e02e7ed3b9 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -46,7 +46,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let hir_id = self.lower_node_id(e.id); return hir::Expr { hir_id, kind, span: self.lower_span(e.span) }; } else { - self.sess + self.tcx.sess .struct_span_err( e.span, "#[rustc_box] requires precisely one argument \ @@ -207,8 +207,8 @@ impl<'hir> LoweringContext<'_, 'hir> { self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims) } ExprKind::Underscore => { - self.sess - .struct_span_err( + self.tcx + .sess.struct_span_err( e.span, "in expressions, `_` can only be used on the left-hand side of an assignment", ) @@ -245,7 +245,8 @@ impl<'hir> LoweringContext<'_, 'hir> { let rest = match &se.rest { StructRest::Base(e) => Some(self.lower_expr(e)), StructRest::Rest(sp) => { - self.sess + self.tcx + .sess .struct_span_err(*sp, "base expression required after `..`") .span_label(*sp, "add a base expression here") .emit(); @@ -474,7 +475,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { let try_span = this.mark_span_with_reason( DesugaringKind::TryBlock, - this.sess.source_map().end_point(body.span), + this.tcx.sess.source_map().end_point(body.span), this.allow_try_trait.clone(), ); @@ -653,7 +654,7 @@ impl<'hir> LoweringContext<'_, 'hir> { Some(hir::GeneratorKind::Async(_)) => {} Some(hir::GeneratorKind::Gen) | None => { let mut err = struct_span_err!( - self.sess, + self.tcx.sess, dot_await_span, E0728, "`await` is only allowed inside `async` functions and blocks" @@ -878,7 +879,7 @@ impl<'hir> LoweringContext<'_, 'hir> { Some(hir::GeneratorKind::Gen) => { if decl.inputs.len() > 1 { struct_span_err!( - self.sess, + self.tcx.sess, fn_decl_span, E0628, "too many parameters for a generator (expected 0 or 1 parameters)" @@ -892,8 +893,13 @@ impl<'hir> LoweringContext<'_, 'hir> { } None => { if movability == Movability::Static { - struct_span_err!(self.sess, fn_decl_span, E0697, "closures cannot be static") - .emit(); + struct_span_err!( + self.tcx.sess, + fn_decl_span, + E0697, + "closures cannot be static" + ) + .emit(); } None } @@ -916,7 +922,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // FIXME(cramertj): allow `async` non-`move` closures with arguments. if capture_clause == CaptureBy::Ref && !decl.inputs.is_empty() { struct_span_err!( - this.sess, + this.tcx.sess, fn_decl_span, E0708, "`async` non-`move` closures with parameters are not currently supported", @@ -1163,7 +1169,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let fields_omitted = match &se.rest { StructRest::Base(e) => { - self.sess + self.tcx + .sess .struct_span_err( e.span, "functional record updates are not allowed in destructuring \ @@ -1371,7 +1378,7 @@ impl<'hir> LoweringContext<'_, 'hir> { Some(hir::GeneratorKind::Gen) => {} Some(hir::GeneratorKind::Async(_)) => { struct_span_err!( - self.sess, + self.tcx.sess, span, E0727, "`async` generators are not yet supported" @@ -1516,7 +1523,7 @@ impl<'hir> LoweringContext<'_, 'hir> { span, self.allow_try_trait.clone(), ); - let try_span = self.sess.source_map().end_point(span); + let try_span = self.tcx.sess.source_map().end_point(span); let try_span = self.mark_span_with_reason( DesugaringKind::QuestionMark, try_span, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 112197c6e39..7da49143b46 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1,7 +1,6 @@ use super::ResolverAstLoweringExt; use super::{AstOwner, ImplTraitContext, ImplTraitPosition}; -use super::{LoweringContext, ParamMode}; -use crate::{Arena, FnDeclKind}; +use super::{FnDeclKind, LoweringContext, ParamMode}; use rustc_ast::ptr::P; use rustc_ast::visit::AssocCtxt; @@ -12,12 +11,9 @@ use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; -use rustc_hir::definitions::Definitions; use rustc_hir::PredicateOrigin; use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::ty::{ResolverAstLowering, ResolverOutputs}; -use rustc_session::cstore::CrateStoreDyn; -use rustc_session::Session; +use rustc_middle::ty::{DefIdTree, ResolverAstLowering, TyCtxt}; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; @@ -27,12 +23,8 @@ use smallvec::{smallvec, SmallVec}; use std::iter; pub(super) struct ItemLowerer<'a, 'hir> { - pub(super) sess: &'a Session, - pub(super) definitions: &'a mut Definitions, - pub(super) cstore: &'a CrateStoreDyn, - pub(super) resolutions: &'a ResolverOutputs, + pub(super) tcx: TyCtxt<'hir>, pub(super) resolver: &'a mut ResolverAstLowering, - pub(super) arena: &'hir Arena<'hir>, pub(super) ast_index: &'a IndexVec<LocalDefId, AstOwner<'a>>, pub(super) owners: &'a mut IndexVec<LocalDefId, hir::MaybeOwner<&'hir hir::OwnerInfo<'hir>>>, } @@ -65,12 +57,9 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { ) { let mut lctx = LoweringContext { // Pseudo-globals. - sess: &self.sess, - definitions: self.definitions, - cstore: self.cstore, - resolutions: self.resolutions, + tcx: self.tcx, resolver: self.resolver, - arena: self.arena, + arena: self.tcx.hir_arena, // HirId handling. bodies: Vec::new(), @@ -144,12 +133,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { fn lower_assoc_item(&mut self, item: &AssocItem, ctxt: AssocCtxt) { let def_id = self.resolver.node_id_to_def_id[&item.id]; - let parent_id = { - let parent = self.definitions.def_key(def_id).parent; - let local_def_index = parent.unwrap(); - LocalDefId { local_def_index } - }; - + let parent_id = self.tcx.local_parent(def_id); let parent_hir = self.lower_node(parent_id).unwrap(); self.with_lctx(item.id, |lctx| { // Evaluate with the lifetimes in `params` in-scope. @@ -1278,7 +1262,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn error_on_invalid_abi(&self, abi: StrLit) { - struct_span_err!(self.sess, abi.span, E0703, "invalid ABI: found `{}`", abi.symbol) + struct_span_err!(self.tcx.sess, abi.span, E0703, "invalid ABI: found `{}`", abi.symbol) .span_label(abi.span, "invalid ABI") .help(&format!("valid ABIs: {}", abi::all_names().join(", "))) .emit(); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index e8b92eaad5c..2dcbd0782ef 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -49,18 +49,15 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; -use rustc_errors::{struct_span_err, Applicability}; +use rustc_errors::{struct_span_err, Applicability, Handler}; use rustc_hir as hir; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; -use rustc_hir::definitions::{DefPathData, Definitions}; +use rustc_hir::definitions::DefPathData; use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate}; use rustc_index::vec::{Idx, IndexVec}; -use rustc_middle::ty::{ResolverAstLowering, ResolverOutputs}; -use rustc_query_system::ich::StableHashingContext; -use rustc_session::cstore::CrateStoreDyn; +use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; use rustc_session::parse::feature_err; -use rustc_session::Session; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -83,19 +80,12 @@ mod item; mod pat; mod path; -rustc_hir::arena_types!(rustc_arena::declare_arena); - -struct LoweringContext<'a, 'hir: 'a> { - /// Used to assign IDs to HIR nodes that do not directly correspond to AST nodes. - sess: &'a Session, - - definitions: &'a mut Definitions, - cstore: &'a CrateStoreDyn, - resolutions: &'a ResolverOutputs, +struct LoweringContext<'a, 'hir> { + tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering, /// Used to allocate HIR nodes. - arena: &'hir Arena<'hir>, + arena: &'hir hir::Arena<'hir>, /// Bodies inside the owner being lowered. bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>, @@ -391,61 +381,58 @@ fn index_crate<'a>( /// Compute the hash for the HIR of the full crate. /// This hash will then be part of the crate_hash which is stored in the metadata. fn compute_hir_hash( - sess: &Session, - definitions: &Definitions, - cstore: &CrateStoreDyn, - resolver: &ResolverOutputs, + tcx: TyCtxt<'_>, owners: &IndexVec<LocalDefId, hir::MaybeOwner<&hir::OwnerInfo<'_>>>, ) -> Fingerprint { let mut hir_body_nodes: Vec<_> = owners .iter_enumerated() .filter_map(|(def_id, info)| { let info = info.as_owner()?; - let def_path_hash = definitions.def_path_hash(def_id); + let def_path_hash = tcx.hir().def_path_hash(def_id); Some((def_path_hash, info)) }) .collect(); hir_body_nodes.sort_unstable_by_key(|bn| bn.0); - let mut stable_hasher = StableHasher::new(); - let mut hcx = StableHashingContext::new(sess, definitions, cstore, &resolver.source_span); - hir_body_nodes.hash_stable(&mut hcx, &mut stable_hasher); - stable_hasher.finish() + tcx.with_stable_hashing_context(|mut hcx| { + let mut stable_hasher = StableHasher::new(); + hir_body_nodes.hash_stable(&mut hcx, &mut stable_hasher); + stable_hasher.finish() + }) } -pub fn lower_crate<'hir>( - sess: &Session, - krate: &Crate, - definitions: &mut Definitions, - cstore: &CrateStoreDyn, - resolutions: &ResolverOutputs, - mut resolver: ResolverAstLowering, - arena: &'hir Arena<'hir>, -) -> &'hir hir::Crate<'hir> { - let _prof_timer = sess.prof.verbose_generic_activity("hir_lowering"); +pub fn lower_to_hir<'hir>(tcx: TyCtxt<'hir>, (): ()) -> hir::Crate<'hir> { + let sess = tcx.sess; + let krate = tcx.untracked_crate.steal(); + let mut resolver = tcx.resolver_for_lowering(()).steal(); - let ast_index = index_crate(&resolver.node_id_to_def_id, krate); - - let mut owners = - IndexVec::from_fn_n(|_| hir::MaybeOwner::Phantom, definitions.def_index_count()); + let ast_index = index_crate(&resolver.node_id_to_def_id, &krate); + let mut owners = IndexVec::from_fn_n( + |_| hir::MaybeOwner::Phantom, + tcx.definitions_untracked().def_index_count(), + ); for def_id in ast_index.indices() { item::ItemLowerer { - sess, - definitions, - cstore, - resolutions, + tcx, resolver: &mut resolver, - arena, ast_index: &ast_index, owners: &mut owners, } .lower_node(def_id); } - let hir_hash = compute_hir_hash(sess, definitions, cstore, resolutions, &owners); - let krate = hir::Crate { owners, hir_hash }; - arena.alloc(krate) + // Drop AST to free memory + std::mem::drop(ast_index); + sess.time("drop_ast", || std::mem::drop(krate)); + + // Discard hygiene data, which isn't required after lowering to HIR. + if !sess.opts.debugging_opts.keep_hygiene_data { + rustc_span::hygiene::clear_syntax_context_map(); + } + + let hir_hash = compute_hir_hash(tcx, &owners); + hir::Crate { owners, hir_hash } } #[derive(Copy, Clone, PartialEq, Debug)] @@ -464,38 +451,25 @@ enum ParenthesizedGenericArgs { } impl<'a, 'hir> LoweringContext<'a, 'hir> { - fn create_stable_hashing_context(&self) -> StableHashingContext<'_> { - StableHashingContext::new( - self.sess, - self.definitions, - self.cstore, - &self.resolutions.source_span, - ) - } - fn create_def( &mut self, parent: LocalDefId, node_id: ast::NodeId, data: DefPathData, ) -> LocalDefId { + debug_assert_ne!(node_id, ast::DUMMY_NODE_ID); assert!( self.opt_local_def_id(node_id).is_none(), "adding a def'n for node-id {:?} and data {:?} but a previous def'n exists: {:?}", node_id, data, - self.definitions.def_key(self.local_def_id(node_id)), + self.tcx.hir().def_key(self.local_def_id(node_id)), ); - let def_id = self.definitions.create_def(parent, data); + let def_id = self.tcx.create_def(parent, data); - // Some things for which we allocate `LocalDefId`s don't correspond to - // anything in the AST, so they don't have a `NodeId`. For these cases - // we don't need a mapping from `NodeId` to `LocalDefId`. - if node_id != ast::DUMMY_NODE_ID { - debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id); - self.resolver.node_id_to_def_id.insert(node_id, def_id); - } + debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id); + self.resolver.node_id_to_def_id.insert(node_id, def_id); def_id } @@ -515,6 +489,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.opt_local_def_id(node).unwrap_or_else(|| panic!("no entry for node id: `{:?}`", node)) } + /// Freshen the `LoweringContext` and ready it to lower a nested item. + /// The lowered item is registered into `self.children`. + /// + /// This function sets up `HirId` lowering infrastructure, + /// and stashes the shared mutable state to avoid pollution by the closure. #[instrument(level = "debug", skip(self, f))] fn with_hir_id_owner( &mut self, @@ -533,8 +512,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { std::mem::replace(&mut self.item_local_id_counter, hir::ItemLocalId::new(1)); let current_impl_trait_defs = std::mem::take(&mut self.impl_trait_defs); let current_impl_trait_bounds = std::mem::take(&mut self.impl_trait_bounds); - // Do not reset `next_node_id` and `node_id_to_def_id` as we want to refer to the - // subdefinitions' nodes. + + // Do not reset `next_node_id` and `node_id_to_def_id`: + // we want `f` to be able to refer to the `LocalDefId`s that the caller created. + // and the caller to refer to some of the subdefinitions' nodes' `LocalDefId`s. // Always allocate the first `HirId` for the owner itself. let _old = self.node_id_to_local_id.insert(owner, hir::ItemLocalId::new(0)); @@ -578,7 +559,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { bodies.sort_by_key(|(k, _)| *k); let bodies = SortedMap::from_presorted_elements(bodies); let (hash_including_bodies, hash_without_bodies) = self.hash_owner(node, &bodies); - let (nodes, parenting) = index::index_hir(self.sess, self.definitions, node, &bodies); + let (nodes, parenting) = + index::index_hir(self.tcx.sess, &*self.tcx.definitions_untracked(), node, &bodies); let nodes = hir::OwnerNodes { hash_including_bodies, hash_without_bodies, @@ -587,10 +569,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { local_id_to_def_id, }; let attrs = { - let mut hcx = self.create_stable_hashing_context(); - let mut stable_hasher = StableHasher::new(); - attrs.hash_stable(&mut hcx, &mut stable_hasher); - let hash = stable_hasher.finish(); + let hash = self.tcx.with_stable_hashing_context(|mut hcx| { + let mut stable_hasher = StableHasher::new(); + attrs.hash_stable(&mut hcx, &mut stable_hasher); + stable_hasher.finish() + }); hir::AttributeMap { map: attrs, hash } }; @@ -604,18 +587,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { node: hir::OwnerNode<'hir>, bodies: &SortedMap<hir::ItemLocalId, &'hir hir::Body<'hir>>, ) -> (Fingerprint, Fingerprint) { - let mut hcx = self.create_stable_hashing_context(); - let mut stable_hasher = StableHasher::new(); - hcx.with_hir_bodies(true, node.def_id(), bodies, |hcx| { - node.hash_stable(hcx, &mut stable_hasher) - }); - let hash_including_bodies = stable_hasher.finish(); - let mut stable_hasher = StableHasher::new(); - hcx.with_hir_bodies(false, node.def_id(), bodies, |hcx| { - node.hash_stable(hcx, &mut stable_hasher) - }); - let hash_without_bodies = stable_hasher.finish(); - (hash_including_bodies, hash_without_bodies) + self.tcx.with_stable_hashing_context(|mut hcx| { + let mut stable_hasher = StableHasher::new(); + hcx.with_hir_bodies(true, node.def_id(), bodies, |hcx| { + node.hash_stable(hcx, &mut stable_hasher) + }); + let hash_including_bodies = stable_hasher.finish(); + let mut stable_hasher = StableHasher::new(); + hcx.with_hir_bodies(false, node.def_id(), bodies, |hcx| { + node.hash_stable(hcx, &mut stable_hasher) + }); + let hash_without_bodies = stable_hasher.finish(); + (hash_including_bodies, hash_without_bodies) + }) } /// This method allocates a new `HirId` for the given `NodeId` and stores it in @@ -656,9 +640,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } + /// Generate a new `HirId` without a backing `NodeId`. fn next_id(&mut self) -> hir::HirId { - let node_id = self.next_node_id(); - self.lower_node_id(node_id) + let owner = self.current_hir_id_owner; + let local_id = self.item_local_id_counter; + assert_ne!(local_id, hir::ItemLocalId::new(0)); + self.item_local_id_counter.increment_by(1); + hir::HirId { owner, local_id } } #[instrument(level = "trace", skip(self))] @@ -691,8 +679,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.resolver.get_import_res(id).present_items() } - fn diagnostic(&self) -> &rustc_errors::Handler { - self.sess.diagnostic() + fn diagnostic(&self) -> &Handler { + self.tcx.sess.diagnostic() } /// Reuses the span but adds information like the kind of the desugaring and features that are @@ -703,18 +691,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { span: Span, allow_internal_unstable: Option<Lrc<[Symbol]>>, ) -> Span { - span.mark_with_reason( - allow_internal_unstable, - reason, - self.sess.edition(), - self.create_stable_hashing_context(), - ) + self.tcx.with_stable_hashing_context(|hcx| { + span.mark_with_reason(allow_internal_unstable, reason, self.tcx.sess.edition(), hcx) + }) } /// Intercept all spans entering HIR. /// Mark a span as relative to the current owning item. fn lower_span(&self, span: Span) -> Span { - if self.sess.opts.debugging_opts.incremental_relative_spans { + if self.tcx.sess.opts.debugging_opts.incremental_relative_spans { span.with_parent(Some(self.current_hir_id_owner)) } else { // Do not make spans relative when not using incremental compilation. @@ -1061,7 +1046,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } fn emit_bad_parenthesized_trait_in_assoc_ty(&self, data: &ParenthesizedArgs) { - let mut err = self.sess.struct_span_err( + let mut err = self.tcx.sess.struct_span_err( data.span, "parenthesized generic arguments cannot be used in associated type constraints", ); @@ -1106,7 +1091,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(<)), ast::GenericArg::Type(ty) => { match ty.kind { - TyKind::Infer if self.sess.features_untracked().generic_arg_infer => { + 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), @@ -1203,7 +1188,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } else { self.next_node_id() }; - let span = self.sess.source_map().next_point(t.span.shrink_to_lo()); + let span = self.tcx.sess.source_map().next_point(t.span.shrink_to_lo()); Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id } }); let lifetime = self.lower_lifetime(®ion); @@ -1307,7 +1292,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } ImplTraitContext::Disallowed(position) => { let mut err = struct_span_err!( - self.sess, + self.tcx.sess, t.span, E0562, "`impl Trait` only allowed in function and inherent method return types, not in {}", @@ -1320,7 +1305,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } TyKind::MacCall(_) => panic!("`TyKind::MacCall` should have been expanded by now"), TyKind::CVarArgs => { - self.sess.delay_span_bug( + self.tcx.sess.delay_span_bug( t.span, "`TyKind::CVarArgs` should have been handled elsewhere", ); @@ -1925,7 +1910,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir_id, name, span: self.lower_span(param.span()), - pure_wrt_drop: self.sess.contains_name(¶m.attrs, sym::may_dangle), + pure_wrt_drop: self.tcx.sess.contains_name(¶m.attrs, sym::may_dangle), kind, colon_span: param.colon_span.map(|s| self.lower_span(s)), } @@ -2067,11 +2052,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_array_length(&mut self, c: &AnonConst) -> hir::ArrayLen { match c.value.kind { ExprKind::Underscore => { - if self.sess.features_untracked().generic_arg_infer { + if self.tcx.features().generic_arg_infer { hir::ArrayLen::Infer(self.lower_node_id(c.id), c.value.span) } else { feature_err( - &self.sess.parse_sess, + &self.tcx.sess.parse_sess, sym::generic_arg_infer, c.value.span, "using `_` for array lengths is unstable", diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 52ba5daf014..393be3b454c 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -133,7 +133,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // We should've returned in the for loop above. - self.sess.diagnostic().span_bug( + self.diagnostic().span_bug( p.span, &format!( "lower_qpath: no final extension segment in {}..{}", @@ -193,7 +193,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { GenericArgs::Parenthesized(ref data) => match parenthesized_generic_args { ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data), ParenthesizedGenericArgs::Err => { - let mut err = struct_span_err!(self.sess, data.span, E0214, "{}", msg); + let mut err = struct_span_err!(self.tcx.sess, data.span, E0214, "{}", msg); err.span_label(data.span, "only `Fn` traits may use parentheses"); // Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait<params...>` if !data.inputs.is_empty() { diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index a5d9a2bc597..08ea00d71ef 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -33,22 +33,6 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { err } - pub(crate) fn cannot_act_on_uninitialized_variable( - &self, - span: Span, - verb: &str, - desc: &str, - ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { - struct_span_err!( - self, - span, - E0381, - "{} of possibly-uninitialized variable: `{}`", - verb, - desc, - ) - } - pub(crate) fn cannot_mutably_borrow_multiply( &self, new_loan_span: Span, @@ -175,8 +159,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { self, new_loan_span, E0501, - "cannot borrow {}{} as {} because previous closure \ - requires unique access", + "cannot borrow {}{} as {} because previous closure requires unique access", desc_new, opt_via, kind_new, @@ -453,9 +436,8 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { self, closure_span, E0373, - "{} may outlive the current function, \ - but it borrows {}, \ - which is owned by the current function", + "{} may outlive the current function, but it borrows {}, which is owned by the current \ + function", closure_kind, borrowed_path, ); @@ -479,7 +461,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> { } #[rustc_lint_diagnostics] - fn struct_span_err_with_code<S: Into<MultiSpan>>( + pub(crate) fn struct_span_err_with_code<S: Into<MultiSpan>>( &self, sp: S, msg: impl Into<DiagnosticMessage>, diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 73c0bf16a1f..b9cfc3732dc 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2,9 +2,12 @@ use either::Either; use rustc_const_eval::util::CallKind; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{ + struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, +}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::ObligationCause; @@ -18,6 +21,7 @@ use rustc_middle::ty::{ self, subst::Subst, suggest_constraining_type_params, EarlyBinder, PredicateKind, Ty, }; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; +use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::sym; use rustc_span::{BytePos, Span}; use rustc_trait_selection::infer::InferCtxtExt; @@ -94,32 +98,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { return; } - let item_msg = - match self.describe_place_with_options(used_place, IncludingDowncast(true)) { - Some(name) => format!("`{}`", name), - None => "value".to_owned(), - }; - let mut err = self.cannot_act_on_uninitialized_variable( + let err = self.report_use_of_uninitialized( + mpi, + used_place, + moved_place, + desired_action, span, - desired_action.as_noun(), - &self - .describe_place_with_options(moved_place, IncludingDowncast(true)) - .unwrap_or_else(|| "_".to_owned()), + use_spans, ); - err.span_label(span, format!("use of possibly-uninitialized {}", item_msg)); - - use_spans.var_span_label_path_only( - &mut err, - format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), - ); - self.buffer_error(err); } else { if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) { if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) { debug!( - "report_use_of_moved_or_uninitialized place: error suppressed \ - mois={:?}", + "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}", move_out_indices ); return; @@ -326,6 +318,130 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } + fn report_use_of_uninitialized( + &self, + mpi: MovePathIndex, + used_place: PlaceRef<'tcx>, + moved_place: PlaceRef<'tcx>, + desired_action: InitializationRequiringAction, + span: Span, + use_spans: UseSpans<'tcx>, + ) -> DiagnosticBuilder<'cx, ErrorGuaranteed> { + // We need all statements in the body where the binding was assigned to to later find all + // the branching code paths where the binding *wasn't* assigned to. + let inits = &self.move_data.init_path_map[mpi]; + let move_path = &self.move_data.move_paths[mpi]; + let decl_span = self.body.local_decls[move_path.place.local].source_info.span; + let mut spans = vec![]; + for init_idx in inits { + let init = &self.move_data.inits[*init_idx]; + let span = init.span(&self.body); + if !span.is_dummy() { + spans.push(span); + } + } + + let (name, desc) = + match self.describe_place_with_options(moved_place, IncludingDowncast(true)) { + Some(name) => (format!("`{name}`"), format!("`{name}` ")), + None => ("the variable".to_string(), String::new()), + }; + let path = match self.describe_place_with_options(used_place, IncludingDowncast(true)) { + Some(name) => format!("`{name}`"), + None => "value".to_string(), + }; + + // We use the statements were the binding was initialized, and inspect the HIR to look + // for the branching codepaths that aren't covered, to point at them. + let hir_id = self.mir_hir_id(); + let map = self.infcx.tcx.hir(); + let body_id = map.body_owned_by(hir_id); + let body = map.body(body_id); + + let mut visitor = ConditionVisitor { spans: &spans, name: &name, errors: vec![] }; + visitor.visit_body(&body); + + let isnt_initialized = if let InitializationRequiringAction::PartialAssignment + | InitializationRequiringAction::Assignment = desired_action + { + // The same error is emitted for bindings that are *sometimes* initialized and the ones + // that are *partially* initialized by assigning to a field of an uninitialized + // binding. We differentiate between them for more accurate wording here. + "isn't fully initialized" + } else if spans + .iter() + .filter(|i| { + // We filter these to avoid misleading wording in cases like the following, + // where `x` has an `init`, but it is in the same place we're looking at: + // ``` + // let x; + // x += 1; + // ``` + !i.contains(span) + // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs` + && !visitor + .errors + .iter() + .map(|(sp, _)| *sp) + .any(|sp| span < sp && !sp.contains(span)) + }) + .count() + == 0 + { + "isn't initialized" + } else { + "is possibly-uninitialized" + }; + + let used = desired_action.as_general_verb_in_past_tense(); + let mut err = + struct_span_err!(self, span, E0381, "{used} binding {desc}{isnt_initialized}"); + use_spans.var_span_label_path_only( + &mut err, + format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()), + ); + + if let InitializationRequiringAction::PartialAssignment + | InitializationRequiringAction::Assignment = desired_action + { + err.help( + "partial initialization isn't supported, fully initialize the binding with a \ + default value and mutate it, or use `std::mem::MaybeUninit`", + ); + } + err.span_label(span, format!("{path} {used} here but it {isnt_initialized}")); + + let mut shown = false; + for (sp, label) in visitor.errors { + if sp < span && !sp.overlaps(span) { + // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention + // match arms coming after the primary span because they aren't relevant: + // ``` + // let x; + // match y { + // _ if { x = 2; true } => {} + // _ if { + // x; //~ ERROR + // false + // } => {} + // _ => {} // We don't want to point to this. + // }; + // ``` + err.span_label(sp, &label); + shown = true; + } + } + if !shown { + for sp in &spans { + if *sp < span && !sp.overlaps(span) { + err.span_label(*sp, "binding initialized here in some conditions"); + } + } + } + err.span_label(decl_span, "binding declared here but left uninitialized"); + err + } + fn suggest_borrow_fn_like( &self, err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, @@ -1628,7 +1744,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { location: Location, ) -> impl Iterator<Item = Location> + Captures<'tcx> + 'a { if location.statement_index == 0 { - let predecessors = body.predecessors()[location.block].to_vec(); + let predecessors = body.basic_blocks.predecessors()[location.block].to_vec(); Either::Left(predecessors.into_iter().map(move |bb| body.terminator_loc(bb))) } else { Either::Right(std::iter::once(Location { @@ -2448,3 +2564,142 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> { } } } + +/// Detect whether one of the provided spans is a statement nested within the top-most visited expr +struct ReferencedStatementsVisitor<'a>(&'a [Span], bool); + +impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> { + fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) { + match s.kind { + hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => { + self.1 = true; + } + _ => {} + } + } +} + +/// Given a set of spans representing statements initializing the relevant binding, visit all the +/// function expressions looking for branching code paths that *do not* initialize the binding. +struct ConditionVisitor<'b> { + spans: &'b [Span], + name: &'b str, + errors: Vec<(Span, String)>, +} + +impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + match ex.kind { + hir::ExprKind::If(cond, body, None) => { + // `if` expressions with no `else` that initialize the binding might be missing an + // `else` arm. + let mut v = ReferencedStatementsVisitor(self.spans, false); + v.visit_expr(body); + if v.1 { + self.errors.push(( + cond.span, + format!( + "if this `if` condition is `false`, {} is not initialized", + self.name, + ), + )); + self.errors.push(( + ex.span.shrink_to_hi(), + format!("an `else` arm might be missing here, initializing {}", self.name), + )); + } + } + hir::ExprKind::If(cond, body, Some(other)) => { + // `if` expressions where the binding is only initialized in one of the two arms + // might be missing a binding initialization. + let mut a = ReferencedStatementsVisitor(self.spans, false); + a.visit_expr(body); + let mut b = ReferencedStatementsVisitor(self.spans, false); + b.visit_expr(other); + match (a.1, b.1) { + (true, true) | (false, false) => {} + (true, false) => { + if other.span.is_desugaring(DesugaringKind::WhileLoop) { + self.errors.push(( + cond.span, + format!( + "if this condition isn't met and the `while` loop runs 0 \ + times, {} is not initialized", + self.name + ), + )); + } else { + self.errors.push(( + body.span.shrink_to_hi().until(other.span), + format!( + "if the `if` condition is `false` and this `else` arm is \ + executed, {} is not initialized", + self.name + ), + )); + } + } + (false, true) => { + self.errors.push(( + cond.span, + format!( + "if this condition is `true`, {} is not initialized", + self.name + ), + )); + } + } + } + hir::ExprKind::Match(e, arms, loop_desugar) => { + // If the binding is initialized in one of the match arms, then the other match + // arms might be missing an initialization. + let results: Vec<bool> = arms + .iter() + .map(|arm| { + let mut v = ReferencedStatementsVisitor(self.spans, false); + v.visit_arm(arm); + v.1 + }) + .collect(); + if results.iter().any(|x| *x) && !results.iter().all(|x| *x) { + for (arm, seen) in arms.iter().zip(results) { + if !seen { + if loop_desugar == hir::MatchSource::ForLoopDesugar { + self.errors.push(( + e.span, + format!( + "if the `for` loop runs 0 times, {} is not initialized ", + self.name + ), + )); + } else if let Some(guard) = &arm.guard { + self.errors.push(( + arm.pat.span.to(guard.body().span), + format!( + "if this pattern and condition are matched, {} is not \ + initialized", + self.name + ), + )); + } else { + self.errors.push(( + arm.pat.span, + format!( + "if this pattern is matched, {} is not initialized", + self.name + ), + )); + } + } + } + } + } + // FIXME: should we also account for binops, particularly `&&` and `||`? `try` should + // also be accounted for. For now it is fine, as if we don't find *any* relevant + // branching code paths, we point at the places where the binding *is* initialized for + // *some* context. + _ => {} + } + walk_expr(self, ex); + } +} diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 3c5dd32d281..8134e122662 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -861,7 +861,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let arg_pos = args .iter() .enumerate() - .filter(|(_, arg)| arg.span == self.body.span) + .filter(|(_, arg)| arg.hir_id == closure_id) .map(|(pos, _)| pos) .next(); let def_id = hir.local_def_id(item_id); @@ -903,9 +903,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { if let Some(span) = arg { err.span_label(span, "change this to accept `FnMut` instead of `Fn`"); err.span_label(func.span, "expects `Fn` instead of `FnMut`"); - if self.infcx.tcx.sess.source_map().is_multiline(self.body.span) { - err.span_label(self.body.span, "in this closure"); - } + err.span_label(self.body.span, "in this closure"); look_at_return = false; } } diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index 0425c53d9dc..721fd3e1c0f 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -26,7 +26,7 @@ pub(super) fn generate_invalidates<'tcx>( if let Some(all_facts) = all_facts { let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation"); - let dominators = body.dominators(); + let dominators = body.basic_blocks.dominators(); let mut ig = InvalidationGenerator { all_facts, borrow_set, diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 7d6f37340c2..2ed35062da1 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -334,7 +334,7 @@ fn do_mir_borrowck<'a, 'tcx>( }; } - let dominators = body.dominators(); + let dominators = body.basic_blocks.dominators(); let mut mbcx = MirBorrowckCtxt { infcx, @@ -907,6 +907,16 @@ impl InitializationRequiringAction { InitializationRequiringAction::PartialAssignment => "partially assigned", } } + + fn as_general_verb_in_past_tense(self) -> &'static str { + match self { + InitializationRequiringAction::Borrow + | InitializationRequiringAction::MatchOn + | InitializationRequiringAction::Use => "used", + InitializationRequiringAction::Assignment => "assigned", + InitializationRequiringAction::PartialAssignment => "partially assigned", + } + } } impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index d0e0203bf8c..ea3602e8a05 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -495,8 +495,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } - NllRegionVariableOrigin::RootEmptyRegion - | NllRegionVariableOrigin::Existential { .. } => { + NllRegionVariableOrigin::Existential { .. } => { // For existential, regions, nothing to do. } } @@ -1410,8 +1409,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.check_bound_universal_region(fr, placeholder, errors_buffer); } - NllRegionVariableOrigin::RootEmptyRegion - | NllRegionVariableOrigin::Existential { .. } => { + NllRegionVariableOrigin::Existential { .. } => { // nothing to check here } } @@ -1513,8 +1511,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.check_bound_universal_region(fr, placeholder, errors_buffer); } - NllRegionVariableOrigin::RootEmptyRegion - | NllRegionVariableOrigin::Existential { .. } => { + NllRegionVariableOrigin::Existential { .. } => { // nothing to check here } } @@ -1788,9 +1785,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { universe1.cannot_name(placeholder.universe) } - NllRegionVariableOrigin::RootEmptyRegion - | NllRegionVariableOrigin::FreeRegion - | NllRegionVariableOrigin::Existential { .. } => false, + NllRegionVariableOrigin::FreeRegion | NllRegionVariableOrigin::Existential { .. } => { + false + } } } @@ -2152,8 +2149,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { let blame_source = match from_region_origin { NllRegionVariableOrigin::FreeRegion | NllRegionVariableOrigin::Existential { from_forall: false } => true, - NllRegionVariableOrigin::RootEmptyRegion - | NllRegionVariableOrigin::Placeholder(_) + NllRegionVariableOrigin::Placeholder(_) | NllRegionVariableOrigin::Existential { from_forall: true } => false, }; diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index d129e845426..7c1fa28b8df 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -1,11 +1,20 @@ +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::vec_map::VecMap; use rustc_hir::def_id::DefId; use rustc_hir::OpaqueTyOrigin; +use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic; use rustc_infer::infer::InferCtxt; +use rustc_infer::infer::TyCtxtInferExt as _; +use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine}; +use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts}; +use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ - self, OpaqueHiddenType, OpaqueTypeKey, TyCtxt, TypeFoldable, TypeVisitable, + self, OpaqueHiddenType, OpaqueTypeKey, ToPredicate, Ty, TyCtxt, TypeFoldable, }; -use rustc_trait_selection::opaque_types::InferCtxtExt; +use rustc_span::Span; +use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::TraitEngineExt as _; use super::RegionInferenceContext; @@ -173,3 +182,474 @@ impl<'tcx> RegionInferenceContext<'tcx> { }) } } + +pub trait InferCtxtExt<'tcx> { + fn infer_opaque_definition_from_instantiation( + &self, + opaque_type_key: OpaqueTypeKey<'tcx>, + instantiated_ty: OpaqueHiddenType<'tcx>, + origin: OpaqueTyOrigin, + ) -> Ty<'tcx>; +} + +impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { + /// Given the fully resolved, instantiated type for an opaque + /// type, i.e., the value of an inference variable like C1 or C2 + /// (*), computes the "definition type" for an opaque type + /// definition -- that is, the inferred value of `Foo1<'x>` or + /// `Foo2<'x>` that we would conceptually use in its definition: + /// ```ignore (illustrative) + /// type Foo1<'x> = impl Bar<'x> = AAA; // <-- this type AAA + /// type Foo2<'x> = impl Bar<'x> = BBB; // <-- or this type BBB + /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } + /// ``` + /// Note that these values are defined in terms of a distinct set of + /// generic parameters (`'x` instead of `'a`) from C1 or C2. The main + /// purpose of this function is to do that translation. + /// + /// (*) C1 and C2 were introduced in the comments on + /// `register_member_constraints`. Read that comment for more context. + /// + /// # Parameters + /// + /// - `def_id`, the `impl Trait` type + /// - `substs`, the substs used to instantiate this opaque type + /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of + /// `opaque_defn.concrete_ty` + #[instrument(level = "debug", skip(self))] + fn infer_opaque_definition_from_instantiation( + &self, + opaque_type_key: OpaqueTypeKey<'tcx>, + instantiated_ty: OpaqueHiddenType<'tcx>, + origin: OpaqueTyOrigin, + ) -> Ty<'tcx> { + if self.is_tainted_by_errors() { + return self.tcx.ty_error(); + } + + let OpaqueTypeKey { def_id, substs } = opaque_type_key; + + // Use substs to build up a reverse map from regions to their + // identity mappings. This is necessary because of `impl + // Trait` lifetimes are computed by replacing existing + // lifetimes with 'static and remapping only those used in the + // `impl Trait` return type, resulting in the parameters + // shifting. + let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id); + debug!(?id_substs); + let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> = + substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect(); + debug!("map = {:#?}", map); + + // Convert the type from the function into a type valid outside + // the function, by replacing invalid regions with 'static, + // after producing an error for each of them. + let definition_ty = instantiated_ty.ty.fold_with(&mut ReverseMapper::new( + self.tcx, + def_id, + map, + instantiated_ty.ty, + instantiated_ty.span, + )); + debug!(?definition_ty); + + if !check_opaque_type_parameter_valid( + self.tcx, + opaque_type_key, + origin, + instantiated_ty.span, + ) { + return self.tcx.ty_error(); + } + + // Only check this for TAIT. RPIT already supports `src/test/ui/impl-trait/nested-return-type2.rs` + // on stable and we'd break that. + if let OpaqueTyOrigin::TyAlias = origin { + // This logic duplicates most of `check_opaque_meets_bounds`. + // FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely. + let param_env = self.tcx.param_env(def_id); + let body_id = self.tcx.local_def_id_to_hir_id(def_id.as_local().unwrap()); + self.tcx.infer_ctxt().enter(move |infcx| { + // Require the hidden type to be well-formed with only the generics of the opaque type. + // Defining use functions may have more bounds than the opaque type, which is ok, as long as the + // hidden type is well formed even without those bounds. + let predicate = + ty::Binder::dummy(ty::PredicateKind::WellFormed(definition_ty.into())) + .to_predicate(infcx.tcx); + let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx); + + // Require that the hidden type actually fulfills all the bounds of the opaque type, even without + // the bounds that the function supplies. + match infcx.register_hidden_type( + OpaqueTypeKey { def_id, substs: id_substs }, + ObligationCause::misc(instantiated_ty.span, body_id), + param_env, + definition_ty, + origin, + ) { + Ok(infer_ok) => { + for obligation in infer_ok.obligations { + fulfillment_cx.register_predicate_obligation(&infcx, obligation); + } + } + Err(err) => { + infcx + .report_mismatched_types( + &ObligationCause::misc(instantiated_ty.span, body_id), + self.tcx.mk_opaque(def_id, id_substs), + definition_ty, + err, + ) + .emit(); + } + } + + fulfillment_cx.register_predicate_obligation( + &infcx, + Obligation::misc(instantiated_ty.span, body_id, param_env, predicate), + ); + + // Check that all obligations are satisfied by the implementation's + // version. + let errors = fulfillment_cx.select_all_or_error(&infcx); + + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + + if errors.is_empty() { + definition_ty + } else { + infcx.report_fulfillment_errors(&errors, None, false); + self.tcx.ty_error() + } + }) + } else { + definition_ty + } + } +} + +fn check_opaque_type_parameter_valid( + tcx: TyCtxt<'_>, + opaque_type_key: OpaqueTypeKey<'_>, + origin: OpaqueTyOrigin, + span: Span, +) -> bool { + match origin { + // No need to check return position impl trait (RPIT) + // because for type and const parameters they are correct + // by construction: we convert + // + // fn foo<P0..Pn>() -> impl Trait + // + // into + // + // type Foo<P0...Pn> + // fn foo<P0..Pn>() -> Foo<P0...Pn>. + // + // For lifetime parameters we convert + // + // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm> + // + // into + // + // type foo::<'p0..'pn>::Foo<'q0..'qm> + // fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>. + // + // which would error here on all of the `'static` args. + OpaqueTyOrigin::FnReturn(..) | OpaqueTyOrigin::AsyncFn(..) => return true, + // Check these + OpaqueTyOrigin::TyAlias => {} + } + let opaque_generics = tcx.generics_of(opaque_type_key.def_id); + let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default(); + for (i, arg) in opaque_type_key.substs.iter().enumerate() { + let arg_is_param = match arg.unpack() { + GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), + GenericArgKind::Lifetime(lt) if lt.is_static() => { + tcx.sess + .struct_span_err(span, "non-defining opaque type use in defining scope") + .span_label( + tcx.def_span(opaque_generics.param_at(i, tcx).def_id), + "cannot use static lifetime; use a bound lifetime \ + instead or remove the lifetime parameter from the \ + opaque type", + ) + .emit(); + return false; + } + GenericArgKind::Lifetime(lt) => { + matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_)) + } + GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)), + }; + + if arg_is_param { + seen_params.entry(arg).or_default().push(i); + } else { + // Prevent `fn foo() -> Foo<u32>` from being defining. + let opaque_param = opaque_generics.param_at(i, tcx); + tcx.sess + .struct_span_err(span, "non-defining opaque type use in defining scope") + .span_note( + tcx.def_span(opaque_param.def_id), + &format!( + "used non-generic {} `{}` for generic parameter", + opaque_param.kind.descr(), + arg, + ), + ) + .emit(); + return false; + } + } + + for (_, indices) in seen_params { + if indices.len() > 1 { + let descr = opaque_generics.param_at(indices[0], tcx).kind.descr(); + let spans: Vec<_> = indices + .into_iter() + .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) + .collect(); + tcx.sess + .struct_span_err(span, "non-defining opaque type use in defining scope") + .span_note(spans, &format!("{} used multiple times", descr)) + .emit(); + return false; + } + } + true +} + +struct ReverseMapper<'tcx> { + tcx: TyCtxt<'tcx>, + + opaque_type_def_id: DefId, + map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, + map_missing_regions_to_empty: bool, + + /// initially `Some`, set to `None` once error has been reported + hidden_ty: Option<Ty<'tcx>>, + + /// Span of function being checked. + span: Span, +} + +impl<'tcx> ReverseMapper<'tcx> { + fn new( + tcx: TyCtxt<'tcx>, + opaque_type_def_id: DefId, + map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, + hidden_ty: Ty<'tcx>, + span: Span, + ) -> Self { + Self { + tcx, + opaque_type_def_id, + map, + map_missing_regions_to_empty: false, + hidden_ty: Some(hidden_ty), + span, + } + } + + fn fold_kind_mapping_missing_regions_to_empty( + &mut self, + kind: GenericArg<'tcx>, + ) -> GenericArg<'tcx> { + assert!(!self.map_missing_regions_to_empty); + self.map_missing_regions_to_empty = true; + let kind = kind.fold_with(self); + self.map_missing_regions_to_empty = false; + kind + } + + fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { + assert!(!self.map_missing_regions_to_empty); + kind.fold_with(self) + } +} + +impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + #[instrument(skip(self), level = "debug")] + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + // Ignore bound regions and `'static` regions that appear in the + // type, we only need to remap regions that reference lifetimes + // from the function declaration. + // This would ignore `'r` in a type like `for<'r> fn(&'r u32)`. + ty::ReLateBound(..) | ty::ReStatic => return r, + + // If regions have been erased (by writeback), don't try to unerase + // them. + ty::ReErased => return r, + + // The regions that we expect from borrow checking. + ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReEmpty(ty::UniverseIndex::ROOT) => {} + + ty::ReEmpty(_) | ty::RePlaceholder(_) | ty::ReVar(_) => { + // All of the regions in the type should either have been + // erased by writeback, or mapped back to named regions by + // borrow checking. + bug!("unexpected region kind in opaque type: {:?}", r); + } + } + + let generics = self.tcx().generics_of(self.opaque_type_def_id); + match self.map.get(&r.into()).map(|k| k.unpack()) { + Some(GenericArgKind::Lifetime(r1)) => r1, + Some(u) => panic!("region mapped to unexpected kind: {:?}", u), + None if self.map_missing_regions_to_empty => self.tcx.lifetimes.re_root_empty, + None if generics.parent.is_some() => { + if let Some(hidden_ty) = self.hidden_ty.take() { + unexpected_hidden_region_diagnostic( + self.tcx, + self.tcx.def_span(self.opaque_type_def_id), + hidden_ty, + r, + ) + .emit(); + } + self.tcx.lifetimes.re_root_empty + } + None => { + self.tcx + .sess + .struct_span_err(self.span, "non-defining opaque type use in defining scope") + .span_label( + self.span, + format!( + "lifetime `{}` is part of concrete type but not used in \ + parameter list of the `impl Trait` type alias", + r + ), + ) + .emit(); + + self.tcx().lifetimes.re_static + } + } + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + match *ty.kind() { + ty::Closure(def_id, substs) => { + // I am a horrible monster and I pray for death. When + // we encounter a closure here, it is always a closure + // from within the function that we are currently + // type-checking -- one that is now being encapsulated + // in an opaque type. Ideally, we would + // go through the types/lifetimes that it references + // and treat them just like we would any other type, + // which means we would error out if we find any + // reference to a type/region that is not in the + // "reverse map". + // + // **However,** in the case of closures, there is a + // somewhat subtle (read: hacky) consideration. The + // problem is that our closure types currently include + // all the lifetime parameters declared on the + // enclosing function, even if they are unused by the + // closure itself. We can't readily filter them out, + // so here we replace those values with `'empty`. This + // can't really make a difference to the rest of the + // compiler; those regions are ignored for the + // outlives relation, and hence don't affect trait + // selection or auto traits, and they are erased + // during codegen. + + let generics = self.tcx.generics_of(def_id); + let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| { + if index < generics.parent_count { + // Accommodate missing regions in the parent kinds... + self.fold_kind_mapping_missing_regions_to_empty(kind) + } else { + // ...but not elsewhere. + self.fold_kind_normally(kind) + } + })); + + self.tcx.mk_closure(def_id, substs) + } + + ty::Generator(def_id, substs, movability) => { + let generics = self.tcx.generics_of(def_id); + let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| { + if index < generics.parent_count { + // Accommodate missing regions in the parent kinds... + self.fold_kind_mapping_missing_regions_to_empty(kind) + } else { + // ...but not elsewhere. + self.fold_kind_normally(kind) + } + })); + + self.tcx.mk_generator(def_id, substs, movability) + } + + ty::Param(param) => { + // Look it up in the substitution list. + match self.map.get(&ty.into()).map(|k| k.unpack()) { + // Found it in the substitution list; replace with the parameter from the + // opaque type. + Some(GenericArgKind::Type(t1)) => t1, + Some(u) => panic!("type mapped to unexpected kind: {:?}", u), + None => { + debug!(?param, ?self.map); + self.tcx + .sess + .struct_span_err( + self.span, + &format!( + "type parameter `{}` is part of concrete type but not \ + used in parameter list for the `impl Trait` type alias", + ty + ), + ) + .emit(); + + self.tcx().ty_error() + } + } + } + + _ => ty.super_fold_with(self), + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + trace!("checking const {:?}", ct); + // Find a const parameter + match ct.kind() { + ty::ConstKind::Param(..) => { + // Look it up in the substitution list. + match self.map.get(&ct.into()).map(|k| k.unpack()) { + // Found it in the substitution list, replace with the parameter from the + // opaque type. + Some(GenericArgKind::Const(c1)) => c1, + Some(u) => panic!("const mapped to unexpected kind: {:?}", u), + None => { + self.tcx + .sess + .struct_span_err( + self.span, + &format!( + "const parameter `{}` is part of concrete type but not \ + used in parameter list for the `impl Trait` type alias", + ct + ), + ) + .emit(); + + self.tcx().const_error(ct.ty()) + } + } + } + + _ => ct, + } + } +} diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs index d4e61ec213b..3795378b568 100644 --- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs +++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs @@ -258,7 +258,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { let block = self.cx.elements.to_location(block_start).block; self.stack.extend( - self.cx.body.predecessors()[block] + self.cx.body.basic_blocks.predecessors()[block] .iter() .map(|&pred_bb| self.cx.body.terminator_loc(pred_bb)) .map(|pred_loc| self.cx.elements.point_from_location(pred_loc)), @@ -354,7 +354,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> { } let body = self.cx.body; - for &pred_block in body.predecessors()[block].iter() { + for &pred_block in body.basic_blocks.predecessors()[block].iter() { debug!("compute_drop_live_points_for_block: pred_block = {:?}", pred_block,); // Check whether the variable is (at least partially) diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index 89d84fcf09c..2a7713bc4df 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -503,7 +503,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> { let root_empty = self .infcx - .next_nll_region_var(NllRegionVariableOrigin::RootEmptyRegion) + .next_nll_region_var(NllRegionVariableOrigin::Existential { from_forall: true }) .to_region_vid(); UniversalRegions { diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 1c507678489..9cd72ed0c67 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -2,8 +2,8 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; -use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Expr, Generics, ItemKind, MetaItem, VariantData}; +use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData}; +use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Span; @@ -80,7 +80,7 @@ pub fn expand_deriving_clone( name: sym::clone, generics: Bounds::empty(), explicit_self: true, - args: Vec::new(), + nonself_args: Vec::new(), ret_ty: Self_, attributes: attrs, unify_fieldless_variants: false, @@ -98,22 +98,31 @@ fn cs_clone_simple( trait_span: Span, substr: &Substructure<'_>, is_union: bool, -) -> P<Expr> { +) -> BlockOrExpr { let mut stmts = Vec::new(); + let mut seen_type_names = FxHashSet::default(); let mut process_variant = |variant: &VariantData| { for field in variant.fields() { - // let _: AssertParamIsClone<FieldTy>; - super::assert_ty_bounds( - cx, - &mut stmts, - field.ty.clone(), - field.span, - &[sym::clone, sym::AssertParamIsClone], - ); + // This basic redundancy checking only prevents duplication of + // assertions like `AssertParamIsClone<Foo>` where the type is a + // simple name. That's enough to get a lot of cases, though. + if let Some(name) = field.ty.kind.is_simple_path() && !seen_type_names.insert(name) { + // Already produced an assertion for this type. + } else { + // let _: AssertParamIsClone<FieldTy>; + super::assert_ty_bounds( + cx, + &mut stmts, + field.ty.clone(), + field.span, + &[sym::clone, sym::AssertParamIsClone], + ); + } } }; if is_union { + // Just a single assertion for unions, that the union impls `Copy`. // let _: AssertParamIsCopy<Self>; let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper))); super::assert_ty_bounds( @@ -139,8 +148,7 @@ fn cs_clone_simple( ), } } - stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span)))); - cx.expr_block(cx.block(trait_span, stmts)) + BlockOrExpr::new_mixed(stmts, cx.expr_deref(trait_span, cx.expr_self(trait_span))) } fn cs_clone( @@ -148,12 +156,12 @@ fn cs_clone( cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, -) -> P<Expr> { +) -> BlockOrExpr { let ctor_path; let all_fields; let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]); - let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo<'_>| { - let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; + let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo| { + let args = vec![cx.expr_addr_of(field.span, field.self_expr.clone())]; cx.expr_call_global(field.span, fn_path.clone(), args) }; @@ -177,7 +185,7 @@ fn cs_clone( } } - match *vdata { + let expr = match *vdata { VariantData::Struct(..) => { let fields = all_fields .iter() @@ -201,5 +209,6 @@ fn cs_clone( cx.expr_call(trait_span, path, subcalls) } VariantData::Unit(..) => cx.expr_path(ctor_path), - } + }; + BlockOrExpr::new_expr(expr) } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index cb2ad283a19..4e798bf6acb 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -2,8 +2,8 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; -use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Expr, MetaItem}; +use rustc_ast::{self as ast, MetaItem}; +use rustc_data_structures::fx::FxHashSet; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; @@ -32,7 +32,7 @@ pub fn expand_deriving_eq( name: sym::assert_receiver_is_total_eq, generics: Bounds::empty(), explicit_self: true, - args: vec![], + nonself_args: vec![], ret_ty: Unit, attributes: attrs, unify_fieldless_variants: true, @@ -52,18 +52,26 @@ fn cs_total_eq_assert( cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, -) -> P<Expr> { +) -> BlockOrExpr { let mut stmts = Vec::new(); + let mut seen_type_names = FxHashSet::default(); let mut process_variant = |variant: &ast::VariantData| { for field in variant.fields() { - // let _: AssertParamIsEq<FieldTy>; - super::assert_ty_bounds( - cx, - &mut stmts, - field.ty.clone(), - field.span, - &[sym::cmp, sym::AssertParamIsEq], - ); + // This basic redundancy checking only prevents duplication of + // assertions like `AssertParamIsEq<Foo>` where the type is a + // simple name. That's enough to get a lot of cases, though. + if let Some(name) = field.ty.kind.is_simple_path() && !seen_type_names.insert(name) { + // Already produced an assertion for this type. + } else { + // let _: AssertParamIsEq<FieldTy>; + super::assert_ty_bounds( + cx, + &mut stmts, + field.ty.clone(), + field.span, + &[sym::cmp, sym::AssertParamIsEq], + ); + } } }; @@ -78,5 +86,5 @@ fn cs_total_eq_assert( } _ => cx.span_bug(trait_span, "unexpected substructure in `derive(Eq)`"), } - cx.expr_block(cx.block(trait_span, stmts)) + BlockOrExpr::new_stmts(stmts) } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index c7850cd4b4c..859e995356e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -2,8 +2,7 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; -use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Expr, MetaItem}; +use rustc_ast::MetaItem; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; @@ -28,7 +27,7 @@ pub fn expand_deriving_ord( name: sym::cmp, generics: Bounds::empty(), explicit_self: true, - args: vec![(self_ref(), sym::other)], + nonself_args: vec![(self_ref(), sym::other)], ret_ty: Path(path_std!(cmp::Ordering)), attributes: attrs, unify_fieldless_variants: true, @@ -40,72 +39,54 @@ pub fn expand_deriving_ord( trait_def.expand(cx, mitem, item, push) } -pub fn ordering_collapsed( - cx: &mut ExtCtxt<'_>, - span: Span, - self_arg_tags: &[Ident], -) -> P<ast::Expr> { - let lft = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[0])); - let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1])); - let fn_cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]); - cx.expr_call_global(span, fn_cmp_path, vec![lft, rgt]) -} - -pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> { +pub fn cs_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { let test_id = Ident::new(sym::cmp, span); - let equals_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); - + let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); let cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]); // Builds: // - // match ::std::cmp::Ord::cmp(&self_field1, &other_field1) { - // ::std::cmp::Ordering::Equal => - // match ::std::cmp::Ord::cmp(&self_field2, &other_field2) { - // ::std::cmp::Ordering::Equal => { - // ... + // match ::core::cmp::Ord::cmp(&self.x, &other.x) { + // ::std::cmp::Ordering::Equal => + // ::core::cmp::Ord::cmp(&self.y, &other.y), + // cmp => cmp, // } - // cmp => cmp - // }, - // cmp => cmp - // } - // - cs_fold( + let expr = cs_fold( // foldr nests the if-elses correctly, leaving the first field // as the outermost one, and the last as the innermost. false, - |cx, span, old, self_f, other_fs| { - // match new { - // ::std::cmp::Ordering::Equal => old, - // cmp => cmp - // } - - let new = { - let [other_f] = other_fs else { - cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`"); - }; - - let args = - vec![cx.expr_addr_of(span, self_f), cx.expr_addr_of(span, other_f.clone())]; - - cx.expr_call_global(span, cmp_path.clone(), args) - }; - - let eq_arm = cx.arm(span, cx.pat_path(span, equals_path.clone()), old); - let neq_arm = cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id)); - - cx.expr_match(span, new, vec![eq_arm, neq_arm]) - }, - cx.expr_path(equals_path.clone()), - Box::new(|cx, span, tag_tuple| { - if tag_tuple.len() != 2 { - cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`") - } else { - ordering_collapsed(cx, span, tag_tuple) - } - }), cx, span, substr, - ) + |cx, fold| match fold { + CsFold::Single(field) => { + let [other_expr] = &field.other_selflike_exprs[..] else { + cx.span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`"); + }; + let args = vec![ + cx.expr_addr_of(field.span, field.self_expr.clone()), + cx.expr_addr_of(field.span, other_expr.clone()), + ]; + cx.expr_call_global(field.span, cmp_path.clone(), args) + } + CsFold::Combine(span, expr1, expr2) => { + let eq_arm = cx.arm(span, cx.pat_path(span, equal_path.clone()), expr1); + let neq_arm = + cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id)); + cx.expr_match(span, expr2, vec![eq_arm, neq_arm]) + } + CsFold::Fieldless => cx.expr_path(equal_path.clone()), + CsFold::EnumNonMatching(span, tag_tuple) => { + if tag_tuple.len() != 2 { + cx.span_bug(span, "not exactly 2 arguments in `derive(Ord)`") + } else { + let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0])); + let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1])); + let fn_cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]); + cx.expr_call_global(span, fn_cmp_path, vec![lft, rgt]) + } + } + }, + ); + BlockOrExpr::new_expr(expr) } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index ca5ca29eb82..724c639984c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -2,8 +2,7 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::{path_local, path_std}; -use rustc_ast::ptr::P; -use rustc_ast::{BinOpKind, Expr, MetaItem}; +use rustc_ast::{BinOpKind, MetaItem}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -15,8 +14,6 @@ pub fn expand_deriving_partial_eq( item: &Annotatable, push: &mut dyn FnMut(Annotatable), ) { - // structures are equal if all fields are equal, and non equal, if - // any fields are not equal or if the enum variants are different fn cs_op( cx: &mut ExtCtxt<'_>, span: Span, @@ -24,41 +21,31 @@ pub fn expand_deriving_partial_eq( op: BinOpKind, combiner: BinOpKind, base: bool, - ) -> P<Expr> { - let op = |cx: &mut ExtCtxt<'_>, span: Span, self_f: P<Expr>, other_fs: &[P<Expr>]| { - let [other_f] = other_fs else { - cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`"); - }; - - cx.expr_binary(span, op, self_f, other_f.clone()) - }; - - cs_fold1( + ) -> BlockOrExpr { + let expr = cs_fold( true, // use foldl - |cx, span, subexpr, self_f, other_fs| { - let eq = op(cx, span, self_f, other_fs); - cx.expr_binary(span, combiner, subexpr, eq) - }, - |cx, args| { - match args { - Some((span, self_f, other_fs)) => { - // Special-case the base case to generate cleaner code. - op(cx, span, self_f, other_fs) - } - None => cx.expr_bool(span, base), - } - }, - Box::new(|cx, span, _| cx.expr_bool(span, !base)), cx, span, substr, - ) + |cx, fold| match fold { + CsFold::Single(field) => { + let [other_expr] = &field.other_selflike_exprs[..] else { + cx.span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`"); + }; + cx.expr_binary(field.span, op, field.self_expr.clone(), other_expr.clone()) + } + CsFold::Combine(span, expr1, expr2) => cx.expr_binary(span, combiner, expr1, expr2), + CsFold::Fieldless => cx.expr_bool(span, base), + CsFold::EnumNonMatching(span, _tag_tuple) => cx.expr_bool(span, !base), + }, + ); + BlockOrExpr::new_expr(expr) } - fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> { + fn cs_eq(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { cs_op(cx, span, substr, BinOpKind::Eq, BinOpKind::And, true) } - fn cs_ne(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> { + fn cs_ne(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { cs_op(cx, span, substr, BinOpKind::Ne, BinOpKind::Or, false) } @@ -70,7 +57,7 @@ pub fn expand_deriving_partial_eq( name: $name, generics: Bounds::empty(), explicit_self: true, - args: vec![(self_ref(), sym::other)], + nonself_args: vec![(self_ref(), sym::other)], ret_ty: Path(path_local!(bool)), attributes: attrs, unify_fieldless_variants: true, diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 07db82fee93..3f9843922da 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -2,8 +2,7 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::{path_std, pathvec_std}; -use rustc_ast::ptr::P; -use rustc_ast::{Expr, MetaItem}; +use rustc_ast::MetaItem; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; @@ -26,7 +25,7 @@ pub fn expand_deriving_partial_ord( name: sym::partial_cmp, generics: Bounds::empty(), explicit_self: true, - args: vec![(self_ref(), sym::other)], + nonself_args: vec![(self_ref(), sym::other)], ret_ty, attributes: attrs, unify_fieldless_variants: true, @@ -48,67 +47,56 @@ pub fn expand_deriving_partial_ord( trait_def.expand(cx, mitem, item, push) } -pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> { +pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { let test_id = Ident::new(sym::cmp, span); - let ordering = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); - let ordering_expr = cx.expr_path(ordering.clone()); - let equals_expr = cx.expr_some(span, ordering_expr); - + let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal])); let partial_cmp_path = cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]); // Builds: // - // match ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1) { - // ::std::option::Option::Some(::std::cmp::Ordering::Equal) => - // match ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2) { - // ::std::option::Option::Some(::std::cmp::Ordering::Equal) => { - // ... - // } - // cmp => cmp - // }, - // cmp => cmp + // match ::core::cmp::PartialOrd::partial_cmp(&self.x, &other.x) { + // ::core::option::Option::Some(::core::cmp::Ordering::Equal) => + // ::core::cmp::PartialOrd::partial_cmp(&self.y, &other.y), + // cmp => cmp, // } - // - cs_fold( + let expr = cs_fold( // foldr nests the if-elses correctly, leaving the first field // as the outermost one, and the last as the innermost. false, - |cx, span, old, self_f, other_fs| { - // match new { - // Some(::std::cmp::Ordering::Equal) => old, - // cmp => cmp - // } - - let new = { - let [other_f] = other_fs else { - cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"); - }; - - let args = - vec![cx.expr_addr_of(span, self_f), cx.expr_addr_of(span, other_f.clone())]; - - cx.expr_call_global(span, partial_cmp_path.clone(), args) - }; - - let eq_arm = cx.arm(span, cx.pat_some(span, cx.pat_path(span, ordering.clone())), old); - let neq_arm = cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id)); - - cx.expr_match(span, new, vec![eq_arm, neq_arm]) - }, - equals_expr, - Box::new(|cx, span, tag_tuple| { - if tag_tuple.len() != 2 { - cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") - } else { - let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0])); - let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1])); - let fn_partial_cmp_path = - cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]); - cx.expr_call_global(span, fn_partial_cmp_path, vec![lft, rgt]) - } - }), cx, span, substr, - ) + |cx, fold| match fold { + CsFold::Single(field) => { + let [other_expr] = &field.other_selflike_exprs[..] else { + cx.span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`"); + }; + let args = vec![ + cx.expr_addr_of(field.span, field.self_expr.clone()), + cx.expr_addr_of(field.span, other_expr.clone()), + ]; + cx.expr_call_global(field.span, partial_cmp_path.clone(), args) + } + CsFold::Combine(span, expr1, expr2) => { + let eq_arm = + cx.arm(span, cx.pat_some(span, cx.pat_path(span, equal_path.clone())), expr1); + let neq_arm = + cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id)); + cx.expr_match(span, expr2, vec![eq_arm, neq_arm]) + } + CsFold::Fieldless => cx.expr_some(span, cx.expr_path(equal_path.clone())), + CsFold::EnumNonMatching(span, tag_tuple) => { + if tag_tuple.len() != 2 { + cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`") + } else { + let lft = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[0])); + let rgt = cx.expr_addr_of(span, cx.expr_ident(span, tag_tuple[1])); + let fn_partial_cmp_path = + cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]); + cx.expr_call_global(span, fn_partial_cmp_path, vec![lft, rgt]) + } + } + }, + ); + BlockOrExpr::new_expr(expr) } diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index 1411c60c0bf..b99198054de 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -2,8 +2,7 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; -use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Expr, MetaItem}; +use rustc_ast::{self as ast, MetaItem}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; @@ -29,7 +28,7 @@ pub fn expand_deriving_debug( name: sym::fmt, generics: Bounds::empty(), explicit_self: true, - args: vec![(fmtr, sym::f)], + nonself_args: vec![(fmtr, sym::f)], ret_ty: Path(path_std!(fmt::Result)), attributes: Vec::new(), unify_fieldless_variants: false, @@ -42,7 +41,7 @@ pub fn expand_deriving_debug( trait_def.expand(cx, mitem, item, push) } -fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> P<Expr> { +fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr { let (ident, vdata, fields) = match substr.fields { Struct(vdata, fields) => (substr.type_ident, *vdata, fields), EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields), @@ -54,7 +53,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> // We want to make sure we have the ctxt set so that we can use unstable methods let span = cx.with_def_site_ctxt(span); let name = cx.expr_lit(span, ast::LitKind::Str(ident.name, ast::StrStyle::Cooked)); - let fmt = substr.nonself_args[0].clone(); + let fmt = substr.nonselflike_args[0].clone(); // Struct and tuples are similar enough that we use the same code for both, // with some extra pieces for structs due to the field names. @@ -74,7 +73,8 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> if fields.is_empty() { // Special case for no fields. let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]); - cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]) + let expr = cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]); + BlockOrExpr::new_expr(expr) } else if fields.len() <= CUTOFF { // Few enough fields that we can use a specific-length method. let debug = if is_struct { @@ -96,11 +96,12 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> args.push(name); } // Use double indirection to make sure this works for unsized types - let field = cx.expr_addr_of(field.span, field.self_.clone()); + let field = cx.expr_addr_of(field.span, field.self_expr.clone()); let field = cx.expr_addr_of(field.span, field); args.push(field); } - cx.expr_call_global(span, fn_path_debug, args) + let expr = cx.expr_call_global(span, fn_path_debug, args); + BlockOrExpr::new_expr(expr) } else { // Enough fields that we must use the any-length method. let mut name_exprs = Vec::with_capacity(fields.len()); @@ -115,7 +116,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> } // Use double indirection to make sure this works for unsized types - let value_ref = cx.expr_addr_of(field.span, field.self_.clone()); + let value_ref = cx.expr_addr_of(field.span, field.self_expr.clone()); value_exprs.push(cx.expr_addr_of(field.span, value_ref)); } @@ -176,8 +177,6 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_> stmts.push(names_let.unwrap()); } stmts.push(values_let); - stmts.push(cx.stmt_expr(expr)); - - cx.expr_block(cx.block(span, stmts)) + BlockOrExpr::new_mixed(stmts, expr) } } diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 16154fb4d03..d688143a2a5 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -36,7 +36,10 @@ pub fn expand_deriving_rustc_decodable( )], }, explicit_self: false, - args: vec![(Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut), sym::d)], + nonself_args: vec![( + Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut), + sym::d, + )], ret_ty: Path(Path::new_( pathvec_std!(result::Result), vec![ @@ -62,8 +65,8 @@ fn decodable_substructure( trait_span: Span, substr: &Substructure<'_>, krate: Symbol, -) -> P<Expr> { - let decoder = substr.nonself_args[0].clone(); +) -> BlockOrExpr { + let decoder = substr.nonselflike_args[0].clone(); let recurse = vec![ Ident::new(krate, trait_span), Ident::new(sym::Decodable, trait_span), @@ -74,7 +77,7 @@ fn decodable_substructure( let blkarg = Ident::new(sym::_d, trait_span); let blkdecoder = cx.expr_ident(trait_span, blkarg); - match *substr.fields { + let expr = match *substr.fields { StaticStruct(_, ref summary) => { let nfields = match *summary { Unnamed(ref fields, _) => fields.len(), @@ -173,7 +176,8 @@ fn decodable_substructure( ) } _ => cx.bug("expected StaticEnum or StaticStruct in derive(Decodable)"), - } + }; + BlockOrExpr::new_expr(expr) } /// Creates a decoder for a single enum variant/struct: diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index d41b25343b0..5177690917f 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -1,11 +1,10 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; -use rustc_ast::ptr::P; +use rustc_ast as ast; use rustc_ast::walk_list; use rustc_ast::EnumDef; use rustc_ast::VariantData; -use rustc_ast::{Expr, MetaItem}; use rustc_errors::Applicability; use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt}; use rustc_span::symbol::Ident; @@ -16,7 +15,7 @@ use smallvec::SmallVec; pub fn expand_deriving_default( cx: &mut ExtCtxt<'_>, span: Span, - mitem: &MetaItem, + mitem: &ast::MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), ) { @@ -35,7 +34,7 @@ pub fn expand_deriving_default( name: kw::Default, generics: Bounds::empty(), explicit_self: false, - args: Vec::new(), + nonself_args: Vec::new(), ret_ty: Self_, attributes: attrs, unify_fieldless_variants: false, @@ -59,12 +58,12 @@ fn default_struct_substructure( trait_span: Span, substr: &Substructure<'_>, summary: &StaticFields, -) -> P<Expr> { +) -> BlockOrExpr { // Note that `kw::Default` is "default" and `sym::Default` is "Default"! let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]); let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new()); - match summary { + let expr = match summary { Unnamed(ref fields, is_tuple) => { if !is_tuple { cx.expr_ident(trait_span, substr.type_ident) @@ -80,31 +79,27 @@ fn default_struct_substructure( .collect(); cx.expr_struct_ident(trait_span, substr.type_ident, default_fields) } - } + }; + BlockOrExpr::new_expr(expr) } fn default_enum_substructure( cx: &mut ExtCtxt<'_>, trait_span: Span, enum_def: &EnumDef, -) -> P<Expr> { - let Ok(default_variant) = extract_default_variant(cx, enum_def, trait_span) else { - return DummyResult::raw_expr(trait_span, true); +) -> BlockOrExpr { + let expr = if let Ok(default_variant) = extract_default_variant(cx, enum_def, trait_span) + && let Ok(_) = validate_default_attribute(cx, default_variant) + { + // We now know there is exactly one unit variant with exactly one `#[default]` attribute. + cx.expr_path(cx.path( + default_variant.span, + vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident], + )) + } else { + DummyResult::raw_expr(trait_span, true) }; - - // At this point, we know that there is exactly one variant with a `#[default]` attribute. The - // attribute hasn't yet been validated. - - if let Err(()) = validate_default_attribute(cx, default_variant) { - return DummyResult::raw_expr(trait_span, true); - } - - // We now know there is exactly one unit variant with exactly one `#[default]` attribute. - - cx.expr_path(cx.path( - default_variant.span, - vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident], - )) + BlockOrExpr::new_expr(expr) } fn extract_default_variant<'a>( diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index 7dc0584618d..49dbe51f762 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -89,8 +89,7 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::pathvec_std; -use rustc_ast::ptr::P; -use rustc_ast::{Expr, ExprKind, MetaItem, Mutability}; +use rustc_ast::{ExprKind, MetaItem, Mutability}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; @@ -121,7 +120,10 @@ pub fn expand_deriving_rustc_encodable( )], }, explicit_self: true, - args: vec![(Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut), sym::s)], + nonself_args: vec![( + Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut), + sym::s, + )], ret_ty: Path(Path::new_( pathvec_std!(result::Result), vec![ @@ -147,8 +149,8 @@ fn encodable_substructure( trait_span: Span, substr: &Substructure<'_>, krate: Symbol, -) -> P<Expr> { - let encoder = substr.nonself_args[0].clone(); +) -> BlockOrExpr { + let encoder = substr.nonselflike_args[0].clone(); // throw an underscore in front to suppress unused variable warnings let blkarg = Ident::new(sym::_e, trait_span); let blkencoder = cx.expr_ident(trait_span, blkarg); @@ -166,12 +168,12 @@ fn encodable_substructure( let fn_emit_struct_field_path = cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_struct_field]); let mut stmts = Vec::new(); - for (i, &FieldInfo { name, ref self_, span, .. }) in fields.iter().enumerate() { + for (i, &FieldInfo { name, ref self_expr, span, .. }) in fields.iter().enumerate() { let name = match name { Some(id) => id.name, None => Symbol::intern(&format!("_field{}", i)), }; - let self_ref = cx.expr_addr_of(span, self_.clone()); + let self_ref = cx.expr_addr_of(span, self_expr.clone()); let enc = cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]); let lambda = cx.lambda1(span, enc, blkarg); let call = cx.expr_call_global( @@ -208,7 +210,7 @@ fn encodable_substructure( let fn_emit_struct_path = cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_struct]); - cx.expr_call_global( + let expr = cx.expr_call_global( trait_span, fn_emit_struct_path, vec![ @@ -217,7 +219,8 @@ fn encodable_substructure( cx.expr_usize(trait_span, fields.len()), blk, ], - ) + ); + BlockOrExpr::new_expr(expr) } EnumMatching(idx, _, variant, ref fields) => { @@ -234,8 +237,8 @@ fn encodable_substructure( let mut stmts = Vec::new(); if !fields.is_empty() { let last = fields.len() - 1; - for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() { - let self_ref = cx.expr_addr_of(span, self_.clone()); + for (i, &FieldInfo { ref self_expr, span, .. }) in fields.iter().enumerate() { + let self_ref = cx.expr_addr_of(span, self_expr.clone()); let enc = cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]); let lambda = cx.lambda1(span, enc, blkarg); @@ -279,12 +282,12 @@ fn encodable_substructure( let blk = cx.lambda1(trait_span, call, blkarg); let fn_emit_enum_path: Vec<_> = cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum]); - let ret = cx.expr_call_global( + let expr = cx.expr_call_global( trait_span, fn_emit_enum_path, vec![encoder, cx.expr_str(trait_span, substr.type_ident.name), blk], ); - cx.expr_block(cx.block(trait_span, vec![me, cx.stmt_expr(ret)])) + BlockOrExpr::new_mixed(vec![me], expr) } _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)"), diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 2a9e37081e0..74e18bffc2e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -227,8 +227,8 @@ pub struct MethodDef<'a> { /// Is there is a `&self` argument? If not, it is a static function. pub explicit_self: bool, - /// Arguments other than the self argument - pub args: Vec<(Ty, Symbol)>, + /// Arguments other than the self argument. + pub nonself_args: Vec<(Ty, Symbol)>, /// Returns type pub ret_ty: Ty, @@ -245,25 +245,24 @@ pub struct MethodDef<'a> { pub struct Substructure<'a> { /// ident of self pub type_ident: Ident, - /// verbatim access to any non-self arguments - pub nonself_args: &'a [P<Expr>], + /// Verbatim access to any non-selflike arguments, i.e. arguments that + /// don't have type `&Self`. + pub nonselflike_args: &'a [P<Expr>], pub fields: &'a SubstructureFields<'a>, } /// Summary of the relevant parts of a struct/enum field. -pub struct FieldInfo<'a> { +pub struct FieldInfo { pub span: Span, /// None for tuple structs/normal enum variants, Some for normal /// structs/struct enum variants. pub name: Option<Ident>, /// The expression corresponding to this field of `self` /// (specifically, a reference to it). - pub self_: P<Expr>, + pub self_expr: P<Expr>, /// The expressions corresponding to references to this field in - /// the other `Self` arguments. - pub other: Vec<P<Expr>>, - /// The attributes on the field - pub attrs: &'a [ast::Attribute], + /// the other selflike arguments. + pub other_selflike_exprs: Vec<P<Expr>>, } /// Fields for a static method @@ -276,11 +275,11 @@ pub enum StaticFields { /// A summary of the possible sets of fields. pub enum SubstructureFields<'a> { - Struct(&'a ast::VariantData, Vec<FieldInfo<'a>>), + Struct(&'a ast::VariantData, Vec<FieldInfo>), /// Matching variants of the enum: variant index, variant count, ast::Variant, /// fields: the field name is only non-`None` in the case of a struct /// variant. - EnumMatching(usize, usize, &'a ast::Variant, Vec<FieldInfo<'a>>), + EnumMatching(usize, usize, &'a ast::Variant, Vec<FieldInfo>), /// Non-matching variants of the enum, but with all state hidden from the /// consequent code. The field is a list of `Ident`s bound to the variant @@ -296,12 +295,7 @@ pub enum SubstructureFields<'a> { /// Combine the values of all the fields together. The last argument is /// all the fields of all the structures. pub type CombineSubstructureFunc<'a> = - Box<dyn FnMut(&mut ExtCtxt<'_>, Span, &Substructure<'_>) -> P<Expr> + 'a>; - -/// Deal with non-matching enum variants. The slice is the identifiers holding -/// the variant index value for each of the `Self` arguments. -pub type EnumNonMatchCollapsedFunc<'a> = - Box<dyn FnMut(&mut ExtCtxt<'_>, Span, &[Ident]) -> P<Expr> + 'a>; + Box<dyn FnMut(&mut ExtCtxt<'_>, Span, &Substructure<'_>) -> BlockOrExpr + 'a>; pub fn combine_substructure( f: CombineSubstructureFunc<'_>, @@ -314,6 +308,48 @@ struct TypeParameter { ty: P<ast::Ty>, } +// The code snippets built up for derived code are sometimes used as blocks +// (e.g. in a function body) and sometimes used as expressions (e.g. in a match +// arm). This structure avoids committing to either form until necessary, +// avoiding the insertion of any unnecessary blocks. +// +// The statements come before the expression. +pub struct BlockOrExpr(Vec<ast::Stmt>, Option<P<Expr>>); + +impl BlockOrExpr { + pub fn new_stmts(stmts: Vec<ast::Stmt>) -> BlockOrExpr { + BlockOrExpr(stmts, None) + } + + pub fn new_expr(expr: P<Expr>) -> BlockOrExpr { + BlockOrExpr(vec![], Some(expr)) + } + + pub fn new_mixed(stmts: Vec<ast::Stmt>, expr: P<Expr>) -> BlockOrExpr { + BlockOrExpr(stmts, Some(expr)) + } + + // Converts it into a block. + fn into_block(mut self, cx: &ExtCtxt<'_>, span: Span) -> P<ast::Block> { + if let Some(expr) = self.1 { + self.0.push(cx.stmt_expr(expr)); + } + cx.block(span, self.0) + } + + // Converts it into an expression. + fn into_expr(self, cx: &ExtCtxt<'_>, span: Span) -> P<Expr> { + if self.0.is_empty() { + match self.1 { + None => cx.expr_block(cx.block(span, vec![])), + Some(expr) => expr, + } + } else { + cx.expr_block(self.into_block(cx, span)) + } + } +} + /// This method helps to extract all the type parameters referenced from a /// type. For a type parameter `<T>`, it looks for either a `TyPath` that /// is not global and starts with `T`, or a `TyQPath`. @@ -740,8 +776,8 @@ impl<'a> TraitDef<'a> { .methods .iter() .map(|method_def| { - let (explicit_self, self_args, nonself_args, tys) = - method_def.split_self_nonself_args(cx, self, type_ident, generics); + let (explicit_self, selflike_args, nonselflike_args, nonself_arg_tys) = + method_def.extract_arg_details(cx, self, type_ident, generics); let body = if from_scratch || method_def.is_static() { method_def.expand_static_struct_method_body( @@ -749,7 +785,7 @@ impl<'a> TraitDef<'a> { self, struct_def, type_ident, - &nonself_args, + &nonselflike_args, ) } else { method_def.expand_struct_method_body( @@ -757,14 +793,22 @@ impl<'a> TraitDef<'a> { self, struct_def, type_ident, - &self_args, - &nonself_args, + &selflike_args, + &nonselflike_args, use_temporaries, is_packed, ) }; - method_def.create_method(cx, self, type_ident, generics, explicit_self, tys, body) + method_def.create_method( + cx, + self, + type_ident, + generics, + explicit_self, + nonself_arg_tys, + body, + ) }) .collect(); @@ -789,8 +833,8 @@ impl<'a> TraitDef<'a> { .methods .iter() .map(|method_def| { - let (explicit_self, self_args, nonself_args, tys) = - method_def.split_self_nonself_args(cx, self, type_ident, generics); + let (explicit_self, selflike_args, nonselflike_args, nonself_arg_tys) = + method_def.extract_arg_details(cx, self, type_ident, generics); let body = if from_scratch || method_def.is_static() { method_def.expand_static_enum_method_body( @@ -798,7 +842,7 @@ impl<'a> TraitDef<'a> { self, enum_def, type_ident, - &nonself_args, + &nonselflike_args, ) } else { method_def.expand_enum_method_body( @@ -806,12 +850,20 @@ impl<'a> TraitDef<'a> { self, enum_def, type_ident, - self_args, - &nonself_args, + selflike_args, + &nonselflike_args, ) }; - method_def.create_method(cx, self, type_ident, generics, explicit_self, tys, body) + method_def.create_method( + cx, + self, + type_ident, + generics, + explicit_self, + nonself_arg_tys, + body, + ) }) .collect(); @@ -825,11 +877,11 @@ impl<'a> MethodDef<'a> { cx: &mut ExtCtxt<'_>, trait_: &TraitDef<'_>, type_ident: Ident, - nonself_args: &[P<Expr>], + nonselflike_args: &[P<Expr>], fields: &SubstructureFields<'_>, - ) -> P<Expr> { + ) -> BlockOrExpr { let span = trait_.span; - let substructure = Substructure { type_ident, nonself_args, fields }; + let substructure = Substructure { type_ident, nonselflike_args, fields }; let mut f = self.combine_substructure.borrow_mut(); let f: &mut CombineSubstructureFunc<'_> = &mut *f; f(cx, span, &substructure) @@ -849,49 +901,51 @@ impl<'a> MethodDef<'a> { !self.explicit_self } - fn split_self_nonself_args( + // The return value includes: + // - explicit_self: The `&self` arg, if present. + // - selflike_args: Expressions for `&self` (if present) and also any other + // args with the same type (e.g. the `other` arg in `PartialEq::eq`). + // - nonselflike_args: Expressions for all the remaining args. + // - nonself_arg_tys: Additional information about all the args other than + // `&self`. + fn extract_arg_details( &self, cx: &mut ExtCtxt<'_>, trait_: &TraitDef<'_>, type_ident: Ident, generics: &Generics, ) -> (Option<ast::ExplicitSelf>, Vec<P<Expr>>, Vec<P<Expr>>, Vec<(Ident, P<ast::Ty>)>) { - let mut self_args = Vec::new(); - let mut nonself_args = Vec::new(); - let mut arg_tys = Vec::new(); + let mut selflike_args = Vec::new(); + let mut nonselflike_args = Vec::new(); + let mut nonself_arg_tys = Vec::new(); let span = trait_.span; - let ast_explicit_self = if self.explicit_self { + let explicit_self = if self.explicit_self { let (self_expr, explicit_self) = ty::get_explicit_self(cx, span); - self_args.push(self_expr); + selflike_args.push(self_expr); Some(explicit_self) } else { None }; - for (ty, name) in self.args.iter() { + for (ty, name) in self.nonself_args.iter() { let ast_ty = ty.to_ty(cx, span, type_ident, generics); let ident = Ident::new(*name, span); - arg_tys.push((ident, ast_ty)); + nonself_arg_tys.push((ident, ast_ty)); let arg_expr = cx.expr_ident(span, ident); - match *ty { - // for static methods, just treat any Self - // arguments as a normal arg - Self_ if !self.is_static() => { - self_args.push(arg_expr); - } - Ref(ref ty, _) if matches!(**ty, Self_) && !self.is_static() => { - self_args.push(cx.expr_deref(span, arg_expr)) - } - _ => { - nonself_args.push(arg_expr); + match ty { + // Selflike (`&Self`) arguments only occur in non-static methods. + Ref(box Self_, _) if !self.is_static() => { + selflike_args.push(cx.expr_deref(span, arg_expr)) } + Self_ => cx.span_bug(span, "`Self` in non-return position"), + _ => nonselflike_args.push(arg_expr), } } - (ast_explicit_self, self_args, nonself_args, arg_tys) + (explicit_self, selflike_args, nonselflike_args, nonself_arg_tys) } fn create_method( @@ -901,27 +955,28 @@ impl<'a> MethodDef<'a> { type_ident: Ident, generics: &Generics, explicit_self: Option<ast::ExplicitSelf>, - arg_types: Vec<(Ident, P<ast::Ty>)>, - body: P<Expr>, + nonself_arg_tys: Vec<(Ident, P<ast::Ty>)>, + body: BlockOrExpr, ) -> P<ast::AssocItem> { let span = trait_.span; // Create the generics that aren't for `Self`. let fn_generics = self.generics.to_generics(cx, span, type_ident, generics); let args = { - let self_args = explicit_self.map(|explicit_self| { + let self_arg = explicit_self.map(|explicit_self| { let ident = Ident::with_dummy_span(kw::SelfLower).with_span_pos(span); ast::Param::from_self(ast::AttrVec::default(), explicit_self, ident) }); - let nonself_args = arg_types.into_iter().map(|(name, ty)| cx.param(span, name, ty)); - self_args.into_iter().chain(nonself_args).collect() + let nonself_args = + nonself_arg_tys.into_iter().map(|(name, ty)| cx.param(span, name, ty)); + self_arg.into_iter().chain(nonself_args).collect() }; let ret_type = self.get_ret_ty(cx, trait_, generics, type_ident); let method_ident = Ident::new(self.name, span); let fn_decl = cx.fn_decl(args, ast::FnRetTy::Ty(ret_type)); - let body_block = cx.block_expr(body); + let body_block = body.into_block(cx, span); let trait_lo_sp = span.shrink_to_lo(); @@ -982,81 +1037,53 @@ impl<'a> MethodDef<'a> { trait_: &TraitDef<'b>, struct_def: &'b VariantData, type_ident: Ident, - self_args: &[P<Expr>], - nonself_args: &[P<Expr>], + selflike_args: &[P<Expr>], + nonselflike_args: &[P<Expr>], use_temporaries: bool, is_packed: bool, - ) -> P<Expr> { - let mut raw_fields = Vec::new(); // Vec<[fields of self], [fields of next Self arg], [etc]> + ) -> BlockOrExpr { let span = trait_.span; - let mut patterns = Vec::new(); + assert!(selflike_args.len() == 1 || selflike_args.len() == 2); - for (i, self_arg) in self_args.iter().enumerate() { - let ident_exprs = if !is_packed { - trait_.create_struct_field_accesses(cx, self_arg, struct_def) - } else { - // Get the pattern for the let-destructuring. - // - // We could use `type_ident` instead of `Self`, but in the case of a type parameter - // shadowing the struct name, that causes a second, unnecessary E0578 error. #97343 - let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]); - let (pat, ident_exprs) = trait_.create_struct_pattern( - cx, - struct_path, - struct_def, - &format!("__self_{}", i), - ast::Mutability::Not, - use_temporaries, - ); - patterns.push(pat); - ident_exprs - }; - raw_fields.push(ident_exprs); - } - - // transpose raw_fields - let fields = if !raw_fields.is_empty() { - let mut raw_fields = raw_fields.into_iter().map(|v| v.into_iter()); - let first_field = raw_fields.next().unwrap(); - let mut other_fields: Vec<vec::IntoIter<_>> = raw_fields.collect(); - first_field - .map(|(span, opt_id, field, attrs)| FieldInfo { - span: span.with_ctxt(trait_.span.ctxt()), - name: opt_id, - self_: field, - other: other_fields - .iter_mut() - .map(|l| { - let (.., ex, _) = l.next().unwrap(); - ex - }) - .collect(), - attrs, - }) - .collect() - } else { - cx.span_bug(span, "no `self` parameter for method in generic `derive`") + let mk_body = |cx, selflike_fields| { + self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &Struct(struct_def, selflike_fields), + ) }; - let mut body = self.call_substructure_method( - cx, - trait_, - type_ident, - nonself_args, - &Struct(struct_def, fields), - ); - if !is_packed { - body.span = span; - body + let selflike_fields = + trait_.create_struct_field_access_fields(cx, selflike_args, struct_def); + mk_body(cx, selflike_fields) } else { + let prefixes: Vec<_> = + (0..selflike_args.len()).map(|i| format!("__self_{}", i)).collect(); + let selflike_fields = + trait_.create_struct_pattern_fields(cx, struct_def, &prefixes, use_temporaries); + let mut body = mk_body(cx, selflike_fields); + + let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]); + let patterns = trait_.create_struct_patterns( + cx, + struct_path, + struct_def, + &prefixes, + ast::Mutability::Not, + use_temporaries, + ); + // Do the let-destructuring. - let mut stmts: Vec<_> = iter::zip(self_args, patterns) - .map(|(arg_expr, pat)| cx.stmt_let_pat(span, pat, arg_expr.clone())) + let mut stmts: Vec<_> = iter::zip(selflike_args, patterns) + .map(|(selflike_arg_expr, pat)| { + cx.stmt_let_pat(span, pat, selflike_arg_expr.clone()) + }) .collect(); - stmts.push(cx.stmt_expr(body)); - - cx.expr_block(cx.block(span, stmts)) + stmts.extend(std::mem::take(&mut body.0)); + BlockOrExpr(stmts, body.1) } } @@ -1066,15 +1093,15 @@ impl<'a> MethodDef<'a> { trait_: &TraitDef<'_>, struct_def: &VariantData, type_ident: Ident, - nonself_args: &[P<Expr>], - ) -> P<Expr> { + nonselflike_args: &[P<Expr>], + ) -> BlockOrExpr { let summary = trait_.summarise_struct(cx, struct_def); self.call_substructure_method( cx, trait_, type_ident, - nonself_args, + nonselflike_args, &StaticStruct(struct_def, summary), ) } @@ -1108,7 +1135,7 @@ impl<'a> MethodDef<'a> { /// } /// } /// ``` - /// Creates a match for a tuple of all `self_args`, where either all + /// Creates a match for a tuple of all `selflike_args`, where either all /// variants match, or it falls into a catch-all for when one variant /// does not match. /// @@ -1121,33 +1148,33 @@ impl<'a> MethodDef<'a> { /// a simple equality check (for PartialEq). /// /// The catch-all handler is provided access the variant index values - /// for each of the self-args, carried in precomputed variables. + /// for each of the selflike_args, carried in precomputed variables. fn expand_enum_method_body<'b>( &self, cx: &mut ExtCtxt<'_>, trait_: &TraitDef<'b>, enum_def: &'b EnumDef, type_ident: Ident, - mut self_args: Vec<P<Expr>>, - nonself_args: &[P<Expr>], - ) -> P<Expr> { + mut selflike_args: Vec<P<Expr>>, + nonselflike_args: &[P<Expr>], + ) -> BlockOrExpr { let span = trait_.span; let variants = &enum_def.variants; - let self_arg_names = iter::once("__self".to_string()) + let prefixes = iter::once("__self".to_string()) .chain( - self_args + selflike_args .iter() .enumerate() .skip(1) - .map(|(arg_count, _self_arg)| format!("__arg_{}", arg_count)), + .map(|(arg_count, _selflike_arg)| format!("__arg_{}", arg_count)), ) .collect::<Vec<String>>(); // The `vi_idents` will be bound, solely in the catch-all, to - // a series of let statements mapping each self_arg to an int + // a series of let statements mapping each selflike_arg to an int // value corresponding to its discriminant. - let vi_idents = self_arg_names + let vi_idents = prefixes .iter() .map(|name| { let vi_suffix = format!("{}_vi", name); @@ -1166,100 +1193,64 @@ impl<'a> MethodDef<'a> { // (Variant1, Variant1, ...) => Body1 // (Variant2, Variant2, ...) => Body2 // ... - // where each tuple has length = self_args.len() + // where each tuple has length = selflike_args.len() + let mut match_arms: Vec<ast::Arm> = variants .iter() .enumerate() .filter(|&(_, v)| !(self.unify_fieldless_variants && v.data.fields().is_empty())) .map(|(index, variant)| { - let mk_self_pat = |cx: &mut ExtCtxt<'_>, self_arg_name: &str| { - let (p, idents) = trait_.create_enum_variant_pattern( - cx, - type_ident, - variant, - self_arg_name, - ast::Mutability::Not, - ); - (cx.pat(span, PatKind::Ref(p, ast::Mutability::Not)), idents) - }; - // A single arm has form (&VariantK, &VariantK, ...) => BodyK // (see "Final wrinkle" note below for why.) - let mut subpats = Vec::with_capacity(self_arg_names.len()); - let mut self_pats_idents = Vec::with_capacity(self_arg_names.len() - 1); - let first_self_pat_idents = { - let (p, idents) = mk_self_pat(cx, &self_arg_names[0]); - subpats.push(p); - idents - }; - for self_arg_name in &self_arg_names[1..] { - let (p, idents) = mk_self_pat(cx, &self_arg_name); - subpats.push(p); - self_pats_idents.push(idents); - } + + let use_temporaries = false; // enums can't be repr(packed) + let fields = trait_.create_struct_pattern_fields( + cx, + &variant.data, + &prefixes, + use_temporaries, + ); + + let sp = variant.span.with_ctxt(trait_.span.ctxt()); + let variant_path = cx.path(sp, vec![type_ident, variant.ident]); + let mut subpats: Vec<_> = trait_ + .create_struct_patterns( + cx, + variant_path, + &variant.data, + &prefixes, + ast::Mutability::Not, + use_temporaries, + ) + .into_iter() + .map(|p| cx.pat(span, PatKind::Ref(p, ast::Mutability::Not))) + .collect(); // Here is the pat = `(&VariantK, &VariantK, ...)` - let single_pat = cx.pat_tuple(span, subpats); + let single_pat = if subpats.len() == 1 { + subpats.pop().unwrap() + } else { + cx.pat_tuple(span, subpats) + }; // For the BodyK, we need to delegate to our caller, // passing it an EnumMatching to indicate which case // we are in. - - // All of the Self args have the same variant in these - // cases. So we transpose the info in self_pats_idents - // to gather the getter expressions together, in the - // form that EnumMatching expects. - - // The transposition is driven by walking across the - // arg fields of the variant for the first self pat. - let field_tuples = first_self_pat_idents - .into_iter() - .enumerate() - // For each arg field of self, pull out its getter expr ... - .map(|(field_index, (span, opt_ident, self_getter_expr, attrs))| { - // ... but FieldInfo also wants getter expr - // for matching other arguments of Self type; - // so walk across the *other* self_pats_idents - // and pull out getter for same field in each - // of them (using `field_index` tracked above). - // That is the heart of the transposition. - let others = self_pats_idents - .iter() - .map(|fields| { - let (_, _opt_ident, ref other_getter_expr, _) = fields[field_index]; - - // All Self args have same variant, so - // opt_idents are the same. (Assert - // here to make it self-evident that - // it is okay to ignore `_opt_ident`.) - assert!(opt_ident == _opt_ident); - - other_getter_expr.clone() - }) - .collect::<Vec<P<Expr>>>(); - - FieldInfo { - span, - name: opt_ident, - self_: self_getter_expr, - other: others, - attrs, - } - }) - .collect::<Vec<FieldInfo<'_>>>(); - + // // Now, for some given VariantK, we have built up // expressions for referencing every field of every // Self arg, assuming all are instances of VariantK. // Build up code associated with such a case. - let substructure = EnumMatching(index, variants.len(), variant, field_tuples); - let arm_expr = self.call_substructure_method( - cx, - trait_, - type_ident, - nonself_args, - &substructure, - ); + let substructure = EnumMatching(index, variants.len(), variant, fields); + let arm_expr = self + .call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &substructure, + ) + .into_expr(cx, span); cx.arm(span, single_pat, arm_expr) }) @@ -1271,15 +1262,18 @@ impl<'a> MethodDef<'a> { // The index and actual variant aren't meaningful in this case, // so just use whatever let substructure = EnumMatching(0, variants.len(), v, Vec::new()); - Some(self.call_substructure_method( - cx, - trait_, - type_ident, - nonself_args, - &substructure, - )) + Some( + self.call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &substructure, + ) + .into_expr(cx, span), + ) } - _ if variants.len() > 1 && self_args.len() > 1 => { + _ if variants.len() > 1 && selflike_args.len() > 1 => { // Since we know that all the arguments will match if we reach // the match expression we add the unreachable intrinsics as the // result of the catch all which should help llvm in optimizing it @@ -1306,8 +1300,8 @@ impl<'a> MethodDef<'a> { // catch-all `_` match, it would trigger the // unreachable-pattern error. // - if variants.len() > 1 && self_args.len() > 1 { - // Build a series of let statements mapping each self_arg + if variants.len() > 1 && selflike_args.len() > 1 { + // Build a series of let statements mapping each selflike_arg // to its discriminant value. // // i.e., for `enum E<T> { A, B(1), C(T, T) }`, and a deriving @@ -1322,10 +1316,14 @@ impl<'a> MethodDef<'a> { // We also build an expression which checks whether all discriminants are equal: // `__self_vi == __arg_1_vi && __self_vi == __arg_2_vi && ...` let mut discriminant_test = cx.expr_bool(span, true); - for (i, (&ident, self_arg)) in iter::zip(&vi_idents, &self_args).enumerate() { - let self_addr = cx.expr_addr_of(span, self_arg.clone()); - let variant_value = - deriving::call_intrinsic(cx, span, sym::discriminant_value, vec![self_addr]); + for (i, (&ident, selflike_arg)) in iter::zip(&vi_idents, &selflike_args).enumerate() { + let selflike_addr = cx.expr_addr_of(span, selflike_arg.clone()); + let variant_value = deriving::call_intrinsic( + cx, + span, + sym::discriminant_value, + vec![selflike_addr], + ); let let_stmt = cx.stmt_let(span, false, ident, variant_value); index_let_stmts.push(let_stmt); @@ -1341,21 +1339,23 @@ impl<'a> MethodDef<'a> { } } - let arm_expr = self.call_substructure_method( - cx, - trait_, - type_ident, - nonself_args, - &catch_all_substructure, - ); + let arm_expr = self + .call_substructure_method( + cx, + trait_, + type_ident, + nonselflike_args, + &catch_all_substructure, + ) + .into_expr(cx, span); - // Final wrinkle: the self_args are expressions that deref + // Final wrinkle: the selflike_args are expressions that deref // down to desired places, but we cannot actually deref // them when they are fed as r-values into a tuple // expression; here add a layer of borrowing, turning // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. - self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg)); - let match_arg = cx.expr(span, ast::ExprKind::Tup(self_args)); + selflike_args.map_in_place(|selflike_arg| cx.expr_addr_of(span, selflike_arg)); + let match_arg = cx.expr(span, ast::ExprKind::Tup(selflike_args)); // Lastly we create an expression which branches on all discriminants being equal // if discriminant_test { @@ -1371,68 +1371,25 @@ impl<'a> MethodDef<'a> { // } let all_match = cx.expr_match(span, match_arg, match_arms); let arm_expr = cx.expr_if(span, discriminant_test, all_match, Some(arm_expr)); - index_let_stmts.push(cx.stmt_expr(arm_expr)); - cx.expr_block(cx.block(span, index_let_stmts)) + BlockOrExpr(index_let_stmts, Some(arm_expr)) } else if variants.is_empty() { - // As an additional wrinkle, For a zero-variant enum A, - // currently the compiler - // will accept `fn (a: &Self) { match *a { } }` - // but rejects `fn (a: &Self) { match (&*a,) { } }` - // as well as `fn (a: &Self) { match ( *a,) { } }` - // - // This means that the strategy of building up a tuple of - // all Self arguments fails when Self is a zero variant - // enum: rustc rejects the expanded program, even though - // the actual code tends to be impossible to execute (at - // least safely), according to the type system. - // - // The most expedient fix for this is to just let the - // code fall through to the catch-all. But even this is - // error-prone, since the catch-all as defined above would - // generate code like this: - // - // _ => { let __self0 = match *self { }; - // let __self1 = match *__arg_0 { }; - // <catch-all-expr> } - // - // Which is yields bindings for variables which type - // inference cannot resolve to unique types. - // - // One option to the above might be to add explicit type - // annotations. But the *only* reason to go down that path - // would be to try to make the expanded output consistent - // with the case when the number of enum variants >= 1. - // - // That just isn't worth it. In fact, trying to generate - // sensible code for *any* deriving on a zero-variant enum - // does not make sense. But at the same time, for now, we - // do not want to cause a compile failure just because the - // user happened to attach a deriving to their - // zero-variant enum. - // - // Instead, just generate a failing expression for the - // zero variant case, skipping matches and also skipping - // delegating back to the end user code entirely. - // - // (See also #4499 and #12609; note that some of the - // discussions there influence what choice we make here; - // e.g., if we feature-gate `match x { ... }` when x refers - // to an uninhabited type (e.g., a zero-variant enum or a - // type holding such an enum), but do not feature-gate - // zero-variant enums themselves, then attempting to - // derive Debug on such a type could here generate code - // that needs the feature gate enabled.) - - deriving::call_unreachable(cx, span) + // There is no sensible code to be generated for *any* deriving on + // a zero-variant enum. So we just generate a failing expression + // for the zero variant case. + BlockOrExpr(vec![], Some(deriving::call_unreachable(cx, span))) } else { - // Final wrinkle: the self_args are expressions that deref + // Final wrinkle: the selflike_args are expressions that deref // down to desired places, but we cannot actually deref // them when they are fed as r-values into a tuple // expression; here add a layer of borrowing, turning // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. - self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg)); - let match_arg = cx.expr(span, ast::ExprKind::Tup(self_args)); - cx.expr_match(span, match_arg, match_arms) + selflike_args.map_in_place(|selflike_arg| cx.expr_addr_of(span, selflike_arg)); + let match_arg = if selflike_args.len() == 1 { + selflike_args.pop().unwrap() + } else { + cx.expr(span, ast::ExprKind::Tup(selflike_args)) + }; + BlockOrExpr(vec![], Some(cx.expr_match(span, match_arg, match_arms))) } } @@ -1442,8 +1399,8 @@ impl<'a> MethodDef<'a> { trait_: &TraitDef<'_>, enum_def: &EnumDef, type_ident: Ident, - nonself_args: &[P<Expr>], - ) -> P<Expr> { + nonselflike_args: &[P<Expr>], + ) -> BlockOrExpr { let summary = enum_def .variants .iter() @@ -1457,7 +1414,7 @@ impl<'a> MethodDef<'a> { cx, trait_, type_ident, - nonself_args, + nonselflike_args, &StaticEnum(enum_def, summary), ) } @@ -1490,230 +1447,210 @@ impl<'a> TraitDef<'a> { } } - fn create_subpatterns( + fn create_struct_patterns( &self, cx: &mut ExtCtxt<'_>, - field_paths: Vec<Ident>, + struct_path: ast::Path, + struct_def: &'a VariantData, + prefixes: &[String], mutbl: ast::Mutability, use_temporaries: bool, ) -> Vec<P<ast::Pat>> { - field_paths + prefixes .iter() - .map(|path| { - let binding_mode = if use_temporaries { - ast::BindingMode::ByValue(ast::Mutability::Not) - } else { - ast::BindingMode::ByRef(mutbl) - }; - cx.pat(path.span, PatKind::Ident(binding_mode, *path, None)) + .map(|prefix| { + let pieces_iter = + struct_def.fields().iter().enumerate().map(|(i, struct_field)| { + let sp = struct_field.span.with_ctxt(self.span.ctxt()); + let binding_mode = if use_temporaries { + ast::BindingMode::ByValue(ast::Mutability::Not) + } else { + ast::BindingMode::ByRef(mutbl) + }; + let ident = self.mk_pattern_ident(prefix, i); + let path = ident.with_span_pos(sp); + ( + sp, + struct_field.ident, + cx.pat(path.span, PatKind::Ident(binding_mode, path, None)), + ) + }); + + let struct_path = struct_path.clone(); + match *struct_def { + VariantData::Struct(..) => { + let field_pats = pieces_iter + .map(|(sp, ident, pat)| { + if ident.is_none() { + cx.span_bug( + sp, + "a braced struct with unnamed fields in `derive`", + ); + } + ast::PatField { + ident: ident.unwrap(), + is_shorthand: false, + attrs: ast::AttrVec::new(), + id: ast::DUMMY_NODE_ID, + span: pat.span.with_ctxt(self.span.ctxt()), + pat, + is_placeholder: false, + } + }) + .collect(); + cx.pat_struct(self.span, struct_path, field_pats) + } + VariantData::Tuple(..) => { + let subpats = pieces_iter.map(|(_, _, subpat)| subpat).collect(); + cx.pat_tuple_struct(self.span, struct_path, subpats) + } + VariantData::Unit(..) => cx.pat_path(self.span, struct_path), + } }) .collect() } - fn create_struct_pattern( - &self, - cx: &mut ExtCtxt<'_>, - struct_path: ast::Path, - struct_def: &'a VariantData, - prefix: &str, - mutbl: ast::Mutability, - use_temporaries: bool, - ) -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) { - let mut paths = Vec::new(); - let mut ident_exprs = Vec::new(); - for (i, struct_field) in struct_def.fields().iter().enumerate() { - let sp = struct_field.span.with_ctxt(self.span.ctxt()); - let ident = Ident::from_str_and_span(&format!("{}_{}", prefix, i), self.span); - paths.push(ident.with_span_pos(sp)); - let val = cx.expr_path(cx.path_ident(sp, ident)); - let val = if use_temporaries { val } else { cx.expr_deref(sp, val) }; - ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..])); - } - - let subpats = self.create_subpatterns(cx, paths, mutbl, use_temporaries); - let pattern = match *struct_def { - VariantData::Struct(..) => { - let field_pats = iter::zip(subpats, &ident_exprs) - .map(|(pat, &(sp, ident, ..))| { - if ident.is_none() { - cx.span_bug(sp, "a braced struct with unnamed fields in `derive`"); - } - ast::PatField { - ident: ident.unwrap(), - is_shorthand: false, - attrs: ast::AttrVec::new(), - id: ast::DUMMY_NODE_ID, - span: pat.span.with_ctxt(self.span.ctxt()), - pat, - is_placeholder: false, - } - }) - .collect(); - cx.pat_struct(self.span, struct_path, field_pats) - } - VariantData::Tuple(..) => cx.pat_tuple_struct(self.span, struct_path, subpats), - VariantData::Unit(..) => cx.pat_path(self.span, struct_path), - }; + fn create_fields<F>(&self, struct_def: &'a VariantData, mk_exprs: F) -> Vec<FieldInfo> + where + F: Fn(usize, &ast::FieldDef, Span) -> Vec<P<ast::Expr>>, + { + struct_def + .fields() + .iter() + .enumerate() + .map(|(i, struct_field)| { + // For this field, get an expr for each selflike_arg. E.g. for + // `PartialEq::eq`, one for each of `&self` and `other`. + let sp = struct_field.span.with_ctxt(self.span.ctxt()); + let mut exprs: Vec<_> = mk_exprs(i, struct_field, sp); + let self_expr = exprs.remove(0); + let other_selflike_exprs = exprs; + FieldInfo { + span: sp.with_ctxt(self.span.ctxt()), + name: struct_field.ident, + self_expr, + other_selflike_exprs, + } + }) + .collect() + } - (pattern, ident_exprs) + fn mk_pattern_ident(&self, prefix: &str, i: usize) -> Ident { + Ident::from_str_and_span(&format!("{}_{}", prefix, i), self.span) } - fn create_struct_field_accesses( + fn create_struct_pattern_fields( &self, cx: &mut ExtCtxt<'_>, - mut self_arg: &P<Expr>, struct_def: &'a VariantData, - ) -> Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])> { - let mut ident_exprs = Vec::new(); - for (i, struct_field) in struct_def.fields().iter().enumerate() { - let sp = struct_field.span.with_ctxt(self.span.ctxt()); - - // We don't the need the deref, if there is one. - if let ast::ExprKind::Unary(ast::UnOp::Deref, inner) = &self_arg.kind { - self_arg = inner; - } - - // Note: we must use `struct_field.span` rather than `span` in the - // `unwrap_or_else` case otherwise the hygiene is wrong and we get - // "field `0` of struct `Point` is private" errors on tuple - // structs. - let val = cx.expr( - sp, - ast::ExprKind::Field( - self_arg.clone(), - struct_field.ident.unwrap_or_else(|| { - Ident::from_str_and_span(&i.to_string(), struct_field.span) - }), - ), - ); - ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..])); - } - ident_exprs + prefixes: &[String], + use_temporaries: bool, + ) -> Vec<FieldInfo> { + self.create_fields(struct_def, |i, _struct_field, sp| { + prefixes + .iter() + .map(|prefix| { + let ident = self.mk_pattern_ident(prefix, i); + let expr = cx.expr_path(cx.path_ident(sp, ident)); + if use_temporaries { expr } else { cx.expr_deref(sp, expr) } + }) + .collect() + }) } - fn create_enum_variant_pattern( + fn create_struct_field_access_fields( &self, cx: &mut ExtCtxt<'_>, - enum_ident: Ident, - variant: &'a ast::Variant, - prefix: &str, - mutbl: ast::Mutability, - ) -> (P<ast::Pat>, Vec<(Span, Option<Ident>, P<Expr>, &'a [ast::Attribute])>) { - let sp = variant.span.with_ctxt(self.span.ctxt()); - let variant_path = cx.path(sp, vec![enum_ident, variant.ident]); - let use_temporaries = false; // enums can't be repr(packed) - self.create_struct_pattern(cx, variant_path, &variant.data, prefix, mutbl, use_temporaries) - } -} - -// helpful premade recipes - -fn cs_fold_fields<'a, F>( - use_foldl: bool, - mut f: F, - base: P<Expr>, - cx: &mut ExtCtxt<'_>, - all_fields: &[FieldInfo<'a>], -) -> P<Expr> -where - F: FnMut(&mut ExtCtxt<'_>, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>, -{ - if use_foldl { - all_fields - .iter() - .fold(base, |old, field| f(cx, field.span, old, field.self_.clone(), &field.other)) - } else { - all_fields - .iter() - .rev() - .fold(base, |old, field| f(cx, field.span, old, field.self_.clone(), &field.other)) - } -} - -fn cs_fold_enumnonmatch( - mut enum_nonmatch_f: EnumNonMatchCollapsedFunc<'_>, - cx: &mut ExtCtxt<'_>, - trait_span: Span, - substructure: &Substructure<'_>, -) -> P<Expr> { - match *substructure.fields { - EnumNonMatchingCollapsed(tuple) => enum_nonmatch_f(cx, trait_span, tuple), - _ => cx.span_bug(trait_span, "cs_fold_enumnonmatch expected an EnumNonMatchingCollapsed"), + selflike_args: &[P<Expr>], + struct_def: &'a VariantData, + ) -> Vec<FieldInfo> { + self.create_fields(struct_def, |i, struct_field, sp| { + selflike_args + .iter() + .map(|mut selflike_arg| { + // We don't the need the deref, if there is one. + if let ast::ExprKind::Unary(ast::UnOp::Deref, inner) = &selflike_arg.kind { + selflike_arg = inner; + } + // Note: we must use `struct_field.span` rather than `span` in the + // `unwrap_or_else` case otherwise the hygiene is wrong and we get + // "field `0` of struct `Point` is private" errors on tuple + // structs. + cx.expr( + sp, + ast::ExprKind::Field( + selflike_arg.clone(), + struct_field.ident.unwrap_or_else(|| { + Ident::from_str_and_span(&i.to_string(), struct_field.span) + }), + ), + ) + }) + .collect() + }) } } -fn cs_fold_static(cx: &mut ExtCtxt<'_>, trait_span: Span) -> P<Expr> { - cx.span_bug(trait_span, "static function in `derive`") +/// The function passed to `cs_fold` is called repeatedly with a value of this +/// type. It describes one part of the code generation. The result is always an +/// expression. +pub enum CsFold<'a> { + /// The basic case: a field expression for one or more selflike args. E.g. + /// for `PartialEq::eq` this is something like `self.x == other.x`. + Single(&'a FieldInfo), + + /// The combination of two field expressions. E.g. for `PartialEq::eq` this + /// is something like `<field1 equality> && <field2 equality>`. + Combine(Span, P<Expr>, P<Expr>), + + // The fallback case for a struct or enum variant with no fields. + Fieldless, + + /// The fallback case for non-matching enum variants. The slice is the + /// identifiers holding the variant index value for each of the `Self` + /// arguments. + EnumNonMatching(Span, &'a [Ident]), } -/// Fold the fields. `use_foldl` controls whether this is done -/// left-to-right (`true`) or right-to-left (`false`). +/// Folds over fields, combining the expressions for each field in a sequence. +/// Statics may not be folded over. pub fn cs_fold<F>( use_foldl: bool, - f: F, - base: P<Expr>, - enum_nonmatch_f: EnumNonMatchCollapsedFunc<'_>, cx: &mut ExtCtxt<'_>, trait_span: Span, substructure: &Substructure<'_>, + mut f: F, ) -> P<Expr> where - F: FnMut(&mut ExtCtxt<'_>, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>, + F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P<Expr>, { match *substructure.fields { EnumMatching(.., ref all_fields) | Struct(_, ref all_fields) => { - cs_fold_fields(use_foldl, f, base, cx, all_fields) - } - EnumNonMatchingCollapsed(..) => { - cs_fold_enumnonmatch(enum_nonmatch_f, cx, trait_span, substructure) - } - StaticEnum(..) | StaticStruct(..) => cs_fold_static(cx, trait_span), - } -} + if all_fields.is_empty() { + return f(cx, CsFold::Fieldless); + } -/// Function to fold over fields, with three cases, to generate more efficient and concise code. -/// When the `substructure` has grouped fields, there are two cases: -/// Zero fields: call the base case function with `None` (like the usual base case of `cs_fold`). -/// One or more fields: call the base case function on the first value (which depends on -/// `use_fold`), and use that as the base case. Then perform `cs_fold` on the remainder of the -/// fields. -/// When the `substructure` is an `EnumNonMatchingCollapsed`, the result of `enum_nonmatch_f` -/// is returned. Statics may not be folded over. -pub fn cs_fold1<F, B>( - use_foldl: bool, - f: F, - mut b: B, - enum_nonmatch_f: EnumNonMatchCollapsedFunc<'_>, - cx: &mut ExtCtxt<'_>, - trait_span: Span, - substructure: &Substructure<'_>, -) -> P<Expr> -where - F: FnMut(&mut ExtCtxt<'_>, Span, P<Expr>, P<Expr>, &[P<Expr>]) -> P<Expr>, - B: FnMut(&mut ExtCtxt<'_>, Option<(Span, P<Expr>, &[P<Expr>])>) -> P<Expr>, -{ - match *substructure.fields { - EnumMatching(.., ref all_fields) | Struct(_, ref all_fields) => { - let (base, rest) = match (all_fields.is_empty(), use_foldl) { - (false, true) => { - let (first, rest) = all_fields.split_first().unwrap(); - let args = (first.span, first.self_.clone(), &first.other[..]); - (b(cx, Some(args)), rest) - } - (false, false) => { - let (last, rest) = all_fields.split_last().unwrap(); - let args = (last.span, last.self_.clone(), &last.other[..]); - (b(cx, Some(args)), rest) - } - (true, _) => (b(cx, None), &all_fields[..]), + let (base_field, rest) = if use_foldl { + all_fields.split_first().unwrap() + } else { + all_fields.split_last().unwrap() }; - cs_fold_fields(use_foldl, f, base, cx, rest) - } - EnumNonMatchingCollapsed(..) => { - cs_fold_enumnonmatch(enum_nonmatch_f, cx, trait_span, substructure) + let base_expr = f(cx, CsFold::Single(base_field)); + + let op = |old, field: &FieldInfo| { + let new = f(cx, CsFold::Single(field)); + f(cx, CsFold::Combine(field.span, old, new)) + }; + + if use_foldl { + rest.iter().fold(base_expr, op) + } else { + rest.iter().rfold(base_expr, op) + } } - StaticEnum(..) | StaticStruct(..) => cs_fold_static(cx, trait_span), + EnumNonMatchingCollapsed(tuple) => f(cx, CsFold::EnumNonMatching(trait_span, tuple)), + StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"), } } diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index 9790449c4b3..2213cd6d37d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -2,8 +2,7 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::{self, path_std, pathvec_std}; -use rustc_ast::ptr::P; -use rustc_ast::{Expr, MetaItem, Mutability}; +use rustc_ast::{MetaItem, Mutability}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -31,7 +30,7 @@ pub fn expand_deriving_hash( name: sym::hash, generics: Bounds { bounds: vec![(typaram, vec![path_std!(hash::Hasher)])] }, explicit_self: true, - args: vec![(Ref(Box::new(Path(arg)), Mutability::Mut), sym::state)], + nonself_args: vec![(Ref(Box::new(Path(arg)), Mutability::Mut), sym::state)], ret_ty: Unit, attributes: vec![], unify_fieldless_variants: true, @@ -45,8 +44,12 @@ pub fn expand_deriving_hash( hash_trait_def.expand(cx, mitem, item, push); } -fn hash_substructure(cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>) -> P<Expr> { - let [state_expr] = substr.nonself_args else { +fn hash_substructure( + cx: &mut ExtCtxt<'_>, + trait_span: Span, + substr: &Substructure<'_>, +) -> BlockOrExpr { + let [state_expr] = substr.nonselflike_args else { cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`"); }; let call_hash = |span, thing_expr| { @@ -79,8 +82,9 @@ fn hash_substructure(cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructu }; stmts.extend( - fields.iter().map(|FieldInfo { ref self_, span, .. }| call_hash(*span, self_.clone())), + fields + .iter() + .map(|FieldInfo { ref self_expr, span, .. }| call_hash(*span, self_expr.clone())), ); - - cx.expr_block(cx.block(trait_span, stmts)) + BlockOrExpr::new_stmts(stmts) } diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 05457ce15e9..50d8fc30d7d 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -66,7 +66,11 @@ fn emit_module( let work_product = if backend_config.disable_incr_cache { None } else { - rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(tcx.sess, &name, &tmp_file) + rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( + tcx.sess, + &name, + &[("o", &tmp_file)], + ) }; ModuleCodegenResult( @@ -82,7 +86,10 @@ fn reuse_workproduct_for_cgu( ) -> CompiledModule { let work_product = cgu.previous_work_product(tcx); let obj_out = tcx.output_filenames(()).temp_path(OutputType::Object, Some(cgu.name().as_str())); - let source_file = rustc_incremental::in_incr_comp_dir_sess(&tcx.sess, &work_product.saved_file); + let source_file = rustc_incremental::in_incr_comp_dir_sess( + &tcx.sess, + &work_product.saved_files.get("o").expect("no saved object file in work product"), + ); if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) { tcx.sess.err(&format!( "unable to copy {} to {}: {}", diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index ce341406eaf..d156f874434 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -12,7 +12,6 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::ScalarInt; use rustc_middle::ty::layout::{TyAndLayout, LayoutOf}; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; -use rustc_span::Symbol; use rustc_target::abi::{self, HasDataLayout, Pointer, Size}; use crate::consts::const_alloc_to_gcc; @@ -125,12 +124,15 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { self.context.new_rvalue_from_double(typ, val) } - fn const_str(&self, s: Symbol) -> (RValue<'gcc>, RValue<'gcc>) { - let s_str = s.as_str(); - let str_global = *self.const_str_cache.borrow_mut().entry(s).or_insert_with(|| { - self.global_string(s_str) - }); - let len = s_str.len(); + fn const_str(&self, s: &str) -> (RValue<'gcc>, RValue<'gcc>) { + let str_global = *self + .const_str_cache + .borrow_mut() + .raw_entry_mut() + .from_key(s) + .or_insert_with(|| (s.to_owned(), self.global_string(s))) + .1; + let len = s.len(); let cs = self.const_ptrcast(str_global.get_address(None), self.type_ptr_to(self.layout_of(self.tcx.types.str_).gcc_type(self, true)), ); diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 44f36cfa4ca..478f6d893dd 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -13,7 +13,7 @@ use rustc_middle::mir::mono::CodegenUnit; use rustc_middle::ty::{self, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt}; use rustc_middle::ty::layout::{FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, TyAndLayout, LayoutOfHelpers}; use rustc_session::Session; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; use rustc_target::abi::{call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx}; use rustc_target::spec::{HasTargetSpec, Target, TlsModel}; @@ -101,7 +101,7 @@ pub struct CodegenCx<'gcc, 'tcx> { pub global_lvalues: RefCell<FxHashMap<RValue<'gcc>, LValue<'gcc>>>, /// Cache of constant strings, - pub const_str_cache: RefCell<FxHashMap<Symbol, LValue<'gcc>>>, + pub const_str_cache: RefCell<FxHashMap<String, LValue<'gcc>>>, /// Cache of globals. pub globals: RefCell<FxHashMap<String, RValue<'gcc>>>, diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 5bfdeb8b93a..399830de84c 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -6,7 +6,14 @@ * TODO(antoyo): remove the patches. */ -#![feature(rustc_private, decl_macro, associated_type_bounds, never_type, trusted_len)] +#![feature( + rustc_private, + decl_macro, + associated_type_bounds, + never_type, + trusted_len, + hash_raw_entry +)] #![allow(broken_intra_doc_links)] #![recursion_limit="256"] #![warn(rust_2018_idioms)] diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs index d37aadeb523..fc20dee4c74 100644 --- a/compiler/rustc_codegen_llvm/src/common.rs +++ b/compiler/rustc_codegen_llvm/src/common.rs @@ -14,7 +14,6 @@ use rustc_middle::bug; use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc, Scalar}; use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; use rustc_middle::ty::ScalarInt; -use rustc_span::symbol::Symbol; use rustc_target::abi::{self, AddressSpace, HasDataLayout, Pointer, Size}; use libc::{c_char, c_uint}; @@ -180,22 +179,27 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> { unsafe { llvm::LLVMConstReal(t, val) } } - fn const_str(&self, s: Symbol) -> (&'ll Value, &'ll Value) { - let s_str = s.as_str(); - let str_global = *self.const_str_cache.borrow_mut().entry(s).or_insert_with(|| { - let sc = self.const_bytes(s_str.as_bytes()); - let sym = self.generate_local_symbol_name("str"); - let g = self.define_global(&sym, self.val_ty(sc)).unwrap_or_else(|| { - bug!("symbol `{}` is already defined", sym); - }); - unsafe { - llvm::LLVMSetInitializer(g, sc); - llvm::LLVMSetGlobalConstant(g, True); - llvm::LLVMRustSetLinkage(g, llvm::Linkage::InternalLinkage); - } - g - }); - let len = s_str.len(); + fn const_str(&self, s: &str) -> (&'ll Value, &'ll Value) { + let str_global = *self + .const_str_cache + .borrow_mut() + .raw_entry_mut() + .from_key(s) + .or_insert_with(|| { + let sc = self.const_bytes(s.as_bytes()); + let sym = self.generate_local_symbol_name("str"); + let g = self.define_global(&sym, self.val_ty(sc)).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", sym); + }); + unsafe { + llvm::LLVMSetInitializer(g, sc); + llvm::LLVMSetGlobalConstant(g, True); + llvm::LLVMRustSetLinkage(g, llvm::Linkage::InternalLinkage); + } + (s.to_owned(), g) + }) + .1; + let len = s.len(); let cs = consts::ptrcast( str_global, self.type_ptr_to(self.layout_of(self.tcx.types.str_).llvm_type(self)), diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index c007728095f..55e4a4a7255 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -26,7 +26,6 @@ use rustc_session::config::{BranchProtection, CFGuard, CFProtection}; use rustc_session::config::{CrateType, DebugInfo, PAuthKey, PacRet}; use rustc_session::Session; use rustc_span::source_map::Span; -use rustc_span::symbol::Symbol; use rustc_target::abi::{ call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx, }; @@ -56,7 +55,7 @@ pub struct CodegenCx<'ll, 'tcx> { pub vtables: RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), &'ll Value>>, /// Cache of constant strings, - pub const_str_cache: RefCell<FxHashMap<Symbol, &'ll Value>>, + pub const_str_cache: RefCell<FxHashMap<String, &'ll Value>>, /// Reverse-direction for const ptrs cast from globals. /// diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 87fbb737ea8..8fc8118849b 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -93,8 +93,9 @@ impl<'tcx> UniqueTypeId<'tcx> { /// Right now this takes the form of a hex-encoded opaque hash value. pub fn generate_unique_id_string(self, tcx: TyCtxt<'tcx>) -> String { let mut hasher = StableHasher::new(); - let mut hcx = tcx.create_stable_hashing_context(); - hcx.while_hashing_spans(false, |hcx| self.hash_stable(hcx, &mut hasher)); + tcx.with_stable_hashing_context(|mut hcx| { + hcx.while_hashing_spans(false, |hcx| self.hash_stable(hcx, &mut hasher)) + }); hasher.finish::<Fingerprint>().to_hex() } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 64ecbc82c56..730048d061b 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -103,14 +103,14 @@ impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> { // for macOS to understand. For more info see #11352 // This can be overridden using --llvm-opts -dwarf-version,N. // Android has the same issue (#22398) - if let Some(version) = sess.target.dwarf_version { - llvm::LLVMRustAddModuleFlag( - self.llmod, - llvm::LLVMModFlagBehavior::Warning, - "Dwarf Version\0".as_ptr().cast(), - version, - ) - } + let dwarf_version = + sess.opts.debugging_opts.dwarf_version.unwrap_or(sess.target.default_dwarf_version); + llvm::LLVMRustAddModuleFlag( + self.llmod, + llvm::LLVMModFlagBehavior::Warning, + "Dwarf Version\0".as_ptr().cast(), + dwarf_version, + ); // Indicate that we want CodeView debug information on MSVC if sess.target.is_like_msvc { diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 6713a756735..a7dd8e16d28 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -5,6 +5,7 @@ //! This API is completely unstable and subject to change. #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(hash_raw_entry)] #![feature(let_chains)] #![feature(let_else)] #![feature(extern_types)] diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 553486ae8ec..1cb8d342381 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -1,13 +1,12 @@ use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_session::cstore::DllImport; use rustc_session::Session; -use rustc_span::symbol::Symbol; use std::io; use std::path::{Path, PathBuf}; pub(super) fn find_library( - name: Symbol, + name: &str, verbatim: bool, search_paths: &[PathBuf], sess: &Session, diff --git a/compiler/rustc_codegen_ssa/src/back/command.rs b/compiler/rustc_codegen_ssa/src/back/command.rs index 6c29692bd3b..9b0ba34135c 100644 --- a/compiler/rustc_codegen_ssa/src/back/command.rs +++ b/compiler/rustc_codegen_ssa/src/back/command.rs @@ -7,7 +7,6 @@ use std::io; use std::mem; use std::process::{self, Output}; -use rustc_span::symbol::Symbol; use rustc_target::spec::LldFlavor; #[derive(Clone)] @@ -47,11 +46,6 @@ impl Command { self } - pub fn sym_arg(&mut self, arg: Symbol) -> &mut Command { - self.arg(arg.as_str()); - self - } - pub fn args<I>(&mut self, args: I) -> &mut Command where I: IntoIterator<Item: AsRef<OsStr>>, diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 72aa790c363..1628d580b88 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -151,11 +151,23 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( return; } - let remove_temps_from_module = |module: &CompiledModule| { - if let Some(ref obj) = module.object { - ensure_removed(sess.diagnostic(), obj); - } - }; + let maybe_remove_temps_from_module = + |preserve_objects: bool, preserve_dwarf_objects: bool, module: &CompiledModule| { + if !preserve_objects { + if let Some(ref obj) = module.object { + ensure_removed(sess.diagnostic(), obj); + } + } + + if !preserve_dwarf_objects { + if let Some(ref dwo_obj) = module.dwarf_object { + ensure_removed(sess.diagnostic(), dwo_obj); + } + } + }; + + let remove_temps_from_module = + |module: &CompiledModule| maybe_remove_temps_from_module(false, false, module); // Otherwise, always remove the metadata and allocator module temporaries. if let Some(ref metadata_module) = codegen_results.metadata_module { @@ -177,15 +189,7 @@ pub fn link_binary<'a, B: ArchiveBuilder<'a>>( debug!(?preserve_objects, ?preserve_dwarf_objects); for module in &codegen_results.modules { - if !preserve_objects { - remove_temps_from_module(module); - } - - if !preserve_dwarf_objects { - if let Some(ref obj) = module.dwarf_object { - ensure_removed(sess.diagnostic(), obj); - } - } + maybe_remove_temps_from_module(preserve_objects, preserve_dwarf_objects, module); } }); @@ -354,7 +358,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>( } if let Some(name) = lib.name { let location = - find_library(name, lib.verbatim.unwrap_or(false), &lib_search_paths, sess); + find_library(name.as_str(), lib.verbatim.unwrap_or(false), &lib_search_paths, sess); ab.add_archive(&location, |_| false).unwrap_or_else(|e| { sess.fatal(&format!( "failed to add native library {}: {}", @@ -649,6 +653,7 @@ fn link_dwarf_object<'a>( sess.struct_err("linking dwarf objects with thorin failed") .note(&format!("{:?}", e)) .emit(); + sess.abort_if_errors(); } } } @@ -1117,7 +1122,7 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) { let path = find_sanitizer_runtime(&sess, &filename); let rpath = path.to_str().expect("non-utf8 component in path"); linker.args(&["-Wl,-rpath", "-Xlinker", rpath]); - linker.link_dylib(Symbol::intern(&filename), false, true); + linker.link_dylib(&filename, false, true); } else { let filename = format!("librustc{}_rt.{}.a", channel, name); let path = find_sanitizer_runtime(&sess, &filename).join(&filename); @@ -2199,6 +2204,7 @@ fn add_local_native_libraries( let Some(name) = lib.name else { continue; }; + let name = name.as_str(); // Skip if this library is the same as the last. last = if (lib.name, lib.kind, lib.verbatim) == last { @@ -2362,6 +2368,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( let Some(name) = lib.name else { continue; }; + let name = name.as_str(); if !relevant_lib(sess, lib) { continue; } @@ -2519,7 +2526,7 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>( } let filestem = cratepath.file_stem().unwrap().to_str().unwrap(); cmd.link_rust_dylib( - Symbol::intern(&unlib(&sess.target, filestem)), + &unlib(&sess.target, filestem), parent.unwrap_or_else(|| Path::new("")), ); } @@ -2551,6 +2558,7 @@ fn add_upstream_native_libraries( let Some(name) = lib.name else { continue; }; + let name = name.as_str(); if !relevant_lib(sess, &lib) { continue; } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index b5b63942e2c..955ee245b28 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -16,7 +16,6 @@ use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, S use rustc_middle::ty::TyCtxt; use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip}; use rustc_session::Session; -use rustc_span::symbol::Symbol; use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor}; use cc::windows_registry; @@ -163,13 +162,13 @@ pub fn get_linker<'a>( pub trait Linker { fn cmd(&mut self) -> &mut Command; fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path); - fn link_dylib(&mut self, lib: Symbol, verbatim: bool, as_needed: bool); - fn link_rust_dylib(&mut self, lib: Symbol, path: &Path); - fn link_framework(&mut self, framework: Symbol, as_needed: bool); - fn link_staticlib(&mut self, lib: Symbol, verbatim: bool); + fn link_dylib(&mut self, lib: &str, verbatim: bool, as_needed: bool); + fn link_rust_dylib(&mut self, lib: &str, path: &Path); + fn link_framework(&mut self, framework: &str, as_needed: bool); + fn link_staticlib(&mut self, lib: &str, verbatim: bool); fn link_rlib(&mut self, lib: &Path); fn link_whole_rlib(&mut self, lib: &Path); - fn link_whole_staticlib(&mut self, lib: Symbol, verbatim: bool, search_path: &[PathBuf]); + fn link_whole_staticlib(&mut self, lib: &str, verbatim: bool, search_path: &[PathBuf]); fn include_path(&mut self, path: &Path); fn framework_path(&mut self, path: &Path); fn output_filename(&mut self, path: &Path); @@ -423,8 +422,8 @@ impl<'a> Linker for GccLinker<'a> { } } - fn link_dylib(&mut self, lib: Symbol, verbatim: bool, as_needed: bool) { - if self.sess.target.os == "illumos" && lib.as_str() == "c" { + fn link_dylib(&mut self, lib: &str, verbatim: bool, as_needed: bool) { + if self.sess.target.os == "illumos" && lib == "c" { // libc will be added via late_link_args on illumos so that it will // appear last in the library search order. // FIXME: This should be replaced by a more complete and generic @@ -454,7 +453,7 @@ impl<'a> Linker for GccLinker<'a> { } } } - fn link_staticlib(&mut self, lib: Symbol, verbatim: bool) { + fn link_staticlib(&mut self, lib: &str, verbatim: bool) { self.hint_static(); self.cmd.arg(format!("-l{}{}", if verbatim { ":" } else { "" }, lib)); } @@ -484,20 +483,20 @@ impl<'a> Linker for GccLinker<'a> { self.linker_arg("-znorelro"); } - fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) { + fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { self.hint_dynamic(); self.cmd.arg(format!("-l{}", lib)); } - fn link_framework(&mut self, framework: Symbol, as_needed: bool) { + fn link_framework(&mut self, framework: &str, as_needed: bool) { self.hint_dynamic(); if !as_needed { // FIXME(81490): ld64 as of macOS 11 supports the -needed_framework // flag but we have no way to detect that here. - // self.cmd.arg("-needed_framework").sym_arg(framework); + // self.cmd.arg("-needed_framework").arg(framework); self.sess.warn("`as-needed` modifier not implemented yet for ld64"); } - self.cmd.arg("-framework").sym_arg(framework); + self.cmd.arg("-framework").arg(framework); } // Here we explicitly ask that the entire archive is included into the @@ -506,7 +505,7 @@ impl<'a> Linker for GccLinker<'a> { // don't otherwise explicitly reference them. This can occur for // libraries which are just providing bindings, libraries with generic // functions, etc. - fn link_whole_staticlib(&mut self, lib: Symbol, verbatim: bool, search_path: &[PathBuf]) { + fn link_whole_staticlib(&mut self, lib: &str, verbatim: bool, search_path: &[PathBuf]) { self.hint_static(); let target = &self.sess.target; if !target.is_like_osx { @@ -836,11 +835,11 @@ impl<'a> Linker for MsvcLinker<'a> { self.cmd.arg("/OPT:NOREF,NOICF"); } - fn link_dylib(&mut self, lib: Symbol, verbatim: bool, _as_needed: bool) { + fn link_dylib(&mut self, lib: &str, verbatim: bool, _as_needed: bool) { self.cmd.arg(format!("{}{}", lib, if verbatim { "" } else { ".lib" })); } - fn link_rust_dylib(&mut self, lib: Symbol, path: &Path) { + fn link_rust_dylib(&mut self, lib: &str, path: &Path) { // When producing a dll, the MSVC linker may not actually emit a // `foo.lib` file if the dll doesn't actually export any symbols, so we // check to see if the file is there and just omit linking to it if it's @@ -851,7 +850,7 @@ impl<'a> Linker for MsvcLinker<'a> { } } - fn link_staticlib(&mut self, lib: Symbol, verbatim: bool) { + fn link_staticlib(&mut self, lib: &str, verbatim: bool) { self.cmd.arg(format!("{}{}", lib, if verbatim { "" } else { ".lib" })); } @@ -890,11 +889,11 @@ impl<'a> Linker for MsvcLinker<'a> { fn framework_path(&mut self, _path: &Path) { bug!("frameworks are not supported on windows") } - fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) { + fn link_framework(&mut self, _framework: &str, _as_needed: bool) { bug!("frameworks are not supported on windows") } - fn link_whole_staticlib(&mut self, lib: Symbol, verbatim: bool, _search_path: &[PathBuf]) { + fn link_whole_staticlib(&mut self, lib: &str, verbatim: bool, _search_path: &[PathBuf]) { self.cmd.arg(format!("/WHOLEARCHIVE:{}{}", lib, if verbatim { "" } else { ".lib" })); } fn link_whole_rlib(&mut self, path: &Path) { @@ -1047,8 +1046,8 @@ impl<'a> Linker for EmLinker<'a> { self.cmd.arg("-L").arg(path); } - fn link_staticlib(&mut self, lib: Symbol, _verbatim: bool) { - self.cmd.arg("-l").sym_arg(lib); + fn link_staticlib(&mut self, lib: &str, _verbatim: bool) { + self.cmd.arg("-l").arg(lib); } fn output_filename(&mut self, path: &Path) { @@ -1059,12 +1058,12 @@ impl<'a> Linker for EmLinker<'a> { self.cmd.arg(path); } - fn link_dylib(&mut self, lib: Symbol, verbatim: bool, _as_needed: bool) { + fn link_dylib(&mut self, lib: &str, verbatim: bool, _as_needed: bool) { // Emscripten always links statically self.link_staticlib(lib, verbatim); } - fn link_whole_staticlib(&mut self, lib: Symbol, verbatim: bool, _search_path: &[PathBuf]) { + fn link_whole_staticlib(&mut self, lib: &str, verbatim: bool, _search_path: &[PathBuf]) { // not supported? self.link_staticlib(lib, verbatim); } @@ -1074,7 +1073,7 @@ impl<'a> Linker for EmLinker<'a> { self.link_rlib(lib); } - fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) { + fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { self.link_dylib(lib, false, true); } @@ -1098,7 +1097,7 @@ impl<'a> Linker for EmLinker<'a> { bug!("frameworks are not supported on Emscripten") } - fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) { + fn link_framework(&mut self, _framework: &str, _as_needed: bool) { bug!("frameworks are not supported on Emscripten") } @@ -1237,12 +1236,12 @@ impl<'a> Linker for WasmLd<'a> { } } - fn link_dylib(&mut self, lib: Symbol, _verbatim: bool, _as_needed: bool) { - self.cmd.arg("-l").sym_arg(lib); + fn link_dylib(&mut self, lib: &str, _verbatim: bool, _as_needed: bool) { + self.cmd.arg("-l").arg(lib); } - fn link_staticlib(&mut self, lib: Symbol, _verbatim: bool) { - self.cmd.arg("-l").sym_arg(lib); + fn link_staticlib(&mut self, lib: &str, _verbatim: bool) { + self.cmd.arg("-l").arg(lib); } fn link_rlib(&mut self, lib: &Path) { @@ -1271,16 +1270,16 @@ impl<'a> Linker for WasmLd<'a> { fn no_relro(&mut self) {} - fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) { - self.cmd.arg("-l").sym_arg(lib); + fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { + self.cmd.arg("-l").arg(lib); } - fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) { + fn link_framework(&mut self, _framework: &str, _as_needed: bool) { panic!("frameworks not supported") } - fn link_whole_staticlib(&mut self, lib: Symbol, _verbatim: bool, _search_path: &[PathBuf]) { - self.cmd.arg("-l").sym_arg(lib); + fn link_whole_staticlib(&mut self, lib: &str, _verbatim: bool, _search_path: &[PathBuf]) { + self.cmd.arg("-l").arg(lib); } fn link_whole_rlib(&mut self, lib: &Path) { @@ -1360,10 +1359,10 @@ pub struct L4Bender<'a> { } impl<'a> Linker for L4Bender<'a> { - fn link_dylib(&mut self, _lib: Symbol, _verbatim: bool, _as_needed: bool) { + fn link_dylib(&mut self, _lib: &str, _verbatim: bool, _as_needed: bool) { bug!("dylibs are not supported on L4Re"); } - fn link_staticlib(&mut self, lib: Symbol, _verbatim: bool) { + fn link_staticlib(&mut self, lib: &str, _verbatim: bool) { self.hint_static(); self.cmd.arg(format!("-PC{}", lib)); } @@ -1404,15 +1403,15 @@ impl<'a> Linker for L4Bender<'a> { fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {} - fn link_rust_dylib(&mut self, _: Symbol, _: &Path) { + fn link_rust_dylib(&mut self, _: &str, _: &Path) { panic!("Rust dylibs not supported"); } - fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) { + fn link_framework(&mut self, _framework: &str, _as_needed: bool) { bug!("frameworks not supported on L4Re"); } - fn link_whole_staticlib(&mut self, lib: Symbol, _verbatim: bool, _search_path: &[PathBuf]) { + fn link_whole_staticlib(&mut self, lib: &str, _verbatim: bool, _search_path: &[PathBuf]) { self.hint_static(); self.cmd.arg("--whole-archive").arg(format!("-l{}", lib)); self.cmd.arg("--no-whole-archive"); @@ -1617,19 +1616,19 @@ impl<'a> Linker for PtxLinker<'a> { self.cmd.arg("-o").arg(path); } - fn link_dylib(&mut self, _lib: Symbol, _verbatim: bool, _as_needed: bool) { + fn link_dylib(&mut self, _lib: &str, _verbatim: bool, _as_needed: bool) { panic!("external dylibs not supported") } - fn link_rust_dylib(&mut self, _lib: Symbol, _path: &Path) { + fn link_rust_dylib(&mut self, _lib: &str, _path: &Path) { panic!("external dylibs not supported") } - fn link_staticlib(&mut self, _lib: Symbol, _verbatim: bool) { + fn link_staticlib(&mut self, _lib: &str, _verbatim: bool) { panic!("staticlibs not supported") } - fn link_whole_staticlib(&mut self, _lib: Symbol, _verbatim: bool, _search_path: &[PathBuf]) { + fn link_whole_staticlib(&mut self, _lib: &str, _verbatim: bool, _search_path: &[PathBuf]) { panic!("staticlibs not supported") } @@ -1637,7 +1636,7 @@ impl<'a> Linker for PtxLinker<'a> { panic!("frameworks not supported") } - fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) { + fn link_framework(&mut self, _framework: &str, _as_needed: bool) { panic!("frameworks not supported") } @@ -1717,19 +1716,19 @@ impl<'a> Linker for BpfLinker<'a> { self.cmd.arg("-o").arg(path); } - fn link_dylib(&mut self, _lib: Symbol, _verbatim: bool, _as_needed: bool) { + fn link_dylib(&mut self, _lib: &str, _verbatim: bool, _as_needed: bool) { panic!("external dylibs not supported") } - fn link_rust_dylib(&mut self, _lib: Symbol, _path: &Path) { + fn link_rust_dylib(&mut self, _lib: &str, _path: &Path) { panic!("external dylibs not supported") } - fn link_staticlib(&mut self, _lib: Symbol, _verbatim: bool) { + fn link_staticlib(&mut self, _lib: &str, _verbatim: bool) { panic!("staticlibs not supported") } - fn link_whole_staticlib(&mut self, _lib: Symbol, _verbatim: bool, _search_path: &[PathBuf]) { + fn link_whole_staticlib(&mut self, _lib: &str, _verbatim: bool, _search_path: &[PathBuf]) { panic!("staticlibs not supported") } @@ -1737,7 +1736,7 @@ impl<'a> Linker for BpfLinker<'a> { panic!("frameworks not supported") } - fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) { + fn link_framework(&mut self, _framework: &str, _as_needed: bool) { panic!("frameworks not supported") } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 632f07c5c2d..f4a5cac872e 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -494,12 +494,18 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( let _timer = sess.timer("copy_all_cgu_workproducts_to_incr_comp_cache_dir"); for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) { - if let Some(path) = &module.object { - if let Some((id, product)) = - copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, path) - { - work_products.insert(id, product); - } + let mut files = Vec::new(); + if let Some(object_file_path) = &module.object { + files.push(("o", object_file_path.as_path())); + } + if let Some(dwarf_object_file_path) = &module.dwarf_object { + files.push(("dwo", dwarf_object_file_path.as_path())); + } + + if let Some((id, product)) = + copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, files.as_slice()) + { + work_products.insert(id, product); } } @@ -856,29 +862,50 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>( assert!(module_config.emit_obj != EmitObj::None); let incr_comp_session_dir = cgcx.incr_comp_session_dir.as_ref().unwrap(); - let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)); - let source_file = in_incr_comp_dir(&incr_comp_session_dir, &module.source.saved_file); - debug!( - "copying pre-existing module `{}` from {:?} to {}", - module.name, - source_file, - obj_out.display() + + let load_from_incr_comp_dir = |output_path: PathBuf, saved_path: &str| { + let source_file = in_incr_comp_dir(&incr_comp_session_dir, saved_path); + debug!( + "copying pre-existing module `{}` from {:?} to {}", + module.name, + source_file, + output_path.display() + ); + match link_or_copy(&source_file, &output_path) { + Ok(_) => Some(output_path), + Err(err) => { + let diag_handler = cgcx.create_diag_handler(); + diag_handler.err(&format!( + "unable to copy {} to {}: {}", + source_file.display(), + output_path.display(), + err + )); + None + } + } + }; + + let object = load_from_incr_comp_dir( + cgcx.output_filenames.temp_path(OutputType::Object, Some(&module.name)), + &module.source.saved_files.get("o").expect("no saved object file in work product"), ); - if let Err(err) = link_or_copy(&source_file, &obj_out) { - let diag_handler = cgcx.create_diag_handler(); - diag_handler.err(&format!( - "unable to copy {} to {}: {}", - source_file.display(), - obj_out.display(), - err - )); - } + let dwarf_object = + module.source.saved_files.get("dwo").as_ref().and_then(|saved_dwarf_object_file| { + let dwarf_obj_out = cgcx + .output_filenames + .split_dwarf_path(cgcx.split_debuginfo, cgcx.split_dwarf_kind, Some(&module.name)) + .expect( + "saved dwarf object in work product but `split_dwarf_path` returned `None`", + ); + load_from_incr_comp_dir(dwarf_obj_out, &saved_dwarf_object_file) + }); WorkItemResult::Compiled(CompiledModule { name: module.name, kind: ModuleKind::Regular, - object: Some(obj_out), - dwarf_object: None, + object, + dwarf_object, bytecode: None, }) } diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs index 8755d91818d..8cd5a0fc247 100644 --- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -701,16 +701,20 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S // If we cannot evaluate the constant to a known type, we fall back // to emitting a stable hash value of the constant. This isn't very pretty // but we get a deterministic, virtually unique value for the constant. - let hcx = &mut tcx.create_stable_hashing_context(); - let mut hasher = StableHasher::new(); - let ct = ct.eval(tcx, ty::ParamEnv::reveal_all()); - hcx.while_hashing_spans(false, |hcx| ct.to_valtree().hash_stable(hcx, &mut hasher)); + // // Let's only emit 64 bits of the hash value. That should be plenty for // avoiding collisions and will make the emitted type names shorter. - // Note: Don't use `StableHashResult` impl of `u64` here directly, since that - // would lead to endianness problems. - let hash: u128 = hasher.finish(); - let hash_short = (hash.to_le() as u64).to_le(); + let hash_short = tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + let ct = ct.eval(tcx, ty::ParamEnv::reveal_all()); + hcx.while_hashing_spans(false, |hcx| { + ct.to_valtree().hash_stable(hcx, &mut hasher) + }); + // Note: Don't use `StableHashResult` impl of `u64` here directly, since that + // would lead to endianness problems. + let hash: u128 = hasher.finish(); + (hash.to_le() as u64).to_le() + }); if cpp_like_debuginfo(tcx) { write!(output, "CONST${:x}", hash_short) diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 5c26168b50d..24da48ead63 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -15,7 +15,7 @@ pub fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx: &FunctionCx<'a, 'tcx, Bx>, ) -> BitSet<mir::Local> { let mir = fx.mir; - let dominators = mir.dominators(); + let dominators = mir.basic_blocks.dominators(); let locals = mir .local_decls .iter() diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index f296a04dea1..b8e3cb32ef6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -489,8 +489,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (LangItem::PanicBoundsCheck, vec![index, len, location]) } _ => { - let msg_str = Symbol::intern(msg.description()); - let msg = bx.const_str(msg_str); + let msg = bx.const_str(msg.description()); // It's `pub fn panic(expr: &str)`, with the wide reference being passed // as two arguments, and `#[track_caller]` adds an implicit third argument. (LangItem::Panic, vec![msg.0, msg.1, location]) @@ -571,7 +570,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } }) }); - let msg = bx.const_str(Symbol::intern(&msg_str)); + let msg = bx.const_str(&msg_str); let location = self.get_caller_location(bx, source_info).immediate(); // Obtain the panic entry point. diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 7ff12823bf7..55ab8b08d4a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -123,7 +123,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Do not generate stores and GEPis for zero-sized fields. if !op.layout.is_zst() { let field_index = active_field_index.unwrap_or(i); - let field = dest.project_field(&mut bx, field_index); + let field = if let mir::AggregateKind::Array(_) = **kind { + let llindex = bx.cx().const_usize(field_index as u64); + dest.project_index(&mut bx, llindex) + } else { + dest.project_field(&mut bx, field_index) + }; op.val.store(&mut bx, field); } } diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs index c3519a24d53..fdc7a30e841 100644 --- a/compiler/rustc_codegen_ssa/src/traits/consts.rs +++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs @@ -2,7 +2,6 @@ use super::BackendTypes; use crate::mir::place::PlaceRef; use rustc_middle::mir::interpret::{ConstAllocation, Scalar}; use rustc_middle::ty::layout::TyAndLayout; -use rustc_span::Symbol; use rustc_target::abi::{self, Size}; pub trait ConstMethods<'tcx>: BackendTypes { @@ -21,7 +20,7 @@ pub trait ConstMethods<'tcx>: BackendTypes { fn const_u8(&self, i: u8) -> Self::Value; fn const_real(&self, t: Self::Type, val: f64) -> Self::Value; - fn const_str(&self, s: Symbol) -> (Self::Value, Self::Value); + fn const_str(&self, s: &str) -> (Self::Value, Self::Value); fn const_struct(&self, elts: &[Self::Value], packed: bool) -> Self::Value; fn const_to_opt_uint(&self, v: Self::Value) -> Option<u64>; diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index ac529bf152f..29ab1d18771 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::AssertMessage; use rustc_session::Limit; use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::{Align, Size}; -use rustc_target::spec::abi::Abi; +use rustc_target::spec::abi::Abi as CallAbi; use crate::interpret::{ self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult, @@ -263,7 +263,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, fn find_mir_or_eval_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, - _abi: Abi, + _abi: CallAbi, args: &[OpTy<'tcx>], _dest: &PlaceTy<'tcx>, _ret: Option<mir::BasicBlock>, diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs new file mode 100644 index 00000000000..a463fe7b970 --- /dev/null +++ b/compiler/rustc_const_eval/src/errors.rs @@ -0,0 +1,89 @@ +use rustc_hir::ConstContext; +use rustc_macros::SessionDiagnostic; +use rustc_span::Span; + +#[derive(SessionDiagnostic)] +#[error(const_eval::unstable_in_stable)] +pub(crate) struct UnstableInStable { + pub gate: String, + #[primary_span] + pub span: Span, + #[suggestion( + const_eval::unstable_sugg, + code = "#[rustc_const_unstable(feature = \"...\", issue = \"...\")]\n", + applicability = "has-placeholders" + )] + #[suggestion( + const_eval::bypass_sugg, + code = "#[rustc_allow_const_fn_unstable({gate})]\n", + applicability = "has-placeholders" + )] + pub attr_span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::thread_local_access, code = "E0625")] +pub(crate) struct NonConstOpErr { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::static_access, code = "E0013")] +#[help] +pub(crate) struct StaticAccessErr { + #[primary_span] + pub span: Span, + pub kind: ConstContext, + #[note(const_eval::teach_note)] + #[help(const_eval::teach_help)] + pub teach: Option<()>, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::raw_ptr_to_int)] +#[note] +#[note(const_eval::note2)] +pub(crate) struct RawPtrToIntErr { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::raw_ptr_comparison)] +#[note] +pub(crate) struct RawPtrComparisonErr { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::panic_non_str)] +pub(crate) struct PanicNonStrErr { + #[primary_span] + pub span: Span, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::mut_deref, code = "E0658")] +pub(crate) struct MutDerefErr { + #[primary_span] + pub span: Span, + pub kind: ConstContext, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::transient_mut_borrow, code = "E0658")] +pub(crate) struct TransientMutBorrowErr { + #[primary_span] + pub span: Span, + pub kind: ConstContext, +} + +#[derive(SessionDiagnostic)] +#[error(const_eval::transient_mut_borrow_raw, code = "E0658")] +pub(crate) struct TransientMutBorrowErrRaw { + #[primary_span] + pub span: Span, + pub kind: ConstContext, +} diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index b3461b414b6..7f8eea94aee 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -10,7 +10,7 @@ use rustc_middle::mir; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::DefId; use rustc_target::abi::Size; -use rustc_target::spec::abi::Abi; +use rustc_target::spec::abi::Abi as CallAbi; use super::{ AllocId, AllocRange, Allocation, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult, @@ -138,7 +138,7 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Whether to enforce integers and floats not having provenance. fn enforce_number_no_provenance(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool; - /// Whether function calls should be [ABI](Abi)-checked. + /// Whether function calls should be [ABI](CallAbi)-checked. fn enforce_abi(_ecx: &InterpCx<'mir, 'tcx, Self>) -> bool { true } @@ -169,7 +169,7 @@ pub trait Machine<'mir, 'tcx>: Sized { fn find_mir_or_eval_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, instance: ty::Instance<'tcx>, - abi: Abi, + abi: CallAbi, args: &[OpTy<'tcx, Self::PointerTag>], destination: &PlaceTy<'tcx, Self::PointerTag>, target: Option<mir::BasicBlock>, @@ -181,7 +181,7 @@ pub trait Machine<'mir, 'tcx>: Sized { fn call_extra_fn( ecx: &mut InterpCx<'mir, 'tcx, Self>, fn_val: Self::ExtraFnVal, - abi: Abi, + abi: CallAbi, args: &[OpTy<'tcx, Self::PointerTag>], destination: &PlaceTy<'tcx, Self::PointerTag>, target: Option<mir::BasicBlock>, @@ -483,7 +483,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) { fn call_extra_fn( _ecx: &mut InterpCx<$mir, $tcx, Self>, fn_val: !, - _abi: Abi, + _abi: CallAbi, _args: &[OpTy<$tcx>], _destination: &PlaceTy<$tcx, Self::PointerTag>, _target: Option<mir::BasicBlock>, diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 805dcb38895..576958a855d 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -78,6 +78,7 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { } #[inline] + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Tag> { match self { Immediate::Scalar(val) => val, @@ -87,11 +88,13 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { } #[inline] + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Tag>> { self.to_scalar_or_uninit().check_init() } #[inline] + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) pub fn to_scalar_or_uninit_pair(self) -> (ScalarMaybeUninit<Tag>, ScalarMaybeUninit<Tag>) { match self { Immediate::ScalarPair(val1, val2) => (val1, val2), @@ -101,6 +104,7 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> { } #[inline] + #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> { let (val1, val2) = self.to_scalar_or_uninit_pair(); Ok((val1.check_init()?, val2.check_init()?)) diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 0bf78446e37..08102585a7b 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -427,7 +427,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, ' err_ub!(DanglingIntPointer(0, _)) => { "a null {kind}" }, err_ub!(DanglingIntPointer(i, _)) => - { "a dangling {kind} (address 0x{i:x} is unallocated)" }, + { "a dangling {kind} (address {i:#x} is unallocated)" }, err_ub!(PointerOutOfBounds { .. }) => { "a dangling {kind} (going beyond the bounds of its allocation)" }, // This cannot happen during const-eval (because interning already detects @@ -941,7 +941,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> // element that byte belongs to so we can // provide an index. let i = usize::try_from( - access.uninit_offset.bytes() / layout.size.bytes(), + access.uninit.start.bytes() / layout.size.bytes(), ) .unwrap(); self.path.push(PathElem::ArrayElem(i)); diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 2d42ae236ad..d65d4f7eb72 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -31,6 +31,7 @@ extern crate tracing; extern crate rustc_middle; pub mod const_eval; +mod errors; pub mod interpret; pub mod transform; pub mod util; diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index f87de4f6a08..3dcd96df33c 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -1,6 +1,6 @@ //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations. -use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed}; +use rustc_errors::{Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitSet; @@ -24,6 +24,7 @@ use super::qualifs::{self, CustomEq, HasMutInterior, NeedsDrop, NeedsNonConstDro use super::resolver::FlowSensitiveAnalysis; use super::{ConstCx, Qualif}; use crate::const_eval::is_unstable_const_fn; +use crate::errors::UnstableInStable; type QualifResults<'mir, 'tcx, Q> = rustc_mir_dataflow::ResultsCursor<'mir, 'tcx, FlowSensitiveAnalysis<'mir, 'mir, 'tcx, Q>>; @@ -1026,23 +1027,5 @@ fn is_int_bool_or_char(ty: Ty<'_>) -> bool { fn emit_unstable_in_stable_error(ccx: &ConstCx<'_, '_>, span: Span, gate: Symbol) { let attr_span = ccx.tcx.def_span(ccx.def_id()).shrink_to_lo(); - ccx.tcx - .sess - .struct_span_err( - span, - &format!("const-stable function cannot use `#[feature({})]`", gate.as_str()), - ) - .span_suggestion( - attr_span, - "if it is not part of the public API, make this function unstably const", - concat!(r#"#[rustc_const_unstable(feature = "...", issue = "...")]"#, '\n'), - Applicability::HasPlaceholders, - ) - .span_suggestion( - attr_span, - "otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks", - format!("#[rustc_allow_const_fn_unstable({})]\n", gate), - Applicability::MaybeIncorrect, - ) - .emit(); + ccx.tcx.sess.emit_err(UnstableInStable { gate: gate.to_string(), span, attr_span }); } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 9574661282b..17376e59e09 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -1,7 +1,9 @@ //! Concrete error types for all operations which may be invalid in a certain const context. use hir::def_id::LocalDefId; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{ + error_code, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, +}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; @@ -20,6 +22,10 @@ use rustc_span::{BytePos, Pos, Span, Symbol}; use rustc_trait_selection::traits::SelectionContext; use super::ConstCx; +use crate::errors::{ + MutDerefErr, NonConstOpErr, PanicNonStrErr, RawPtrComparisonErr, RawPtrToIntErr, + StaticAccessErr, TransientMutBorrowErr, TransientMutBorrowErrRaw, +}; use crate::util::{call_kind, CallDesugaringKind, CallKind}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -590,17 +596,17 @@ impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let raw = match self.0 { - hir::BorrowKind::Raw => "raw ", - hir::BorrowKind::Ref => "", - }; - - feature_err( - &ccx.tcx.sess.parse_sess, - sym::const_mut_refs, - span, - &format!("{}mutable references are not allowed in {}s", raw, ccx.const_kind()), - ) + let kind = ccx.const_kind(); + match self.0 { + hir::BorrowKind::Raw => ccx + .tcx + .sess + .create_feature_err(TransientMutBorrowErrRaw { span, kind }, sym::const_mut_refs), + hir::BorrowKind::Ref => ccx + .tcx + .sess + .create_feature_err(TransientMutBorrowErr { span, kind }, sym::const_mut_refs), + } } } @@ -621,12 +627,9 @@ impl<'tcx> NonConstOp<'tcx> for MutDeref { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - feature_err( - &ccx.tcx.sess.parse_sess, - sym::const_mut_refs, - span, - &format!("mutation through a reference is not allowed in {}s", ccx.const_kind()), - ) + ccx.tcx + .sess + .create_feature_err(MutDerefErr { span, kind: ccx.const_kind() }, sym::const_mut_refs) } } @@ -639,10 +642,7 @@ impl<'tcx> NonConstOp<'tcx> for PanicNonStr { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - ccx.tcx.sess.struct_span_err( - span, - "argument to `panic!()` in a const context must have type `&str`", - ) + ccx.tcx.sess.create_err(PanicNonStrErr { span }) } } @@ -657,15 +657,7 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrComparison { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let mut err = ccx - .tcx - .sess - .struct_span_err(span, "pointers cannot be reliably compared during const eval"); - err.note( - "see issue #53020 <https://github.com/rust-lang/rust/issues/53020> \ - for more information", - ); - err + ccx.tcx.sess.create_err(RawPtrComparisonErr { span }) } } @@ -701,15 +693,7 @@ impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let mut err = ccx - .tcx - .sess - .struct_span_err(span, "pointers cannot be cast to integers during const eval"); - err.note("at compile-time, pointers do not have an integer value"); - err.note( - "avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior", - ); - err + ccx.tcx.sess.create_err(RawPtrToIntErr { span }) } } @@ -730,24 +714,11 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let mut err = struct_span_err!( - ccx.tcx.sess, + ccx.tcx.sess.create_err(StaticAccessErr { span, - E0013, - "{}s cannot refer to statics", - ccx.const_kind() - ); - err.help( - "consider extracting the value of the `static` to a `const`, and referring to that", - ); - if ccx.tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "`static` and `const` variables can refer to other `const` variables. \ - A `const` variable, however, cannot refer to a `static` variable.", - ); - err.help("To fix this, the value can be extracted to a `const` and then used."); - } - err + kind: ccx.const_kind(), + teach: ccx.tcx.sess.teach(&error_code!(E0013)).then_some(()), + }) } } @@ -760,13 +731,7 @@ impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess { ccx: &ConstCx<'_, 'tcx>, span: Span, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - struct_span_err!( - ccx.tcx.sess, - span, - E0625, - "thread-local statics cannot be \ - accessed at compile-time" - ) + ccx.tcx.sess.create_err(NonConstOpErr { span }) } } diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 6298fa7f062..12527a9b2ae 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -856,7 +856,8 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { literal: ConstantKind::from_const(_const, tcx), })) }; - let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut(); + let blocks = self.source.basic_blocks.as_mut(); + let local_decls = &mut self.source.local_decls; let loc = candidate.location; let statement = &mut blocks[loc.block].statements[loc.statement_index]; match statement.kind { @@ -865,7 +866,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> { Rvalue::Ref(ref mut region, borrow_kind, ref mut place), )) => { // Use the underlying local for this (necessarily interior) borrow. - let ty = local_decls.local_decls()[place.local].ty; + let ty = local_decls[place.local].ty; let span = statement.source_info.span; let ref_ty = tcx.mk_ref( diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 7b9c6329d32..56ecc553528 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -12,6 +12,7 @@ use rustc_middle::mir::{ Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK, }; use rustc_middle::ty::fold::BottomUpFolder; +use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::storage::always_live_locals; @@ -275,7 +276,14 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } }; - match parent_ty.ty.kind() { + let kind = match parent_ty.ty.kind() { + &ty::Opaque(def_id, substs) => { + self.tcx.bound_type_of(def_id).subst(self.tcx, substs).kind() + } + kind => kind, + }; + + match kind { ty::Tuple(fields) => { let Some(f_ty) = fields.get(f.as_usize()) else { fail_out_of_bounds(self, location); @@ -299,12 +307,39 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { }; check_equal(self, location, f_ty); } - ty::Generator(_, substs, _) => { - let substs = substs.as_generator(); - let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else { - fail_out_of_bounds(self, location); - return; + &ty::Generator(def_id, substs, _) => { + let f_ty = if let Some(var) = parent_ty.variant_index { + let gen_body = if def_id == self.body.source.def_id() { + self.body + } else { + self.tcx.optimized_mir(def_id) + }; + + let Some(layout) = gen_body.generator_layout() else { + self.fail(location, format!("No generator layout for {:?}", parent_ty)); + return; + }; + + let Some(&local) = layout.variant_fields[var].get(f) else { + fail_out_of_bounds(self, location); + return; + }; + + let Some(&f_ty) = layout.field_tys.get(local) else { + self.fail(location, format!("Out of bounds local {:?} for {:?}", local, parent_ty)); + return; + }; + + f_ty + } else { + let Some(f_ty) = substs.as_generator().prefix_tys().nth(f.index()) else { + fail_out_of_bounds(self, location); + return; + }; + + f_ty }; + check_equal(self, location, f_ty); } _ => { @@ -328,6 +363,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { { self.fail(location, format!("{:?}, has deref at the wrong place", place)); } + + self.super_place(place, cntxt, location); } fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 390a44d3f33..0a2d2b40709 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -10,6 +10,7 @@ #![feature(array_windows)] #![feature(associated_type_bounds)] #![feature(auto_traits)] +#![feature(cell_leak)] #![feature(control_flow_enum)] #![feature(extend_one)] #![feature(let_else)] diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 4437c0b1b69..cf0940df9e4 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -539,6 +539,33 @@ impl<T> RwLock<T> { pub fn borrow_mut(&self) -> WriteGuard<'_, T> { self.write() } + + #[cfg(not(parallel_compiler))] + #[inline(always)] + pub fn clone_guard<'a>(rg: &ReadGuard<'a, T>) -> ReadGuard<'a, T> { + ReadGuard::clone(rg) + } + + #[cfg(parallel_compiler)] + #[inline(always)] + pub fn clone_guard<'a>(rg: &ReadGuard<'a, T>) -> ReadGuard<'a, T> { + ReadGuard::rwlock(&rg).read() + } + + #[cfg(not(parallel_compiler))] + #[inline(always)] + pub fn leak(&self) -> &T { + ReadGuard::leak(self.read()) + } + + #[cfg(parallel_compiler)] + #[inline(always)] + pub fn leak(&self) -> &T { + let guard = self.read(); + let ret = unsafe { &*(&*guard as *const T) }; + std::mem::forget(guard); + ret + } } // FIXME: Probably a bad idea diff --git a/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl b/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl index 4a42d52f710..1d3e33c8185 100644 --- a/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl +++ b/compiler/rustc_error_messages/locales/en-US/builtin_macros.ftl @@ -1,5 +1,5 @@ -builtin_macros-requires-cfg-pattern = +builtin-macros-requires-cfg-pattern = macro requires a cfg-pattern as an argument .label = cfg-pattern required -builtin_macros-expected-one-cfg-pattern = expected 1 cfg-pattern +builtin-macros-expected-one-cfg-pattern = expected 1 cfg-pattern diff --git a/compiler/rustc_error_messages/locales/en-US/const_eval.ftl b/compiler/rustc_error_messages/locales/en-US/const_eval.ftl new file mode 100644 index 00000000000..3f2ff861001 --- /dev/null +++ b/compiler/rustc_error_messages/locales/en-US/const_eval.ftl @@ -0,0 +1,31 @@ +const-eval-unstable-in-stable = + const-stable function cannot use `#[feature({$gate})]` + .unstable-sugg = if it is not part of the public API, make this function unstably const + .bypass-sugg = otherwise `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks + +const-eval-thread-local-access = + thread-local statics cannot be accessed at compile-time + +const-eval-static-access = + {$kind}s cannot refer to statics + .help = consider extracting the value of the `static` to a `const`, and referring to that + .teach-note = `static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable. + .teach-help = To fix this, the value can be extracted to a `const` and then used. + +const-eval-raw-ptr-to-int = + pointers cannot be cast to integers during const eval + .note = at compile-time, pointers do not have an integer value + .note2 = avoiding this restriction via `transmute`, `union`, or raw pointers leads to compile-time undefined behavior + +const-eval-raw-ptr-comparison = + pointers cannot be reliably compared during const eval + .note = see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information + +const-eval-panic-non-str = argument to `panic!()` in a const context must have type `&str` + +const-eval-mut-deref = + mutation through a reference is not allowed in {$kind}s + +const-eval-transient-mut-borrow = mutable references are not allowed in {$kind}s + +const-eval-transient-mut-borrow-raw = raw mutable references are not allowed in {$kind}s diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 563d0534d8f..5a482bc5b2c 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -37,6 +37,7 @@ fluent_messages! { parser => "../locales/en-US/parser.ftl", privacy => "../locales/en-US/privacy.ftl", typeck => "../locales/en-US/typeck.ftl", + const_eval => "../locales/en-US/const_eval.ftl", } pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES}; diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index 89557626057..7d7e92c5229 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -13,6 +13,7 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_span = { path = "../rustc_span" } rustc_macros = { path = "../rustc_macros" } rustc_data_structures = { path = "../rustc_data_structures" } +rustc_hir = { path = "../rustc_hir" } rustc_lint_defs = { path = "../rustc_lint_defs" } unicode-width = "0.1.4" atty = "0.2" diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 0d1d017d874..9429ad1a897 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -5,6 +5,7 @@ use crate::{ }; use rustc_data_structures::stable_map::FxHashMap; use rustc_error_messages::FluentValue; +use rustc_hir as hir; use rustc_lint_defs::{Applicability, LintExpectationId}; use rustc_span::edition::LATEST_STABLE_EDITION; use rustc_span::symbol::{Ident, Symbol}; @@ -160,6 +161,16 @@ impl<'source> Into<FluentValue<'source>> for DiagnosticArgValue<'source> { } } +impl IntoDiagnosticArg for hir::ConstContext { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + DiagnosticArgValue::Str(Cow::Borrowed(match self { + hir::ConstContext::ConstFn => "constant function", + hir::ConstContext::Static(_) => "static", + hir::ConstContext::Const => "constant", + })) + } +} + /// Trait implemented by error types. This should not be implemented manually. Instead, use /// `#[derive(SessionSubdiagnostic)]` -- see [rustc_macros::SessionSubdiagnostic]. #[rustc_diagnostic_item = "AddSubdiagnostic"] diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 6d74e9a9f2b..85ea8eb3937 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -63,7 +63,7 @@ impl HumanReadableErrorType { bundle: Option<Lrc<FluentBundle>>, fallback_bundle: LazyFallbackBundle, teach: bool, - terminal_width: Option<usize>, + diagnostic_width: Option<usize>, macro_backtrace: bool, ) -> EmitterWriter { let (short, color_config) = self.unzip(); @@ -76,7 +76,7 @@ impl HumanReadableErrorType { short, teach, color, - terminal_width, + diagnostic_width, macro_backtrace, ) } @@ -710,7 +710,7 @@ pub struct EmitterWriter { short_message: bool, teach: bool, ui_testing: bool, - terminal_width: Option<usize>, + diagnostic_width: Option<usize>, macro_backtrace: bool, } @@ -730,7 +730,7 @@ impl EmitterWriter { fallback_bundle: LazyFallbackBundle, short_message: bool, teach: bool, - terminal_width: Option<usize>, + diagnostic_width: Option<usize>, macro_backtrace: bool, ) -> EmitterWriter { let dst = Destination::from_stderr(color_config); @@ -742,7 +742,7 @@ impl EmitterWriter { short_message, teach, ui_testing: false, - terminal_width, + diagnostic_width, macro_backtrace, } } @@ -755,7 +755,7 @@ impl EmitterWriter { short_message: bool, teach: bool, colored: bool, - terminal_width: Option<usize>, + diagnostic_width: Option<usize>, macro_backtrace: bool, ) -> EmitterWriter { EmitterWriter { @@ -766,7 +766,7 @@ impl EmitterWriter { short_message, teach, ui_testing: false, - terminal_width, + diagnostic_width, macro_backtrace, } } @@ -1615,7 +1615,7 @@ impl EmitterWriter { width_offset + annotated_file.multiline_depth + 1 }; - let column_width = if let Some(width) = self.terminal_width { + let column_width = if let Some(width) = self.diagnostic_width { width.saturating_sub(code_offset) } else if self.ui_testing { DEFAULT_COLUMN_WIDTH diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index d4d1491c169..b8cd334b4c6 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -42,7 +42,7 @@ pub struct JsonEmitter { pretty: bool, ui_testing: bool, json_rendered: HumanReadableErrorType, - terminal_width: Option<usize>, + diagnostic_width: Option<usize>, macro_backtrace: bool, } @@ -54,7 +54,7 @@ impl JsonEmitter { fallback_bundle: LazyFallbackBundle, pretty: bool, json_rendered: HumanReadableErrorType, - terminal_width: Option<usize>, + diagnostic_width: Option<usize>, macro_backtrace: bool, ) -> JsonEmitter { JsonEmitter { @@ -66,7 +66,7 @@ impl JsonEmitter { pretty, ui_testing: false, json_rendered, - terminal_width, + diagnostic_width, macro_backtrace, } } @@ -76,7 +76,7 @@ impl JsonEmitter { json_rendered: HumanReadableErrorType, fluent_bundle: Option<Lrc<FluentBundle>>, fallback_bundle: LazyFallbackBundle, - terminal_width: Option<usize>, + diagnostic_width: Option<usize>, macro_backtrace: bool, ) -> JsonEmitter { let file_path_mapping = FilePathMapping::empty(); @@ -87,7 +87,7 @@ impl JsonEmitter { fallback_bundle, pretty, json_rendered, - terminal_width, + diagnostic_width, macro_backtrace, ) } @@ -100,7 +100,7 @@ impl JsonEmitter { fallback_bundle: LazyFallbackBundle, pretty: bool, json_rendered: HumanReadableErrorType, - terminal_width: Option<usize>, + diagnostic_width: Option<usize>, macro_backtrace: bool, ) -> JsonEmitter { JsonEmitter { @@ -112,7 +112,7 @@ impl JsonEmitter { pretty, ui_testing: false, json_rendered, - terminal_width, + diagnostic_width, macro_backtrace, } } @@ -345,7 +345,7 @@ impl Diagnostic { je.fluent_bundle.clone(), je.fallback_bundle.clone(), false, - je.terminal_width, + je.diagnostic_width, je.macro_backtrace, ) .ui_testing(je.ui_testing) diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index 064ed0cde96..69ad623b7ea 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" doctest = false [dependencies] +rustc_arena = { path = "../rustc_arena" } rustc_target = { path = "../rustc_target" } rustc_macros = { path = "../rustc_macros" } rustc_data_structures = { path = "../rustc_data_structures" } diff --git a/compiler/rustc_hir/src/arena.rs b/compiler/rustc_hir/src/arena.rs index 5d1314ebb48..a6d10f3adae 100644 --- a/compiler/rustc_hir/src/arena.rs +++ b/compiler/rustc_hir/src/arena.rs @@ -9,7 +9,7 @@ macro_rules! arena_types { // HIR types [] hir_krate: rustc_hir::Crate<'tcx>, [] arm: rustc_hir::Arm<'tcx>, - [] asm_operand: (rustc_hir::InlineAsmOperand<'tcx>, Span), + [] asm_operand: (rustc_hir::InlineAsmOperand<'tcx>, rustc_span::Span), [] asm_template: rustc_ast::InlineAsmTemplatePiece, [] attribute: rustc_ast::Attribute, [] block: rustc_hir::Block<'tcx>, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index a2ef158ce8d..acd77f5d2ee 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1595,6 +1595,9 @@ impl fmt::Display for ConstContext { } } +// NOTE: `IntoDiagnosticArg` impl for `ConstContext` lives in `rustc_errors` +// due to a cyclical dependency between hir that crate. + /// A literal. pub type Lit = Spanned<LitKind>; diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 384a0f9c225..531d9f14040 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -19,7 +19,7 @@ //! - Example: Examine each expression to look for its type and do some check or other. //! - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to //! `nested_filter::OnlyBodies` (and implement `nested_visit_map`), and use -//! `tcx.hir().deep_visit_all_item_likes(&mut visitor)`. Within your +//! `tcx.hir().visit_all_item_likes_in_crate(&mut visitor)`. Within your //! `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget to invoke //! `intravisit::walk_expr()` to keep walking the subparts). //! - Pro: Visitor methods for any kind of HIR node, not just item-like things. @@ -190,7 +190,7 @@ use nested_filter::NestedFilter; /// (this is why the module is called `intravisit`, to distinguish it /// from the AST's `visit` module, which acts differently). If you /// simply want to visit all items in the crate in some order, you -/// should call `Crate::visit_all_items`. Otherwise, see the comment +/// should call `tcx.hir().visit_all_item_likes_in_crate`. Otherwise, see the comment /// on `visit_nested_item` for details on how to visit nested items. /// /// If you want to ensure that your code handles every variant diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 9f32a7da159..0f9e6fa7b98 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -18,6 +18,8 @@ extern crate rustc_macros; #[macro_use] extern crate rustc_data_structures; +extern crate self as rustc_hir; + mod arena; pub mod def; pub mod def_path_hash_map; @@ -41,3 +43,5 @@ pub use hir_id::*; pub use lang_items::{LangItem, LanguageItems}; pub use stable_hash_impls::HashStableContext; pub use target::{MethodKind, Target}; + +arena_types!(rustc_arena::declare_arena); diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index a89b9eafaa6..93528b4514b 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -75,7 +75,7 @@ pub fn assert_dep_graph(tcx: TyCtxt<'_>) { let mut visitor = IfThisChanged { tcx, if_this_changed: vec![], then_this_would_need: vec![] }; visitor.process_attrs(hir::CRATE_HIR_ID); - tcx.hir().deep_visit_all_item_likes(&mut visitor); + tcx.hir().visit_all_item_likes_in_crate(&mut visitor); (visitor.if_this_changed, visitor.then_this_would_need) }; diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 9c325faae80..f59d8d596b9 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -161,19 +161,13 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { Decodable::decode(&mut work_product_decoder); for swp in work_products { - let mut all_files_exist = true; - let path = in_incr_comp_dir_sess(sess, &swp.work_product.saved_file); - if !path.exists() { - all_files_exist = false; - - if sess.opts.debugging_opts.incremental_info { - eprintln!( - "incremental: could not find file for work \ - product: {}", - path.display() - ); + let all_files_exist = swp.work_product.saved_files.iter().all(|(_, path)| { + let exists = in_incr_comp_dir_sess(sess, path).exists(); + if !exists && sess.opts.debugging_opts.incremental_info { + eprintln!("incremental: could not find file for work product: {path}",); } - } + exists + }); if all_files_exist { debug!("reconcile_work_products: all files for {:?} exist", swp); diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 79836d66011..4059b7cfc8e 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -108,16 +108,17 @@ pub fn save_work_product_index( for (id, wp) in previous_work_products.iter() { if !new_work_products.contains_key(id) { work_product::delete_workproduct_files(sess, wp); - debug_assert!(!in_incr_comp_dir_sess(sess, &wp.saved_file).exists()); + debug_assert!( + !wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) + ); } } // Check that we did not delete one of the current work-products: debug_assert!({ - new_work_products - .iter() - .map(|(_, wp)| in_incr_comp_dir_sess(sess, &wp.saved_file)) - .all(|path| path.exists()) + new_work_products.iter().all(|(_, wp)| { + wp.saved_files.iter().all(|(_, path)| in_incr_comp_dir_sess(sess, path).exists()) + }) }); } diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs index 4789c0f581f..1b184eca964 100644 --- a/compiler/rustc_incremental/src/persist/work_product.rs +++ b/compiler/rustc_incremental/src/persist/work_product.rs @@ -3,6 +3,7 @@ //! [work products]: WorkProduct use crate::persist::fs::*; +use rustc_data_structures::stable_map::FxHashMap; use rustc_fs_util::link_or_copy; use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_session::Session; @@ -13,38 +14,41 @@ use std::path::Path; pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( sess: &Session, cgu_name: &str, - path: &Path, + files: &[(&'static str, &Path)], ) -> Option<(WorkProductId, WorkProduct)> { - debug!("copy_cgu_workproduct_to_incr_comp_cache_dir({:?},{:?})", cgu_name, path); + debug!(?cgu_name, ?files); sess.opts.incremental.as_ref()?; - let file_name = format!("{}.o", cgu_name); - let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); - let saved_file = match link_or_copy(path, &path_in_incr_dir) { - Ok(_) => file_name, - Err(err) => { - sess.warn(&format!( - "error copying object file `{}` to incremental directory as `{}`: {}", - path.display(), - path_in_incr_dir.display(), - err - )); - return None; + let mut saved_files = FxHashMap::default(); + for (ext, path) in files { + let file_name = format!("{cgu_name}.{ext}"); + let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name); + match link_or_copy(path, &path_in_incr_dir) { + Ok(_) => { + let _ = saved_files.insert(ext.to_string(), file_name); + } + Err(err) => { + sess.warn(&format!( + "error copying object file `{}` to incremental directory as `{}`: {}", + path.display(), + path_in_incr_dir.display(), + err + )); + } } - }; - - let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_file }; + } + let work_product = WorkProduct { cgu_name: cgu_name.to_string(), saved_files }; + debug!(?work_product); let work_product_id = WorkProductId::from_cgu_name(cgu_name); Some((work_product_id, work_product)) } /// Removes files for a given work product. pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) { - let path = in_incr_comp_dir_sess(sess, &work_product.saved_file); - match std_fs::remove_file(&path) { - Ok(()) => {} - Err(err) => { + for (_, path) in &work_product.saved_files { + let path = in_incr_comp_dir_sess(sess, path); + if let Err(err) = std_fs::remove_file(&path) { sess.warn(&format!( "file-system error deleting outdated file `{}`: {}", path.display(), diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 976874c7cee..5b664e19c18 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -1546,6 +1546,16 @@ impl<T: Idx> GrowableBitSet<T> { let (word_index, mask) = word_index_and_mask(elem); self.bit_set.words.get(word_index).map_or(false, |word| (word & mask) != 0) } + + #[inline] + pub fn iter(&self) -> BitIter<'_, T> { + self.bit_set.iter() + } + + #[inline] + pub fn len(&self) -> usize { + self.bit_set.count() + } } impl<T: Idx> From<BitSet<T>> for GrowableBitSet<T> { diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs index fad949a3bc6..d566634a492 100644 --- a/compiler/rustc_infer/src/infer/free_regions.rs +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -4,7 +4,7 @@ //! and use that to decide when one free region outlives another, and so forth. use rustc_data_structures::transitive_relation::TransitiveRelation; -use rustc_middle::ty::{self, Lift, Region, TyCtxt}; +use rustc_middle::ty::{Lift, Region, TyCtxt}; /// Combines a `FreeRegionMap` and a `TyCtxt`. /// @@ -49,7 +49,7 @@ impl<'tcx> FreeRegionMap<'tcx> { // (with the exception that `'static: 'x` is not notable) pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); - if self.is_free_or_static(sub) && self.is_free(sup) { + if sub.is_free_or_static() && sup.is_free() { self.relation.add(sub, sup) } } @@ -68,7 +68,7 @@ impl<'tcx> FreeRegionMap<'tcx> { r_a: Region<'tcx>, r_b: Region<'tcx>, ) -> bool { - assert!(self.is_free_or_static(r_a) && self.is_free_or_static(r_b)); + assert!(r_a.is_free_or_static() && r_b.is_free_or_static()); let re_static = tcx.lifetimes.re_static; if self.check_relation(re_static, r_b) { // `'a <= 'static` is always true, and not stored in the @@ -85,20 +85,6 @@ impl<'tcx> FreeRegionMap<'tcx> { r_a == r_b || self.relation.contains(r_a, r_b) } - /// True for free regions other than `'static`. - pub fn is_free(&self, r: Region<'_>) -> bool { - matches!(*r, ty::ReEarlyBound(_) | ty::ReFree(_)) - } - - /// True if `r` is a free region or static of the sort that this - /// free region map can be used with. - pub fn is_free_or_static(&self, r: Region<'_>) -> bool { - match *r { - ty::ReStatic => true, - _ => self.is_free(r), - } - } - /// Computes the least-upper-bound of two free regions. In some /// cases, this is more conservative than necessary, in order to /// avoid making arbitrary choices. See @@ -110,8 +96,8 @@ impl<'tcx> FreeRegionMap<'tcx> { r_b: Region<'tcx>, ) -> Region<'tcx> { debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); - assert!(self.is_free(r_a)); - assert!(self.is_free(r_b)); + assert!(r_a.is_free()); + assert!(r_b.is_free()); let result = if r_a == r_b { r_a } else { diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs index 87fa22b3835..3783cfb4cc5 100644 --- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -47,7 +47,6 @@ pub(crate) fn resolve<'tcx>( #[derive(Clone)] pub struct LexicalRegionResolutions<'tcx> { pub(crate) values: IndexVec<RegionVid, VarValue<'tcx>>, - pub(crate) error_region: ty::Region<'tcx>, } #[derive(Copy, Clone, Debug)] @@ -140,7 +139,6 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { /// empty region. The `expansion` phase will grow this larger. fn construct_var_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> { LexicalRegionResolutions { - error_region: tcx.lifetimes.re_static, values: IndexVec::from_fn_n( |vid| { let vid_universe = self.var_infos[vid].universe; @@ -310,7 +308,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // Check for the case where we know that `'b: 'static` -- in that case, // `a <= b` for all `a`. - let b_free_or_static = self.region_rels.free_regions.is_free_or_static(b); + let b_free_or_static = b.is_free_or_static(); if b_free_or_static && sub_free_regions(tcx.lifetimes.re_static, b) { return true; } @@ -320,7 +318,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { // `lub` relationship defined below, since sometimes the "lub" // is actually the `postdom_upper_bound` (see // `TransitiveRelation` for more details). - let a_free_or_static = self.region_rels.free_regions.is_free_or_static(a); + let a_free_or_static = a.is_free_or_static(); if a_free_or_static && b_free_or_static { return sub_free_regions(a, b); } @@ -864,10 +862,7 @@ impl<'tcx> LexicalRegionResolutions<'tcx> { where T: TypeFoldable<'tcx>, { - tcx.fold_regions(value, |r, _db| match *r { - ty::ReVar(rid) => self.resolve_var(rid), - _ => r, - }) + tcx.fold_regions(value, |r, _db| self.resolve_region(tcx, r)) } fn value(&self, rid: RegionVid) -> &VarValue<'tcx> { @@ -878,12 +873,19 @@ impl<'tcx> LexicalRegionResolutions<'tcx> { &mut self.values[rid] } - pub fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { - let result = match self.values[rid] { - VarValue::Value(r) => r, - VarValue::ErrorValue => self.error_region, + pub(crate) fn resolve_region( + &self, + tcx: TyCtxt<'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + let result = match *r { + ty::ReVar(rid) => match self.values[rid] { + VarValue::Value(r) => r, + VarValue::ErrorValue => tcx.lifetimes.re_static, + }, + _ => r, }; - debug!("resolve_var({:?}) = {:?}", rid, result); + debug!("resolve_region({:?}) = {:?}", r, result); result } } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 991fd23ab43..28f037cc61a 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -466,9 +466,6 @@ pub enum NllRegionVariableOrigin { /// from a `for<'a> T` binder). Meant to represent "any region". Placeholder(ty::PlaceholderRegion), - /// The variable we create to represent `'empty(U0)`. - RootEmptyRegion, - Existential { /// If this is true, then this variable was created to represent a lifetime /// bound in a `for` binder. For example, it might have been created to @@ -891,6 +888,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .region_constraints_added_in_snapshot(&snapshot.undo_snapshot) } + pub fn opaque_types_added_in_snapshot(&self, snapshot: &CombinedSnapshot<'a, 'tcx>) -> bool { + self.inner.borrow().undo_log.opaque_types_in_snapshot(&snapshot.undo_snapshot) + } + pub fn add_given(&self, sub: ty::Region<'tcx>, sup: ty::RegionVid) { self.inner.borrow_mut().unwrap_region_constraints().add_given(sub, sup); } @@ -1250,7 +1251,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { }; let lexical_region_resolutions = LexicalRegionResolutions { - error_region: self.tcx.lifetimes.re_static, values: rustc_index::vec::IndexVec::from_elem_n( crate::infer::lexical_region_resolve::VarValue::Value(self.tcx.lifetimes.re_erased), var_infos.len(), diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 26d689f29ee..f11701bba6f 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -95,7 +95,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } let (a, b) = if a_is_expected { (a, b) } else { (b, a) }; let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() { - ty::Opaque(def_id, substs) => { + ty::Opaque(def_id, substs) if def_id.is_local() => { let origin = if self.defining_use_anchor.is_some() { // Check that this is `impl Trait` type is // declared by `parent_def_id` -- i.e., one whose diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index a6145687429..3d99f0958f7 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -206,13 +206,13 @@ impl<'a, 'tcx> FallibleTypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { fn try_fold_region(&mut self, r: ty::Region<'tcx>) -> Result<ty::Region<'tcx>, Self::Error> { match *r { - ty::ReVar(rid) => Ok(self + ty::ReVar(_) => Ok(self .infcx .lexical_region_resolutions .borrow() .as_ref() .expect("region resolution not performed") - .resolve_var(rid)), + .resolve_region(self.infcx.tcx, r)), _ => Ok(r), } } diff --git a/compiler/rustc_infer/src/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs index 1b696f21cbc..74a26ebc39f 100644 --- a/compiler/rustc_infer/src/infer/undo_log.rs +++ b/compiler/rustc_infer/src/infer/undo_log.rs @@ -185,6 +185,10 @@ impl<'tcx> InferCtxtUndoLogs<'tcx> { }) } + pub(crate) fn opaque_types_in_snapshot(&self, s: &Snapshot<'tcx>) -> bool { + self.logs[s.undo_len..].iter().any(|log| matches!(log, UndoLog::OpaqueTypes(..))) + } + pub(crate) fn region_constraints( &self, ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone { diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs index 932e597509f..5d22f9f972e 100644 --- a/compiler/rustc_infer/src/traits/project.rs +++ b/compiler/rustc_infer/src/traits/project.rs @@ -203,7 +203,7 @@ impl<'tcx> ProjectionCache<'_, 'tcx> { Some(&ProjectionCacheEntry::NormalizedTy { ref ty, complete: _ }) => { info!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty); let mut ty = ty.clone(); - if result == EvaluationResult::EvaluatedToOk { + if result.must_apply_considering_regions() { ty.obligations = vec![]; } map.insert(key, ProjectionCacheEntry::NormalizedTy { ty, complete: Some(result) }); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 5b263aded9c..b7d1d6edfaa 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -14,7 +14,6 @@ use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, PResult}; use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand}; use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; use rustc_hir::definitions::Definitions; -use rustc_hir::Crate; use rustc_lint::{EarlyCheckNode, LintStore}; use rustc_metadata::creader::CStore; use rustc_metadata::{encode_metadata, EncodedMetadata}; @@ -482,37 +481,6 @@ pub fn configure_and_expand( Ok(krate) } -fn lower_to_hir<'tcx>( - sess: &Session, - definitions: &mut Definitions, - cstore: &CrateStoreDyn, - resolutions: &ty::ResolverOutputs, - resolver: ty::ResolverAstLowering, - krate: Rc<ast::Crate>, - arena: &'tcx rustc_ast_lowering::Arena<'tcx>, -) -> &'tcx Crate<'tcx> { - // Lower AST to HIR. - let hir_crate = rustc_ast_lowering::lower_crate( - sess, - &krate, - definitions, - cstore, - resolutions, - resolver, - arena, - ); - - // Drop AST to free memory - sess.time("drop_ast", || std::mem::drop(krate)); - - // Discard hygiene data, which isn't required after lowering to HIR. - if !sess.opts.debugging_opts.keep_hygiene_data { - rustc_span::hygiene::clear_syntax_context_map(); - } - - hir_crate -} - // Returns all the paths that correspond to generated files. fn generated_output_paths( sess: &Session, @@ -777,6 +745,7 @@ pub fn prepare_outputs( pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| { let providers = &mut Providers::default(); providers.analysis = analysis; + providers.hir_crate = rustc_ast_lowering::lower_to_hir; proc_macro_decls::provide(providers); rustc_const_eval::provide(providers); rustc_middle::hir::provide(providers); @@ -823,7 +792,7 @@ impl<'tcx> QueryContext<'tcx> { pub fn create_global_ctxt<'tcx>( compiler: &'tcx Compiler, lint_store: Lrc<LintStore>, - krate: Rc<ast::Crate>, + krate: Lrc<ast::Crate>, dep_graph: DepGraph, resolver: Rc<RefCell<BoxedResolver>>, outputs: OutputFilenames, @@ -831,29 +800,17 @@ pub fn create_global_ctxt<'tcx>( queries: &'tcx OnceCell<TcxQueries<'tcx>>, global_ctxt: &'tcx OnceCell<GlobalCtxt<'tcx>>, arena: &'tcx WorkerLocal<Arena<'tcx>>, - hir_arena: &'tcx WorkerLocal<rustc_ast_lowering::Arena<'tcx>>, + hir_arena: &'tcx WorkerLocal<rustc_hir::Arena<'tcx>>, ) -> QueryContext<'tcx> { // We're constructing the HIR here; we don't care what we will // read, since we haven't even constructed the *input* to // incr. comp. yet. dep_graph.assert_ignored(); - let (mut definitions, cstore, resolver_outputs, resolver_for_lowering) = + let (definitions, cstore, resolver_outputs, resolver_for_lowering) = BoxedResolver::to_resolver_outputs(resolver); let sess = &compiler.session(); - - // Lower AST to HIR. - let krate = lower_to_hir( - sess, - &mut definitions, - &*cstore, - &resolver_outputs, - resolver_for_lowering, - krate, - hir_arena, - ); - let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess); let codegen_backend = compiler.codegen_backend(); @@ -877,9 +834,11 @@ pub fn create_global_ctxt<'tcx>( sess, lint_store, arena, + hir_arena, definitions, cstore, resolver_outputs, + resolver_for_lowering, krate, dep_graph, queries.on_disk_cache.as_ref().map(OnDiskCache::as_dyn), diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 136f0443fa0..8ffb1ad0539 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -72,13 +72,13 @@ pub struct Queries<'tcx> { queries: OnceCell<TcxQueries<'tcx>>, arena: WorkerLocal<Arena<'tcx>>, - hir_arena: WorkerLocal<rustc_ast_lowering::Arena<'tcx>>, + hir_arena: WorkerLocal<rustc_hir::Arena<'tcx>>, dep_graph_future: Query<Option<DepGraphFuture>>, parse: Query<ast::Crate>, crate_name: Query<String>, register_plugins: Query<(ast::Crate, Lrc<LintStore>)>, - expansion: Query<(Rc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>, + expansion: Query<(Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>, dep_graph: Query<DepGraph>, prepare_outputs: Query<OutputFilenames>, global_ctxt: Query<QueryContext<'tcx>>, @@ -92,7 +92,7 @@ impl<'tcx> Queries<'tcx> { gcx: OnceCell::new(), queries: OnceCell::new(), arena: WorkerLocal::new(|_| Arena::default()), - hir_arena: WorkerLocal::new(|_| rustc_ast_lowering::Arena::default()), + hir_arena: WorkerLocal::new(|_| rustc_hir::Arena::default()), dep_graph_future: Default::default(), parse: Default::default(), crate_name: Default::default(), @@ -164,7 +164,7 @@ impl<'tcx> Queries<'tcx> { pub fn expansion( &self, - ) -> Result<&Query<(Rc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>> { + ) -> Result<&Query<(Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>> { tracing::trace!("expansion"); self.expansion.compute(|| { let crate_name = self.crate_name()?.peek().clone(); @@ -180,7 +180,7 @@ impl<'tcx> Queries<'tcx> { let krate = resolver.access(|resolver| { passes::configure_and_expand(sess, &lint_store, krate, &crate_name, resolver) })?; - Ok((Rc::new(krate), Rc::new(RefCell::new(resolver)), lint_store)) + Ok((Lrc::new(krate), Rc::new(RefCell::new(resolver)), lint_store)) }) } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 30a29ed6ed3..55827ff583a 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -649,6 +649,7 @@ fn test_debugging_options_tracking_hash() { untracked!(dlltool, Some(PathBuf::from("custom_dlltool.exe"))); untracked!(dont_buffer_diagnostics, true); untracked!(dump_dep_graph, true); + untracked!(dump_drop_tracking_cfg, Some("cfg.dot".to_string())); untracked!(dump_mir, Some(String::from("abc"))); untracked!(dump_mir_dataflow, true); untracked!(dump_mir_dir, String::from("abc")); @@ -689,7 +690,6 @@ fn test_debugging_options_tracking_hash() { untracked!(span_debug, true); untracked!(span_free_formats, true); untracked!(temps_dir, Some(String::from("abc"))); - untracked!(terminal_width, Some(80)); untracked!(threads, 99); untracked!(time, true); untracked!(time_llvm_passes, true); @@ -733,6 +733,7 @@ fn test_debugging_options_tracking_hash() { tracked!(dep_info_omit_d_target, true); tracked!(drop_tracking, true); tracked!(dual_proc_macros, true); + tracked!(dwarf_version, Some(5)); tracked!(fewer_names, Some(true)); tracked!(force_unstable_if_unmarked, true); tracked!(fuel, Some(("abc".to_string(), 99))); diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 83328093e9f..5725c240320 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -34,7 +34,7 @@ use rustc_middle::middle::stability; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools, Ty, TyCtxt}; -use rustc_session::lint::BuiltinLintDiagnostics; +use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId}; use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; use rustc_session::Session; use rustc_span::lev_distance::find_best_match_for_name; @@ -906,6 +906,29 @@ pub trait LintContext: Sized { ) { self.lookup(lint, None as Option<Span>, decorate); } + + /// This returns the lint level for the given lint at the current location. + fn get_lint_level(&self, lint: &'static Lint) -> Level; + + /// This function can be used to manually fulfill an expectation. This can + /// be used for lints which contain several spans, and should be suppressed, + /// if either location was marked with an expectation. + /// + /// Note that this function should only be called for [`LintExpectationId`]s + /// retrieved from the current lint pass. Buffered or manually created ids can + /// cause ICEs. + fn fulfill_expectation(&self, expectation: LintExpectationId) { + // We need to make sure that submitted expectation ids are correctly fulfilled suppressed + // and stored between compilation sessions. To not manually do these steps, we simply create + // a dummy diagnostic and emit is as usual, which will be suppressed and stored like a normal + // expected lint diagnostic. + self.sess() + .struct_expect( + "this is a dummy diagnostic, to submit and store an expectation", + expectation, + ) + .emit(); + } } impl<'a> EarlyContext<'a> { @@ -953,6 +976,10 @@ impl LintContext for LateContext<'_> { None => self.tcx.struct_lint_node(lint, hir_id, decorate), } } + + fn get_lint_level(&self, lint: &'static Lint) -> Level { + self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs).0 + } } impl LintContext for EarlyContext<'_> { @@ -975,6 +1002,10 @@ impl LintContext for EarlyContext<'_> { ) { self.builder.struct_lint(lint, span.map(|s| s.into()), decorate) } + + fn get_lint_level(&self, lint: &'static Lint) -> Level { + self.builder.lint_level(lint).0 + } } impl<'tcx> LateContext<'tcx> { diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index c1d8d76c975..27f67207209 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -34,7 +34,7 @@ use tracing::debug; /// Extract the `LintStore` from the query context. /// This function exists because we've erased `LintStore` as `dyn Any` in the context. -pub(crate) fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore { +pub fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore { let store: &dyn Any = &*tcx.lint_store; store.downcast_ref().unwrap() } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index c1255ae5056..aaee0caa070 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -99,7 +99,7 @@ pub use builtin::SoftLints; pub use context::{CheckLintNameResult, FindLintError, LintStore}; pub use context::{EarlyContext, LateContext, LintContext}; pub use early::{check_ast_node, EarlyCheckNode}; -pub use late::check_crate; +pub use late::{check_crate, unerased_lint_store}; pub use passes::{EarlyLintPass, LateLintPass}; pub use rustc_session::lint::Level::{self, *}; pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId}; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 40601bb5aad..9fc2249b290 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -520,6 +520,11 @@ declare_lint! { /// The `expect` attribute can be removed if this is intended behavior otherwise /// it should be investigated why the expected lint is no longer issued. /// + /// In rare cases, the expectation might be emitted at a different location than + /// shown in the shown code snippet. In most cases, the `#[expect]` attribute + /// works when added to the outer scope. A few lints can only be expected + /// on a crate level. + /// /// Part of RFC 2383. The progress is being tracked in [#54503] /// /// [#54503]: https://github.com/rust-lang/rust/issues/54503 diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 1cd19c7eaab..48f441e69d6 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -232,6 +232,13 @@ impl Level { Level::Deny | Level::Forbid => true, } } + + pub fn get_expectation_id(&self) -> Option<LintExpectationId> { + match self { + Level::Expect(id) | Level::ForceWarn(Some(id)) => Some(*id), + _ => None, + } + } } /// Specification of a single lint. diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs index 2317186e655..2758fcd1310 100644 --- a/compiler/rustc_macros/src/diagnostics/fluent.rs +++ b/compiler/rustc_macros/src/diagnostics/fluent.rs @@ -189,9 +189,13 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok if let Entry::Message(Message { id: Identifier { name }, attributes, .. }) = entry { let _ = previous_defns.entry(name.to_string()).or_insert(ident_span); - // `typeck-foo-bar` => `foo_bar` + // `typeck-foo-bar` => `foo_bar` (in `typeck.ftl`) + // `const-eval-baz` => `baz` (in `const_eval.ftl`) let snake_name = Ident::new( - &name.replace(&format!("{}-", res.ident), "").replace("-", "_"), + // FIXME: should probably trim prefix, not replace all occurrences + &name + .replace(&format!("{}-", res.ident).replace("_", "-"), "") + .replace("-", "_"), span, ); constants.extend(quote! { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index bb4b502bded..caf5965c3a4 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -419,11 +419,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { return; } - self.tcx.hir().deep_visit_all_item_likes(self); + self.tcx.hir().visit_all_item_likes_in_crate(self); } fn encode_def_path_table(&mut self) { - let table = self.tcx.definitions_untracked().def_path_table(); + let table = self.tcx.def_path_table(); if self.is_proc_macro { for def_index in std::iter::once(CRATE_DEF_INDEX) .chain(self.tcx.resolutions(()).proc_macros.iter().map(|p| p.local_def_index)) @@ -443,9 +443,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } fn encode_def_path_hash_map(&mut self) -> LazyValue<DefPathHashMapRef<'static>> { - self.lazy(DefPathHashMapRef::BorrowedFromTcx( - self.tcx.definitions_untracked().def_path_hash_to_def_index_map(), - )) + self.lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map())) } fn encode_source_map(&mut self) -> LazyArray<rustc_span::SourceFile> { @@ -614,7 +612,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let interpret_alloc_index_bytes = self.position() - i; // Encode the proc macro data. This affects 'tables', - // so we need to do this before we encode the tables + // so we need to do this before we encode the tables. + // This overwrites def_keys, so it must happen after encode_def_path_table. i = self.position(); let proc_macro_data = self.encode_proc_macros(); let proc_macro_data_bytes = self.position() - i; @@ -992,8 +991,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { return; } let tcx = self.tcx; - let hir = tcx.hir(); - for local_id in hir.iter_local_def_id() { + for local_id in tcx.iter_local_def_id() { let def_id = local_id.to_def_id(); let def_kind = tcx.opt_def_kind(local_id); let Some(def_kind) = def_kind else { continue }; @@ -1854,12 +1852,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debug!("EncodeContext::encode_traits_and_impls()"); empty_proc_macro!(self); let tcx = self.tcx; - let mut ctx = tcx.create_stable_hashing_context(); let mut all_impls: Vec<_> = tcx.crate_inherent_impls(()).incoherent_impls.iter().collect(); - all_impls.sort_by_cached_key(|&(&simp, _)| { - let mut hasher = StableHasher::new(); - simp.hash_stable(&mut ctx, &mut hasher); - hasher.finish::<Fingerprint>(); + tcx.with_stable_hashing_context(|mut ctx| { + all_impls.sort_by_cached_key(|&(&simp, _)| { + let mut hasher = StableHasher::new(); + simp.hash_stable(&mut ctx, &mut hasher); + hasher.finish::<Fingerprint>() + }) }); let all_impls: Vec<_> = all_impls .into_iter() diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 555baae35f5..2d095438fc4 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -183,6 +183,9 @@ rustc_dep_node_append!([define_dep_nodes!][ <'tcx> // We use this for most things when incr. comp. is turned off. [] Null, + // We use this to create a forever-red node. + [] Red, + [anon] TraitSelect, // WARNING: if `Symbol` is changed, make sure you update `make_compile_codegen_unit` below. diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index e335cb395f8..c8b3b52b0fb 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -23,6 +23,7 @@ pub type EdgeFilter = rustc_query_system::dep_graph::debug::EdgeFilter<DepKind>; impl rustc_query_system::dep_graph::DepKind for DepKind { const NULL: Self = DepKind::Null; + const RED: Self = DepKind::Red; fn debug_node(node: &DepNode, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}(", node.kind)?; @@ -71,8 +72,8 @@ impl<'tcx> DepContext for TyCtxt<'tcx> { type DepKind = DepKind; #[inline] - fn create_stable_hashing_context(&self) -> StableHashingContext<'_> { - TyCtxt::create_stable_hashing_context(*self) + fn with_stable_hashing_context<R>(&self, f: impl FnOnce(StableHashingContext<'_>) -> R) -> R { + TyCtxt::with_stable_hashing_context(*self, f) } #[inline] diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index cda0a60fa4e..3e99ba5742a 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -218,13 +218,6 @@ impl<'hir> Map<'hir> { self.tcx.local_def_id_to_hir_id(def_id) } - pub fn iter_local_def_id(self) -> impl Iterator<Item = LocalDefId> + 'hir { - // Create a dependency to the crate to be sure we re-execute this when the amount of - // definitions change. - self.tcx.ensure().hir_crate(()); - self.tcx.definitions_untracked().iter_local_def_id() - } - /// Do not call this function directly. The query should be called. pub(super) fn opt_def_kind(self, local_def_id: LocalDefId) -> Option<DefKind> { let hir_id = self.local_def_id_to_hir_id(local_def_id); @@ -568,7 +561,7 @@ impl<'hir> Map<'hir> { } } - /// Walks the contents of a crate. See also `Crate::visit_all_items`. + /// Walks the contents of the local crate. See also `visit_all_item_likes_in_crate`. pub fn walk_toplevel_module(self, visitor: &mut impl Visitor<'hir>) { let (top_mod, span, hir_id) = self.get_module(CRATE_DEF_ID); visitor.visit_mod(top_mod, span, hir_id); @@ -588,53 +581,61 @@ impl<'hir> Map<'hir> { } } - /// Visits all items in the crate in some deterministic (but - /// unspecified) order. If you need to process every item, - /// and care about nesting -- usually because your algorithm - /// follows lexical scoping rules -- then this method is the best choice. - /// If you don't care about nesting, you should use the `tcx.hir_crate_items()` query - /// or `items()` instead. + /// Visits all item-likes in the crate in some deterministic (but unspecified) order. If you + /// need to process every item-like, and don't care about visiting nested items in a particular + /// order then this method is the best choice. If you do care about this nesting, you should + /// use the `tcx.hir().walk_toplevel_module`. + /// + /// Note that this function will access HIR for all the item-likes in the crate. If you only + /// need to access some of them, it is usually better to manually loop on the iterators + /// provided by `tcx.hir_crate_items(())`. /// /// Please see the notes in `intravisit.rs` for more information. - pub fn deep_visit_all_item_likes<V>(self, visitor: &mut V) + pub fn visit_all_item_likes_in_crate<V>(self, visitor: &mut V) where V: Visitor<'hir>, { - let krate = self.krate(); - for owner in krate.owners.iter().filter_map(|i| i.as_owner()) { - match owner.node() { - OwnerNode::Item(item) => visitor.visit_item(item), - OwnerNode::ForeignItem(item) => visitor.visit_foreign_item(item), - OwnerNode::ImplItem(item) => visitor.visit_impl_item(item), - OwnerNode::TraitItem(item) => visitor.visit_trait_item(item), - OwnerNode::Crate(_) => {} - } + let krate = self.tcx.hir_crate_items(()); + + for id in krate.items() { + visitor.visit_item(self.item(id)); + } + + for id in krate.trait_items() { + visitor.visit_trait_item(self.trait_item(id)); + } + + for id in krate.impl_items() { + visitor.visit_impl_item(self.impl_item(id)); + } + + for id in krate.foreign_items() { + visitor.visit_foreign_item(self.foreign_item(id)); } } - /// If you don't care about nesting, you should use the - /// `tcx.hir_module_items()` query or `module_items()` instead. - /// Please see notes in `deep_visit_all_item_likes`. - pub fn deep_visit_item_likes_in_module<V>(self, module: LocalDefId, visitor: &mut V) + /// This method is the equivalent of `visit_all_item_likes_in_crate` but restricted to + /// item-likes in a single module. + pub fn visit_item_likes_in_module<V>(self, module: LocalDefId, visitor: &mut V) where V: Visitor<'hir>, { let module = self.tcx.hir_module_items(module); - for id in module.items.iter() { - visitor.visit_item(self.item(*id)); + for id in module.items() { + visitor.visit_item(self.item(id)); } - for id in module.trait_items.iter() { - visitor.visit_trait_item(self.trait_item(*id)); + for id in module.trait_items() { + visitor.visit_trait_item(self.trait_item(id)); } - for id in module.impl_items.iter() { - visitor.visit_impl_item(self.impl_item(*id)); + for id in module.impl_items() { + visitor.visit_impl_item(self.impl_item(id)); } - for id in module.foreign_items.iter() { - visitor.visit_foreign_item(self.foreign_item(*id)); + for id in module.foreign_items() { + visitor.visit_foreign_item(self.foreign_item(id)); } } @@ -1020,6 +1021,7 @@ impl<'hir> Map<'hir> { _ => named_span(item.span, item.ident, None), }, Node::Ctor(_) => return self.opt_span(self.get_parent_node(hir_id)), + Node::Expr(Expr { kind: ExprKind::Closure { fn_decl_span, .. }, .. }) => *fn_decl_span, _ => self.span_with_body(hir_id), }; Some(span) @@ -1142,34 +1144,35 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, crate_num: CrateNum) -> Svh { source_file_names.sort_unstable(); - let mut hcx = tcx.create_stable_hashing_context(); - let mut stable_hasher = StableHasher::new(); - hir_body_hash.hash_stable(&mut hcx, &mut stable_hasher); - upstream_crates.hash_stable(&mut hcx, &mut stable_hasher); - source_file_names.hash_stable(&mut hcx, &mut stable_hasher); - if tcx.sess.opts.debugging_opts.incremental_relative_spans { - let definitions = &tcx.definitions_untracked(); - let mut owner_spans: Vec<_> = krate - .owners - .iter_enumerated() - .filter_map(|(def_id, info)| { - let _ = info.as_owner()?; - let def_path_hash = definitions.def_path_hash(def_id); - let span = resolutions.source_span[def_id]; - debug_assert_eq!(span.parent(), None); - Some((def_path_hash, span)) - }) - .collect(); - owner_spans.sort_unstable_by_key(|bn| bn.0); - owner_spans.hash_stable(&mut hcx, &mut stable_hasher); - } - tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher); - tcx.sess.local_stable_crate_id().hash_stable(&mut hcx, &mut stable_hasher); - // Hash visibility information since it does not appear in HIR. - resolutions.visibilities.hash_stable(&mut hcx, &mut stable_hasher); - resolutions.has_pub_restricted.hash_stable(&mut hcx, &mut stable_hasher); + let crate_hash: Fingerprint = tcx.with_stable_hashing_context(|mut hcx| { + let mut stable_hasher = StableHasher::new(); + hir_body_hash.hash_stable(&mut hcx, &mut stable_hasher); + upstream_crates.hash_stable(&mut hcx, &mut stable_hasher); + source_file_names.hash_stable(&mut hcx, &mut stable_hasher); + if tcx.sess.opts.debugging_opts.incremental_relative_spans { + let definitions = tcx.definitions_untracked(); + let mut owner_spans: Vec<_> = krate + .owners + .iter_enumerated() + .filter_map(|(def_id, info)| { + let _ = info.as_owner()?; + let def_path_hash = definitions.def_path_hash(def_id); + let span = resolutions.source_span[def_id]; + debug_assert_eq!(span.parent(), None); + Some((def_path_hash, span)) + }) + .collect(); + owner_spans.sort_unstable_by_key(|bn| bn.0); + owner_spans.hash_stable(&mut hcx, &mut stable_hasher); + } + tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher); + tcx.sess.local_stable_crate_id().hash_stable(&mut hcx, &mut stable_hasher); + // Hash visibility information since it does not appear in HIR. + resolutions.visibilities.hash_stable(&mut hcx, &mut stable_hasher); + resolutions.has_pub_restricted.hash_stable(&mut hcx, &mut stable_hasher); + stable_hasher.finish() + }); - let crate_hash: Fingerprint = stable_hasher.finish(); Svh::new(crate_hash.to_smaller_hash()) } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 12209d6725c..070a063c881 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -111,7 +111,6 @@ pub fn provide(providers: &mut Providers) { let hir = tcx.hir(); hir.get_module_parent_node(hir.local_def_id_to_hir_id(id)) }; - providers.hir_crate = |tcx, ()| tcx.untracked_crate; providers.hir_crate_items = map::hir_crate_items; providers.crate_hash = map::crate_hash; providers.hir_module_items = map::hir_module_items; diff --git a/compiler/rustc_middle/src/hir/nested_filter.rs b/compiler/rustc_middle/src/hir/nested_filter.rs index d56e87bbb47..6896837aa91 100644 --- a/compiler/rustc_middle/src/hir/nested_filter.rs +++ b/compiler/rustc_middle/src/hir/nested_filter.rs @@ -8,7 +8,7 @@ use rustc_hir::intravisit::nested_filter::NestedFilter; /// constant arguments of types, e.g. in `let _: [(); /* HERE */];`. /// /// **This is the most common choice.** A very common pattern is -/// to use `deep_visit_all_item_likes()` as an outer loop, +/// to use `visit_all_item_likes_in_crate()` as an outer loop, /// and to have the visitor that visits the contents of each item /// using this setting. pub struct OnlyBodies(()); diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs new file mode 100644 index 00000000000..78080fcd581 --- /dev/null +++ b/compiler/rustc_middle/src/mir/basic_blocks.rs @@ -0,0 +1,147 @@ +use crate::mir::graph_cyclic_cache::GraphIsCyclicCache; +use crate::mir::predecessors::{PredecessorCache, Predecessors}; +use crate::mir::switch_sources::{SwitchSourceCache, SwitchSources}; +use crate::mir::traversal::PostorderCache; +use crate::mir::{BasicBlock, BasicBlockData, Successors, START_BLOCK}; + +use rustc_data_structures::graph; +use rustc_data_structures::graph::dominators::{dominators, Dominators}; +use rustc_index::vec::IndexVec; + +#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)] +pub struct BasicBlocks<'tcx> { + basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>, + predecessor_cache: PredecessorCache, + switch_source_cache: SwitchSourceCache, + is_cyclic: GraphIsCyclicCache, + postorder_cache: PostorderCache, +} + +impl<'tcx> BasicBlocks<'tcx> { + #[inline] + pub fn new(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self { + BasicBlocks { + basic_blocks, + predecessor_cache: PredecessorCache::new(), + switch_source_cache: SwitchSourceCache::new(), + is_cyclic: GraphIsCyclicCache::new(), + postorder_cache: PostorderCache::new(), + } + } + + /// Returns true if control-flow graph contains a cycle reachable from the `START_BLOCK`. + #[inline] + pub fn is_cfg_cyclic(&self) -> bool { + self.is_cyclic.is_cyclic(self) + } + + #[inline] + pub fn dominators(&self) -> Dominators<BasicBlock> { + dominators(&self) + } + + /// Returns predecessors for each basic block. + #[inline] + pub fn predecessors(&self) -> &Predecessors { + self.predecessor_cache.compute(&self.basic_blocks) + } + + /// Returns basic blocks in a postorder. + #[inline] + pub fn postorder(&self) -> &[BasicBlock] { + self.postorder_cache.compute(&self.basic_blocks) + } + + /// `switch_sources()[&(target, switch)]` returns a list of switch + /// values that lead to a `target` block from a `switch` block. + #[inline] + pub fn switch_sources(&self) -> &SwitchSources { + self.switch_source_cache.compute(&self.basic_blocks) + } + + /// Returns mutable reference to basic blocks. Invalidates CFG cache. + #[inline] + pub fn as_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> { + self.invalidate_cfg_cache(); + &mut self.basic_blocks + } + + /// Get mutable access to basic blocks without invalidating the CFG cache. + /// + /// By calling this method instead of e.g. [`BasicBlocks::as_mut`] you promise not to change + /// the CFG. This means that + /// + /// 1) The number of basic blocks remains unchanged + /// 2) The set of successors of each terminator remains unchanged. + /// 3) For each `TerminatorKind::SwitchInt`, the `targets` remains the same and the terminator + /// kind is not changed. + /// + /// If any of these conditions cannot be upheld, you should call [`BasicBlocks::invalidate_cfg_cache`]. + #[inline] + pub fn as_mut_preserves_cfg(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> { + &mut self.basic_blocks + } + + /// Invalidates cached information about the CFG. + /// + /// You will only ever need this if you have also called [`BasicBlocks::as_mut_preserves_cfg`]. + /// All other methods that allow you to mutate the basic blocks also call this method + /// themselves, thereby avoiding any risk of accidentaly cache invalidation. + pub fn invalidate_cfg_cache(&mut self) { + self.predecessor_cache.invalidate(); + self.switch_source_cache.invalidate(); + self.is_cyclic.invalidate(); + self.postorder_cache.invalidate(); + } +} + +impl<'tcx> std::ops::Deref for BasicBlocks<'tcx> { + type Target = IndexVec<BasicBlock, BasicBlockData<'tcx>>; + + #[inline] + fn deref(&self) -> &IndexVec<BasicBlock, BasicBlockData<'tcx>> { + &self.basic_blocks + } +} + +impl<'tcx> graph::DirectedGraph for BasicBlocks<'tcx> { + type Node = BasicBlock; +} + +impl<'tcx> graph::WithNumNodes for BasicBlocks<'tcx> { + #[inline] + fn num_nodes(&self) -> usize { + self.basic_blocks.len() + } +} + +impl<'tcx> graph::WithStartNode for BasicBlocks<'tcx> { + #[inline] + fn start_node(&self) -> Self::Node { + START_BLOCK + } +} + +impl<'tcx> graph::WithSuccessors for BasicBlocks<'tcx> { + #[inline] + fn successors(&self, node: Self::Node) -> <Self as graph::GraphSuccessors<'_>>::Iter { + self.basic_blocks[node].terminator().successors() + } +} + +impl<'a, 'b> graph::GraphSuccessors<'b> for BasicBlocks<'a> { + type Item = BasicBlock; + type Iter = Successors<'b>; +} + +impl<'tcx, 'graph> graph::GraphPredecessors<'graph> for BasicBlocks<'tcx> { + type Item = BasicBlock; + type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicBlock>>; +} + +impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> { + #[inline] + fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter { + self.predecessors()[node].iter().copied() + } +} diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 1bbd71c3f1f..ae333846f06 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -179,6 +179,11 @@ pub fn alloc_range(start: Size, size: Size) -> AllocRange { } impl AllocRange { + #[inline] + pub fn from(r: Range<Size>) -> Self { + alloc_range(r.start, r.end - r.start) // `Size` subtraction (overflow-checked) + } + #[inline(always)] pub fn end(self) -> Size { self.start + self.size // This does overflow checking. @@ -1095,9 +1100,9 @@ impl InitMask { /// Returns `Ok(())` if it's initialized. Otherwise returns a range of byte /// indexes for the first contiguous span of the uninitialized access. #[inline] - pub fn is_range_initialized(&self, start: Size, end: Size) -> Result<(), Range<Size>> { + pub fn is_range_initialized(&self, start: Size, end: Size) -> Result<(), AllocRange> { if end > self.len { - return Err(self.len..end); + return Err(AllocRange::from(self.len..end)); } let uninit_start = self.find_bit(start, end, false); @@ -1105,7 +1110,7 @@ impl InitMask { match uninit_start { Some(uninit_start) => { let uninit_end = self.find_bit(uninit_start, end, true).unwrap_or(end); - Err(uninit_start..uninit_end) + Err(AllocRange::from(uninit_start..uninit_end)) } None => Ok(()), } @@ -1176,19 +1181,17 @@ impl<Tag: Copy, Extra> Allocation<Tag, Extra> { /// /// Returns `Ok(())` if it's initialized. Otherwise returns the range of byte /// indexes of the first contiguous uninitialized access. - fn is_init(&self, range: AllocRange) -> Result<(), Range<Size>> { + fn is_init(&self, range: AllocRange) -> Result<(), AllocRange> { self.init_mask.is_range_initialized(range.start, range.end()) // `Size` addition } /// Checks that a range of bytes is initialized. If not, returns the `InvalidUninitBytes` /// error which will report the first range of bytes which is uninitialized. fn check_init(&self, range: AllocRange) -> AllocResult { - self.is_init(range).map_err(|idx_range| { + self.is_init(range).map_err(|uninit_range| { AllocError::InvalidUninitBytes(Some(UninitBytesAccess { - access_offset: range.start, - access_size: range.size, - uninit_offset: idx_range.start, - uninit_size: idx_range.end - idx_range.start, // `Size` subtraction + access: range, + uninit: uninit_range, })) }) } diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 2a1fd6f736e..795f23edb31 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -1,4 +1,4 @@ -use super::{AllocId, ConstAlloc, Pointer, Scalar}; +use super::{AllocId, AllocRange, ConstAlloc, Pointer, Scalar}; use crate::mir::interpret::ConstValue; use crate::ty::{layout, query::TyCtxtAt, tls, FnSig, Ty, ValTree}; @@ -162,9 +162,9 @@ impl fmt::Display for InvalidProgramInfo<'_> { AlreadyReported(ErrorGuaranteed { .. }) => { write!(f, "encountered constants with type errors, stopping evaluation") } - Layout(ref err) => write!(f, "{}", err), - FnAbiAdjustForForeignAbi(ref err) => write!(f, "{}", err), - SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{}`", ty), + Layout(ref err) => write!(f, "{err}"), + FnAbiAdjustForForeignAbi(ref err) => write!(f, "{err}"), + SizeOfUnsizedType(ty) => write!(f, "size_of called on unsized type `{ty}`"), } } } @@ -205,14 +205,10 @@ impl fmt::Display for CheckInAllocMsg { /// Details of an access to uninitialized bytes where it is not allowed. #[derive(Debug)] pub struct UninitBytesAccess { - /// Location of the original memory access. - pub access_offset: Size, - /// Size of the original memory access. - pub access_size: Size, - /// Location of the first uninitialized byte that was accessed. - pub uninit_offset: Size, - /// Number of consecutive uninitialized bytes that were accessed. - pub uninit_size: Size, + /// Range of the original memory access. + pub access: AllocRange, + /// Range of the uninit memory that was encountered. (Might not be maximal.) + pub uninit: AllocRange, } /// Information about a size mismatch. @@ -308,30 +304,28 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use UndefinedBehaviorInfo::*; match self { - Ub(msg) => write!(f, "{}", msg), + Ub(msg) => write!(f, "{msg}"), Unreachable => write!(f, "entering unreachable code"), BoundsCheckFailed { ref len, ref index } => { - write!(f, "indexing out of bounds: the len is {} but the index is {}", len, index) + write!(f, "indexing out of bounds: the len is {len} but the index is {index}") } DivisionByZero => write!(f, "dividing by zero"), RemainderByZero => write!(f, "calculating the remainder with a divisor of zero"), DivisionOverflow => write!(f, "overflow in signed division (dividing MIN by -1)"), RemainderOverflow => write!(f, "overflow in signed remainder (dividing MIN by -1)"), PointerArithOverflow => write!(f, "overflowing in-bounds pointer arithmetic"), - InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {}", msg), + InvalidMeta(msg) => write!(f, "invalid metadata in wide pointer: {msg}"), InvalidVtableDropFn(sig) => write!( f, - "invalid drop function signature: got {}, expected exactly one argument which must be a pointer type", - sig + "invalid drop function signature: got {sig}, expected exactly one argument which must be a pointer type", ), InvalidVtableSize => { write!(f, "invalid vtable: size is bigger than largest supported object") } - InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {}", msg), + InvalidVtableAlignment(msg) => write!(f, "invalid vtable: alignment {msg}"), UnterminatedCString(p) => write!( f, - "reading a null-terminated string starting at {:?} with no null found before end of allocation", - p, + "reading a null-terminated string starting at {p:?} with no null found before end of allocation", ), PointerUseAfterFree(a) => { write!(f, "pointer to {a:?} was dereferenced after this allocation got freed") @@ -359,41 +353,36 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { } AlignmentCheckFailed { required, has } => write!( f, - "accessing memory with alignment {}, but alignment {} is required", - has.bytes(), - required.bytes() + "accessing memory with alignment {has}, but alignment {required} is required", + has = has.bytes(), + required = required.bytes() ), WriteToReadOnly(a) => write!(f, "writing to {a:?} which is read-only"), DerefFunctionPointer(a) => write!(f, "accessing {a:?} which contains a function"), ValidationFailure { path: None, msg } => { - write!(f, "constructing invalid value: {}", msg) + write!(f, "constructing invalid value: {msg}") } ValidationFailure { path: Some(path), msg } => { - write!(f, "constructing invalid value at {}: {}", path, msg) + write!(f, "constructing invalid value at {path}: {msg}") } InvalidBool(b) => { - write!(f, "interpreting an invalid 8-bit value as a bool: 0x{:02x}", b) + write!(f, "interpreting an invalid 8-bit value as a bool: 0x{b:02x}") } InvalidChar(c) => { - write!(f, "interpreting an invalid 32-bit value as a char: 0x{:08x}", c) + write!(f, "interpreting an invalid 32-bit value as a char: 0x{c:08x}") } - InvalidTag(val) => write!(f, "enum value has invalid tag: {:x}", val), + InvalidTag(val) => write!(f, "enum value has invalid tag: {val:x}"), InvalidFunctionPointer(p) => { - write!(f, "using {:?} as function pointer but it does not point to a function", p) + write!(f, "using {p:?} as function pointer but it does not point to a function") } - InvalidStr(err) => write!(f, "this string is not valid UTF-8: {}", err), - InvalidUninitBytes(Some((alloc, access))) => write!( + InvalidStr(err) => write!(f, "this string is not valid UTF-8: {err}"), + InvalidUninitBytes(Some((alloc, info))) => write!( f, - "reading {} byte{} of memory starting at {:?}, \ - but {} byte{} {} uninitialized starting at {:?}, \ + "reading memory at {alloc:?}{access:?}, \ + but memory is uninitialized at {uninit:?}, \ and this operation requires initialized memory", - access.access_size.bytes(), - pluralize!(access.access_size.bytes()), - Pointer::new(*alloc, access.access_offset), - access.uninit_size.bytes(), - pluralize!(access.uninit_size.bytes()), - pluralize!("is", access.uninit_size.bytes()), - Pointer::new(*alloc, access.uninit_offset), + access = info.access, + uninit = info.uninit, ), InvalidUninitBytes(None) => write!( f, @@ -402,8 +391,7 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> { DeadLocal => write!(f, "accessing a dead local variable"), ScalarSizeMismatch(self::ScalarSizeMismatch { target_size, data_size }) => write!( f, - "scalar size mismatch: expected {} bytes but got {} bytes instead", - target_size, data_size + "scalar size mismatch: expected {target_size} bytes but got {data_size} bytes instead", ), UninhabitedEnumVariantWritten => { write!(f, "writing discriminant of an uninhabited enum") @@ -437,13 +425,13 @@ impl fmt::Display for UnsupportedOpInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use UnsupportedOpInfo::*; match self { - Unsupported(ref msg) => write!(f, "{}", msg), + Unsupported(ref msg) => write!(f, "{msg}"), ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes"), PartialPointerOverwrite(ptr) => { - write!(f, "unable to overwrite parts of a pointer in memory at {:?}", ptr) + write!(f, "unable to overwrite parts of a pointer in memory at {ptr:?}") } - ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({:?})", did), - ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did), + ThreadLocalStatic(did) => write!(f, "cannot access thread local static ({did:?})"), + ReadExternStatic(did) => write!(f, "cannot read from extern static ({did:?})"), } } } @@ -526,11 +514,11 @@ impl fmt::Display for InterpError<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use InterpError::*; match *self { - Unsupported(ref msg) => write!(f, "{}", msg), - InvalidProgram(ref msg) => write!(f, "{}", msg), - UndefinedBehavior(ref msg) => write!(f, "{}", msg), - ResourceExhaustion(ref msg) => write!(f, "{}", msg), - MachineStop(ref msg) => write!(f, "{}", msg), + Unsupported(ref msg) => write!(f, "{msg}"), + InvalidProgram(ref msg) => write!(f, "{msg}"), + UndefinedBehavior(ref msg) => write!(f, "{msg}"), + ResourceExhaustion(ref msg) => write!(f, "{msg}"), + MachineStop(ref msg) => write!(f, "{msg}"), } } } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index e7d7317456c..f136b8e2368 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -5,7 +5,6 @@ use crate::mir::interpret::{ AllocRange, ConstAllocation, ConstValue, GlobalAlloc, LitToConstInput, Scalar, }; -use crate::mir::traversal::PostorderCache; use crate::mir::visit::MirVisitable; use crate::ty::codec::{TyDecoder, TyEncoder}; use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; @@ -27,8 +26,7 @@ use rustc_target::abi::{Size, VariantIdx}; use polonius_engine::Atom; pub use rustc_ast::Mutability; use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::graph::dominators::{dominators, Dominators}; -use rustc_data_structures::graph::{self, GraphSuccessors}; +use rustc_data_structures::graph::dominators::Dominators; use rustc_index::bit_set::BitMatrix; use rustc_index::vec::{Idx, IndexVec}; use rustc_serialize::{Decodable, Encodable}; @@ -43,11 +41,10 @@ use std::fmt::{self, Debug, Display, Formatter, Write}; use std::ops::{ControlFlow, Index, IndexMut}; use std::{iter, mem}; -use self::graph_cyclic_cache::GraphIsCyclicCache; -use self::predecessors::{PredecessorCache, Predecessors}; pub use self::query::*; -use self::switch_sources::{SwitchSourceCache, SwitchSources}; +pub use basic_blocks::BasicBlocks; +mod basic_blocks; pub mod coverage; mod generic_graph; pub mod generic_graphviz; @@ -189,7 +186,7 @@ pub struct GeneratorInfo<'tcx> { pub struct Body<'tcx> { /// A list of basic blocks. References to basic block use a newtyped index type [`BasicBlock`] /// that indexes into this vector. - basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>, + pub basic_blocks: BasicBlocks<'tcx>, /// Records how far through the "desugaring and optimization" process this particular /// MIR has traversed. This is particularly useful when inlining, since in that context @@ -257,11 +254,6 @@ pub struct Body<'tcx> { /// potentially allow things like `[u8; std::mem::size_of::<T>() * 0]` due to this. pub is_polymorphic: bool, - predecessor_cache: PredecessorCache, - switch_source_cache: SwitchSourceCache, - is_cyclic: GraphIsCyclicCache, - postorder_cache: PostorderCache, - pub tainted_by_errors: Option<ErrorGuaranteed>, } @@ -289,7 +281,7 @@ impl<'tcx> Body<'tcx> { let mut body = Body { phase: MirPhase::Built, source, - basic_blocks, + basic_blocks: BasicBlocks::new(basic_blocks), source_scopes, generator: generator_kind.map(|generator_kind| { Box::new(GeneratorInfo { @@ -307,10 +299,6 @@ impl<'tcx> Body<'tcx> { span, required_consts: Vec::new(), is_polymorphic: false, - predecessor_cache: PredecessorCache::new(), - switch_source_cache: SwitchSourceCache::new(), - is_cyclic: GraphIsCyclicCache::new(), - postorder_cache: PostorderCache::new(), tainted_by_errors, }; body.is_polymorphic = body.has_param_types_or_consts(); @@ -326,7 +314,7 @@ impl<'tcx> Body<'tcx> { let mut body = Body { phase: MirPhase::Built, source: MirSource::item(CRATE_DEF_ID.to_def_id()), - basic_blocks, + basic_blocks: BasicBlocks::new(basic_blocks), source_scopes: IndexVec::new(), generator: None, local_decls: IndexVec::new(), @@ -337,10 +325,6 @@ impl<'tcx> Body<'tcx> { required_consts: Vec::new(), var_debug_info: Vec::new(), is_polymorphic: false, - predecessor_cache: PredecessorCache::new(), - switch_source_cache: SwitchSourceCache::new(), - is_cyclic: GraphIsCyclicCache::new(), - postorder_cache: PostorderCache::new(), tainted_by_errors: None, }; body.is_polymorphic = body.has_param_types_or_consts(); @@ -354,74 +338,7 @@ impl<'tcx> Body<'tcx> { #[inline] pub fn basic_blocks_mut(&mut self) -> &mut IndexVec<BasicBlock, BasicBlockData<'tcx>> { - // Because the user could mutate basic block terminators via this reference, we need to - // invalidate the caches. - // - // FIXME: Use a finer-grained API for this, so only transformations that alter terminators - // invalidate the caches. - self.invalidate_cfg_cache(); - &mut self.basic_blocks - } - - #[inline] - pub fn basic_blocks_and_local_decls_mut( - &mut self, - ) -> (&mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, &mut LocalDecls<'tcx>) { - self.invalidate_cfg_cache(); - (&mut self.basic_blocks, &mut self.local_decls) - } - - #[inline] - pub fn basic_blocks_local_decls_mut_and_var_debug_info( - &mut self, - ) -> ( - &mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, - &mut LocalDecls<'tcx>, - &mut Vec<VarDebugInfo<'tcx>>, - ) { - self.invalidate_cfg_cache(); - (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info) - } - - /// Get mutable access to parts of the Body without invalidating the CFG cache. - /// - /// By calling this method instead of eg [`Body::basic_blocks_mut`], you promise not to change - /// the CFG. This means that - /// - /// 1) The number of basic blocks remains unchanged - /// 2) The set of successors of each terminator remains unchanged. - /// 3) For each `TerminatorKind::SwitchInt`, the `targets` remains the same and the terminator - /// kind is not changed. - /// - /// If any of these conditions cannot be upheld, you should call [`Body::invalidate_cfg_cache`]. - #[inline] - pub fn basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate( - &mut self, - ) -> ( - &mut IndexVec<BasicBlock, BasicBlockData<'tcx>>, - &mut LocalDecls<'tcx>, - &mut Vec<VarDebugInfo<'tcx>>, - ) { - (&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info) - } - - /// Invalidates cached information about the CFG. - /// - /// You will only ever need this if you have also called - /// [`Body::basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate`]. All other methods - /// that allow you to mutate the body also call this method themselves, thereby avoiding any - /// risk of accidentaly cache invalidation. - pub fn invalidate_cfg_cache(&mut self) { - self.predecessor_cache.invalidate(); - self.switch_source_cache.invalidate(); - self.is_cyclic.invalidate(); - self.postorder_cache.invalidate(); - } - - /// Returns `true` if a cycle exists in the control-flow graph that is reachable from the - /// `START_BLOCK`. - pub fn is_cfg_cyclic(&self) -> bool { - self.is_cyclic.is_cyclic(self) + self.basic_blocks.as_mut() } #[inline] @@ -495,14 +412,6 @@ impl<'tcx> Body<'tcx> { self.local_decls.drain(self.arg_count + 1..) } - /// Changes a statement to a nop. This is both faster than deleting instructions and avoids - /// invalidating statement indices in `Location`s. - pub fn make_statement_nop(&mut self, location: Location) { - let block = &mut self.basic_blocks[location.block]; - debug_assert!(location.statement_index < block.statements.len()); - block.statements[location.statement_index].make_nop() - } - /// Returns the source info associated with `location`. pub fn source_info(&self, location: Location) -> &SourceInfo { let block = &self[location.block]; @@ -539,23 +448,6 @@ impl<'tcx> Body<'tcx> { } #[inline] - pub fn predecessors(&self) -> &Predecessors { - self.predecessor_cache.compute(&self.basic_blocks) - } - - /// `body.switch_sources()[&(target, switch)]` returns a list of switch - /// values that lead to a `target` block from a `switch` block. - #[inline] - pub fn switch_sources(&self) -> &SwitchSources { - self.switch_source_cache.compute(&self.basic_blocks) - } - - #[inline] - pub fn dominators(&self) -> Dominators<BasicBlock> { - dominators(self) - } - - #[inline] pub fn yield_ty(&self) -> Option<Ty<'tcx>> { self.generator.as_ref().and_then(|generator| generator.yield_ty) } @@ -599,7 +491,7 @@ impl<'tcx> Index<BasicBlock> for Body<'tcx> { impl<'tcx> IndexMut<BasicBlock> for Body<'tcx> { #[inline] fn index_mut(&mut self, index: BasicBlock) -> &mut BasicBlockData<'tcx> { - &mut self.basic_blocks_mut()[index] + &mut self.basic_blocks.as_mut()[index] } } @@ -1156,6 +1048,8 @@ impl BasicBlock { /////////////////////////////////////////////////////////////////////////// // BasicBlockData +/// Data for a basic block, including a list of its statements. +/// /// See [`BasicBlock`] for documentation on what basic blocks are at a high level. #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct BasicBlockData<'tcx> { @@ -1187,7 +1081,7 @@ impl<'tcx> BasicBlockData<'tcx> { /// Accessor for terminator. /// /// Terminator may not be None after construction of the basic block is complete. This accessor - /// provides a convenience way to reach the terminator. + /// provides a convenient way to reach the terminator. #[inline] pub fn terminator(&self) -> &Terminator<'tcx> { self.terminator.as_ref().expect("invalid terminator state") @@ -1394,6 +1288,7 @@ impl<O: fmt::Debug> fmt::Debug for AssertKind<O> { /////////////////////////////////////////////////////////////////////////// // Statements +/// A statement in a basic block, including information about its source code. #[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] pub struct Statement<'tcx> { pub source_info: SourceInfo, @@ -2890,48 +2785,6 @@ fn pretty_print_const_value<'tcx>( }) } -impl<'tcx> graph::DirectedGraph for Body<'tcx> { - type Node = BasicBlock; -} - -impl<'tcx> graph::WithNumNodes for Body<'tcx> { - #[inline] - fn num_nodes(&self) -> usize { - self.basic_blocks.len() - } -} - -impl<'tcx> graph::WithStartNode for Body<'tcx> { - #[inline] - fn start_node(&self) -> Self::Node { - START_BLOCK - } -} - -impl<'tcx> graph::WithSuccessors for Body<'tcx> { - #[inline] - fn successors(&self, node: Self::Node) -> <Self as GraphSuccessors<'_>>::Iter { - self.basic_blocks[node].terminator().successors() - } -} - -impl<'a, 'b> graph::GraphSuccessors<'b> for Body<'a> { - type Item = BasicBlock; - type Iter = Successors<'b>; -} - -impl<'tcx, 'graph> graph::GraphPredecessors<'graph> for Body<'tcx> { - type Item = BasicBlock; - type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicBlock>>; -} - -impl<'tcx> graph::WithPredecessors for Body<'tcx> { - #[inline] - fn predecessors(&self, node: Self::Node) -> <Self as graph::GraphPredecessors<'_>>::Iter { - self.predecessors()[node].iter().copied() - } -} - /// `Location` represents the position of the start of the statement; or, if /// `statement_index` equals the number of statements, then the start of the /// terminator. @@ -2968,7 +2821,7 @@ impl Location { return true; } - let predecessors = body.predecessors(); + let predecessors = body.basic_blocks.predecessors(); // If we're in another block, then we want to check that block is a predecessor of `other`. let mut queue: Vec<BasicBlock> = predecessors[other.block].to_vec(); diff --git a/compiler/rustc_middle/src/mir/traversal.rs b/compiler/rustc_middle/src/mir/traversal.rs index 30648679dae..627dc32f37e 100644 --- a/compiler/rustc_middle/src/mir/traversal.rs +++ b/compiler/rustc_middle/src/mir/traversal.rs @@ -104,22 +104,25 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> { /// /// A Postorder traversal of this graph is `D B C A` or `D C B A` pub struct Postorder<'a, 'tcx> { - body: &'a Body<'tcx>, + basic_blocks: &'a IndexVec<BasicBlock, BasicBlockData<'tcx>>, visited: BitSet<BasicBlock>, visit_stack: Vec<(BasicBlock, Successors<'a>)>, root_is_start_block: bool, } impl<'a, 'tcx> Postorder<'a, 'tcx> { - pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> Postorder<'a, 'tcx> { + pub fn new( + basic_blocks: &'a IndexVec<BasicBlock, BasicBlockData<'tcx>>, + root: BasicBlock, + ) -> Postorder<'a, 'tcx> { let mut po = Postorder { - body, - visited: BitSet::new_empty(body.basic_blocks().len()), + basic_blocks, + visited: BitSet::new_empty(basic_blocks.len()), visit_stack: Vec::new(), root_is_start_block: root == START_BLOCK, }; - let data = &po.body[root]; + let data = &po.basic_blocks[root]; if let Some(ref term) = data.terminator { po.visited.insert(root); @@ -190,7 +193,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> { }; if self.visited.insert(bb) { - if let Some(term) = &self.body[bb].terminator { + if let Some(term) = &self.basic_blocks[bb].terminator { self.visit_stack.push((bb, term.successors())); } } @@ -199,7 +202,7 @@ impl<'a, 'tcx> Postorder<'a, 'tcx> { } pub fn postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> Postorder<'a, 'tcx> { - Postorder::new(body, START_BLOCK) + Postorder::new(&body.basic_blocks, START_BLOCK) } impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> { @@ -211,12 +214,12 @@ impl<'a, 'tcx> Iterator for Postorder<'a, 'tcx> { self.traverse_successor(); } - next.map(|(bb, _)| (bb, &self.body[bb])) + next.map(|(bb, _)| (bb, &self.basic_blocks[bb])) } fn size_hint(&self) -> (usize, Option<usize>) { // All the blocks, minus the number of blocks we've visited. - let upper = self.body.basic_blocks().len() - self.visited.count(); + let upper = self.basic_blocks.len() - self.visited.count(); let lower = if self.root_is_start_block { // We will visit all remaining blocks exactly once. @@ -263,10 +266,8 @@ pub struct ReversePostorder<'a, 'tcx> { impl<'a, 'tcx> ReversePostorder<'a, 'tcx> { pub fn new(body: &'a Body<'tcx>, root: BasicBlock) -> ReversePostorder<'a, 'tcx> { - let blocks: Vec<_> = Postorder::new(body, root).map(|(bb, _)| bb).collect(); - + let blocks: Vec<_> = Postorder::new(&body.basic_blocks, root).map(|(bb, _)| bb).collect(); let len = blocks.len(); - ReversePostorder { body, blocks, idx: len } } } @@ -334,10 +335,8 @@ impl<'a, 'tcx> Iterator for ReversePostorderIter<'a, 'tcx> { impl<'a, 'tcx> ExactSizeIterator for ReversePostorderIter<'a, 'tcx> {} pub fn reverse_postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> ReversePostorderIter<'a, 'tcx> { - let blocks = body.postorder_cache.compute(body); - + let blocks = body.basic_blocks.postorder(); let len = blocks.len(); - ReversePostorderIter { body, blocks, idx: len } } @@ -360,7 +359,7 @@ impl PostorderCache { /// Returns the `&[BasicBlocks]` represents the postorder graph for this MIR. #[inline] - pub(super) fn compute(&self, body: &Body<'_>) -> &[BasicBlock] { + pub(super) fn compute(&self, body: &IndexVec<BasicBlock, BasicBlockData<'_>>) -> &[BasicBlock] { self.cache.get_or_init(|| Postorder::new(body, START_BLOCK).map(|(bb, _)| bb).collect()) } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 57c4f3f3ba3..cbc45526e89 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -26,6 +26,12 @@ rustc_queries! { desc { "get the resolver outputs" } } + query resolver_for_lowering(_: ()) -> &'tcx Steal<ty::ResolverAstLowering> { + eval_always + no_hash + desc { "get the resolver for lowering" } + } + /// Return the span for a definition. /// Contrary to `def_span` below, this query returns the full absolute span of the definition. /// This span is meant for dep-tracking rather than diagnostics. It should not be used outside @@ -40,7 +46,8 @@ rustc_queries! { /// This is because the `hir_crate` query gives you access to all other items. /// To avoid this fate, do not call `tcx.hir().krate()`; instead, /// prefer wrappers like `tcx.visit_all_items_in_krate()`. - query hir_crate(key: ()) -> &'tcx Crate<'tcx> { + query hir_crate(key: ()) -> Crate<'tcx> { + storage(ArenaCacheSelector<'tcx>) eval_always desc { "get the crate HIR" } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index eee44df8645..fe7f72024d3 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -546,9 +546,17 @@ pub enum SelectionError<'tcx> { ErrorReporting, /// Multiple applicable `impl`s where found. The `DefId`s correspond to /// all the `impl`s' Items. - Ambiguous(Vec<DefId>), + Ambiguous(Vec<AmbiguousSelection>), } +#[derive(Copy, Clone, Debug)] +pub enum AmbiguousSelection { + Impl(DefId), + ParamEnv(Span), +} + +TrivialTypeTraversalAndLiftImpls! { AmbiguousSelection, } + /// When performing resolution, it is typically the case that there /// can be one of three outcomes: /// diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 34703b62820..0ca5a532b75 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -176,6 +176,10 @@ pub enum EvaluationResult { EvaluatedToOk, /// Evaluation successful, but there were unevaluated region obligations. EvaluatedToOkModuloRegions, + /// Evaluation successful, but need to rerun because opaque types got + /// hidden types assigned without it being known whether the opaque types + /// are within their defining scope + EvaluatedToOkModuloOpaqueTypes, /// Evaluation is known to be ambiguous -- it *might* hold for some /// assignment of inference variables, but it might not. /// @@ -252,9 +256,11 @@ impl EvaluationResult { pub fn may_apply(self) -> bool { match self { - EvaluatedToOk | EvaluatedToOkModuloRegions | EvaluatedToAmbig | EvaluatedToUnknown => { - true - } + EvaluatedToOkModuloOpaqueTypes + | EvaluatedToOk + | EvaluatedToOkModuloRegions + | EvaluatedToAmbig + | EvaluatedToUnknown => true, EvaluatedToErr | EvaluatedToRecur => false, } @@ -264,7 +270,11 @@ impl EvaluationResult { match self { EvaluatedToUnknown | EvaluatedToRecur => true, - EvaluatedToOk | EvaluatedToOkModuloRegions | EvaluatedToAmbig | EvaluatedToErr => false, + EvaluatedToOkModuloOpaqueTypes + | EvaluatedToOk + | EvaluatedToOkModuloRegions + | EvaluatedToAmbig + | EvaluatedToErr => false, } } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index af071b4e939..a594dab2e20 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -32,12 +32,13 @@ use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; -use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal}; +use rustc_data_structures::sync::{self, Lock, Lrc, ReadGuard, RwLock, WorkerLocal}; use rustc_data_structures::vec_map::VecMap; use rustc_errors::{DecorateLint, ErrorGuaranteed, LintDiagnosticBuilder, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE}; +use rustc_hir::definitions::Definitions; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{ @@ -1045,6 +1046,7 @@ impl<'tcx> Deref for TyCtxt<'tcx> { pub struct GlobalCtxt<'tcx> { pub arena: &'tcx WorkerLocal<Arena<'tcx>>, + pub hir_arena: &'tcx WorkerLocal<hir::Arena<'tcx>>, interners: CtxtInterners<'tcx>, @@ -1069,13 +1071,15 @@ pub struct GlobalCtxt<'tcx> { /// Common consts, pre-interned for your convenience. pub consts: CommonConsts<'tcx>, - definitions: rustc_hir::definitions::Definitions, + definitions: RwLock<Definitions>, cstore: Box<CrateStoreDyn>, /// Output of the resolver. pub(crate) untracked_resolutions: ty::ResolverOutputs, - - pub(crate) untracked_crate: &'tcx hir::Crate<'tcx>, + untracked_resolver_for_lowering: Steal<ty::ResolverAstLowering>, + /// The entire crate as AST. This field serves as the input for the hir_crate query, + /// which lowers it from AST to HIR. It must not be read or used by anything else. + pub untracked_crate: Steal<Lrc<ast::Crate>>, /// This provides access to the incremental compilation on-disk cache for query results. /// Do not access this directly. It is only meant to be used by @@ -1233,10 +1237,12 @@ impl<'tcx> TyCtxt<'tcx> { s: &'tcx Session, lint_store: Lrc<dyn Any + sync::Send + sync::Sync>, arena: &'tcx WorkerLocal<Arena<'tcx>>, - definitions: rustc_hir::definitions::Definitions, + hir_arena: &'tcx WorkerLocal<hir::Arena<'tcx>>, + definitions: Definitions, cstore: Box<CrateStoreDyn>, untracked_resolutions: ty::ResolverOutputs, - krate: &'tcx hir::Crate<'tcx>, + untracked_resolver_for_lowering: ty::ResolverAstLowering, + krate: Lrc<ast::Crate>, dep_graph: DepGraph, on_disk_cache: Option<&'tcx dyn OnDiskCache<'tcx>>, queries: &'tcx dyn query::QueryEngine<'tcx>, @@ -1263,16 +1269,18 @@ impl<'tcx> TyCtxt<'tcx> { sess: s, lint_store, arena, + hir_arena, interners, dep_graph, - definitions, + definitions: RwLock::new(definitions), cstore, - untracked_resolutions, prof: s.prof.clone(), types: common_types, lifetimes: common_lifetimes, consts: common_consts, - untracked_crate: krate, + untracked_resolutions, + untracked_resolver_for_lowering: Steal::new(untracked_resolver_for_lowering), + untracked_crate: Steal::new(krate), on_disk_cache, queries, query_caches: query::QueryCaches::default(), @@ -1368,7 +1376,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn def_key(self, id: DefId) -> rustc_hir::definitions::DefKey { // Accessing the DefKey is ok, since it is part of DefPathHash. if let Some(id) = id.as_local() { - self.definitions.def_key(id) + self.definitions_untracked().def_key(id) } else { self.cstore.def_key(id) } @@ -1382,7 +1390,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn def_path(self, id: DefId) -> rustc_hir::definitions::DefPath { // Accessing the DefPath is ok, since it is part of DefPathHash. if let Some(id) = id.as_local() { - self.definitions.def_path(id) + self.definitions_untracked().def_path(id) } else { self.cstore.def_path(id) } @@ -1392,7 +1400,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn def_path_hash(self, def_id: DefId) -> rustc_hir::definitions::DefPathHash { // Accessing the DefPathHash is ok, it is incr. comp. stable. if let Some(def_id) = def_id.as_local() { - self.definitions.def_path_hash(def_id) + self.definitions_untracked().def_path_hash(def_id) } else { self.cstore.def_path_hash(def_id) } @@ -1429,7 +1437,7 @@ impl<'tcx> TyCtxt<'tcx> { // If this is a DefPathHash from the local crate, we can look up the // DefId in the tcx's `Definitions`. if stable_crate_id == self.sess.local_stable_crate_id() { - self.definitions.local_def_path_hash_to_def_id(hash, err).to_def_id() + self.definitions.read().local_def_path_hash_to_def_id(hash, err).to_def_id() } else { // If this is a DefPathHash from an upstream crate, let the CrateStore map // it to a DefId. @@ -1460,6 +1468,64 @@ impl<'tcx> TyCtxt<'tcx> { ) } + /// Create a new definition within the incr. comp. engine. + pub fn create_def(self, parent: LocalDefId, data: hir::definitions::DefPathData) -> LocalDefId { + // This function modifies `self.definitions` using a side-effect. + // We need to ensure that these side effects are re-run by the incr. comp. engine. + // Depending on the forever-red node will tell the graph that the calling query + // needs to be re-evaluated. + use rustc_query_system::dep_graph::DepNodeIndex; + self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE); + + // The following call has the side effect of modifying the tables inside `definitions`. + // These very tables are relied on by the incr. comp. engine to decode DepNodes and to + // decode the on-disk cache. + // + // Any LocalDefId which is used within queries, either as key or result, either: + // - has been created before the construction of the TyCtxt; + // - has been created by this call to `create_def`. + // As a consequence, this LocalDefId is always re-created before it is needed by the incr. + // comp. engine itself. + // + // This call also writes to the value of `source_span` and `expn_that_defined` queries. + // This is fine because: + // - those queries are `eval_always` so we won't miss their result changing; + // - this write will have happened before these queries are called. + self.definitions.write().create_def(parent, data) + } + + pub fn iter_local_def_id(self) -> impl Iterator<Item = LocalDefId> + 'tcx { + // Create a dependency to the crate to be sure we re-execute this when the amount of + // definitions change. + self.ensure().hir_crate(()); + // Leak a read lock once we start iterating on definitions, to prevent adding new onces + // while iterating. If some query needs to add definitions, it should be `ensure`d above. + let definitions = self.definitions.leak(); + definitions.iter_local_def_id() + } + + pub fn def_path_table(self) -> &'tcx rustc_hir::definitions::DefPathTable { + // Create a dependency to the crate to be sure we reexcute this when the amount of + // definitions change. + self.ensure().hir_crate(()); + // Leak a read lock once we start iterating on definitions, to prevent adding new onces + // while iterating. If some query needs to add definitions, it should be `ensure`d above. + let definitions = self.definitions.leak(); + definitions.def_path_table() + } + + pub fn def_path_hash_to_def_index_map( + self, + ) -> &'tcx rustc_hir::def_path_hash_map::DefPathHashMap { + // Create a dependency to the crate to be sure we reexcute this when the amount of + // definitions change. + self.ensure().hir_crate(()); + // Leak a read lock once we start iterating on definitions, to prevent adding new onces + // while iterating. If some query needs to add definitions, it should be `ensure`d above. + let definitions = self.definitions.leak(); + definitions.def_path_hash_to_def_index_map() + } + /// Note that this is *untracked* and should only be used within the query /// system if the result is otherwise tracked through queries pub fn cstore_untracked(self) -> &'tcx CrateStoreDyn { @@ -1468,8 +1534,9 @@ impl<'tcx> TyCtxt<'tcx> { /// Note that this is *untracked* and should only be used within the query /// system if the result is otherwise tracked through queries - pub fn definitions_untracked(self) -> &'tcx hir::definitions::Definitions { - &self.definitions + #[inline] + pub fn definitions_untracked(self) -> ReadGuard<'tcx, Definitions> { + self.definitions.read() } /// Note that this is *untracked* and should only be used within the query @@ -1480,23 +1547,18 @@ impl<'tcx> TyCtxt<'tcx> { } #[inline(always)] - pub fn create_stable_hashing_context(self) -> StableHashingContext<'tcx> { - StableHashingContext::new( - self.sess, - &self.definitions, - &*self.cstore, - &self.untracked_resolutions.source_span, - ) - } - - #[inline(always)] - pub fn create_no_span_stable_hashing_context(self) -> StableHashingContext<'tcx> { - StableHashingContext::ignore_spans( + pub fn with_stable_hashing_context<R>( + self, + f: impl FnOnce(StableHashingContext<'_>) -> R, + ) -> R { + let definitions = self.definitions_untracked(); + let hcx = StableHashingContext::new( self.sess, - &self.definitions, + &*definitions, &*self.cstore, &self.untracked_resolutions.source_span, - ) + ); + f(hcx) } pub fn serialize_query_result_cache(self, encoder: FileEncoder) -> FileEncodeResult { @@ -2304,7 +2366,7 @@ impl<'tcx> TyCtxt<'tcx> { self.interners.intern_ty( st, self.sess, - &self.definitions, + &self.definitions.read(), &*self.cstore, // This is only used to create a stable hashing context. &self.untracked_resolutions.source_span, @@ -2922,6 +2984,7 @@ fn ptr_eq<T, U>(t: *const T, u: *const U) -> bool { pub fn provide(providers: &mut ty::query::Providers) { providers.resolutions = |tcx, ()| &tcx.untracked_resolutions; + providers.resolver_for_lowering = |tcx, ()| &tcx.untracked_resolver_for_lowering; providers.module_reexports = |tcx, id| tcx.resolutions(()).reexport_map.get(&id).map(|v| &v[..]); providers.crate_name = |tcx, id| { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 3a795af2121..f15108fb750 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1089,6 +1089,7 @@ impl<'tcx> InstantiatedPredicates<'tcx> { #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable, Lift)] #[derive(TypeFoldable, TypeVisitable)] pub struct OpaqueTypeKey<'tcx> { + // FIXME(oli-obk): make this a LocalDefId pub def_id: DefId, pub substs: SubstsRef<'tcx>, } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 815e39aab57..03e4319bbf1 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1570,6 +1570,19 @@ impl<'tcx> Region<'tcx> { _ => bug!("free_region_binding_scope invoked on inappropriate region: {:?}", self), } } + + /// True for free regions other than `'static`. + pub fn is_free(self) -> bool { + matches!(*self, ty::ReEarlyBound(_) | ty::ReFree(_)) + } + + /// True if `self` is a free region or static. + pub fn is_free_or_static(self) -> bool { + match *self { + ty::ReStatic => true, + _ => self.is_free(), + } + } } /// Type utilities diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 3a876df84c2..0e581d7f1f7 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -142,16 +142,16 @@ impl<'tcx> TyCtxt<'tcx> { /// Creates a hash of the type `Ty` which will be the same no matter what crate /// context it's calculated within. This is used by the `type_id` intrinsic. pub fn type_id_hash(self, ty: Ty<'tcx>) -> u64 { - let mut hasher = StableHasher::new(); - let mut hcx = self.create_stable_hashing_context(); - // We want the type_id be independent of the types free regions, so we // erase them. The erase_regions() call will also anonymize bound // regions, which is desirable too. let ty = self.erase_regions(ty); - hcx.while_hashing_spans(false, |hcx| ty.hash_stable(hcx, &mut hasher)); - hasher.finish() + self.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + hcx.while_hashing_spans(false, |hcx| ty.hash_stable(hcx, &mut hasher)); + hasher.finish() + }) } pub fn res_generics_def_id(self, res: Res) -> Option<DefId> { diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index 5470cc1262e..d21a8c4f9b9 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -2,7 +2,7 @@ use rustc_data_structures::graph::iterate::{ NodeStatus, TriColorDepthFirstSearch, TriColorVisitor, }; use rustc_hir::intravisit::FnKind; -use rustc_middle::mir::{BasicBlock, Body, Operand, TerminatorKind}; +use rustc_middle::mir::{BasicBlock, BasicBlocks, Body, Operand, TerminatorKind}; use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; use rustc_middle::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt}; use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION; @@ -30,7 +30,9 @@ pub(crate) fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { }; let mut vis = Search { tcx, body, reachable_recursive_calls: vec![], trait_substs }; - if let Some(NonRecursive) = TriColorDepthFirstSearch::new(&body).run_from_start(&mut vis) { + if let Some(NonRecursive) = + TriColorDepthFirstSearch::new(&body.basic_blocks).run_from_start(&mut vis) + { return; } if vis.reachable_recursive_calls.is_empty() { @@ -101,7 +103,7 @@ impl<'mir, 'tcx> Search<'mir, 'tcx> { } } -impl<'mir, 'tcx> TriColorVisitor<&'mir Body<'tcx>> for Search<'mir, 'tcx> { +impl<'mir, 'tcx> TriColorVisitor<BasicBlocks<'tcx>> for Search<'mir, 'tcx> { type BreakVal = NonRecursive; fn node_examined( diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 05a4d7bbf3e..5c77f3ea395 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -228,7 +228,7 @@ impl Direction for Backward { ) where A: Analysis<'tcx>, { - for pred in body.predecessors()[bb].iter().copied() { + for pred in body.basic_blocks.predecessors()[bb].iter().copied() { match body[pred].terminator().kind { // Apply terminator-specific edge effects. // @@ -316,7 +316,7 @@ where fn apply(&mut self, mut apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget)) { assert!(!self.effects_applied); - let values = &self.body.switch_sources()[&(self.bb, self.pred)]; + let values = &self.body.basic_blocks.switch_sources()[&(self.bb, self.pred)]; let targets = values.iter().map(|&value| SwitchIntTarget { value, target: self.bb }); let mut tmp = None; diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index 20e14a77c1e..180376d648a 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -101,7 +101,7 @@ where // transfer function for each block exactly once (assuming that we process blocks in RPO). // // In this case, there's no need to compute the block transfer functions ahead of time. - if !body.is_cfg_cyclic() { + if !body.basic_blocks.is_cfg_cyclic() { return Self::new(tcx, body, analysis, None); } diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index 67c16e6c084..f9fd6c9c56b 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -1,7 +1,7 @@ //! A framework that can express both [gen-kill] and generic dataflow problems. //! -//! To actually use this framework, you must implement either the `Analysis` or the -//! `GenKillAnalysis` trait. If your transfer function can be expressed with only gen/kill +//! To use this framework, implement either the [`Analysis`] or the +//! [`GenKillAnalysis`] trait. If your transfer function can be expressed with only gen/kill //! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The //! `impls` module contains several examples of gen/kill dataflow analyses. //! @@ -96,7 +96,7 @@ impl<T: Idx> BitSetExt<T> for ChunkedBitSet<T> { } } -/// Define the domain of a dataflow problem. +/// Defines the domain of a dataflow problem. /// /// This trait specifies the lattice on which this analysis operates (the domain) as well as its /// initial value at the entry point of each basic block. @@ -113,12 +113,12 @@ pub trait AnalysisDomain<'tcx> { /// suitable as part of a filename. const NAME: &'static str; - /// The initial value of the dataflow state upon entry to each basic block. + /// Returns the initial value of the dataflow state upon entry to each basic block. fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain; /// Mutates the initial value of the dataflow state upon entry to the `START_BLOCK`. /// - /// For backward analyses, initial state besides the bottom value is not yet supported. Trying + /// For backward analyses, initial state (besides the bottom value) is not yet supported. Trying /// to mutate the initial state will result in a panic. // // FIXME: For backward dataflow analyses, the initial state should be applied to every basic @@ -155,9 +155,9 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// Updates the current dataflow state with an effect that occurs immediately *before* the /// given statement. /// - /// This method is useful if the consumer of the results of this analysis needs only to observe + /// This method is useful if the consumer of the results of this analysis only needs to observe /// *part* of the effect of a statement (e.g. for two-phase borrows). As a general rule, - /// analyses should not implement this without implementing `apply_statement_effect`. + /// analyses should not implement this without also implementing `apply_statement_effect`. fn apply_before_statement_effect( &self, _state: &mut Self::Domain, @@ -184,7 +184,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> { /// /// This method is useful if the consumer of the results of this analysis needs only to observe /// *part* of the effect of a terminator (e.g. for two-phase borrows). As a general rule, - /// analyses should not implement this without implementing `apply_terminator_effect`. + /// analyses should not implement this without also implementing `apply_terminator_effect`. fn apply_before_terminator_effect( &self, _state: &mut Self::Domain, diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index eae9313b771..f6b5af90a85 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -153,7 +153,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc _: &mir::Statement<'tcx>, loc: Location, ) { - // If we move from a place then only stops needing storage *after* + // If we move from a place then it only stops needing storage *after* // that statement. self.check_for_move(trans, loc); } diff --git a/compiler/rustc_mir_transform/src/add_call_guards.rs b/compiler/rustc_mir_transform/src/add_call_guards.rs index 10d52271734..f12c8560c0e 100644 --- a/compiler/rustc_mir_transform/src/add_call_guards.rs +++ b/compiler/rustc_mir_transform/src/add_call_guards.rs @@ -39,7 +39,7 @@ impl<'tcx> MirPass<'tcx> for AddCallGuards { impl AddCallGuards { pub fn add_call_guards(&self, body: &mut Body<'_>) { let mut pred_count: IndexVec<_, _> = - body.predecessors().iter().map(|ps| ps.len()).collect(); + body.basic_blocks.predecessors().iter().map(|ps| ps.len()).collect(); pred_count[START_BLOCK] += 1; // We need a place to store the new blocks generated diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index 0f87e638d26..5d15f03491d 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -91,7 +91,8 @@ impl<'tcx> MirPass<'tcx> for AddRetag { super::add_call_guards::AllCallEdges.run_pass(tcx, body); let (span, arg_count) = (body.span, body.arg_count); - let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); + let basic_blocks = body.basic_blocks.as_mut(); + let local_decls = &body.local_decls; let needs_retag = |place: &Place<'tcx>| { // FIXME: Instead of giving up for unstable places, we should introduce // a temporary and retag on that. diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 01eda979f9e..fb5423dd157 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -22,8 +22,8 @@ use rustc_middle::ty::{ self, ConstKind, EarlyBinder, Instance, ParamEnv, Ty, TyCtxt, TypeVisitable, }; use rustc_span::{def_id::DefId, Span}; -use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; -use rustc_target::spec::abi::Abi; +use rustc_target::abi::{self, HasDataLayout, Size, TargetDataLayout}; +use rustc_target::spec::abi::Abi as CallAbi; use rustc_trait_selection::traits; use crate::MirPass; @@ -59,11 +59,8 @@ macro_rules! throw_machine_stop_str { pub struct ConstProp; impl<'tcx> MirPass<'tcx> for ConstProp { - fn is_enabled(&self, _sess: &rustc_session::Session) -> bool { - // FIXME(#70073): Unlike the other passes in "optimizations", this one emits errors, so it - // runs even when MIR optimizations are disabled. We should separate the lint out from the - // transform and move the lint as early in the pipeline as possible. - true + fn is_enabled(&self, sess: &rustc_session::Session) -> bool { + sess.mir_opt_level() >= 1 } #[instrument(skip(self, tcx), level = "debug")] @@ -198,7 +195,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> fn find_mir_or_eval_fn( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _instance: ty::Instance<'tcx>, - _abi: Abi, + _abi: CallAbi, _args: &[OpTy<'tcx>], _destination: &PlaceTy<'tcx>, _target: Option<BasicBlock>, @@ -662,6 +659,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { (Ok(_), Ok(_)) => return this.ecx.eval_rvalue_into_place(rvalue, place), }; + if !matches!(const_arg.layout.abi, abi::Abi::Scalar(..)) { + // We cannot handle Scalar Pair stuff. + return this.ecx.eval_rvalue_into_place(rvalue, place); + } + let arg_value = const_arg.to_scalar()?.to_bits(const_arg.layout.size)?; let dest = this.ecx.eval_place(place)?; @@ -794,12 +796,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { /// Returns `true` if and only if this `op` should be const-propagated into. fn should_const_prop(&mut self, op: &OpTy<'tcx>) -> bool { - let mir_opt_level = self.tcx.sess.mir_opt_level(); - - if mir_opt_level == 0 { - return false; - } - if !self.tcx.consider_optimizing(|| format!("ConstantPropagation - OpTy: {:?}", op)) { return false; } diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index 280ed17f03c..09a5cb8280f 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -24,7 +24,7 @@ use rustc_middle::ty::{ use rustc_session::lint; use rustc_span::{def_id::DefId, Span}; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; -use rustc_target::spec::abi::Abi; +use rustc_target::spec::abi::Abi as CallAbi; use rustc_trait_selection::traits; use crate::MirLint; @@ -191,7 +191,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx> fn find_mir_or_eval_fn( _ecx: &mut InterpCx<'mir, 'tcx, Self>, _instance: ty::Instance<'tcx>, - _abi: Abi, + _abi: CallAbi, _args: &[OpTy<'tcx>], _destination: &PlaceTy<'tcx>, _target: Option<BasicBlock>, diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 510f1e64ed1..759ea7cd328 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -80,7 +80,7 @@ impl CoverageGraph { IndexVec<BasicCoverageBlock, BasicCoverageBlockData>, IndexVec<BasicBlock, Option<BasicCoverageBlock>>, ) { - let num_basic_blocks = mir_body.num_nodes(); + let num_basic_blocks = mir_body.basic_blocks.len(); let mut bcbs = IndexVec::with_capacity(num_basic_blocks); let mut bb_to_bcb = IndexVec::from_elem_n(None, num_basic_blocks); @@ -95,7 +95,7 @@ impl CoverageGraph { let mut basic_blocks = Vec::new(); for (bb, data) in mir_cfg_without_unwind { if let Some(last) = basic_blocks.last() { - let predecessors = &mir_body.predecessors()[bb]; + let predecessors = &mir_body.basic_blocks.predecessors()[bb]; if predecessors.len() > 1 || !predecessors.contains(last) { // The `bb` has more than one _incoming_ edge, and should start its own // `BasicCoverageBlockData`. (Note, the `basic_blocks` vector does not yet diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 782b620e28f..88ad5b7ef14 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -15,7 +15,6 @@ use spans::{CoverageSpan, CoverageSpans}; use crate::MirPass; use rustc_data_structures::graph::WithNumNodes; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; use rustc_index::vec::IndexVec; use rustc_middle::hir; @@ -576,12 +575,6 @@ fn get_body_span<'tcx>( fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 { // FIXME(cjgillot) Stop hashing HIR manually here. - let mut hcx = tcx.create_no_span_stable_hashing_context(); - let mut stable_hasher = StableHasher::new(); let owner = hir_body.id().hir_id.owner; - let bodies = &tcx.hir_owner_nodes(owner).unwrap().bodies; - hcx.with_hir_bodies(false, owner, bodies, |hcx| { - hir_body.value.hash_stable(hcx, &mut stable_hasher) - }); - stable_hasher.finish() + tcx.hir_owner_nodes(owner).unwrap().hash_including_bodies.to_smaller_hash() } diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 82070b90325..423e78317aa 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -321,7 +321,8 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> { } fn mir_to_initial_sorted_coverage_spans(&self) -> Vec<CoverageSpan> { - let mut initial_spans = Vec::<CoverageSpan>::with_capacity(self.mir_body.num_nodes() * 2); + let mut initial_spans = + Vec::<CoverageSpan>::with_capacity(self.mir_body.basic_blocks.len() * 2); for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() { initial_spans.extend(self.bcb_to_initial_coverage_spans(bcb, bcb_data)); } diff --git a/compiler/rustc_mir_transform/src/coverage/tests.rs b/compiler/rustc_mir_transform/src/coverage/tests.rs index 213bb6608e1..6380f03528a 100644 --- a/compiler/rustc_mir_transform/src/coverage/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/tests.rs @@ -222,6 +222,7 @@ fn print_mir_graphviz(name: &str, mir_body: &Body<'_>) { bb, debug::term_type(&data.terminator().kind), mir_body + .basic_blocks .successors(bb) .map(|successor| { format!(" {:?} -> {:?};", bb, successor) }) .join("\n") diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index c96497abf8f..9163672f570 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -66,7 +66,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, borrowed: &BitS return; } - let bbs = body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate().0; + let bbs = body.basic_blocks.as_mut_preserves_cfg(); for Location { block, statement_index } in patch { bbs[block].statements[statement_index].make_nop(); } diff --git a/compiler/rustc_mir_transform/src/deaggregator.rs b/compiler/rustc_mir_transform/src/deaggregator.rs index 01f490e23bf..b93fe5879f4 100644 --- a/compiler/rustc_mir_transform/src/deaggregator.rs +++ b/compiler/rustc_mir_transform/src/deaggregator.rs @@ -11,9 +11,7 @@ impl<'tcx> MirPass<'tcx> for Deaggregator { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let (basic_blocks, local_decls, _) = - body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate(); - let local_decls = &*local_decls; + let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); for bb in basic_blocks { bb.expand_statements(|stmt| { // FIXME(eddyb) don't match twice on `stmt.kind` (post-NLL). @@ -38,7 +36,7 @@ impl<'tcx> MirPass<'tcx> for Deaggregator { Some(expand_aggregate( lhs, operands.into_iter().map(|op| { - let ty = op.ty(local_decls, tcx); + let ty = op.ty(&body.local_decls, tcx); (op, ty) }), *kind, diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index a80d2fbd644..44e3945d6fc 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -110,13 +110,13 @@ impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs { let patch = MirPatch::new(body); - let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); + let local_decls = &mut body.local_decls; let mut visitor = ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch }; for (block, BasicBlockData { statements, terminator, .. }) in - basic_blocks.iter_enumerated_mut() + body.basic_blocks.as_mut().iter_enumerated_mut() { let mut index = 0; for statement in statements { diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 12f5764152e..8f049a182ee 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -588,7 +588,7 @@ impl<'tcx> Inliner<'tcx> { ); expn_data.def_site = callee_body.span; let expn_data = - LocalExpnId::fresh(expn_data, self.tcx.create_stable_hashing_context()); + self.tcx.with_stable_hashing_context(|hcx| LocalExpnId::fresh(expn_data, hcx)); let mut integrator = Integrator { args: &args, new_locals: Local::new(caller_body.local_decls.len()).., diff --git a/compiler/rustc_mir_transform/src/instcombine.rs b/compiler/rustc_mir_transform/src/instcombine.rs index ea10ec5f25c..2f3c65869ef 100644 --- a/compiler/rustc_mir_transform/src/instcombine.rs +++ b/compiler/rustc_mir_transform/src/instcombine.rs @@ -16,9 +16,8 @@ impl<'tcx> MirPass<'tcx> for InstCombine { } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); - let ctx = InstCombineContext { tcx, local_decls }; - for block in basic_blocks.iter_mut() { + let ctx = InstCombineContext { tcx, local_decls: &body.local_decls }; + for block in body.basic_blocks.as_mut() { for statement in block.statements.iter_mut() { match statement.kind { StatementKind::Assign(box (_place, ref mut rvalue)) => { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 9776cd0ab8b..0887775aae5 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -173,7 +173,7 @@ fn mir_keys(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LocalDefId> { intravisit::walk_struct_def(self, v) } } - tcx.hir().deep_visit_all_item_likes(&mut GatherCtors { tcx, set: &mut set }); + tcx.hir().visit_all_item_likes_in_crate(&mut GatherCtors { tcx, set: &mut set }); set } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 989b94b68c1..b7ba616510c 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -11,8 +11,8 @@ pub struct LowerIntrinsics; impl<'tcx> MirPass<'tcx> for LowerIntrinsics { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); - for block in basic_blocks { + let local_decls = &body.local_decls; + for block in body.basic_blocks.as_mut() { let terminator = block.terminator.as_mut().unwrap(); if let TerminatorKind::Call { func, args, destination, target, .. } = &mut terminator.kind diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 813ab4001a7..47848cfa497 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -27,12 +27,10 @@ pub fn lower_slice_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { }; // The one successor remains unchanged, so no need to invalidate - let (basic_blocks, local_decls, _) = - body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate(); - + let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); for block in basic_blocks { // lower `<[_]>::len` calls - lower_slice_len_call(tcx, block, &*local_decls, slice_len_fn_item_def_id); + lower_slice_len_call(tcx, block, &body.local_decls, slice_len_fn_item_def_id); } } diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs index 7cd7d26328a..a0ba69c89b0 100644 --- a/compiler/rustc_mir_transform/src/match_branches.rs +++ b/compiler/rustc_mir_transform/src/match_branches.rs @@ -48,7 +48,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { let def_id = body.source.def_id(); let param_env = tcx.param_env(def_id); - let (bbs, local_decls) = body.basic_blocks_and_local_decls_mut(); + let bbs = body.basic_blocks.as_mut(); let mut should_cleanup = false; 'outer: for bb_idx in bbs.indices() { if !tcx.consider_optimizing(|| format!("MatchBranchSimplification {:?} ", def_id)) { @@ -108,7 +108,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification { // Introduce a temporary for the discriminant value. let source_info = bbs[bb_idx].terminator().source_info; - let discr_local = local_decls.push(LocalDecl::new(switch_ty, source_info.span)); + let discr_local = body.local_decls.push(LocalDecl::new(switch_ty, source_info.span)); // We already checked that first and second are different blocks, // and bb_idx has a different terminator from both of them. diff --git a/compiler/rustc_mir_transform/src/normalize_array_len.rs b/compiler/rustc_mir_transform/src/normalize_array_len.rs index 3396a446df2..c0217a10541 100644 --- a/compiler/rustc_mir_transform/src/normalize_array_len.rs +++ b/compiler/rustc_mir_transform/src/normalize_array_len.rs @@ -33,8 +33,8 @@ impl<'tcx> MirPass<'tcx> for NormalizeArrayLen { pub fn normalize_array_len_calls<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // We don't ever touch terminators, so no need to invalidate the CFG cache - let (basic_blocks, local_decls, _) = - body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate(); + let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); + let local_decls = &mut body.local_decls; // do a preliminary analysis to see if we ever have locals of type `[T;N]` or `&[T;N]` let mut interesting_locals = BitSet::new_empty(local_decls.len()); diff --git a/compiler/rustc_mir_transform/src/nrvo.rs b/compiler/rustc_mir_transform/src/nrvo.rs index d29d17399af..bb063915f55 100644 --- a/compiler/rustc_mir_transform/src/nrvo.rs +++ b/compiler/rustc_mir_transform/src/nrvo.rs @@ -133,7 +133,7 @@ fn find_local_assigned_to_return_place( return local; } - match body.predecessors()[block].as_slice() { + match body.basic_blocks.predecessors()[block].as_slice() { &[pred] => block = pred, _ => return None, } diff --git a/compiler/rustc_mir_transform/src/remove_storage_markers.rs b/compiler/rustc_mir_transform/src/remove_storage_markers.rs index 5bb4f8bb9b3..dbe082e9093 100644 --- a/compiler/rustc_mir_transform/src/remove_storage_markers.rs +++ b/compiler/rustc_mir_transform/src/remove_storage_markers.rs @@ -17,7 +17,7 @@ impl<'tcx> MirPass<'tcx> for RemoveStorageMarkers { } trace!("Running RemoveStorageMarkers on {:?}", body.source); - for data in body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate().0 { + for data in body.basic_blocks.as_mut_preserves_cfg() { data.statements.retain(|statement| match statement.kind { StatementKind::StorageLive(..) | StatementKind::StorageDead(..) diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs index 921a11a3a06..84ccf6e1f61 100644 --- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs +++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs @@ -20,11 +20,10 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops { let param_env = tcx.param_env_reveal_all_normalized(did); let mut should_simplify = false; - let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); - for block in basic_blocks { + for block in body.basic_blocks.as_mut() { let terminator = block.terminator_mut(); if let TerminatorKind::Drop { place, target, .. } = terminator.kind { - let ty = place.ty(local_decls, tcx); + let ty = place.ty(&body.local_decls, tcx); if ty.ty.needs_drop(tcx, param_env) { continue; } diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 34941c1907d..40be4f146db 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -18,9 +18,9 @@ impl<'tcx> MirPass<'tcx> for RemoveZsts { return; } let param_env = tcx.param_env(body.source.def_id()); - let (basic_blocks, local_decls, _) = - body.basic_blocks_local_decls_mut_and_var_debug_info_no_invalidate(); - for block in basic_blocks.iter_mut() { + let basic_blocks = body.basic_blocks.as_mut_preserves_cfg(); + let local_decls = &body.local_decls; + for block in basic_blocks { for statement in block.statements.iter_mut() { if let StatementKind::Assign(box (place, _)) | StatementKind::Deinit(box place) = statement.kind diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs index 33ea1c4ba2f..194c2794aac 100644 --- a/compiler/rustc_mir_transform/src/separate_const_switch.rs +++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs @@ -61,7 +61,7 @@ impl<'tcx> MirPass<'tcx> for SeparateConstSwitch { /// Returns the amount of blocks that were duplicated pub fn separate_const_switch(body: &mut Body<'_>) -> usize { let mut new_blocks: SmallVec<[(BasicBlock, BasicBlock); 6]> = SmallVec::new(); - let predecessors = body.predecessors(); + let predecessors = body.basic_blocks.predecessors(); 'block_iter: for (block_id, block) in body.basic_blocks().iter_enumerated() { if let TerminatorKind::SwitchInt { discr: Operand::Copy(switch_place) | Operand::Move(switch_place), diff --git a/compiler/rustc_mir_transform/src/simplify_try.rs b/compiler/rustc_mir_transform/src/simplify_try.rs index 6902213ddad..fca9f7eeb24 100644 --- a/compiler/rustc_mir_transform/src/simplify_try.rs +++ b/compiler/rustc_mir_transform/src/simplify_try.rs @@ -386,14 +386,17 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { trace!("running SimplifyArmIdentity on {:?}", source); let local_uses = LocalUseCounter::get_local_uses(body); - let (basic_blocks, local_decls, debug_info) = - body.basic_blocks_local_decls_mut_and_var_debug_info(); - for bb in basic_blocks { + for bb in body.basic_blocks.as_mut() { if let Some(opt_info) = - get_arm_identity_info(&bb.statements, local_decls.len(), debug_info) + get_arm_identity_info(&bb.statements, body.local_decls.len(), &body.var_debug_info) { trace!("got opt_info = {:#?}", opt_info); - if !optimization_applies(&opt_info, local_decls, &local_uses, &debug_info) { + if !optimization_applies( + &opt_info, + &body.local_decls, + &local_uses, + &body.var_debug_info, + ) { debug!("optimization skipped for {:?}", source); continue; } @@ -431,7 +434,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity { // Fix the debug info to point to the right local for dbg_index in opt_info.dbg_info_to_adjust { - let dbg_info = &mut debug_info[dbg_index]; + let dbg_info = &mut body.var_debug_info[dbg_index]; assert!( matches!(dbg_info.value, VarDebugInfoContents::Place(_)), "value was not a Place" diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index bf685aa8cad..87bc0d9762e 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -271,8 +271,7 @@ impl<'a> Parser<'a> { // MACRO_RULES ITEM self.parse_item_macro_rules(vis, has_bang)? } else if self.isnt_macro_invocation() - && (self.token.is_ident_named(Symbol::intern("import")) - || self.token.is_ident_named(Symbol::intern("using"))) + && (self.token.is_ident_named(sym::import) || self.token.is_ident_named(sym::using)) { return self.recover_import_as_use(); } else if self.isnt_macro_invocation() && vis.kind.is_pub() { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 8c123c052e5..d0723c68a77 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2428,7 +2428,7 @@ fn check_non_exported_macro_for_invalid_attrs(tcx: TyCtxt<'_>, item: &Item<'_>) fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { let check_attr_visitor = &mut CheckAttrVisitor { tcx }; - tcx.hir().deep_visit_item_likes_in_module(module_def_id, check_attr_visitor); + tcx.hir().visit_item_likes_in_module(module_def_id, check_attr_visitor); if module_def_id.is_top_level_module() { check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None); check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs()); diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 996ca66de0e..31c159c1f75 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -56,7 +56,7 @@ impl NonConstExpr { fn check_mod_const_bodies(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { let mut vis = CheckConstVisitor::new(tcx); - tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut vis); + tcx.hir().visit_item_likes_in_module(module_def_id, &mut vis); } pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs index 23ff0a91159..9deb0042ff3 100644 --- a/compiler/rustc_passes/src/hir_id_validator.rs +++ b/compiler/rustc_passes/src/hir_id_validator.rs @@ -1,9 +1,9 @@ -use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lock; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID}; use rustc_hir::intravisit; use rustc_hir::{HirId, ItemLocalId}; +use rustc_index::bit_set::GrowableBitSet; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; @@ -15,32 +15,35 @@ pub fn check_crate(tcx: TyCtxt<'_>) { crate::hir_stats::print_hir_stats(tcx); } - let errors = Lock::new(Vec::new()); - let hir_map = tcx.hir(); + #[cfg(debug_assertions)] + { + let errors = Lock::new(Vec::new()); + let hir_map = tcx.hir(); - hir_map.par_for_each_module(|module_id| { - let mut v = HirIdValidator { - hir_map, - owner: None, - hir_ids_seen: Default::default(), - errors: &errors, - }; + hir_map.par_for_each_module(|module_id| { + let mut v = HirIdValidator { + hir_map, + owner: None, + hir_ids_seen: Default::default(), + errors: &errors, + }; - tcx.hir().deep_visit_item_likes_in_module(module_id, &mut v); - }); + tcx.hir().visit_item_likes_in_module(module_id, &mut v); + }); - let errors = errors.into_inner(); + let errors = errors.into_inner(); - if !errors.is_empty() { - let message = errors.iter().fold(String::new(), |s1, s2| s1 + "\n" + s2); - tcx.sess.delay_span_bug(rustc_span::DUMMY_SP, &message); + if !errors.is_empty() { + let message = errors.iter().fold(String::new(), |s1, s2| s1 + "\n" + s2); + tcx.sess.delay_span_bug(rustc_span::DUMMY_SP, &message); + } } } struct HirIdValidator<'a, 'hir> { hir_map: Map<'hir>, owner: Option<LocalDefId>, - hir_ids_seen: FxHashSet<ItemLocalId>, + hir_ids_seen: GrowableBitSet<ItemLocalId>, errors: &'a Lock<Vec<String>>, } @@ -80,7 +83,7 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> { if max != self.hir_ids_seen.len() - 1 { // Collect the missing ItemLocalIds let missing: Vec<_> = (0..=max as u32) - .filter(|&i| !self.hir_ids_seen.contains(&ItemLocalId::from_u32(i))) + .filter(|&i| !self.hir_ids_seen.contains(ItemLocalId::from_u32(i))) .collect(); // Try to map those to something more useful @@ -106,7 +109,7 @@ impl<'a, 'hir> HirIdValidator<'a, 'hir> { missing_items, self.hir_ids_seen .iter() - .map(|&local_id| HirId { owner, local_id }) + .map(|local_id| HirId { owner, local_id }) .map(|h| format!("({:?} {})", h, self.hir_map.node_to_string(h))) .collect::<Vec<_>>() ) diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 80a263f4cb2..0070c0699a4 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -140,7 +140,7 @@ fn live_node_kind_to_string(lnk: LiveNodeKind, tcx: TyCtxt<'_>) -> String { } fn check_mod_liveness(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx)); + tcx.hir().visit_item_likes_in_module(module_def_id, &mut IrMaps::new(tcx)); } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs index 9cfef26fd03..68b2a052391 100644 --- a/compiler/rustc_passes/src/loops.rs +++ b/compiler/rustc_passes/src/loops.rs @@ -31,7 +31,7 @@ struct CheckLoopVisitor<'a, 'hir> { } fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().deep_visit_item_likes_in_module( + tcx.hir().visit_item_likes_in_module( module_def_id, &mut CheckLoopVisitor { sess: &tcx.sess, hir_map: tcx.hir(), cx: Normal }, ); diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs index 40844b84af0..20765abf392 100644 --- a/compiler/rustc_passes/src/naked_functions.rs +++ b/compiler/rustc_passes/src/naked_functions.rs @@ -14,7 +14,7 @@ use rustc_span::Span; use rustc_target::spec::abi::Abi; fn check_mod_naked_functions(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut CheckNakedFunctions { tcx }); + tcx.hir().visit_item_likes_in_module(module_def_id, &mut CheckNakedFunctions { tcx }); } pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 9eaefc8b8aa..12050dceb60 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -660,7 +660,7 @@ fn stability_index(tcx: TyCtxt<'_>, (): ()) -> Index { /// Cross-references the feature names of unstable APIs with enabled /// features and possibly prints errors. fn check_mod_unstable_api_usage(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut Checker { tcx }); + tcx.hir().visit_item_likes_in_module(module_def_id, &mut Checker { tcx }); } pub(crate) fn provide(providers: &mut Providers) { @@ -890,7 +890,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { let mut missing = MissingStabilityAnnotations { tcx, access_levels }; missing.check_missing_stability(CRATE_DEF_ID, tcx.hir().span(CRATE_HIR_ID)); tcx.hir().walk_toplevel_module(&mut missing); - tcx.hir().deep_visit_all_item_likes(&mut missing); + tcx.hir().visit_all_item_likes_in_crate(&mut missing); } let declared_lang_features = &tcx.features().declared_lang_features; diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index b01c512a3b4..4c25075327f 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -653,12 +653,11 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for ExpnId { #[cfg(debug_assertions)] { use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; - let mut hcx = decoder.tcx.create_stable_hashing_context(); - let mut hasher = StableHasher::new(); - hcx.while_hashing_spans(true, |hcx| { - expn_id.expn_data().hash_stable(hcx, &mut hasher) + let local_hash: u64 = decoder.tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + expn_id.expn_data().hash_stable(&mut hcx, &mut hasher); + hasher.finish() }); - let local_hash: u64 = hasher.finish(); debug_assert_eq!(hash.local_hash(), local_hash); } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 3ed6632ba66..333dc5aa668 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -291,11 +291,12 @@ macro_rules! define_queries { .and_then(|def_id| tcx.opt_def_kind(def_id)) }; let hash = || { - let mut hcx = tcx.create_stable_hashing_context(); - let mut hasher = StableHasher::new(); - std::mem::discriminant(&kind).hash_stable(&mut hcx, &mut hasher); - key.hash_stable(&mut hcx, &mut hasher); - hasher.finish::<u64>() + tcx.with_stable_hashing_context(|mut hcx|{ + let mut hasher = StableHasher::new(); + std::mem::discriminant(&kind).hash_stable(&mut hcx, &mut hasher); + key.hash_stable(&mut hcx, &mut hasher); + hasher.finish::<u64>() + }) }; QueryStackFrame::new(name, description, span, def_kind, hash) @@ -376,6 +377,17 @@ macro_rules! define_queries { } } + // We use this for the forever-red node. + pub fn Red() -> DepKindStruct { + DepKindStruct { + is_anon: false, + is_eval_always: false, + fingerprint_style: FingerprintStyle::Unit, + force_from_dep_node: Some(|_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node)), + try_load_from_on_disk_cache: None, + } + } + pub fn TraitSelect() -> DepKindStruct { DepKindStruct { is_anon: true, diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index bb2179a2495..c6210095b60 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -131,12 +131,11 @@ where #[inline(always)] default fn to_fingerprint(&self, tcx: Ctxt) -> Fingerprint { - let mut hcx = tcx.create_stable_hashing_context(); - let mut hasher = StableHasher::new(); - - self.hash_stable(&mut hcx, &mut hasher); - - hasher.finish() + tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + self.hash_stable(&mut hcx, &mut hasher); + hasher.finish() + }) } #[inline(always)] diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 341cf8f827b..0da42fddd4c 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -43,6 +43,7 @@ rustc_index::newtype_index! { impl DepNodeIndex { pub const INVALID: DepNodeIndex = DepNodeIndex::MAX; pub const SINGLETON_DEPENDENCYLESS_ANON_NODE: DepNodeIndex = DepNodeIndex::from_u32(0); + pub const FOREVER_RED_NODE: DepNodeIndex = DepNodeIndex::from_u32(1); } impl std::convert::From<DepNodeIndex> for QueryInvocationId { @@ -124,6 +125,8 @@ impl<K: DepKind> DepGraph<K> { record_stats, ); + let colors = DepNodeColorMap::new(prev_graph_node_count); + // Instantiate a dependy-less node only once for anonymous queries. let _green_node_index = current.intern_new_node( profiler, @@ -131,7 +134,19 @@ impl<K: DepKind> DepGraph<K> { smallvec![], Fingerprint::ZERO, ); - debug_assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE); + assert_eq!(_green_node_index, DepNodeIndex::SINGLETON_DEPENDENCYLESS_ANON_NODE); + + // Instantiate a dependy-less red node only once for anonymous queries. + let (_red_node_index, _prev_and_index) = current.intern_node( + profiler, + &prev_graph, + DepNode { kind: DepKind::RED, hash: Fingerprint::ZERO.into() }, + smallvec![], + None, + false, + ); + assert_eq!(_red_node_index, DepNodeIndex::FOREVER_RED_NODE); + assert!(matches!(_prev_and_index, None | Some((_, DepNodeColor::Red)))); DepGraph { data: Some(Lrc::new(DepGraphData { @@ -140,7 +155,7 @@ impl<K: DepKind> DepGraph<K> { current, processed_side_effects: Default::default(), previous: prev_graph, - colors: DepNodeColorMap::new(prev_graph_node_count), + colors, debug_loaded_from_disk: Default::default(), })), virtual_dep_node_index: Lrc::new(AtomicU32::new(0)), @@ -328,10 +343,8 @@ impl<K: DepKind> DepGraph<K> { let dcx = cx.dep_context(); let hashing_timer = dcx.profiler().incr_result_hashing(); - let current_fingerprint = hash_result.map(|f| { - let mut hcx = dcx.create_stable_hashing_context(); - f(&mut hcx, &result) - }); + let current_fingerprint = + hash_result.map(|f| dcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, &result))); let print_status = cfg!(debug_assertions) && dcx.sess().opts.debugging_opts.dep_tasks; @@ -886,8 +899,12 @@ impl<K: DepKind> DepGraph<K> { #[derive(Clone, Debug, Encodable, Decodable)] pub struct WorkProduct { pub cgu_name: String, - /// Saved file associated with this CGU. - pub saved_file: String, + /// Saved files associated with this CGU. In each key/value pair, the value is the path to the + /// saved file and the key is some identifier for the type of file being saved. + /// + /// By convention, file extensions are currently used as identifiers, i.e. the key "o" maps to + /// the object file's path, and "dwo" to the dwarf object file's path. + pub saved_files: FxHashMap<String, String>, } // Index type for `DepNodeData`'s edges. @@ -967,6 +984,7 @@ impl<K: DepKind> CurrentDepGraph<K> { let nanos = duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64; let mut stable_hasher = StableHasher::new(); nanos.hash(&mut stable_hasher); + let anon_id_seed = stable_hasher.finish(); #[cfg(debug_assertions)] let forbidden_edge = match env::var("RUST_FORBID_DEP_GRAPH_EDGE") { @@ -1002,7 +1020,7 @@ impl<K: DepKind> CurrentDepGraph<K> { ) }), prev_index_to_index: Lock::new(IndexVec::from_elem_n(None, prev_graph_node_count)), - anon_id_seed: stable_hasher.finish(), + anon_id_seed, #[cfg(debug_assertions)] forbidden_edge, total_read_count: AtomicU64::new(0), diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 5907ae309ca..342d95ca490 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -23,7 +23,7 @@ pub trait DepContext: Copy { type DepKind: self::DepKind; /// Create a hashing context for hashing new results. - fn create_stable_hashing_context(&self) -> StableHashingContext<'_>; + fn with_stable_hashing_context<R>(&self, f: impl FnOnce(StableHashingContext<'_>) -> R) -> R; /// Access the DepGraph. fn dep_graph(&self) -> &DepGraph<Self::DepKind>; @@ -85,8 +85,12 @@ impl FingerprintStyle { /// Describe the different families of dependency nodes. pub trait DepKind: Copy + fmt::Debug + Eq + Hash + Send + Encodable<FileEncoder> + 'static { + /// DepKind to use when incr. comp. is turned off. const NULL: Self; + /// DepKind to use to create the initial forever-red node. + const RED: Self; + /// Implementation of `std::fmt::Debug` for `DepNode`. fn debug_node(node: &DepNode<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result; diff --git a/compiler/rustc_query_system/src/ich/hcx.rs b/compiler/rustc_query_system/src/ich/hcx.rs index 62a1f776fb3..843f6f9d703 100644 --- a/compiler/rustc_query_system/src/ich/hcx.rs +++ b/compiler/rustc_query_system/src/ich/hcx.rs @@ -1,4 +1,5 @@ use crate::ich; + use rustc_ast as ast; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, HashingControls, StableHasher}; @@ -118,13 +119,13 @@ impl<'a> StableHashingContext<'a> { &mut self, hash_bodies: bool, owner: LocalDefId, - bodies: &'a SortedMap<hir::ItemLocalId, &'a hir::Body<'a>>, - f: impl FnOnce(&mut Self), + bodies: &SortedMap<hir::ItemLocalId, &hir::Body<'_>>, + f: impl FnOnce(&mut StableHashingContext<'_>), ) { - let prev = self.body_resolver; - self.body_resolver = BodyResolver::Traverse { hash_bodies, owner, bodies }; - f(self); - self.body_resolver = prev; + f(&mut StableHashingContext { + body_resolver: BodyResolver::Traverse { hash_bodies, owner, bodies }, + ..self.clone() + }); } #[inline] diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 3e4c7ad9f8f..bbcd00be943 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -542,8 +542,7 @@ fn incremental_verify_ich<CTX, K, V: Debug>( debug!("BEGIN verify_ich({:?})", dep_node); let new_hash = query.hash_result.map_or(Fingerprint::ZERO, |f| { - let mut hcx = tcx.create_stable_hashing_context(); - f(&mut hcx, result) + tcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, result)) }); let old_hash = tcx.dep_graph().prev_fingerprint_of(dep_node); debug!("END verify_ich({:?})", dep_node); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 501a81c9032..18ffe9528f5 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1514,10 +1514,16 @@ impl<'a> Resolver<'a> { && let ModuleKind::Def(DefKind::Enum, def_id, _) = parent_scope.module.kind && let Some(span) = self.opt_span(def_id) { - err.span_help( - self.session.source_map().guess_head_span(span), - "consider adding `#[derive(Default)]` to this enum", - ); + let source_map = self.session.source_map(); + let head_span = source_map.guess_head_span(span); + if let Ok(head) = source_map.span_to_snippet(head_span) { + err.span_suggestion(head_span, "consider adding a derive", format!("#[derive(Default)]\n{head}"), Applicability::MaybeIncorrect); + } else { + err.span_help( + head_span, + "consider adding `#[derive(Default)]` to this enum", + ); + } } for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { if let Ok(binding) = self.early_resolve_ident_in_lexical_scope( diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index e7717f1367c..b7da0f22942 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -726,6 +726,7 @@ impl Default for Options { prints: Vec::new(), cg: Default::default(), error_format: ErrorOutputType::default(), + diagnostic_width: None, externs: Externs(BTreeMap::new()), crate_name: None, libs: Vec::new(), @@ -1427,6 +1428,12 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> { never = never colorize output", "auto|always|never", ), + opt::opt_s( + "", + "diagnostic-width", + "Inform rustc of the width of the output so that diagnostics can be truncated to fit", + "WIDTH", + ), opt::multi_s( "", "remap-path-prefix", @@ -2202,6 +2209,10 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let error_format = parse_error_format(matches, color, json_rendered); + let diagnostic_width = matches.opt_get("diagnostic-width").unwrap_or_else(|_| { + early_error(error_format, "`--diagnostic-width` must be an positive integer"); + }); + let unparsed_crate_types = matches.opt_strs("crate-type"); let crate_types = parse_crate_types_from_list(unparsed_crate_types) .unwrap_or_else(|e| early_error(error_format, &e)); @@ -2474,6 +2485,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { prints, cg, error_format, + diagnostic_width, externs, unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()), crate_name, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index be70ea5d5e4..8f1057b793f 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -170,6 +170,7 @@ top_level_options!( test: bool [TRACKED], error_format: ErrorOutputType [UNTRACKED], + diagnostic_width: Option<usize> [UNTRACKED], /// If `Some`, enable incremental compilation, using the given /// directory to store intermediate results. @@ -1245,6 +1246,8 @@ options! { dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) \ (default: no)"), + dump_drop_tracking_cfg: Option<String> = (None, parse_opt_string, [UNTRACKED], + "dump drop-tracking control-flow graph as a `.dot` file (default: no)"), dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED], "dump MIR state to file. `val` is used to select which passes and functions to dump. For example: @@ -1269,6 +1272,8 @@ options! { computed `block` spans (one span encompassing a block's terminator and \ all statements). If `-Z instrument-coverage` is also enabled, create \ an additional `.html` file showing the computed coverage spans."), + dwarf_version: Option<u32> = (None, parse_opt_number, [TRACKED], + "version of DWARF debug information to emit (default: 2 or 4, depending on platform)"), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], "emit a section containing stack size metadata (default: no)"), fewer_names: Option<bool> = (None, parse_opt_bool, [TRACKED], @@ -1388,6 +1393,8 @@ options! { "panic strategy for out-of-memory handling"), osx_rpath_install_name: bool = (false, parse_bool, [TRACKED], "pass `-install_name @rpath/...` to the macOS linker (default: no)"), + diagnostic_width: Option<usize> = (None, parse_opt_number, [UNTRACKED], + "set the current output width for diagnostic truncation"), panic_abort_tests: bool = (false, parse_bool, [TRACKED], "support compiling tests with panic=abort (default: no)"), panic_in_drop: PanicStrategy = (PanicStrategy::Unwind, parse_panic_strategy, [TRACKED], @@ -1514,8 +1521,6 @@ options! { "show extended diagnostic help (default: no)"), temps_dir: Option<String> = (None, parse_opt_string, [UNTRACKED], "the directory the intermediate files are written to"), - terminal_width: Option<usize> = (None, parse_opt_number, [UNTRACKED], - "set the current terminal width"), // Diagnostics are considered side-effects of a query (see `QuerySideEffects`) and are saved // alongside query results and changes to translation options can affect diagnostics - so // translation options should be tracked. diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 2a5ddd4e9e4..1cccef2f64f 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -2,7 +2,7 @@ use crate::cgu_reuse_tracker::CguReuseTracker; use crate::code_stats::CodeStats; pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo}; use crate::config::{self, CrateType, OutputType, SwitchWithOptPath}; -use crate::parse::ParseSess; +use crate::parse::{add_feature_diagnostics, ParseSess}; use crate::search_paths::{PathKind, SearchPath}; use crate::{filesearch, lint}; @@ -458,6 +458,15 @@ impl Session { ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { self.parse_sess.create_err(err) } + pub fn create_feature_err<'a>( + &'a self, + err: impl SessionDiagnostic<'a>, + feature: Symbol, + ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + let mut err = self.parse_sess.create_err(err); + add_feature_diagnostics(&mut err, &self.parse_sess, feature); + err + } pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed { self.parse_sess.emit_err(err) } @@ -1162,7 +1171,7 @@ fn default_emitter( fallback_bundle, short, sopts.debugging_opts.teach, - sopts.debugging_opts.terminal_width, + sopts.diagnostic_width, macro_backtrace, ), Some(dst) => EmitterWriter::new( @@ -1173,7 +1182,7 @@ fn default_emitter( short, false, // no teach messages when writing to a buffer false, // no colors when writing to a buffer - None, // no terminal width + None, // no diagnostic width macro_backtrace, ), }; @@ -1188,7 +1197,7 @@ fn default_emitter( fallback_bundle, pretty, json_rendered, - sopts.debugging_opts.terminal_width, + sopts.diagnostic_width, macro_backtrace, ) .ui_testing(sopts.debugging_opts.ui_testing), @@ -1202,7 +1211,7 @@ fn default_emitter( fallback_bundle, pretty, json_rendered, - sopts.debugging_opts.terminal_width, + sopts.diagnostic_width, macro_backtrace, ) .ui_testing(sopts.debugging_opts.ui_testing), @@ -1489,6 +1498,12 @@ fn validate_commandline_args_with_session_available(sess: &Session) { )) } } + + if let Some(dwarf_version) = sess.opts.debugging_opts.dwarf_version { + if dwarf_version > 5 { + sess.err(&format!("requested DWARF version {} is greater than 5", dwarf_version)); + } + } } /// Holds data on the current incremental compilation session, if there is one. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 65a2a18e02f..9b6967621f1 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -786,6 +786,7 @@ symbols! { impl_lint_pass, impl_macros, impl_trait_in_bindings, + import, import_shadowing, imported_main, in_band_lifetimes, @@ -1523,6 +1524,7 @@ symbols! { use_nested_groups, used, used_with_arg, + using, usize, v1, va_arg, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 470438471cb..e3045c9321d 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -96,47 +96,48 @@ fn get_symbol_hash<'tcx>( let substs = instance.substs; debug!("get_symbol_hash(def_id={:?}, parameters={:?})", def_id, substs); - let mut hasher = StableHasher::new(); - let mut hcx = tcx.create_stable_hashing_context(); - - record_time(&tcx.sess.perf_stats.symbol_hash_time, || { - // the main symbol name is not necessarily unique; hash in the - // compiler's internal def-path, guaranteeing each symbol has a - // truly unique path - tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher); - - // Include the main item-type. Note that, in this case, the - // assertions about `needs_subst` may not hold, but this item-type - // ought to be the same for every reference anyway. - assert!(!item_type.has_erasable_regions()); - hcx.while_hashing_spans(false, |hcx| { - item_type.hash_stable(hcx, &mut hasher); - - // If this is a function, we hash the signature as well. - // This is not *strictly* needed, but it may help in some - // situations, see the `run-make/a-b-a-linker-guard` test. - if let ty::FnDef(..) = item_type.kind() { - item_type.fn_sig(tcx).hash_stable(hcx, &mut hasher); - } + tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + + record_time(&tcx.sess.perf_stats.symbol_hash_time, || { + // the main symbol name is not necessarily unique; hash in the + // compiler's internal def-path, guaranteeing each symbol has a + // truly unique path + tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher); + + // Include the main item-type. Note that, in this case, the + // assertions about `needs_subst` may not hold, but this item-type + // ought to be the same for every reference anyway. + assert!(!item_type.has_erasable_regions()); + hcx.while_hashing_spans(false, |hcx| { + item_type.hash_stable(hcx, &mut hasher); + + // If this is a function, we hash the signature as well. + // This is not *strictly* needed, but it may help in some + // situations, see the `run-make/a-b-a-linker-guard` test. + if let ty::FnDef(..) = item_type.kind() { + item_type.fn_sig(tcx).hash_stable(hcx, &mut hasher); + } - // also include any type parameters (for generic items) - substs.hash_stable(hcx, &mut hasher); + // also include any type parameters (for generic items) + substs.hash_stable(hcx, &mut hasher); - if let Some(instantiating_crate) = instantiating_crate { - tcx.def_path_hash(instantiating_crate.as_def_id()) - .stable_crate_id() - .hash_stable(hcx, &mut hasher); - } + if let Some(instantiating_crate) = instantiating_crate { + tcx.def_path_hash(instantiating_crate.as_def_id()) + .stable_crate_id() + .hash_stable(hcx, &mut hasher); + } - // We want to avoid accidental collision between different types of instances. - // Especially, `VtableShim`s and `ReifyShim`s may overlap with their original - // instances without this. - discriminant(&instance.def).hash_stable(hcx, &mut hasher); + // We want to avoid accidental collision between different types of instances. + // Especially, `VtableShim`s and `ReifyShim`s may overlap with their original + // instances without this. + discriminant(&instance.def).hash_stable(hcx, &mut hasher); + }); }); - }); - // 64 bits should be enough to avoid collisions. - hasher.finish::<u64>() + // 64 bits should be enough to avoid collisions. + hasher.finish::<u64>() + }) } // Follow C++ namespace-mangling style, see diff --git a/compiler/rustc_target/src/spec/android_base.rs b/compiler/rustc_target/src/spec/android_base.rs index c2b9d696776..dc06597db6f 100644 --- a/compiler/rustc_target/src/spec/android_base.rs +++ b/compiler/rustc_target/src/spec/android_base.rs @@ -3,7 +3,7 @@ use crate::spec::TargetOptions; pub fn opts() -> TargetOptions { let mut base = super::linux_base::opts(); base.os = "android".into(); - base.dwarf_version = Some(2); + base.default_dwarf_version = 2; base.position_independent_executables = true; base.has_thread_local = false; // This is for backward compatibility, see https://github.com/rust-lang/rust/issues/49867 diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs index e8460a509e2..713dc9a1f0e 100644 --- a/compiler/rustc_target/src/spec/apple_base.rs +++ b/compiler/rustc_target/src/spec/apple_base.rs @@ -28,7 +28,7 @@ pub fn opts(os: &'static str) -> TargetOptions { executables: true, families: cvs!["unix"], is_like_osx: true, - dwarf_version: Some(2), + default_dwarf_version: 2, frame_pointer: FramePointer::Always, has_rpath: true, dll_suffix: ".dylib".into(), diff --git a/compiler/rustc_target/src/spec/dragonfly_base.rs b/compiler/rustc_target/src/spec/dragonfly_base.rs index b59322d07f5..c1e469746cb 100644 --- a/compiler/rustc_target/src/spec/dragonfly_base.rs +++ b/compiler/rustc_target/src/spec/dragonfly_base.rs @@ -9,7 +9,7 @@ pub fn opts() -> TargetOptions { has_rpath: true, position_independent_executables: true, relro_level: RelroLevel::Full, - dwarf_version: Some(2), + default_dwarf_version: 2, ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/freebsd_base.rs b/compiler/rustc_target/src/spec/freebsd_base.rs index a7e0f9f7041..36312d26e37 100644 --- a/compiler/rustc_target/src/spec/freebsd_base.rs +++ b/compiler/rustc_target/src/spec/freebsd_base.rs @@ -10,7 +10,7 @@ pub fn opts() -> TargetOptions { position_independent_executables: true, relro_level: RelroLevel::Full, abi_return_struct_as_int: true, - dwarf_version: Some(2), + default_dwarf_version: 2, ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index a08603da040..48ccb10f214 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1275,9 +1275,9 @@ pub struct TargetOptions { pub is_like_msvc: bool, /// Whether a target toolchain is like WASM. pub is_like_wasm: bool, - /// Version of DWARF to use if not using the default. + /// Default supported version of DWARF on this platform. /// Useful because some platforms (osx, bsd) only want up to DWARF2. - pub dwarf_version: Option<u32>, + pub default_dwarf_version: u32, /// Whether the linker support GNU-like arguments such as -O. Defaults to true. pub linker_is_gnu: bool, /// The MinGW toolchain has a known issue that prevents it from correctly @@ -1539,7 +1539,7 @@ impl Default for TargetOptions { is_like_windows: false, is_like_msvc: false, is_like_wasm: false, - dwarf_version: None, + default_dwarf_version: 4, linker_is_gnu: true, allows_weak_linkage: true, has_rpath: false, @@ -1778,13 +1778,13 @@ impl Target { base.$key_name = s; } } ); - ($key_name:ident, Option<u32>) => ( { + ($key_name:ident, u32) => ( { let name = (stringify!($key_name)).replace("_", "-"); if let Some(s) = obj.remove(&name).and_then(|b| b.as_u64()) { if s < 1 || s > 5 { return Err("Not a valid DWARF version number".into()); } - base.$key_name = Some(s as u32); + base.$key_name = s as u32; } } ); ($key_name:ident, Option<u64>) => ( { @@ -2143,7 +2143,7 @@ impl Target { key!(is_like_windows, bool); key!(is_like_msvc, bool); key!(is_like_wasm, bool); - key!(dwarf_version, Option<u32>); + key!(default_dwarf_version, u32); key!(linker_is_gnu, bool); key!(allows_weak_linkage, bool); key!(has_rpath, bool); @@ -2387,7 +2387,7 @@ impl ToJson for Target { target_option_val!(is_like_windows); target_option_val!(is_like_msvc); target_option_val!(is_like_wasm); - target_option_val!(dwarf_version); + target_option_val!(default_dwarf_version); target_option_val!(linker_is_gnu); target_option_val!(allows_weak_linkage); target_option_val!(has_rpath); diff --git a/compiler/rustc_target/src/spec/netbsd_base.rs b/compiler/rustc_target/src/spec/netbsd_base.rs index 69016d77cf9..40ef04ba043 100644 --- a/compiler/rustc_target/src/spec/netbsd_base.rs +++ b/compiler/rustc_target/src/spec/netbsd_base.rs @@ -11,7 +11,7 @@ pub fn opts() -> TargetOptions { position_independent_executables: true, relro_level: RelroLevel::Full, use_ctors_section: true, - dwarf_version: Some(2), + default_dwarf_version: 2, ..Default::default() } } diff --git a/compiler/rustc_target/src/spec/openbsd_base.rs b/compiler/rustc_target/src/spec/openbsd_base.rs index bbd322bb6ce..51cecdd47ea 100644 --- a/compiler/rustc_target/src/spec/openbsd_base.rs +++ b/compiler/rustc_target/src/spec/openbsd_base.rs @@ -11,7 +11,7 @@ pub fn opts() -> TargetOptions { position_independent_executables: true, frame_pointer: FramePointer::Always, // FIXME 43575: should be MayOmit... relro_level: RelroLevel::Full, - dwarf_version: Some(2), + default_dwarf_version: 2, ..Default::default() } } diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 44ff3fd7306..282ee632ce5 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -37,5 +37,4 @@ extern crate smallvec; pub mod autoderef; pub mod infer; -pub mod opaque_types; pub mod traits; diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs deleted file mode 100644 index d290f7b074c..00000000000 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ /dev/null @@ -1,545 +0,0 @@ -use crate::traits; -use crate::traits::error_reporting::InferCtxtExt as _; -use crate::traits::TraitEngineExt as _; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::DefId; -use rustc_hir::OpaqueTyOrigin; -use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic; -use rustc_infer::infer::{InferCtxt, TyCtxtInferExt as _}; -use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine}; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts}; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, ToPredicate, Ty, TyCtxt}; -use rustc_span::Span; - -pub trait InferCtxtExt<'tcx> { - fn infer_opaque_definition_from_instantiation( - &self, - opaque_type_key: OpaqueTypeKey<'tcx>, - instantiated_ty: OpaqueHiddenType<'tcx>, - origin: OpaqueTyOrigin, - ) -> Ty<'tcx>; -} - -impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { - /// Given the fully resolved, instantiated type for an opaque - /// type, i.e., the value of an inference variable like C1 or C2 - /// (*), computes the "definition type" for an opaque type - /// definition -- that is, the inferred value of `Foo1<'x>` or - /// `Foo2<'x>` that we would conceptually use in its definition: - /// ```ignore (illustrative) - /// type Foo1<'x> = impl Bar<'x> = AAA; // <-- this type AAA - /// type Foo2<'x> = impl Bar<'x> = BBB; // <-- or this type BBB - /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } - /// ``` - /// Note that these values are defined in terms of a distinct set of - /// generic parameters (`'x` instead of `'a`) from C1 or C2. The main - /// purpose of this function is to do that translation. - /// - /// (*) C1 and C2 were introduced in the comments on - /// `register_member_constraints`. Read that comment for more context. - /// - /// # Parameters - /// - /// - `def_id`, the `impl Trait` type - /// - `substs`, the substs used to instantiate this opaque type - /// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of - /// `opaque_defn.concrete_ty` - #[instrument(level = "debug", skip(self))] - fn infer_opaque_definition_from_instantiation( - &self, - opaque_type_key: OpaqueTypeKey<'tcx>, - instantiated_ty: OpaqueHiddenType<'tcx>, - origin: OpaqueTyOrigin, - ) -> Ty<'tcx> { - if self.is_tainted_by_errors() { - return self.tcx.ty_error(); - } - - let OpaqueTypeKey { def_id, substs } = opaque_type_key; - - // Use substs to build up a reverse map from regions to their - // identity mappings. This is necessary because of `impl - // Trait` lifetimes are computed by replacing existing - // lifetimes with 'static and remapping only those used in the - // `impl Trait` return type, resulting in the parameters - // shifting. - let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id); - debug!(?id_substs); - let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> = - substs.iter().enumerate().map(|(index, subst)| (subst, id_substs[index])).collect(); - debug!("map = {:#?}", map); - - // Convert the type from the function into a type valid outside - // the function, by replacing invalid regions with 'static, - // after producing an error for each of them. - let definition_ty = instantiated_ty.ty.fold_with(&mut ReverseMapper::new( - self.tcx, - def_id, - map, - instantiated_ty.ty, - instantiated_ty.span, - )); - debug!(?definition_ty); - - if !check_opaque_type_parameter_valid( - self.tcx, - opaque_type_key, - origin, - instantiated_ty.span, - ) { - return self.tcx.ty_error(); - } - - // Only check this for TAIT. RPIT already supports `src/test/ui/impl-trait/nested-return-type2.rs` - // on stable and we'd break that. - if let OpaqueTyOrigin::TyAlias = origin { - // This logic duplicates most of `check_opaque_meets_bounds`. - // FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely. - let param_env = self.tcx.param_env(def_id); - let body_id = self.tcx.local_def_id_to_hir_id(def_id.as_local().unwrap()); - self.tcx.infer_ctxt().enter(move |infcx| { - // Require the hidden type to be well-formed with only the generics of the opaque type. - // Defining use functions may have more bounds than the opaque type, which is ok, as long as the - // hidden type is well formed even without those bounds. - let predicate = - ty::Binder::dummy(ty::PredicateKind::WellFormed(definition_ty.into())) - .to_predicate(infcx.tcx); - let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx); - - // Require that the hidden type actually fulfills all the bounds of the opaque type, even without - // the bounds that the function supplies. - match infcx.register_hidden_type( - OpaqueTypeKey { def_id, substs: id_substs }, - ObligationCause::misc(instantiated_ty.span, body_id), - param_env, - definition_ty, - origin, - ) { - Ok(infer_ok) => { - for obligation in infer_ok.obligations { - fulfillment_cx.register_predicate_obligation(&infcx, obligation); - } - } - Err(err) => { - infcx - .report_mismatched_types( - &ObligationCause::misc(instantiated_ty.span, body_id), - self.tcx.mk_opaque(def_id, id_substs), - definition_ty, - err, - ) - .emit(); - } - } - - fulfillment_cx.register_predicate_obligation( - &infcx, - Obligation::misc(instantiated_ty.span, body_id, param_env, predicate), - ); - - // Check that all obligations are satisfied by the implementation's - // version. - let errors = fulfillment_cx.select_all_or_error(&infcx); - - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - - if errors.is_empty() { - definition_ty - } else { - infcx.report_fulfillment_errors(&errors, None, false); - self.tcx.ty_error() - } - }) - } else { - definition_ty - } - } -} - -fn check_opaque_type_parameter_valid( - tcx: TyCtxt<'_>, - opaque_type_key: OpaqueTypeKey<'_>, - origin: OpaqueTyOrigin, - span: Span, -) -> bool { - match origin { - // No need to check return position impl trait (RPIT) - // because for type and const parameters they are correct - // by construction: we convert - // - // fn foo<P0..Pn>() -> impl Trait - // - // into - // - // type Foo<P0...Pn> - // fn foo<P0..Pn>() -> Foo<P0...Pn>. - // - // For lifetime parameters we convert - // - // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm> - // - // into - // - // type foo::<'p0..'pn>::Foo<'q0..'qm> - // fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>. - // - // which would error here on all of the `'static` args. - OpaqueTyOrigin::FnReturn(..) | OpaqueTyOrigin::AsyncFn(..) => return true, - // Check these - OpaqueTyOrigin::TyAlias => {} - } - let opaque_generics = tcx.generics_of(opaque_type_key.def_id); - let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default(); - for (i, arg) in opaque_type_key.substs.iter().enumerate() { - let arg_is_param = match arg.unpack() { - GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)), - GenericArgKind::Lifetime(lt) if lt.is_static() => { - tcx.sess - .struct_span_err(span, "non-defining opaque type use in defining scope") - .span_label( - tcx.def_span(opaque_generics.param_at(i, tcx).def_id), - "cannot use static lifetime; use a bound lifetime \ - instead or remove the lifetime parameter from the \ - opaque type", - ) - .emit(); - return false; - } - GenericArgKind::Lifetime(lt) => { - matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_)) - } - GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)), - }; - - if arg_is_param { - seen_params.entry(arg).or_default().push(i); - } else { - // Prevent `fn foo() -> Foo<u32>` from being defining. - let opaque_param = opaque_generics.param_at(i, tcx); - tcx.sess - .struct_span_err(span, "non-defining opaque type use in defining scope") - .span_note( - tcx.def_span(opaque_param.def_id), - &format!( - "used non-generic {} `{}` for generic parameter", - opaque_param.kind.descr(), - arg, - ), - ) - .emit(); - return false; - } - } - - for (_, indices) in seen_params { - if indices.len() > 1 { - let descr = opaque_generics.param_at(indices[0], tcx).kind.descr(); - let spans: Vec<_> = indices - .into_iter() - .map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id)) - .collect(); - tcx.sess - .struct_span_err(span, "non-defining opaque type use in defining scope") - .span_note(spans, &format!("{} used multiple times", descr)) - .emit(); - return false; - } - } - true -} - -struct ReverseMapper<'tcx> { - tcx: TyCtxt<'tcx>, - - opaque_type_def_id: DefId, - map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, - map_missing_regions_to_empty: bool, - - /// initially `Some`, set to `None` once error has been reported - hidden_ty: Option<Ty<'tcx>>, - - /// Span of function being checked. - span: Span, -} - -impl<'tcx> ReverseMapper<'tcx> { - fn new( - tcx: TyCtxt<'tcx>, - opaque_type_def_id: DefId, - map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>>, - hidden_ty: Ty<'tcx>, - span: Span, - ) -> Self { - Self { - tcx, - opaque_type_def_id, - map, - map_missing_regions_to_empty: false, - hidden_ty: Some(hidden_ty), - span, - } - } - - fn fold_kind_mapping_missing_regions_to_empty( - &mut self, - kind: GenericArg<'tcx>, - ) -> GenericArg<'tcx> { - assert!(!self.map_missing_regions_to_empty); - self.map_missing_regions_to_empty = true; - let kind = kind.fold_with(self); - self.map_missing_regions_to_empty = false; - kind - } - - fn fold_kind_normally(&mut self, kind: GenericArg<'tcx>) -> GenericArg<'tcx> { - assert!(!self.map_missing_regions_to_empty); - kind.fold_with(self) - } -} - -impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - #[instrument(skip(self), level = "debug")] - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match *r { - // Ignore bound regions and `'static` regions that appear in the - // type, we only need to remap regions that reference lifetimes - // from the function declaration. - // This would ignore `'r` in a type like `for<'r> fn(&'r u32)`. - ty::ReLateBound(..) | ty::ReStatic => return r, - - // If regions have been erased (by writeback), don't try to unerase - // them. - ty::ReErased => return r, - - // The regions that we expect from borrow checking. - ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReEmpty(ty::UniverseIndex::ROOT) => {} - - ty::ReEmpty(_) | ty::RePlaceholder(_) | ty::ReVar(_) => { - // All of the regions in the type should either have been - // erased by writeback, or mapped back to named regions by - // borrow checking. - bug!("unexpected region kind in opaque type: {:?}", r); - } - } - - let generics = self.tcx().generics_of(self.opaque_type_def_id); - match self.map.get(&r.into()).map(|k| k.unpack()) { - Some(GenericArgKind::Lifetime(r1)) => r1, - Some(u) => panic!("region mapped to unexpected kind: {:?}", u), - None if self.map_missing_regions_to_empty => self.tcx.lifetimes.re_root_empty, - None if generics.parent.is_some() => { - if let Some(hidden_ty) = self.hidden_ty.take() { - unexpected_hidden_region_diagnostic( - self.tcx, - self.tcx.def_span(self.opaque_type_def_id), - hidden_ty, - r, - ) - .emit(); - } - self.tcx.lifetimes.re_root_empty - } - None => { - self.tcx - .sess - .struct_span_err(self.span, "non-defining opaque type use in defining scope") - .span_label( - self.span, - format!( - "lifetime `{}` is part of concrete type but not used in \ - parameter list of the `impl Trait` type alias", - r - ), - ) - .emit(); - - self.tcx().lifetimes.re_static - } - } - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match *ty.kind() { - ty::Closure(def_id, substs) => { - // I am a horrible monster and I pray for death. When - // we encounter a closure here, it is always a closure - // from within the function that we are currently - // type-checking -- one that is now being encapsulated - // in an opaque type. Ideally, we would - // go through the types/lifetimes that it references - // and treat them just like we would any other type, - // which means we would error out if we find any - // reference to a type/region that is not in the - // "reverse map". - // - // **However,** in the case of closures, there is a - // somewhat subtle (read: hacky) consideration. The - // problem is that our closure types currently include - // all the lifetime parameters declared on the - // enclosing function, even if they are unused by the - // closure itself. We can't readily filter them out, - // so here we replace those values with `'empty`. This - // can't really make a difference to the rest of the - // compiler; those regions are ignored for the - // outlives relation, and hence don't affect trait - // selection or auto traits, and they are erased - // during codegen. - - let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| { - if index < generics.parent_count { - // Accommodate missing regions in the parent kinds... - self.fold_kind_mapping_missing_regions_to_empty(kind) - } else { - // ...but not elsewhere. - self.fold_kind_normally(kind) - } - })); - - self.tcx.mk_closure(def_id, substs) - } - - ty::Generator(def_id, substs, movability) => { - let generics = self.tcx.generics_of(def_id); - let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| { - if index < generics.parent_count { - // Accommodate missing regions in the parent kinds... - self.fold_kind_mapping_missing_regions_to_empty(kind) - } else { - // ...but not elsewhere. - self.fold_kind_normally(kind) - } - })); - - self.tcx.mk_generator(def_id, substs, movability) - } - - ty::Param(param) => { - // Look it up in the substitution list. - match self.map.get(&ty.into()).map(|k| k.unpack()) { - // Found it in the substitution list; replace with the parameter from the - // opaque type. - Some(GenericArgKind::Type(t1)) => t1, - Some(u) => panic!("type mapped to unexpected kind: {:?}", u), - None => { - debug!(?param, ?self.map); - self.tcx - .sess - .struct_span_err( - self.span, - &format!( - "type parameter `{}` is part of concrete type but not \ - used in parameter list for the `impl Trait` type alias", - ty - ), - ) - .emit(); - - self.tcx().ty_error() - } - } - } - - _ => ty.super_fold_with(self), - } - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - trace!("checking const {:?}", ct); - // Find a const parameter - match ct.kind() { - ty::ConstKind::Param(..) => { - // Look it up in the substitution list. - match self.map.get(&ct.into()).map(|k| k.unpack()) { - // Found it in the substitution list, replace with the parameter from the - // opaque type. - Some(GenericArgKind::Const(c1)) => c1, - Some(u) => panic!("const mapped to unexpected kind: {:?}", u), - None => { - self.tcx - .sess - .struct_span_err( - self.span, - &format!( - "const parameter `{}` is part of concrete type but not \ - used in parameter list for the `impl Trait` type alias", - ct - ), - ) - .emit(); - - self.tcx().const_error(ct.ty()) - } - } - } - - _ => ct, - } - } -} - -/// Given a set of predicates that apply to an object type, returns -/// the region bounds that the (erased) `Self` type must -/// outlive. Precisely *because* the `Self` type is erased, the -/// parameter `erased_self_ty` must be supplied to indicate what type -/// has been used to represent `Self` in the predicates -/// themselves. This should really be a unique type; `FreshTy(0)` is a -/// popular choice. -/// -/// N.B., in some cases, particularly around higher-ranked bounds, -/// this function returns a kind of conservative approximation. -/// That is, all regions returned by this function are definitely -/// required, but there may be other region bounds that are not -/// returned, as well as requirements like `for<'a> T: 'a`. -/// -/// Requires that trait definitions have been processed so that we can -/// elaborate predicates and walk supertraits. -#[instrument(skip(tcx, predicates), level = "debug")] -pub(crate) fn required_region_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - erased_self_ty: Ty<'tcx>, - predicates: impl Iterator<Item = ty::Predicate<'tcx>>, -) -> Vec<ty::Region<'tcx>> { - assert!(!erased_self_ty.has_escaping_bound_vars()); - - traits::elaborate_predicates(tcx, predicates) - .filter_map(|obligation| { - debug!(?obligation); - match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Projection(..) - | ty::PredicateKind::Trait(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::RegionOutlives(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ref t, ref r)) => { - // Search for a bound of the form `erased_self_ty - // : 'a`, but be wary of something like `for<'a> - // erased_self_ty : 'a` (we interpret a - // higher-ranked bound like that as 'static, - // though at present the code in `fulfill.rs` - // considers such bounds to be unsatisfiable, so - // it's kind of a moot point since you could never - // construct such an object, but this seems - // correct even if that code changes). - if t == &erased_self_ty && !r.has_escaping_bound_vars() { - Some(*r) - } else { - None - } - } - } - }) - .collect() -} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 8efefd476ab..aa1c9136289 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -23,7 +23,7 @@ use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; use rustc_infer::infer::error_reporting::same_type_modulo_infer; -use rustc_infer::traits::TraitEngine; +use rustc_infer::traits::{AmbiguousSelection, TraitEngine}; use rustc_middle::thir::abstract_const::NotConstEvaluatable; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::error::ExpectedFound; @@ -1404,7 +1404,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[DefId], + impls: &[AmbiguousSelection], predicate: ty::Predicate<'tcx>, ); @@ -2020,6 +2020,14 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { ); match selcx.select_from_obligation(&obligation) { Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => { + if self.is_tainted_by_errors() && subst.is_none() { + // If `subst.is_none()`, then this is probably two param-env + // candidates or impl candidates that are equal modulo lifetimes. + // Therefore, if we've already emitted an error, just skip this + // one, since it's not particularly actionable. + err.cancel(); + return; + } self.annotate_source_of_ambiguity(&mut err, &impls, predicate); } _ => { @@ -2170,24 +2178,35 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[DefId], + impls: &[AmbiguousSelection], predicate: ty::Predicate<'tcx>, ) { let mut spans = vec![]; let mut crates = vec![]; let mut post = vec![]; - for def_id in impls { - match self.tcx.span_of_impl(*def_id) { - Ok(span) => spans.push(span), - Err(name) => { - crates.push(name); - if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) { - post.push(header); + let mut or_where_clause = false; + for ambig in impls { + match ambig { + AmbiguousSelection::Impl(def_id) => match self.tcx.span_of_impl(*def_id) { + Ok(span) => spans.push(span), + Err(name) => { + crates.push(name); + if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) { + post.push(header); + } } + }, + AmbiguousSelection::ParamEnv(span) => { + or_where_clause = true; + spans.push(*span); } } } - let msg = format!("multiple `impl`s satisfying `{}` found", predicate); + let msg = format!( + "multiple `impl`s{} satisfying `{}` found", + if or_where_clause { " or `where` clauses" } else { "" }, + predicate + ); let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect(); crate_names.sort(); crate_names.dedup(); diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 31d54b5b403..33585de6001 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -777,6 +777,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { Ok( EvaluationResult::EvaluatedToOk | EvaluationResult::EvaluatedToOkModuloRegions + | EvaluationResult::EvaluatedToOkModuloOpaqueTypes | EvaluationResult::EvaluatedToAmbig, ) => {} _ => return false, 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 0f7af41cfe3..21e14eae0ee 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -6,9 +6,11 @@ //! //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly use hir::LangItem; +use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_infer::traits::TraitEngine; +use rustc_infer::traits::util::elaborate_predicates_with_span; +use rustc_infer::traits::{AmbiguousSelection, TraitEngine}; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -199,11 +201,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // and report ambiguity. if i > 1 { debug!("multiple matches, ambig"); + + // Collect a list of (probable) spans that point to a param-env candidate + let tcx = self.infcx.tcx; + let owner = stack.obligation.cause.body_id.owner.to_def_id(); + let predicates = tcx.predicates_of(owner).instantiate_identity(tcx); + let param_env_spans: FxHashMap<_, _> = elaborate_predicates_with_span( + tcx, + std::iter::zip(predicates.predicates, predicates.spans), + ) + .filter_map(|obligation| { + let kind = obligation.predicate.kind(); + if let ty::PredicateKind::Trait(trait_pred) = kind.skip_binder() { + if trait_pred.trait_ref + == ty::TraitRef::identity(tcx, trait_pred.def_id()) + .skip_binder() + { + // HACK: Remap the `Self: Trait` predicate that every trait has to a more useful span + Some(( + kind.rebind(trait_pred), + tcx.def_span(trait_pred.def_id()), + )) + } else { + Some((kind.rebind(trait_pred), obligation.cause.span)) + } + } else { + None + } + }) + .collect(); + return Err(Ambiguous( candidates .into_iter() .filter_map(|c| match c.candidate { - SelectionCandidate::ImplCandidate(def_id) => Some(def_id), + SelectionCandidate::ImplCandidate(def_id) => { + Some(AmbiguousSelection::Impl(def_id)) + } + SelectionCandidate::ParamCandidate(predicate) => { + Some(AmbiguousSelection::ParamEnv( + *param_env_spans.get(&predicate)?, + )) + } _ => None, }) .collect(), diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 596ee435622..b205ca8fa11 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -394,6 +394,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Err(_) => return Ok(EvaluatedToErr), } + if self.infcx.opaque_types_added_in_snapshot(snapshot) { + return Ok(result.max(EvaluatedToOkModuloOpaqueTypes)); + } + match self.infcx.region_constraints_added_in_snapshot(snapshot) { None => Ok(result), Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)), diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index f3ae34ce30a..94ca138b9d2 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -58,10 +58,7 @@ pub fn search_for_structural_match_violation<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option<NonStructuralMatchTy<'tcx>> { - // FIXME: we should instead pass in an `infcx` from the outside. - tcx.infer_ctxt().enter(|infcx| { - ty.visit_with(&mut Search { infcx, span, seen: FxHashSet::default() }).break_value() - }) + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default() }).break_value() } /// This method returns true if and only if `adt_ty` itself has been marked as @@ -114,27 +111,23 @@ fn type_marked_structural<'tcx>( /// This implements the traversal over the structure of a given type to try to /// find instances of ADTs (specifically structs or enums) that do not implement /// the structural-match traits (`StructuralPartialEq` and `StructuralEq`). -struct Search<'a, 'tcx> { +struct Search<'tcx> { span: Span, - infcx: InferCtxt<'a, 'tcx>, + tcx: TyCtxt<'tcx>, /// Tracks ADTs previously encountered during search, so that /// we will not recur on them again. seen: FxHashSet<hir::def_id::DefId>, } -impl<'a, 'tcx> Search<'a, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - +impl<'tcx> Search<'tcx> { fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool { - adt_ty.is_structural_eq_shallow(self.tcx()) + adt_ty.is_structural_eq_shallow(self.tcx) } } -impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { +impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { type BreakTy = NonStructuralMatchTy<'tcx>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { @@ -193,7 +186,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { return ControlFlow::CONTINUE; } ty::Array(_, n) - if { n.try_eval_usize(self.tcx(), ty::ParamEnv::reveal_all()) == Some(0) } => + if { n.try_eval_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0) } => { // rust-lang/rust#62336: ignore type of contents // for empty array. @@ -214,7 +207,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { bug!("unexpected type during structural-match checking: {:?}", ty); } ty::Error(_) => { - self.tcx().sess.delay_span_bug(self.span, "ty::Error in structural-match check"); + self.tcx.sess.delay_span_bug(self.span, "ty::Error in structural-match check"); // We still want to check other types after encountering an error, // as this may still emit relevant errors. return ControlFlow::CONTINUE; @@ -244,9 +237,9 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for Search<'a, 'tcx> { // even though we skip super_visit_with, we must recur on // fields of ADT. - let tcx = self.tcx(); + let tcx = self.tcx; adt_def.all_fields().map(|field| field.ty(tcx, substs)).try_for_each(|field_ty| { - let ty = self.tcx().normalize_erasing_regions(ty::ParamEnv::empty(), field_ty); + let ty = self.tcx.normalize_erasing_regions(ty::ParamEnv::empty(), field_ty); debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty); ty.visit_with(self) }) diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index d43b3c9091f..7b5e1498f26 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1,5 +1,4 @@ use crate::infer::InferCtxt; -use crate::opaque_types::required_region_bounds; use crate::traits; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -271,7 +270,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } fn normalize(mut self) -> Vec<traits::PredicateObligation<'tcx>> { - let cause = self.cause(traits::MiscObligation); + let cause = self.cause(traits::WellFormed(None)); let infcx = &mut self.infcx; let param_env = self.param_env; let mut obligations = Vec::with_capacity(self.out.len()); @@ -385,7 +384,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.out.extend(obligations); let tcx = self.tcx(); - let cause = self.cause(traits::MiscObligation); + let cause = self.cause(traits::WellFormed(None)); let param_env = self.param_env; let depth = self.recursion_depth; @@ -445,7 +444,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let predicate = ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(uv.shrink())) .to_predicate(self.tcx()); - let cause = self.cause(traits::MiscObligation); + let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( cause, self.recursion_depth, @@ -457,7 +456,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let resolved = self.infcx.shallow_resolve(infer); // the `InferConst` changed, meaning that we made progress. if resolved != infer { - let cause = self.cause(traits::MiscObligation); + let cause = self.cause(traits::WellFormed(None)); let resolved_constant = self.infcx.tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Infer(resolved), @@ -648,7 +647,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let defer_to_coercion = self.tcx().features().object_safe_for_dispatch; if !defer_to_coercion { - let cause = self.cause(traits::MiscObligation); + let cause = self.cause(traits::WellFormed(None)); let component_traits = data.auto_traits().chain(data.principal_def_id()); let tcx = self.tcx(); self.out.extend(component_traits.map(|did| { @@ -679,7 +678,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let ty = self.infcx.shallow_resolve(ty); if let ty::Infer(ty::TyVar(_)) = ty.kind() { // Not yet resolved, but we've made progress. - let cause = self.cause(traits::MiscObligation); + let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( cause, self.recursion_depth, @@ -810,3 +809,63 @@ pub fn object_region_bounds<'tcx>( required_region_bounds(tcx, open_ty, predicates) } + +/// Given a set of predicates that apply to an object type, returns +/// the region bounds that the (erased) `Self` type must +/// outlive. Precisely *because* the `Self` type is erased, the +/// parameter `erased_self_ty` must be supplied to indicate what type +/// has been used to represent `Self` in the predicates +/// themselves. This should really be a unique type; `FreshTy(0)` is a +/// popular choice. +/// +/// N.B., in some cases, particularly around higher-ranked bounds, +/// this function returns a kind of conservative approximation. +/// That is, all regions returned by this function are definitely +/// required, but there may be other region bounds that are not +/// returned, as well as requirements like `for<'a> T: 'a`. +/// +/// Requires that trait definitions have been processed so that we can +/// elaborate predicates and walk supertraits. +#[instrument(skip(tcx, predicates), level = "debug")] +pub(crate) fn required_region_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + erased_self_ty: Ty<'tcx>, + predicates: impl Iterator<Item = ty::Predicate<'tcx>>, +) -> Vec<ty::Region<'tcx>> { + assert!(!erased_self_ty.has_escaping_bound_vars()); + + traits::elaborate_predicates(tcx, predicates) + .filter_map(|obligation| { + debug!(?obligation); + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Projection(..) + | ty::PredicateKind::Trait(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ref t, ref r)) => { + // Search for a bound of the form `erased_self_ty + // : 'a`, but be wary of something like `for<'a> + // erased_self_ty : 'a` (we interpret a + // higher-ranked bound like that as 'static, + // though at present the code in `fulfill.rs` + // considers such bounds to be unsatisfiable, so + // it's kind of a moot point since you could never + // construct such an object, but this seems + // correct even if that code changes). + if t == &erased_self_ty && !r.has_escaping_bound_vars() { + Some(*r) + } else { + None + } + } + } + }) + .collect() +} diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 6744536338c..fd6376ef6ee 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -204,14 +204,6 @@ bitflags! { | TypeFlags::HAS_CT_INFER.bits | TypeFlags::HAS_TY_PLACEHOLDER.bits | TypeFlags::HAS_CT_PLACEHOLDER.bits - // The `evaluate_obligation` query does not return further - // obligations. If it evaluates an obligation with an opaque - // type, that opaque type may get compared to another type, - // constraining it. We would lose this information. - // FIXME: differentiate between crate-local opaque types - // and opaque types from other crates, as only opaque types - // from the local crate can possibly be a local name - | TypeFlags::HAS_TY_OPAQUE.bits // We consider 'freshened' types and constants // to depend on a particular fn. // The freshening process throws away information, diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index 2c8be88ef6c..3af73abe5ce 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -117,7 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; // we must check that return type of called functions is WF: - self.register_wf_obligation(output.into(), call_expr.span, traits::MiscObligation); + self.register_wf_obligation(output.into(), call_expr.span, traits::WellFormed(None)); output } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index f3341e72e73..cf7de1dc016 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -496,7 +496,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> { let t = <dyn AstConv<'_>>::ast_ty_to_ty(self, ast_t); - self.register_wf_obligation(t.into(), ast_t.span, traits::MiscObligation); + self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None)); t } @@ -526,7 +526,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_wf_obligation( c.into(), self.tcx.hir().span(ast_c.hir_id), - ObligationCauseCode::MiscObligation, + ObligationCauseCode::WellFormed(None), ); c } @@ -544,7 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_wf_obligation( c.into(), self.tcx.hir().span(ast_c.hir_id), - ObligationCauseCode::MiscObligation, + ObligationCauseCode::WellFormed(None), ); c } @@ -607,7 +607,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for arg in substs.iter().filter(|arg| { matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) }) { - self.register_wf_obligation(arg, expr.span, traits::MiscObligation); + self.register_wf_obligation(arg, expr.span, traits::WellFormed(None)); } } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs index c2b4c478ba3..887c791af76 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs @@ -109,7 +109,7 @@ rustc_index::newtype_index! { } /// Identifies a value whose drop state we need to track. -#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)] +#[derive(PartialEq, Eq, Hash, Clone, Copy)] enum TrackedValue { /// Represents a named variable, such as a let binding, parameter, or upvar. /// @@ -121,6 +121,21 @@ enum TrackedValue { Temporary(HirId), } +impl Debug for TrackedValue { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + ty::tls::with_opt(|opt_tcx| { + if let Some(tcx) = opt_tcx { + write!(f, "{}", tcx.hir().node_to_string(self.hir_id())) + } else { + match self { + Self::Variable(hir_id) => write!(f, "Variable({:?})", hir_id), + Self::Temporary(hir_id) => write!(f, "Temporary({:?})", hir_id), + } + } + }) + } +} + impl TrackedValue { fn hir_id(&self) -> HirId { match self { @@ -148,7 +163,7 @@ enum TrackedValueConversionError { /// Place projects are not currently supported. /// /// The reasoning around these is kind of subtle, so we choose to be more - /// conservative around these for now. There is not reason in theory we + /// conservative around these for now. There is no reason in theory we /// cannot support these, we just have not implemented it yet. PlaceProjectionsNotSupported, } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs index 365ff429243..111d534abf8 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs @@ -33,6 +33,9 @@ pub(super) fn build_control_flow_graph<'tcx>( intravisit::walk_body(&mut drop_range_visitor, body); drop_range_visitor.drop_ranges.process_deferred_edges(); + if let Some(filename) = &tcx.sess.opts.debugging_opts.dump_drop_tracking_cfg { + super::cfg_visualize::write_graph_to_file(&drop_range_visitor.drop_ranges, filename, tcx); + } (drop_range_visitor.drop_ranges, drop_range_visitor.places.borrowed_temporaries) } @@ -126,13 +129,14 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { /// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all /// expressions. This method consumes a little deeper into the expression when needed. fn consume_expr(&mut self, expr: &hir::Expr<'_>) { - debug!("consuming expr {:?}, count={:?}", expr.hir_id, self.expr_index); + debug!("consuming expr {:?}, count={:?}", expr.kind, self.expr_index); let places = self .places .consumed .get(&expr.hir_id) .map_or(vec![], |places| places.iter().cloned().collect()); for place in places { + trace!(?place, "consuming place"); for_each_consumable(self.hir, place, |value| self.record_drop(value)); } } diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs index 20aad7aedf7..c0a0bfe8e1c 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs @@ -2,6 +2,7 @@ //! flow graph when needed for debugging. use rustc_graphviz as dot; +use rustc_middle::ty::TyCtxt; use super::{DropRangesBuilder, PostOrderId}; @@ -9,22 +10,35 @@ use super::{DropRangesBuilder, PostOrderId}; /// /// It is not normally called, but is kept around to easily add debugging /// code when needed. -#[allow(dead_code)] -pub(super) fn write_graph_to_file(drop_ranges: &DropRangesBuilder, filename: &str) { - dot::render(drop_ranges, &mut std::fs::File::create(filename).unwrap()).unwrap(); +pub(super) fn write_graph_to_file( + drop_ranges: &DropRangesBuilder, + filename: &str, + tcx: TyCtxt<'_>, +) { + dot::render( + &DropRangesGraph { drop_ranges, tcx }, + &mut std::fs::File::create(filename).unwrap(), + ) + .unwrap(); } -impl<'a> dot::GraphWalk<'a> for DropRangesBuilder { +struct DropRangesGraph<'a, 'tcx> { + drop_ranges: &'a DropRangesBuilder, + tcx: TyCtxt<'tcx>, +} + +impl<'a> dot::GraphWalk<'a> for DropRangesGraph<'_, '_> { type Node = PostOrderId; type Edge = (PostOrderId, PostOrderId); fn nodes(&'a self) -> dot::Nodes<'a, Self::Node> { - self.nodes.iter_enumerated().map(|(i, _)| i).collect() + self.drop_ranges.nodes.iter_enumerated().map(|(i, _)| i).collect() } fn edges(&'a self) -> dot::Edges<'a, Self::Edge> { - self.nodes + self.drop_ranges + .nodes .iter_enumerated() .flat_map(|(i, node)| { if node.successors.len() == 0 { @@ -45,7 +59,7 @@ impl<'a> dot::GraphWalk<'a> for DropRangesBuilder { } } -impl<'a> dot::Labeller<'a> for DropRangesBuilder { +impl<'a> dot::Labeller<'a> for DropRangesGraph<'_, '_> { type Node = PostOrderId; type Edge = (PostOrderId, PostOrderId); @@ -61,15 +75,15 @@ impl<'a> dot::Labeller<'a> for DropRangesBuilder { fn node_label(&'a self, n: &Self::Node) -> dot::LabelText<'a> { dot::LabelText::LabelStr( format!( - "{:?}, local_id: {}", - n, - self.post_order_map + "{n:?}: {}", + self.drop_ranges + .post_order_map .iter() .find(|(_hir_id, &post_order_id)| post_order_id == *n) - .map_or("<unknown>".into(), |(hir_id, _)| format!( - "{}", - hir_id.local_id.index() - )) + .map_or("<unknown>".into(), |(hir_id, _)| self + .tcx + .hir() + .node_to_string(*hir_id)) ) .into(), ) diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs index b22b791f629..67cc46f21f0 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs +++ b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -75,6 +75,7 @@ impl<'tcx> ExprUseDelegate<'tcx> { if !self.places.consumed.contains_key(&consumer) { self.places.consumed.insert(consumer, <_>::default()); } + debug!(?consumer, ?target, "mark_consumed"); self.places.consumed.get_mut(&consumer).map(|places| places.insert(target)); } @@ -136,13 +137,16 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, diag_expr_id: HirId, ) { - let parent = match self.tcx.hir().find_parent_node(place_with_id.hir_id) { + let hir = self.tcx.hir(); + let parent = match hir.find_parent_node(place_with_id.hir_id) { Some(parent) => parent, None => place_with_id.hir_id, }; debug!( - "consume {:?}; diag_expr_id={:?}, using parent {:?}", - place_with_id, diag_expr_id, parent + "consume {:?}; diag_expr_id={}, using parent {}", + place_with_id, + hir.node_to_string(diag_expr_id), + hir.node_to_string(parent) ); place_with_id .try_into() diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index 4061b7cae7c..d8cdc9275f4 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -501,7 +501,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // the function type must also be well-formed (this is not // implied by the substs being well-formed because of inherent // impls and late-bound regions - see issue #28609). - self.register_wf_obligation(fty.into(), self.span, traits::MiscObligation); + self.register_wf_obligation(fty.into(), self.span, traits::WellFormed(None)); } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 4e1a105fc71..7bf167426f7 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -348,9 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let type_param = generics.type_param(param_type, self.tcx); Some(self.tcx.def_span(type_param.def_id)) } - ty::Adt(def, _) if def.did().is_local() => { - tcx.def_ident_span(def.did()).map(|span| span) - } + ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())), _ => None, }; @@ -621,12 +619,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Find all the requirements that come from a local `impl` block. let mut skip_list: FxHashSet<_> = Default::default(); let mut spanned_predicates: FxHashMap<MultiSpan, _> = Default::default(); - for (data, p, parent_p, impl_def_id, cause_span) in unsatisfied_predicates + for (data, p, parent_p, impl_def_id, cause) in unsatisfied_predicates .iter() .filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c))) .filter_map(|(p, parent, c)| match c.code() { ObligationCauseCode::ImplDerivedObligation(ref data) => { - Some((&data.derived, p, parent, data.impl_def_id, data.span)) + Some((&data.derived, p, parent, data.impl_def_id, data)) } _ => None, }) @@ -695,9 +693,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let _ = format_pred(*pred); } skip_list.insert(p); - let mut spans = if cause_span != *item_span { - let mut spans: MultiSpan = cause_span.into(); - spans.push_span_label(cause_span, unsatisfied_msg); + let mut spans = if cause.span != *item_span { + let mut spans: MultiSpan = cause.span.into(); + spans.push_span_label(cause.span, unsatisfied_msg); spans } else { ident.span.into() @@ -709,7 +707,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Unmet obligation coming from an `impl`. Some(Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), + kind: + hir::ItemKind::Impl(hir::Impl { + of_trait, self_ty, generics, .. + }), span: item_span, .. })) if !matches!( @@ -725,14 +726,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(ExpnKind::Macro(MacroKind::Derive, _)) ) => { + let sized_pred = + unsatisfied_predicates.iter().any(|(pred, _, _)| { + match pred.kind().skip_binder() { + ty::PredicateKind::Trait(pred) => { + Some(pred.def_id()) + == self.tcx.lang_items().sized_trait() + && pred.polarity == ty::ImplPolarity::Positive + } + _ => false, + } + }); + for param in generics.params { + if param.span == cause.span && sized_pred { + let (sp, sugg) = match param.colon_span { + Some(sp) => (sp.shrink_to_hi(), " ?Sized +"), + None => (param.span.shrink_to_hi(), ": ?Sized"), + }; + err.span_suggestion_verbose( + sp, + "consider relaxing the type parameter's implicit \ + `Sized` bound", + sugg, + Applicability::MachineApplicable, + ); + } + } if let Some(pred) = parent_p { // Done to add the "doesn't satisfy" `span_label`. let _ = format_pred(*pred); } skip_list.insert(p); - let mut spans = if cause_span != *item_span { - let mut spans: MultiSpan = cause_span.into(); - spans.push_span_label(cause_span, unsatisfied_msg); + let mut spans = if cause.span != *item_span { + let mut spans: MultiSpan = cause.span.into(); + spans.push_span_label(cause.span, unsatisfied_msg); spans } else { let mut spans = Vec::with_capacity(2); diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 87f85a9842f..74546ed9080 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -747,14 +747,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (migration_string, migrated_variables_concat) = migration_suggestion_for_2229(self.tcx, &need_migrations); - let local_def_id = closure_def_id.expect_local(); - let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); - let closure_span = self.tcx.hir().span(closure_hir_id); - let closure_head_span = self.tcx.sess.source_map().guess_head_span(closure_span); + let closure_hir_id = + self.tcx.hir().local_def_id_to_hir_id(closure_def_id.expect_local()); + let closure_head_span = self.tcx.def_span(closure_def_id); self.tcx.struct_span_lint_hir( lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_hir_id, - closure_head_span, + closure_head_span, |lint| { let mut diagnostics_builder = lint.build( &reasons.migration_message(), @@ -827,12 +826,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { migrated_variables_concat ); + let closure_span = self.tcx.hir().span_with_body(closure_hir_id); let mut closure_body_span = { // If the body was entirely expanded from a macro // invocation, i.e. the body is not contained inside the // closure span, then we walk up the expansion until we // find the span before the expansion. - let s = self.tcx.hir().span(body_id.hir_id); + let s = self.tcx.hir().span_with_body(body_id.hir_id); s.find_ancestor_inside(closure_span).unwrap_or(s) }; diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 84a8cc431f4..f93f567fb20 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -1180,7 +1180,7 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_fo fcx.register_bound( item_ty, tcx.require_lang_item(LangItem::Sized, None), - traits::ObligationCause::new(ty_span, fcx.body_id, traits::MiscObligation), + traits::ObligationCause::new(ty_span, fcx.body_id, traits::WellFormed(None)), ); } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index a0cbb7c2c5a..44b9c8392f8 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -59,7 +59,7 @@ struct OnlySelfBounds(bool); // Main entry point fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { - tcx.hir().deep_visit_item_likes_in_module(module_def_id, &mut CollectItemTypesVisitor { tcx }); + tcx.hir().visit_item_likes_in_module(module_def_id, &mut CollectItemTypesVisitor { tcx }); } pub fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_typeck/src/hir_wf_check.rs b/compiler/rustc_typeck/src/hir_wf_check.rs index 4392b9aada9..81108fe0a47 100644 --- a/compiler/rustc_typeck/src/hir_wf_check.rs +++ b/compiler/rustc_typeck/src/hir_wf_check.rs @@ -72,7 +72,7 @@ fn diagnostic_hir_wf_check<'tcx>( let cause = traits::ObligationCause::new( ty.span, self.hir_id, - traits::ObligationCauseCode::MiscObligation, + traits::ObligationCauseCode::WellFormed(None), ); fulfill.register_predicate_obligation( &infcx, |
